87 lines
2 KiB
Nix
87 lines
2 KiB
Nix
let
|
|
keyvalue = key: value:
|
|
assert builtins.isString key;
|
|
if builtins.isAttrs value
|
|
then builtins.trace "Skipping ${key} as it is a set" ""
|
|
else if value == "" || value == [] || value == false
|
|
then ""
|
|
else if value == true
|
|
then key
|
|
else ''${key}="${toString value}"'';
|
|
in rec {
|
|
toHTML = elem:
|
|
if builtins.isString elem
|
|
then elem
|
|
else if builtins.isList elem
|
|
then builtins.toString (map toHTML elem)
|
|
else "<${elem.tag} ${writeAttrs elem.attrs or {}}>${
|
|
toHTML elem.child or ""
|
|
}</${elem.tag}>";
|
|
|
|
writeAttrs = attrs:
|
|
toString (builtins.filter (value: value != "") (builtins.attrValues
|
|
(builtins.mapAttrs (key: value:
|
|
if (builtins.isPath value)
|
|
then keyvalue key (baseNameOf value)
|
|
else if (builtins.substring 0 2 key) == "__"
|
|
then ""
|
|
else keyvalue key value)
|
|
attrs)));
|
|
|
|
tag = t: ({
|
|
tag = t;
|
|
attrs = {};
|
|
}
|
|
// baseTag);
|
|
|
|
baseTag = {
|
|
__toString = self: toString (self "");
|
|
__functor = self: child: (
|
|
if !(isTag child)
|
|
then
|
|
(
|
|
if isSet child
|
|
then incorporateAttrs self child
|
|
else
|
|
throw "tag child must be tag, list, or string, got ${
|
|
builtins.typeOf child
|
|
}"
|
|
)
|
|
else
|
|
self
|
|
// {
|
|
inherit child;
|
|
__toString = toHTML;
|
|
}
|
|
);
|
|
};
|
|
|
|
incorporateAttrs = self: attrs: self // {attrs = self.attrs // attrs;};
|
|
|
|
isSet = a: builtins.isAttrs a && !a ? __toString;
|
|
|
|
isTag = tag: (builtins.isString tag
|
|
|| builtins.isList tag
|
|
|| (tag ? __toString && builtins.isFunction tag.__toString));
|
|
|
|
addToHead = doc: heads:
|
|
doc
|
|
// {
|
|
head = doc.head ++ heads;
|
|
};
|
|
|
|
document = {
|
|
head ? [],
|
|
body ? [],
|
|
attrs ? {},
|
|
}: {
|
|
inherit head body attrs;
|
|
__toString = self: ''
|
|
<!DOCTYPE html>
|
|
${(tag "html" [
|
|
(tag "head" self.head)
|
|
(tag "body" self.body)
|
|
])}
|
|
'';
|
|
};
|
|
}
|