commit 55d561f5c11fe91ca0aacc27755dcdabdf70f29e
parent 4fb352c507697ba4410211f4080952215fcabcaa
Author: Luke Willis <lukejw@loquat.dev>
Date: Fri, 4 Jul 2025 23:05:12 -0400
Cleanup metadata parsing and usage
Diffstat:
5 files changed, 48 insertions(+), 54 deletions(-)
diff --git a/example/pages/about.md b/example/pages/about.md
@@ -1,2 +1,2 @@
-# About Me
+`((title . "About Me"))
This is some information about me.
diff --git a/example/posts/hello-world.md b/example/posts/hello-world.md
@@ -1,3 +1,2 @@
`((title . "Hello, world!") (description . "You'll never guess what this post says"))
-# GRUG Is Finally Working!!!
This is a test post.
diff --git a/example/posts/update-situation.md b/example/posts/update-situation.md
@@ -1,3 +1,2 @@
`((title . "Update Situation") (description . "In this post, I talk about another update."))
-# The Situation
Boys, we did it. There's another situation.
diff --git a/grug/builders.scm b/grug/builders.scm
@@ -1,12 +1,9 @@
(define-module (grug builders)
- #:use-module (ice-9 popen)
- #:use-module (ice-9 textual-ports)
#:use-module (ice-9 pretty-print)
- #:use-module (ice-9 rdelim)
- #:use-module (ice-9 eval-string)
+ #:use-module (ice-9 receive)
#:use-module (htmlprag)
- #:use-module (grug readers)
#:use-module (grug utils)
+ #:use-module (grug readers)
#:export (copy-directory
simple-pages
blog))
@@ -30,14 +27,15 @@
(copy-file path output-path)))
(reverse (ls-recursive directory))))
-(define (basic-template body)
+;; metadata should be an a-list
+(define (basic-template body metadata)
`(*TOP* (*DECL* DOCTYPE html)
(html
(head
(meta (@ (charset "utf-8")))
- (title "Hello, world!"))
+ (title ,(assoc-ref metadata 'title)))
(body
- (h1 "Test site")
+ (h1 ,(assoc-ref metadata 'title))
,@body))))
;; Build the pages in the given directory and copy them to the site.
@@ -62,19 +60,15 @@
".html")))
(unless (file-exists? output-dir) (mkdir output-dir))
(format #t "\t~A -> ~A\n" path output-path)
- ;; Build html (no metadata parsing)
- (call-with-input-file path
- (lambda (port)
- (let* ((input (get-string-all port))
- (base-shtml (cmark input))
- (built-shtml (template base-shtml))
- (output (shtml->html built-shtml)))
- (call-with-output-file
- output-path
- (lambda (port)
- (display output port))))))))
+ (receive (metadata contents)
+ (load-string-with-metadata path)
+ (let* ((base-shtml (cmark contents))
+ (built-shtml (template base-shtml metadata))
+ (output (shtml->html built-shtml)))
+ (write-string-to-path output output-path)))))
(reverse (ls-recursive directory))))
+;; `posts` should be a list of a-lists containing post metadata
(define (basic-collection-template posts)
`((h1 "Posts")
,@(map (lambda (post)
@@ -83,23 +77,17 @@
(p ,(assoc-ref post 'description))))
posts)))
-
;; Build a blog using the posts in the given directory.
;;
;; Builds prefix/index.html and posts in prefix/post-prefix/.
-;; Post files should define metadata on the first in the form of an a-list.
-;;
-;; Example: `((title . "Hello, world!") (description . "Foo bar baz..."))
-;;
-;; This will be parsed and a list of all the collected metadata will be passed to
-;; collection-template, where you can process it any way you like.
-;; A single entry titled uri will be added.
-;;
+;; Posts have 'uri added to their metadata.
+;;
;; TODO: Don't assume a markdown reader
(define* (blog directory
#:key
(prefix "site")
(post-prefix "posts")
+ (metadata `((title . "Recent Posts")))
(template basic-template)
(collection-template basic-collection-template))
(display "blog\n")
@@ -123,29 +111,16 @@
output-name)))
(unless (file-exists? output-dir) (mkdir output-dir))
(format #t "\t~A -> ~A\n" path output-path)
-
- (call-with-input-file path
- (lambda (port)
- ;; Strip and parse post metadata from the first line of the file
- (define metadata (eval-string (read-line port)))
-
- ;; Build post html from the rest of the file
- (let* ((input (get-string-all port))
- (base-shtml (cmark input))
- (built-shtml (template base-shtml))
- (output (shtml->html built-shtml)))
- (call-with-output-file
- output-path
- (lambda (port)
- (display output port))))
-
- ;; Collect metadata with uri added
- (acons 'uri relative-output-path metadata)))))
+ (receive (metadata contents)
+ (load-string-with-metadata path)
+ (let* ((base-shtml (cmark contents))
+ (built-shtml (template base-shtml metadata))
+ (output (shtml->html built-shtml)))
+ (write-string-to-path output output-path))
+ (acons 'uri relative-output-path metadata))))
(reverse (ls-recursive directory)))))
;; Build index.html
- (let ((output (shtml->html (template (collection-template posts))))
+ (let ((output (shtml->html (template (collection-template posts) metadata)))
(output-path (string-append prefix "/index.html")))
(format #t "\t~A\n" output-path)
- (call-with-output-file output-path
- (lambda (port)
- (display output port))))))
+ (write-string-to-path output output-path))))
diff --git a/grug/utils.scm b/grug/utils.scm
@@ -1,8 +1,13 @@
;;; Utility functions for Grug.
(define-module (grug utils)
+ #:use-module (ice-9 eval-string)
#:use-module (ice-9 ftw)
- #:export (ls-recursive))
+ #:use-module (ice-9 rdelim)
+ #:use-module (ice-9 textual-ports)
+ #:export (ls-recursive
+ load-string-with-metadata
+ write-string-to-path))
;; Return a list of file paths by recursively searching a given directory.
;; This is basically like running `ls -aR`.
@@ -12,3 +17,19 @@
(err (lambda (path stat errno result) (error "Failed to list file" errno path)))
(noop (lambda (path stat result) result)))
(file-system-fold enter? leaf noop noop noop err '() directory)))
+
+;; Load a file, parse metadata from the top of the file and return the rest as a string.
+;; Using eval-string might be overkill. Perhaps a custom parser should be written?
+;; TODO: Allow for multi-line metadata
+(define (load-string-with-metadata path)
+ (call-with-input-file
+ path
+ (lambda (port)
+ (values (eval-string (read-line port))
+ (get-string-all port)))))
+
+;; Write a string to the file at the given path.
+(define (write-string-to-path string path)
+ (call-with-output-file path
+ (lambda (port)
+ (display string port))))