@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 127 lines 2.9 KB
Raw
1 package templates
2
3 import (
4 "encoding/json"
5 "fmt"
6 "strings"
7 "unicode"
8
9 "github.com/BurntSushi/toml"
10 "gopkg.in/yaml.v3"
11 )
12
13 func extractFrontMatter(input string) (map[string]any, string, error) {
14 // get the bounds of the first non-empty line
15 var firstLineStart, firstLineEnd int
16 lineEmpty := true
17 for i, b := range input {
18 if b == '\n' {
19 firstLineStart = firstLineEnd
20 if firstLineStart > 0 {
21 firstLineStart++ // skip newline character
22 }
23 firstLineEnd = i
24 if !lineEmpty {
25 break
26 }
27 continue
28 }
29 lineEmpty = lineEmpty && unicode.IsSpace(b)
30 }
31 firstLine := input[firstLineStart:firstLineEnd]
32
33 // ensure residue windows carriage return byte is removed
34 firstLine = strings.TrimSpace(firstLine)
35
36 // see what kind of front matter there is, if any
37 var closingFence []string
38 var fmParser func([]byte) (map[string]any, error)
39 for _, fmType := range supportedFrontMatterTypes {
40 if firstLine == fmType.FenceOpen {
41 closingFence = fmType.FenceClose
42 fmParser = fmType.ParseFunc
43 break
44 }
45 }
46
47 if fmParser == nil {
48 // no recognized front matter; whole document is body
49 return nil, input, nil
50 }
51
52 // find end of front matter: the closing fence that comes first
53 var fmEndFence string
54 fmEndFenceStart := -1
55 for _, fence := range closingFence {
56 index := strings.Index(input[firstLineEnd:], "\n"+fence)
57 if index >= 0 && (fmEndFenceStart < 0 || index < fmEndFenceStart) {
58 fmEndFenceStart = index
59 fmEndFence = fence
60 }
61 }
62 if fmEndFenceStart < 0 {
63 return nil, "", fmt.Errorf("unterminated front matter")
64 }
65 fmEndFenceStart += firstLineEnd + 1 // add 1 to account for newline
66
67 // extract and parse front matter
68 frontMatter := input[firstLineEnd:fmEndFenceStart]
69 fm, err := fmParser([]byte(frontMatter))
70 if err != nil {
71 return nil, "", err
72 }
73
74 // the rest is the body
75 body := input[fmEndFenceStart+len(fmEndFence):]
76
77 return fm, body, nil
78 }
79
80 func yamlFrontMatter(input []byte) (map[string]any, error) {
81 m := make(map[string]any)
82 err := yaml.Unmarshal(input, &m)
83 return m, err
84 }
85
86 func tomlFrontMatter(input []byte) (map[string]any, error) {
87 m := make(map[string]any)
88 err := toml.Unmarshal(input, &m)
89 return m, err
90 }
91
92 func jsonFrontMatter(input []byte) (map[string]any, error) {
93 input = append([]byte{'{'}, input...)
94 input = append(input, '}')
95 m := make(map[string]any)
96 err := json.Unmarshal(input, &m)
97 return m, err
98 }
99
100 type parsedMarkdownDoc struct {
101 Meta map[string]any `json:"meta,omitempty"`
102 Body string `json:"body,omitempty"`
103 }
104
105 type frontMatterType struct {
106 FenceOpen string
107 FenceClose []string
108 ParseFunc func(input []byte) (map[string]any, error)
109 }
110
111 var supportedFrontMatterTypes = []frontMatterType{
112 {
113 FenceOpen: "---",
114 FenceClose: []string{"---", "..."},
115 ParseFunc: yamlFrontMatter,
116 },
117 {
118 FenceOpen: "+++",
119 FenceClose: []string{"+++"},
120 ParseFunc: tomlFrontMatter,
121 },
122 {
123 FenceOpen: "{",
124 FenceClose: []string{"}"},
125 ParseFunc: jsonFrontMatter,
126 },
127 }