slidewalk/internal/parser/frontmatter.go
2026-07-15 20:14:07 +02:00

73 lines
2.1 KiB
Go

package parser
import (
"bytes"
"fmt"
"gopkg.in/yaml.v3"
)
// frontmatterDelim is the delimiter line that opens and closes a YAML
// frontmatter block at the top of a slide file.
var frontmatterDelim = []byte("---")
// frontmatter holds the metadata fields recognized in a slide's YAML
// frontmatter block.
type frontmatter struct {
Title string `yaml:"title"`
Class string `yaml:"class"`
Notes string `yaml:"notes"`
Skip bool `yaml:"skip"`
Incremental bool `yaml:"incremental"`
}
// splitFrontmatter separates a leading YAML frontmatter block (delimited by
// "---" lines) from the remaining Markdown body. If data has no frontmatter
// block, it returns a nil frontmatter slice and the original data as body.
func splitFrontmatter(data []byte) (fm []byte, body []byte) {
rest := bytes.TrimLeft(data, "\r\n")
if !bytes.HasPrefix(rest, frontmatterDelim) {
return nil, data
}
afterOpen := rest[len(frontmatterDelim):]
afterOpen = bytes.TrimLeft(afterOpen, " \t")
if len(afterOpen) > 0 && afterOpen[0] != '\n' && afterOpen[0] != '\r' {
// The "---" is followed by other content on the same line, so it is
// not a frontmatter delimiter.
return nil, data
}
nlIdx := bytes.IndexByte(afterOpen, '\n')
if nlIdx == -1 {
return nil, data
}
remainder := afterOpen[nlIdx+1:]
closeIdx := bytes.Index(remainder, []byte("\n---"))
if closeIdx == -1 {
return nil, data
}
fm = remainder[:closeIdx]
after := remainder[closeIdx+len("\n---"):]
after = bytes.TrimLeft(after, "\r")
if nl := bytes.IndexByte(after, '\n'); nl != -1 {
after = after[nl+1:]
} else {
after = nil
}
return fm, after
}
// parseFrontmatter extracts and parses the frontmatter block from data,
// returning the parsed metadata and the remaining Markdown body.
func parseFrontmatter(data []byte) (frontmatter, []byte, error) {
raw, body := splitFrontmatter(data)
var fm frontmatter
if raw == nil {
return fm, body, nil
}
if err := yaml.Unmarshal(raw, &fm); err != nil {
return fm, body, fmt.Errorf("parsing frontmatter: %w", err)
}
return fm, body, nil
}