package doc import ( "fmt" "strings" ) // Front — шапка файла. Ключей немного и все они плоские, поэтому разбор здесь // свой: тащить YAML ради четырёх строк `ключ: значение` не за что. // // Ось слоя объявляется здесь ключами lang и stack, а не выводится из пути // (META-38); отсутствие обоих означает базовый слой темы. type Front struct { Topic string Prefix string Lang string Stack string Extends string // At — номер строки, на которой объявлен ключ; нужен, чтобы находка // показывала на объявление, а не на начало файла. At map[string]int // Unknown — ключи, которых модель не знает. Unknown []string // End — номер строки закрывающего разделителя. Тело файла начинается // со следующей. End int // Present — была ли шапка вообще. Present bool } // Axis отвечает, объявлена ли у слоя ось. Слой без ключей оси — базовый. func (f Front) Axis() bool { return f.Lang != "" || f.Stack != "" } // parseFront разбирает шапку из строк файла. Возвращает шапку и номер первой // строки тела. func parseFront(lines []string) (Front, int, error) { front := Front{At: make(map[string]int)} if len(lines) == 0 || strings.TrimSpace(lines[0]) != "---" { return front, 1, nil } front.Present = true for i := 1; i < len(lines); i++ { line := lines[i] num := i + 1 if strings.TrimSpace(line) == "---" { front.End = num return front, num + 1, nil } if strings.TrimSpace(line) == "" { continue } key, value, ok := strings.Cut(line, ":") if !ok { return front, num, fmt.Errorf("строка %d шапки не имеет вида «ключ: значение»", num) } key = strings.TrimSpace(key) value = strings.TrimSpace(value) front.At[key] = num switch key { case "topic": front.Topic = value case "prefix": front.Prefix = value case "lang": front.Lang = value case "stack": front.Stack = value case "extends": front.Extends = value default: front.Unknown = append(front.Unknown, key) } } return front, len(lines) + 1, fmt.Errorf("шапка не закрыта разделителем ---") }