package doc import ( "fmt" "strings" ) // Front is the front matter of a file. There are few keys and all of them are // flat, so the parsing is done here: pulling in YAML for four `key: value` // lines earns nothing. // // The axis of a layer is declared here by the lang and stack keys rather than // derived from the path (META-38); the absence of both means the base layer of // the topic. type Front struct { Topic string Prefix string Lang string Stack string Extends string // Origin is the key a copy carries in a consuming repository: the name of // the topic it was assembled from. It is the whole front matter of a copy // and it appears nowhere in a suite — a file that lost it is no longer a // copy and is never overwritten again. Origin string // At is the line a key was declared on, so a finding can point at the // declaration rather than at the top of the file. At map[string]int // Unknown lists keys the model does not know. Unknown []string // End is the line of the closing delimiter. The body of the file starts // on the next one. End int // Present says whether there was any front matter at all. Present bool } // Axis reports whether the layer declares an axis. A layer without one is the // base layer. func (f Front) Axis() bool { return f.Lang != "" || f.Stack != "" } // parseFront parses the front matter out of the file's lines. It returns the // front matter and the number of the first line of the body. 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("front matter line %d is not of the form \"key: value\"", 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 case "origin": front.Origin = value default: front.Unknown = append(front.Unknown, key) } } return front, len(lines) + 1, fmt.Errorf("front matter is not closed by a --- delimiter") }