// Package check runs the integrity checks of a suite. // // The split into families is taken from the language and kept in the code: the // form of a rule is checked in any file the language employs; spread only in // convention files, because those checks are about a document travelling to a // consumer. The third part of the list — rows of a table being mutually // exclusive, the scope being covered, a norm being self-sufficient — is not // here: it does not yield to parsing text and stays the reader's work. package check import ( "fmt" "sort" ) // Severity tells an error from a warning. An error is a violation named by a // rule of the suite; a warning is something worth a look. type Severity int const ( Error Severity = iota + 1 Warning ) func (s Severity) String() string { if s == Warning { return "warning" } return "error" } // Family is the family of checks a finding came from. type Family string const ( Manifest Family = "manifest" Form Family = "form" Spread Family = "spread" Links Family = "links" ) // Finding is a single finding. type Finding struct { Severity Severity Family Family // Path is the path of the file from the root of the suite; empty when the // finding is about the suite as a whole. Path string // Line is the line of the file; zero when the finding is not bound to one. Line int Msg string } // Report accumulates the findings of one run. type Report struct { findings []Finding } // Errorf records an error. func (r *Report) Errorf(f Family, path string, line int, format string, args ...any) { r.add(Error, f, path, line, format, args...) } // Warnf records a warning. func (r *Report) Warnf(f Family, path string, line int, format string, args ...any) { r.add(Warning, f, path, line, format, args...) } func (r *Report) add(s Severity, f Family, path string, line int, format string, args ...any) { r.findings = append(r.findings, Finding{ Severity: s, Family: f, Path: path, Line: line, Msg: fmt.Sprintf(format, args...), }) } // Findings hands over the findings ordered by file and line. func (r *Report) Findings() []Finding { out := make([]Finding, len(r.findings)) copy(out, r.findings) sort.SliceStable(out, func(i, j int) bool { if out[i].Path != out[j].Path { return out[i].Path < out[j].Path } return out[i].Line < out[j].Line }) return out } // Errors counts the findings of error severity. func (r *Report) Errors() int { n := 0 for _, f := range r.findings { if f.Severity == Error { n++ } } return n } // Warnings counts the warnings. func (r *Report) Warnings() int { return len(r.findings) - r.Errors() }