package api import "strings" // frontmatter is an ordered, line-preserving view of a note's YAML // frontmatter block. It deliberately avoids a full YAML round-trip: the // brain writes flat `key: value` frontmatter by hand, and a yaml.v3 // re-marshal would reorder keys and strip comments. Preserving the // original lines verbatim keeps brain_update a surgical edit — only the // keys it manages (updated_at, supersedes, supersede_reason) change. type frontmatter struct { lines []fmLine } // fmLine is one frontmatter line. For `key: value` lines, key and value // are populated; for blank lines, comments, or anything that isn't a // simple scalar pair, key is empty and raw holds the line verbatim. type fmLine struct { key string value string raw string } // parseFrontmatter splits src into its frontmatter block and body. A // frontmatter block is recognised only when the file opens with a `---` // fence and a closing `---` fence follows. Otherwise the whole input is // the body and the returned frontmatter is empty. func parseFrontmatter(src string) (frontmatter, string) { var fm frontmatter if !strings.HasPrefix(src, "---\n") { return fm, src } rest := src[len("---\n"):] end := strings.Index(rest, "\n---\n") if end < 0 { // Opening fence with no closing fence — treat as bodyless content. return fm, src } block := rest[:end] body := rest[end+len("\n---\n"):] for _, line := range strings.Split(block, "\n") { key, val, ok := strings.Cut(line, ":") key = strings.TrimSpace(key) if !ok || key == "" || strings.HasPrefix(strings.TrimSpace(line), "#") { fm.lines = append(fm.lines, fmLine{raw: line}) continue } fm.lines = append(fm.lines, fmLine{key: key, value: strings.TrimSpace(val)}) } return fm, body } // get returns the value for key, or "" if absent. func (f *frontmatter) get(key string) string { for _, l := range f.lines { if l.key == key { return l.value } } return "" } // set overrides the value for an existing key in place, or appends a new // `key: value` line when the key is absent. func (f *frontmatter) set(key, value string) { for i := range f.lines { if f.lines[i].key == key { f.lines[i].value = value return } } f.lines = append(f.lines, fmLine{key: key, value: value}) } // render serialises the frontmatter back into a `---`-fenced block. An // empty frontmatter renders to the empty string so bodies without a // header stay header-less. func (f *frontmatter) render() string { if len(f.lines) == 0 { return "" } var b strings.Builder b.WriteString("---\n") for _, l := range f.lines { if l.key == "" { b.WriteString(l.raw) } else { b.WriteString(l.key) b.WriteString(": ") b.WriteString(l.value) } b.WriteByte('\n') } b.WriteString("---\n") return b.String() }