- комментарии, тексты ошибок, вывод CLI и сообщения тестов теперь на английском - по-русски остались только литералы словаря ru и содержимое фикстур: это данные под проверкой, а не текст инструмента - согласование числительных в итоге упростилось до английского plural
120 lines
3.6 KiB
Go
120 lines
3.6 KiB
Go
package check
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"git.vakhrushev.me/av/convy/internal/manifest"
|
|
"git.vakhrushev.me/av/convy/internal/suite"
|
|
)
|
|
|
|
// Suite runs every check of a suite and returns the report.
|
|
func Suite(s *suite.Suite) *Report {
|
|
rep := &Report{}
|
|
checkManifest(s, rep)
|
|
checkBaseLayers(s, rep)
|
|
|
|
for _, d := range s.Docs {
|
|
checkForm(s, d, rep)
|
|
checkLinks(s, d, rep)
|
|
if d.Front.Topic != "" {
|
|
checkSpread(s, d, rep)
|
|
}
|
|
}
|
|
return rep
|
|
}
|
|
|
|
// checkManifest checks the manifest itself: the shape of prefixes, live and
|
|
// retired not overlapping, the declared files being present and no undeclared
|
|
// ones lying around.
|
|
func checkManifest(s *suite.Suite, rep *Report) {
|
|
m := s.Manifest
|
|
|
|
for _, key := range m.Undecoded {
|
|
rep.Warnf(Manifest, manifest.Name, 0, "the key %s is unknown to the tool", key)
|
|
}
|
|
for _, name := range []string{m.Language.Description, m.Language.Reading} {
|
|
if name != "" && !s.Exists(name) {
|
|
rep.Errorf(Manifest, manifest.Name, 0, "the [language] section declares the document %q, and the file is missing", name)
|
|
}
|
|
}
|
|
|
|
byPath := make(map[string][]string)
|
|
for _, prefix := range m.LivePrefixes() {
|
|
if err := manifest.ValidPrefix(prefix); err != nil {
|
|
rep.Errorf(Manifest, manifest.Name, 0, "%s", err)
|
|
}
|
|
if m.PrefixRetired(prefix) {
|
|
rep.Errorf(Manifest, manifest.Name, 0,
|
|
"prefix %s is listed both live and retired: a retired one is never reissued", prefix)
|
|
}
|
|
path := m.Prefixes.Live[prefix]
|
|
byPath[path] = append(byPath[path], prefix)
|
|
}
|
|
for _, path := range sortedKeys(byPath) {
|
|
if prefixes := byPath[path]; len(prefixes) > 1 {
|
|
sort.Strings(prefixes)
|
|
rep.Errorf(Manifest, manifest.Name, 0,
|
|
"the file %q has several prefixes declared for it (%v): a prefix belongs to one file", path, prefixes)
|
|
}
|
|
}
|
|
for prefix := range m.Prefixes.Retired {
|
|
if err := manifest.ValidPrefix(prefix); err != nil {
|
|
rep.Errorf(Manifest, manifest.Name, 0, "among the retired ones: %s", err)
|
|
}
|
|
}
|
|
|
|
for _, prefix := range sortedKeys(s.Missing) {
|
|
rep.Errorf(Manifest, manifest.Name, 0,
|
|
"prefix %s is assigned to the file %q, which the suite does not hold", prefix, s.Missing[prefix])
|
|
}
|
|
for _, path := range s.Unregistered {
|
|
rep.Errorf(Manifest, path, 1,
|
|
"the file is written in the conventions language yet not declared in the suite manifest: for the suite it does not exist")
|
|
}
|
|
for _, err := range s.Broken {
|
|
rep.Errorf(Manifest, manifest.Name, 0, "%s", err)
|
|
}
|
|
|
|
for _, topic := range m.LiveTopics() {
|
|
if m.TopicRetired(topic) {
|
|
rep.Errorf(Manifest, manifest.Name, 0,
|
|
"topic %q is listed both live and retired", topic)
|
|
}
|
|
if len(s.Layers(topic)) == 0 {
|
|
rep.Errorf(Manifest, manifest.Name, 0,
|
|
"topic %q is declared live while the suite holds no layer of it: a topic lives as long as at least one layer does", topic)
|
|
}
|
|
}
|
|
}
|
|
|
|
// checkBaseLayers checks that a topic holds no more than one layer without axis
|
|
// keys. The base layer is the only one of its kind: it reaches every copy, and a
|
|
// second such layer would mean two base texts in one assembled file.
|
|
func checkBaseLayers(s *suite.Suite, rep *Report) {
|
|
for _, topic := range s.Manifest.LiveTopics() {
|
|
var base []string
|
|
for _, d := range s.Layers(topic) {
|
|
if !d.Front.Axis() {
|
|
base = append(base, d.Path)
|
|
}
|
|
}
|
|
if len(base) > 1 {
|
|
sort.Strings(base)
|
|
for _, path := range base[1:] {
|
|
rep.Errorf(Spread, path, 1,
|
|
"topic %q holds more than one layer without axis keys: the base layer is the only one of its kind, and the candidates are %v",
|
|
topic, base)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func sortedKeys[V any](m map[string]V) []string {
|
|
keys := make([]string, 0, len(m))
|
|
for k := range m {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
return keys
|
|
}
|