parse-options.fnl (4253B)
1 (local yaml (require :lyaml)) 2 3 (local { : view } (require :fennel)) 4 5 (fn basename [str ext] 6 (-> str 7 (string.gsub "(.*/)(.*)" "%2") 8 (string.gsub (.. ext "$") ""))) 9 10 (fn headline [name] 11 (let [title (assert (basename name ".nix")) 12 len (title:len)] 13 (.. title "\n" (string.rep "=" len)))) 14 15 (fn read-preamble [pathname] 16 (let [pathname (if (string.match pathname ".nix$") 17 pathname 18 (.. pathname "/default.nix"))] 19 (with-open [f (assert (io.open pathname :r))] 20 (accumulate [lines nil 21 l (f:lines) 22 :until (not (= (string.sub l 1 2) "##"))] 23 (.. (or lines "") (string.gsub l "^## *" "") "\n"))))) 24 25 (fn relative-pathname [pathname] 26 (let [pathname 27 (if (string.match pathname ".nix$") 28 pathname 29 (.. pathname "/default.nix"))] 30 (pick-values 1 (string.gsub pathname "^/nix/store/[^/]+/" "modules/")))) 31 32 (fn strip-newlines [text] 33 (-> text 34 (string.gsub "\n([^\n])" " %1") 35 (string.gsub "\n\n+" "\n"))) 36 37 (fn indent [n text] 38 (let [margin (string.rep " " n)] 39 (.. margin (string.gsub (or text "") "\n +" (.. "\n" margin ))))) 40 41 (fn indent-literal [n text] 42 (let [margin (string.rep " " n)] 43 (.. margin (string.gsub (or text "") "\n" (.. "\n" margin ))))) 44 45 (fn extract-text [description] 46 (match description 47 { :_type "literalExpression" : text } text 48 (where s (= (type s) "string")) description 49 _ nil)) 50 51 (fn escape-backslash [s] 52 (string.gsub s "\\" "\\\\")) 53 54 (fn print-option [o offset] 55 (let [i (or offset 0)] 56 (print (indent i (.. " * option ``" o.name "``"))) 57 (case (-?> o.description extract-text strip-newlines) 58 descr (print (indent (+ 4 i) descr))) 59 (print) 60 (print (indent (+ 4 i) (.. "**type** " (escape-backslash o.type) "\n"))) 61 (when o.example 62 (print (indent (+ 4 i) "**example**")) (print) 63 (print (indent (+ 4 i) ".. code-block:: nix")) 64 (print) 65 (print (indent-literal (+ 8 i) (extract-text o.example)) "\n") 66 (print)) 67 68 (when (extract-text o.default) 69 (print (indent (+ 4 i) "**default**")) (print) 70 (print (indent (+ 4 i) ".. code-block:: nix")) 71 (print) 72 (print (indent-literal (+ 8 i) (extract-text o.default)) "\n") 73 (print)))) 74 75 (fn print-service [o] 76 (print (.. " * service ``" o.name "``")) 77 (match (extract-text o.description) 78 descr (print (indent 4 descr))) 79 (print) 80 (print (indent 4 "**Service parameters**\n")) 81 (each [_ param (ipairs o.parameters)] 82 (print-option param 4))) 83 84 (fn output? [option] 85 (match option.loc 86 ["system" "outputs" & _] true 87 _ false)) 88 89 (fn is-service? [o] 90 (= o.type "parametrisable s6-rc service definition")) 91 92 (fn has-service? [module] 93 (accumulate [found false 94 _ o (ipairs module)] 95 (or found (is-service? o)))) 96 97 (fn sort-modules [modules] 98 ;; modules containing services should sort _after_ modules 99 ;; that contain only options 100 (doto (icollect [n m (pairs modules)] 101 { :name n :service? (has-service? m) :module m }) 102 (table.sort 103 (fn [a b] 104 (if (and a.service? (not b.service?)) 105 false 106 (and b.service? (not a.service?)) 107 true 108 (< a.name b.name)))))) 109 110 (fn sort-options [module] 111 (let [options (icollect [_ o (ipairs module)] 112 (if (not (output? o)) 113 o))] 114 (doto options (table.sort (fn [a b] (< a.name b.name)))))) 115 116 (let [raw (yaml.load (io.read "*a")) 117 modules {}] 118 (each [_ option (ipairs raw)] 119 (each [_ path (ipairs option.declarations)] 120 (let [e (or (. modules path) [])] 121 (table.insert e option) 122 (tset modules path e)))) 123 (tset modules "lib/modules.nix" nil) 124 (let [modules (sort-modules modules)] 125 (each [_ {: name : module : service?} (ipairs modules)] 126 (let [options (sort-options module)] 127 (when (> (# options) 0) 128 (print (or (read-preamble name) (headline name))) 129 (when service? 130 (print "**path** " (.. ":file:`" (relative-pathname name) "`"))) 131 (print) 132 (each [_ o (ipairs options)] 133 (if (is-service? o) 134 (print-service o) 135 (print-option o))))))))