Home

Cobblers

This is the first useful thing I've written in Racket, converting simple gemtext to html.

My aim in using gemtext is to pare down what I write so that I don't get bogged down with how things look, or think about any audience other than myself.

cobblers.rkt:

#! /usr/bin/env racket
#lang racket

(require racket/cmdline)
(require racket/file)
(require string-util)
(require gemtext)

(define (transform-url url-string)
  (regexp-replace #rx".gmi" url-string ".html"))

(define (sanitize-text text)
  (let* ((text (regexp-replace* #rx"&" text "\\&"))
         (text (regexp-replace* #rx"<" text "\\&lt;"))
         (text (regexp-replace* #rx">" text "\\&gt;")))
    text))

(define (gmi-heading->html element)
  (let ((level (gmi-heading-level element))
        (text  (gmi-heading-text  element)))
    (format "<h~a>~a</h~a>\n"
            level
            (sanitize-text text)
            level)))

(define (gmi-link->html element)
  (let ((url  (gmi-link-url element))
        (text (gmi-link-text element)))
    (format "<a href='~a'>~a</a><br/>\n"
            (transform-url url)
            (sanitize-text text))))

(define (gmi-list->html elements)
  (format "<ul>\n~a</ul>\n"
          (apply string-append
                 (for/list ((element (gmi-list-items elements)))
                   (format "<li>~a</li>\n" (sanitize-text element))))))

(define (gmi-blockquote->html element)
  (format "<blockquote>~a</blockquote>\n"
          (sanitize-text (gmi-blockquote-body element))))

(define (gmi-pre->html elements)
  (format "<pre>\n~a</pre>\n"
          (apply string-append
                 (for/list ((element (gmi-pre-body elements)))
                   (format "~a\n" (sanitize-text element))))))

(define (gmi->html gmi-elements)
  (format (string-append (file->string "./template/prelude.html")
                         "~a"
                         (file->string "./template/postlude.html"))
          (apply string-append
                 (for/list ((element gmi-elements))
                   (cond ((gmi-heading? element) (gmi-heading->html element))
                         ((gmi-link? element) (gmi-link->html element))
                         ((gmi-list? element) (gmi-list->html element))
                         ((gmi-blockquote? element) (gmi-blockquote->html element))
                         ((gmi-pre? element) (gmi-pre->html element))
                         ((string? element) (if (string=? element "")
                                                ""
                                                (format "<p>~a</p>\n" (sanitize-text element))))
                         (else ""))))))

(define (main)
  (command-line #:args (input-file-name output-file-name)
                (display-to-file (gmi->html (string->gmi (file->string input-file-name)))
                                 output-file-name
                                 #:exists 'replace)))

(main)

That does the conversion for a single file, so then I use make to automate the process of building, and publishing, the site.

Makefile:

GMI_FILES = $(shell find gemini/ -type f -name '*.gmi')
HTML_FILES = $(patsubst gemini/%.gmi, website/%.html, $(GMI_FILES))

build: $(HTML_FILES) website/style.css

all: clean build publish

website/%.html: gemini/%.gmi
	@mkdir -p "$(@D)"
	$(CURDIR)/cobblers.rkt "$<" "$@"

website/style.css: template/style.css
	@cp template/style.css website/style.css

clean:
	rm -rf website/*

publish:
	rsync -a --delete-after website/ monospod@tty.sdf.org:html/

... and it's "Cobblers", because it's both cobbled together, and a comment on what I'm sure I'll be writing.


Written by Monospod.