// Package check прогоняет проверки целостности набора. // // Деление на семейства взято из языка и сохранено в коде: форма правила // проверяется в любом файле, который язык употребляет; распространение — только // в файлах конвенций, потому что эти проверки о том, что документ уезжает к // потребителю. Третья часть списка — взаимоисключительность строк таблицы, // покрытие области действия, самодостаточность нормы — сюда не входит: она не // даётся разбором текста и остаётся работой читателя. package check import ( "fmt" "sort" ) // Severity различает ошибку и предупреждение. Ошибка — нарушение, названное // правилом набора; предупреждение — то, что стоит посмотреть глазами. type Severity int const ( Error Severity = iota + 1 Warning ) func (s Severity) String() string { if s == Warning { return "предупреждение" } return "ошибка" } // Family — семейство проверок, из которого пришла находка. type Family string const ( Manifest Family = "манифест" Form Family = "форма" Spread Family = "распространение" Links Family = "ссылки" ) // Finding — одна находка. type Finding struct { Severity Severity Family Family // Path — путь файла от корня набора; пусто, если находка о наборе целиком. Path string // Line — строка файла; ноль, если находка не привязана к строке. Line int Msg string } // Report накапливает находки одного прогона. type Report struct { findings []Finding } // Errorf записывает ошибку. func (r *Report) Errorf(f Family, path string, line int, format string, args ...any) { r.add(Error, f, path, line, format, args...) } // Warnf записывает предупреждение. 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 отдаёт находки в порядке файла и строки. Находки о наборе целиком // идут первыми: пока манифест не сходится, остальное читать рано. 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 считает находки уровня ошибки. func (r *Report) Errors() int { n := 0 for _, f := range r.findings { if f.Severity == Error { n++ } } return n } // Warnings считает предупреждения. func (r *Report) Warnings() int { return len(r.findings) - r.Errors() }