liminix

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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))))))))