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 | 1 | module gitbay.org/cmc/gallery |
| 2 | 2 | |
| 3 | 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 | } |