// SPDX-License-Identifier: GPL-3.0-or-later package main import ( "bytes" "context" "fmt" "io" "strings" "krino/internal/cond" "krino/internal/engine" "krino/internal/xdg" ) func init() { commands["explain"] = cmdExplain } // cmdExplain evaluates every rule of FILE's directory against it and shows // each test's result. func cmdExplain(g *globals, args []string, stdout, stderr io.Writer) int { fs := flagSet("explain", g) if code, ok := parse(fs, args, stdout, stderr); !ok { return code } if fs.NArg() != 1 { return usageError(stderr, "usage: krino explain FILE") } minAge, setMinAge, err := minAgeOverride(g) if err != nil { return usageError(stderr, err.Error()) } e, errs := engine.Load(mainFile(g)) if len(errs) > 0 { printDiags(stderr, errs) return 2 } if setMinAge { applyMinAge(e, minAge) } e.CacheDir = cacheDir() x, err := e.Explain(context.Background(), xdg.Expand(fs.Arg(0))) if err != nil { fmt.Fprintf(stderr, "krino: %v\n", err) return 2 } fmt.Fprintf(stdout, "%s (directory %s)\n", display(xdg.Abbrev(x.File.Path)), x.Dir.Name) if x.Skip != "" { fmt.Fprintf(stdout, "krino would not look at this file: %s\n", x.Skip) } if x.Excluded != "" { fmt.Fprintf(stdout, "krino would set this file aside: %s\n", x.Excluded) } fmt.Fprintln(stdout) for _, xt := range x.Excludes { status := "no" if xt.Match { status = "MATCH" } fmt.Fprintf(stdout, "%s: %s\n", xt.Text, status) printTrace(stdout, xt.Trace) } for _, rt := range x.Rules { if rt.Stopped != "" { fmt.Fprintf(stdout, "rule %s: not evaluated, %s\n", rt.Rule.Name, rt.Stopped) continue } status := "no" if rt.Match { status = "MATCH" } fmt.Fprintf(stdout, "rule %s: %s\n", rt.Rule.Name, status) printTrace(stdout, rt.Trace) } return 0 } // printTrace writes t's Format output with every line prefixed by two // spaces. func printTrace(w io.Writer, t *cond.Trace) { var buf bytes.Buffer displayTrace(t).Format(&buf) for _, line := range strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") { fmt.Fprintf(w, " %s\n", line) } }