Commit 8e0015d7de
8e0015d7dea9ddcbb069f682018d077cd0493414
parent: 54d8129f92
Verified · cmc
cmc <hello@cleberg.net> · 2026-09-25 23:23 UTC
Parse org keywords and body
Layout: unified · split
go.mod
+4
| @@ -1,3 +1,7 @@ |
| 1 | module gitbay.org/cmc/gallery |
1 | module gitbay.org/cmc/gallery |
| 2 | |
2 | |
| 3 | go 1.27.1 |
3 | go 1.27.1 |
| |
4 | |
| |
5 | require github.com/niklasfasching/go-org v1.9.1 |
| |
6 | |
| |
7 | require golang.org/x/net v0.38.0 // indirect |
go.sum
added
+6
| @@ -0,0 +1,6 @@ |
| |
1 | github.com/niklasfasching/go-org v1.9.1 h1:/3s4uTPOF06pImGa2Yvlp24yKXZoTYM+nsIlMzfpg/0= |
| |
2 | github.com/niklasfasching/go-org v1.9.1/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48= |
| |
3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
| |
4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
| |
5 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= |
| |
6 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= |
internal/library/org.go
added
+34
| @@ -0,0 +1,34 @@ |
| |
1 | package library |
| |
2 | |
| |
3 | import ( |
| |
4 | "html/template" |
| |
5 | "strings" |
| |
6 | |
| |
7 | "github.com/niklasfasching/go-org/org" |
| |
8 | ) |
| |
9 | |
| |
10 | // parseOrg splits "#+KEY: value" lines out as lower-cased keywords and |
| |
11 | // renders the rest of the document to HTML. |
| |
12 | func parseOrg(src []byte, path string) (map[string]string, template.HTML, error) { |
| |
13 | kw := map[string]string{} |
| |
14 | var body []string |
| |
15 | for _, line := range strings.Split(string(src), "\n") { |
| |
16 | t := strings.TrimSpace(line) |
| |
17 | if rest, ok := strings.CutPrefix(t, "#+"); ok { |
| |
18 | if k, v, ok := strings.Cut(rest, ":"); ok && k != "" && !strings.ContainsAny(k, " \t") { |
| |
19 | kw[strings.ToLower(k)] = strings.TrimSpace(v) |
| |
20 | continue |
| |
21 | } |
| |
22 | } |
| |
23 | body = append(body, line) |
| |
24 | } |
| |
25 | text := strings.TrimSpace(strings.Join(body, "\n")) |
| |
26 | if text == "" { |
| |
27 | return kw, "", nil |
| |
28 | } |
| |
29 | out, err := org.New().Parse(strings.NewReader(text), path).Write(org.NewHTMLWriter()) |
| |
30 | if err != nil { |
| |
31 | return nil, "", err |
| |
32 | } |
| |
33 | return kw, template.HTML(out), nil |
| |
34 | } |
internal/library/org_test.go
added
+32
| @@ -0,0 +1,32 @@ |
| |
1 | package library |
| |
2 | |
| |
3 | import ( |
| |
4 | "strings" |
| |
5 | "testing" |
| |
6 | ) |
| |
7 | |
| |
8 | func TestParseOrg(t *testing.T) { |
| |
9 | src := "#+title: Tokyo: nights\n#+COVER: a.jpg\n\n#+begin_quote\nA /quiet/ week.\n#+end_quote\n" |
| |
10 | kw, html, err := parseOrg([]byte(src), "album.org") |
| |
11 | if err != nil { |
| |
12 | t.Fatal(err) |
| |
13 | } |
| |
14 | if len(kw) != 2 || kw["title"] != "Tokyo: nights" || kw["cover"] != "a.jpg" { |
| |
15 | t.Fatalf("keywords = %v", kw) |
| |
16 | } |
| |
17 | for _, want := range []string{"<blockquote>", "<em>quiet</em>"} { |
| |
18 | if !strings.Contains(string(html), want) { |
| |
19 | t.Errorf("html missing %q:\n%s", want, html) |
| |
20 | } |
| |
21 | } |
| |
22 | } |
| |
23 | |
| |
24 | func TestParseOrgKeywordsOnly(t *testing.T) { |
| |
25 | kw, html, err := parseOrg([]byte("#+title: x\n\n"), "album.org") |
| |
26 | if err != nil { |
| |
27 | t.Fatal(err) |
| |
28 | } |
| |
29 | if kw["title"] != "x" || html != "" { |
| |
30 | t.Fatalf("kw=%v html=%q", kw, html) |
| |
31 | } |
| |
32 | } |