{ lib }: with lib; with builtins; rec { # Use `term` to construct XML. # # Examples: # # (term "bool" null null) # (term "cool" null []) # (term "fool" { hurr = "durr"; } null) # (term "hool" null [ # (term "tool" null null) # ]) # # See `render` for how these get transformed into actuall XML documents. # term = name: attrs: content: { inherit name attrs content; }; empty = term null null null; # Ref http://www.w3.org/TR/xml/#syntax # # Example: # # (quote "") #===> <cheez!> # quote = let sub = { "&" = "&"; "<" = "<"; ">" = ">"; "'" = "'"; "\"" = """; }; in stringAsChars (c: sub.${c} or c); # Turn an XML element to an XML document string. doc = t: "${render t}"; # Render an XML element to a string. # # Rendering `empty` yields the empty string. # # Examples: # # (term "bool" null null) #===> # (term "cool" null []) #===> # (term "fool" { hurr = "durr"; } null) #===> # (term "hool" null [ # (term "tool" null null) # ]) #===> # render = let render-attrs = attrs: getAttr (typeOf attrs) { null = ""; set = concatStrings (mapAttrsToList (n: v: " ${n}=\"${v}\"") attrs); }; render-content = content: getAttr (typeOf content) { bool = toJSON content; int = toJSON content; list = concatMapStrings render content; string = quote content; }; in { name, attrs, content }: # XXX we're currently encoding too much information with `null`.. if name == null then if content == null then "" else content else let attrs' = render-attrs attrs; content' = render-content content; in if content == null then "<${name}${attrs'}/>" else "<${name}${attrs'}>${content'}"; }