package convids import ( "context" "errors" "fmt" "io" "os" "os/exec" fp "path/filepath" "regexp" "strings" "gopkg.in/yaml.v3" ) func NewData(path string) (*Data, error) { f, err := os.Open(path) if err != nil { return nil, err } ydec := yaml.NewDecoder(f) var data Data err = ydec.Decode(&data) return &data, err } func ensureExtRe(c *Config) (err error) { if c.extRe != nil { return nil } c.extRe, err = regexp.Compile("(" + strings.Join(c.Extensions, "|") + ")$") return } type ShowWalker func(show *Show, path string) error type GroupPrinter func(name string, group Shows) var ErrNoFiles = errors.New("no files processed") var ErrSkipped = errors.New("skipped") type processInput struct { file os.DirEntry source string show *Show extRe *regexp.Regexp skips []string walk ShowWalker } func processFile(input processInput) (processed bool, processError, cbError error) { file := input.file if file.IsDir() { return } if !input.extRe.MatchString(file.Name()) { return } show := input.show var found bool found, processError = show.Match(file.Name()) if processError != nil || !found { return } path := fp.Join(input.source, file.Name()) for _, ext := range input.skips { if !strings.HasPrefix(ext, ".") { ext = "." + ext } skipper := path + ext _, statErr := os.Stat(skipper) if !errors.Is(statErr, os.ErrNotExist) { return processed, ErrSkipped, cbError } } cbError = input.walk(show, path) if cbError == nil { processed = true } return } type fileMap map[string][]os.DirEntry func (fm fileMap) GetFiles(path string) []os.DirEntry { files, ok := fm[path] if ok { return files } fm.ReadPath(path) return fm[path] } func (fm fileMap) ReadPath(path string) { if _, ok := fm[path]; ok { return } fm[path], _ = os.ReadDir(path) } func WalkFiles(d *Data, stopOnError bool, gp GroupPrinter, cb ShowWalker) (err error) { err = ensureExtRe(d.Config) if err != nil { return } if cb == nil { cb = DryRun(os.Stdout) } count := 0 allFiles := fileMap{} var files []os.DirEntry if err != nil { return } for s := range d.AllShows(gp) { if len(s.Sources) == 0 { s.Sources = []string{d.Config.Source} } showstart: for _, source := range s.Sources { files = allFiles.GetFiles(source) for _, file := range files { pIn := processInput{file, source, s, d.Config.extRe, d.Config.Skip, cb} processed, processErr, cbErr := processFile(pIn) if !processed && processErr == nil && cbErr == nil { continue } if errors.Is(processErr, ErrSkipped) { continue } if processErr != nil { return processErr } if cbErr != nil && stopOnError { return cbErr } if cbErr == nil && processed { count++ } if cbErr != nil { err = cbErr } break showstart } } } if err == nil && count < 1 { fmt.Println("Found nothing") return ErrNoFiles } return } func DryRun(out io.Writer) ShowWalker { return func(s *Show, path string) error { fmt.Fprintf(out, "Saving %s to %s\n", path, s.Folder) return ErrSkipped } } func GetShow(ctx context.Context) ShowWalker { return GetShowWithIO(ctx, os.Stdin, os.Stdout, os.Stderr) } func GetShowWithIO(ctx context.Context, stdin io.Reader, stdout io.Writer, stderr io.Writer) ShowWalker { return func(s *Show, path string) error { cmd := exec.CommandContext(ctx, "get-shows", s.Folder, path) cmd.Stdin = stdin cmd.Stdout = stdout cmd.Stderr = stderr return cmd.Run() } } func PrintGroupName(out io.Writer) GroupPrinter { return func(name string, group Shows) { fmt.Fprintf(out, "Checking %s shows\n\n", name) } }