package app import ( "fmt" "os" "path/filepath" "strings" "gitea.maximumdirect.net/eric/weatherreporter/internal/report" ) func plannedBatchOutputPath(outputDir string, planned plannedBatchReport) (string, error) { outputName, err := planned.Resolved.OutputName() if err != nil { return "", err } return validateOutputPath(filepath.Join(outputDir, outputName)) } func prepareBatchOutputs(outputDir string, plannedReports []plannedBatchReport) error { for index := range plannedReports { outputPath, err := plannedBatchOutputPath(outputDir, plannedReports[index]) if err != nil { return err } plannedReports[index].OutputPath = outputPath } return nil } func resolveReportOutputPath(workingDir, override, configuredDir string, resolved report.Resolved) (string, error) { outputName, err := resolved.OutputName() if err != nil { return "", err } if override != "" { return resolveOutputPath(workingDir, override, outputName) } outputDir, err := resolveOutputDir(workingDir, configuredDir) if err != nil { return "", err } return validateOutputPath(filepath.Join(outputDir, outputName)) } func resolveOutputDirWithConfigured(workingDir, override, configuredDir string) (string, error) { directory := configuredDir if override != "" { directory = override } return resolveOutputDir(workingDir, directory) } func resolveOutputDir(workingDir, override string) (string, error) { workingDir, err := validateWorkingDir(workingDir) if err != nil { return "", err } if override == "" { return workingDir, nil } if strings.TrimSpace(override) == "" { return "", fmt.Errorf("output directory is required") } directory := override if !filepath.IsAbs(directory) { directory = filepath.Join(workingDir, directory) } directory = filepath.Clean(directory) if info, err := os.Stat(directory); err == nil && !info.IsDir() { return "", fmt.Errorf("output directory %q is not a directory", directory) } else if err != nil && !os.IsNotExist(err) { return "", fmt.Errorf("inspect output directory %q: %w", directory, err) } return directory, nil } func resolveOutputPath(workingDir, override, defaultName string) (string, error) { workingDir, err := validateWorkingDir(workingDir) if err != nil { return "", err } path := override if path == "" { path = defaultName } if strings.TrimSpace(path) == "" { return "", fmt.Errorf("final output path is required") } if !filepath.IsAbs(path) { path = filepath.Join(workingDir, path) } return validateOutputPath(path) } func validateWorkingDir(workingDir string) (string, error) { if strings.TrimSpace(workingDir) == "" { return "", fmt.Errorf("working directory is required") } if !filepath.IsAbs(workingDir) { return "", fmt.Errorf("working directory %q must be absolute", workingDir) } return filepath.Clean(workingDir), nil } func validateOutputPath(path string) (string, error) { if strings.TrimSpace(path) == "" { return "", fmt.Errorf("final output path is required") } path = filepath.Clean(path) if !filepath.IsAbs(path) { return "", fmt.Errorf("final output path %q must be absolute", path) } if filepath.Dir(path) == path { return "", fmt.Errorf("final output path %q must not be a filesystem root", path) } if info, err := os.Stat(path); err == nil && info.IsDir() { return "", fmt.Errorf("final output path %q is a directory", path) } else if err != nil && !os.IsNotExist(err) { return "", fmt.Errorf("inspect final output path %q: %w", path, err) } return path, nil }