package export import ( "fmt" "rideaware/internal/user" "rideaware/internal/workout" ) type Service struct { workoutRepo *workout.Repository userRepo *user.Repository } func NewService() *Service { return &Service{ workoutRepo: workout.NewRepository(), userRepo: user.NewRepository(), } } func (s *Service) ExportFIT(workoutID, userID uint) ([]byte, string, error) { w, err := s.workoutRepo.GetWorkoutByID(workoutID, userID) if err != nil { return nil, "", fmt.Errorf("workout not found: %w", err) } u, err := s.userRepo.GetUserByID(userID) if err != nil { return nil, "", fmt.Errorf("user not found: %w", err) } ftp := 0 if u.Profile != nil { ftp = u.Profile.FTP } if ftp <= 0 { return nil, "", fmt.Errorf("FTP must be set in your profile before exporting FIT files") } data, err := EncodeFITWorkout(w, ftp) if err != nil { return nil, "", err } filename := sanitizeFilename(w.Title) + ".fit" return data, filename, nil } func (s *Service) ExportZWO(workoutID, userID uint) ([]byte, string, error) { w, err := s.workoutRepo.GetWorkoutByID(workoutID, userID) if err != nil { return nil, "", fmt.Errorf("workout not found: %w", err) } data, err := GenerateZWO(w) if err != nil { return nil, "", err } filename := sanitizeFilename(w.Title) + ".zwo" return data, filename, nil } func sanitizeFilename(name string) string { if name == "" { return "workout" } result := make([]byte, 0, len(name)) for _, c := range name { switch { case c >= 'a' && c <= 'z', c >= 'A' && c <= 'Z', c >= '0' && c <= '9': result = append(result, byte(c)) case c == ' ' || c == '-' || c == '_': result = append(result, '_') } } if len(result) == 0 { return "workout" } return string(result) }