package cli import ( "errors" "flag" "fmt" "io" "os" "path/filepath" "git.vakhrushev.me/av/convy/internal/check" "git.vakhrushev.me/av/convy/internal/manifest" "git.vakhrushev.me/av/convy/internal/suite" ) func runSuiteCheck(env Env, args []string) ExitCode { fs := flag.NewFlagSet("convy suite check", flag.ContinueOnError) fs.SetOutput(env.Err) root := fs.String("root", "", "корень набора; по умолчанию ищется вверх от текущей директории") quiet := fs.Bool("quiet", false, "печатать только находки") if err := fs.Parse(args); err != nil { return Usage } dir := *root if dir == "" { found, err := manifest.Find(env.Dir) if err != nil { if errors.Is(err, manifest.ErrNotFound) { fmt.Fprintf(env.Err, "здесь не набор конвенций: рядом и выше нет %s\n", manifest.Name) if _, err := os.Stat(filepath.Join(env.Dir, ".conventions.toml")); err == nil { fmt.Fprintln(env.Err, "это проект — проверка того, что здесь, называется \"convy check\"") } return Usage } fmt.Fprintln(env.Err, err) return Usage } dir = found } s, err := suite.Load(dir) if err != nil { fmt.Fprintln(env.Err, err) return Usage } rep := check.Suite(s) printReport(env.Out, rep, s, *quiet) if rep.Errors() > 0 { return Failed } return OK } func printReport(w io.Writer, rep *check.Report, s *suite.Suite, quiet bool) { findings := rep.Findings() current := "" for _, f := range findings { if f.Path != current { if current != "" { fmt.Fprintln(w) } fmt.Fprintf(w, "%s\n", f.Path) current = f.Path } where := "" if f.Line > 0 { where = fmt.Sprintf(":%d", f.Line) } fmt.Fprintf(w, " %s%s %s [%s]\n", label(f.Severity), where, f.Msg, f.Family) } if quiet { return } if len(findings) > 0 { fmt.Fprintln(w) } fmt.Fprintf(w, "набор: %s, %s, версия языка %d (%s)\n", plural(len(s.Docs), "файл", "файла", "файлов"), plural(len(s.Manifest.LiveTopics()), "тема", "темы", "тем"), s.Manifest.Language.Version, s.Manifest.Language.Lang) switch { case rep.Errors() > 0: fmt.Fprintf(w, "ошибок: %d, предупреждений: %d\n", rep.Errors(), rep.Warnings()) case rep.Warnings() > 0: fmt.Fprintf(w, "ошибок нет, предупреждений: %d\n", rep.Warnings()) default: fmt.Fprintln(w, "целостность набора в порядке") } } // plural согласует существительное с числом: 1 файл, 2 файла, 5 файлов. func plural(n int, one, few, many string) string { word := many switch { case n%100 >= 11 && n%100 <= 14: case n%10 == 1: word = one case n%10 >= 2 && n%10 <= 4: word = few } return fmt.Sprintf("%d %s", n, word) } func label(s check.Severity) string { if s == check.Warning { return "предупреждение" } return "ошибка" }