(ns asciidoc.render (:require [clojure.pprint :refer [pprint]] [asciidoc.types :as types] [clojure.spec.alpha :as s] [clojure.zip :as zip])) (defn- block-zip [root-block] (zip/zipper #(s/conform ::block %) :content (fn [block children] (assoc block :content children)) root-block)) (defn- escape-line [line] ;; TODO line) (declare render*) (defn- render-attributes [attributes] (let [sorted-attributes (->> attributes seq (sort-by first))] (doseq [[k v] sorted-attributes] (printf ":%s: %s\n" (name k) v)))) (defn- render-document-header [depth {:keys [arguments] :as block}] (println "=" (:title arguments)) (when-let [author (:author arguments)] (println author) (when-let [version (:version arguments)] (println version))) (when-let [attributes (:a arguments)] (render-attributes attributes)) (println) (run! #(render* (inc depth) %) (:content block))) (defn- render-p [block] (apply println (:content block))) (defn- render-description [depth {{:keys [described]} :arguments :as block}] (printf "%s::\n" described) (run! #(render* (inc depth) %) (:content block))) (defn- render-section [depth {:keys [arguments] :as block}] (print (apply str (repeat (inc depth) \=)) (:title arguments) "\n\n") (run! #(render* (inc depth) %) (:content block)) (println)) (defn- render* [depth block] (case (:context block) :document (do (assert (zero? depth) "Document block should only occur as root node.") (render-document-header depth block)) :section (render-section depth block) :p (render-p block) :description (render-description depth block) :<> (run! #(render* (inc depth) %) (apply concat (:content block))) (throw (ex-info "no case" {:for (:context block) :block block})))) (def my-manpage [:document {:title "sydnix(1)" :author "Madeleine Sydney Ĺšlaga" :a {:doctype "manpage" :manmanual "SYDNIX" :mansource "SYDNIX"}} [:section {:title "Name"} [:p "sydnix - Inspect and operate upon the system."]] [:section {:title "Synopsis"} [:p "*sydnix* [_COMMAND_]... [_OPTION_]... [_FILE_]..."]] [:section {:title "Options"} [:description {:described "*--flake=URI*"} "UJri is a flake"]]]) (defn conform! [spec x] (let [x' (s/conform spec x)] (if (= x' :clojure.spec.alpha/invalid) (throw (ex-info "invalid" (s/explain-data spec x))) x'))) ;; Currently unrelentingly recursive. We'll fix this if/when it bnecomes a ;; problem. Shame Clojure skimps out on general TCO. }:( (defn render "Render an AsciiDoc block to `*out*`." [block] (render* 0 (conform! :asciidoc.types/document block)))