package step import "strings" const frontMatterRule = "---" // Meta represents optional metadata that can occur at the very start of // a document. type Meta = map[string] string // SplitMeta parses the metadata (if it exists), returning a map representing it // along with the rest of the input as a string. If there is no metadata, an // empty map will be returned. func SplitMeta (input string) (Meta, string, error) { // i hate crlf!!!!! uwehhh!!! TODO remove input = strings.ReplaceAll(input, "\r\n", "\n") // TODO call internal function that takes in an io.Reader and scans it // by line instead of operating directly on the string. have that call // yet another function which will solve #11 // stop if there is no front matter if !strings.HasPrefix(input, frontMatterRule + "\n") { return Meta { }, input, nil } // get the start and the end of the front matter input = strings.TrimPrefix(input, frontMatterRule) index := strings.Index(input, "\n" + frontMatterRule + "\n") if index < 0 { return nil, "", ErrMetaNeverClosed } frontMatterRaw := input[:index] body := input[index + len(frontMatterRule) + 2:] // parse meta meta, err := ParseMeta(frontMatterRaw) if err != nil { return Meta { }, "", err } return meta, body, nil } // ParseMeta parses isolated metadata (without the horizontal starting and // ending rules). func ParseMeta (input string) (Meta, error) { meta := make(Meta) for _, line := range strings.Split(input, "\n") { line = strings.TrimSpace(line) if line == "" { continue } key, value, ok := strings.Cut(line, ":") if !ok { return nil, ErrMetaMalformed } key = strings.ToLower(strings.TrimSpace(key)) value = strings.TrimSpace(value) if key == "" { return nil, ErrMetaMalformed } if _, exists := meta[key]; exists { return nil, ErrMetaDuplicateKey } meta[key] = value } return meta, nil }