2023-12-24 19:19:12 +01:00
|
|
|
{ lib, korora, ... }:
|
|
|
|
let
|
|
|
|
k = korora;
|
|
|
|
|
2024-09-11 15:59:50 +02:00
|
|
|
# {{{ Helpers
|
2023-12-24 19:19:12 +01:00
|
|
|
helpers = rec {
|
2024-09-11 15:59:50 +02:00
|
|
|
removeEmptyLines =
|
|
|
|
str:
|
2023-12-25 14:14:33 +01:00
|
|
|
lib.pipe str [
|
|
|
|
(lib.splitString "\n")
|
2024-09-11 15:59:50 +02:00
|
|
|
(lib.lists.filter (line: lib.any (c: c != " ") (lib.stringToCharacters line)))
|
2023-12-25 14:14:33 +01:00
|
|
|
(lib.concatStringsSep "\n")
|
|
|
|
];
|
|
|
|
|
2024-09-11 15:59:50 +02:00
|
|
|
hasType =
|
|
|
|
type: value:
|
|
|
|
let
|
|
|
|
err = type.verify value;
|
|
|
|
in
|
2024-07-28 20:01:45 +02:00
|
|
|
lib.assertMsg (err == null) err;
|
|
|
|
|
2024-09-11 15:59:50 +02:00
|
|
|
lua =
|
|
|
|
value:
|
|
|
|
assert hasType k.string value;
|
|
|
|
lib.fix (self: {
|
|
|
|
inherit value;
|
2024-07-28 20:01:45 +02:00
|
|
|
__luaEncoderTag = "lua";
|
2024-09-11 15:59:50 +02:00
|
|
|
__functor =
|
|
|
|
_: arg:
|
|
|
|
if builtins.typeOf arg == "path" then
|
|
|
|
if arg == /. then
|
|
|
|
self
|
|
|
|
else
|
|
|
|
let
|
|
|
|
object = self (builtins.dirOf arg);
|
|
|
|
attr = builtins.toString (builtins.baseNameOf arg);
|
|
|
|
in
|
|
|
|
lua "${encode object}.${attr}"
|
|
|
|
else
|
|
|
|
lua "${value}(${encode arg})";
|
|
|
|
});
|
2024-07-28 20:01:45 +02:00
|
|
|
|
2024-09-11 15:59:50 +02:00
|
|
|
# {{{ Verification helpers
|
2023-12-25 14:14:33 +01:00
|
|
|
toPretty = lib.generators.toPretty { indent = " "; };
|
|
|
|
withError = cond: err: if cond then null else err;
|
|
|
|
hasProp = obj: p: obj ? ${p};
|
2024-09-11 15:59:50 +02:00
|
|
|
propExists = p: obj: helpers.withError (helpers.hasProp obj p) "Expected property ${p}";
|
|
|
|
propXor =
|
|
|
|
a: b: obj:
|
|
|
|
helpers.withError (
|
|
|
|
(hasProp obj a) != (hasProp obj b)
|
|
|
|
) "Expected only one of the properties ${a} and ${b} in object ${toPretty obj}";
|
|
|
|
propOnlyOne =
|
|
|
|
props: obj:
|
|
|
|
helpers.withError (
|
|
|
|
1 == lib.count (hasProp obj) props
|
|
|
|
) "Expected exactly one property from the list ${toPretty props} in object ${toPretty obj}";
|
|
|
|
propImplies =
|
|
|
|
a: b: obj:
|
|
|
|
helpers.withError (
|
|
|
|
(hasProp obj a) -> (hasProp obj b)
|
|
|
|
) "Property ${a} should imply property ${b} in object ${toPretty obj}";
|
|
|
|
mkVerify =
|
|
|
|
checks: obj:
|
2023-12-25 14:14:33 +01:00
|
|
|
assert lib.all lib.isFunction checks;
|
2023-12-24 19:19:12 +01:00
|
|
|
let
|
2023-12-25 14:14:33 +01:00
|
|
|
results = lib.lists.map (c: c obj) checks;
|
2023-12-24 19:19:12 +01:00
|
|
|
errors = lib.lists.filter (v: v != null) results;
|
|
|
|
in
|
2023-12-25 14:14:33 +01:00
|
|
|
assert lib.all lib.isString errors;
|
2024-09-11 15:59:50 +02:00
|
|
|
if errors == [ ] then
|
|
|
|
null
|
2023-12-24 19:19:12 +01:00
|
|
|
else
|
2024-09-11 15:59:50 +02:00
|
|
|
let
|
|
|
|
prettyErrors = lib.lists.map (s: "\n- ${s}") errors;
|
2023-12-24 19:19:12 +01:00
|
|
|
in
|
2023-12-25 14:14:33 +01:00
|
|
|
"Multiple errors occured: ${lib.concatStrings prettyErrors}";
|
|
|
|
# }}}
|
|
|
|
# {{{ Custom types
|
2024-09-11 15:59:50 +02:00
|
|
|
intersection =
|
|
|
|
l: r:
|
|
|
|
k.typedef' "${l.name} ∧ ${r.name}" (
|
|
|
|
helpers.mkVerify [
|
|
|
|
l
|
|
|
|
r
|
|
|
|
]
|
|
|
|
);
|
2023-12-24 19:19:12 +01:00
|
|
|
|
2023-12-25 14:14:33 +01:00
|
|
|
# dependentAttrsOf =
|
|
|
|
# name: mkType:
|
|
|
|
# let
|
|
|
|
# typeError = name: v: "Expected type '${name}' but value '${toPretty v}' is of type '${typeOf v}'";
|
|
|
|
# addErrorContext = context: error: if error == null then null else "${context}: ${error}";
|
|
|
|
#
|
|
|
|
# withErrorContext = addErrorContext "in ${name} value";
|
|
|
|
# in
|
|
|
|
# k.typedef' name
|
|
|
|
# (v:
|
|
|
|
# if ! lib.isAttrs v then
|
|
|
|
# typeError name v
|
|
|
|
# else
|
|
|
|
# withErrorContext
|
|
|
|
# (mkVerify
|
|
|
|
# (lib.mapAttrsToList (k: _: mkType k) v)
|
|
|
|
# v));
|
|
|
|
# }}}
|
|
|
|
# {{{ Encoding helpers
|
2024-09-11 15:59:50 +02:00
|
|
|
mkRawLuaObject = chunks: ''
|
|
|
|
{
|
|
|
|
${lib.concatStringsSep "," (lib.filter (s: s != "") chunks)}
|
|
|
|
}
|
|
|
|
'';
|
2023-12-24 19:19:12 +01:00
|
|
|
|
2024-09-11 15:59:50 +02:00
|
|
|
encodeString =
|
|
|
|
given:
|
2024-04-23 08:33:17 +02:00
|
|
|
if lib.hasInfix "\n" given then
|
|
|
|
''[[${(toString given)}]]'' # This is not perfect but oh well
|
|
|
|
else
|
2024-09-11 15:59:50 +02:00
|
|
|
''"${
|
|
|
|
lib.escape [
|
|
|
|
"\""
|
|
|
|
"\\"
|
|
|
|
] (toString given)
|
|
|
|
}"'';
|
2023-12-25 14:14:33 +01:00
|
|
|
|
2024-09-11 15:59:50 +02:00
|
|
|
mkAttrName =
|
|
|
|
s:
|
2023-12-24 19:19:12 +01:00
|
|
|
let
|
2024-04-23 08:33:17 +02:00
|
|
|
# A "good enough" approach to figuring out the correct encoding for attribute names
|
|
|
|
allowedStartingChars = lib.stringToCharacters "abcdefghijklmnopqrstuvwxyz_";
|
|
|
|
allowedChars = allowedStartingChars ++ lib.stringToCharacters "0123456789";
|
2024-09-11 15:59:50 +02:00
|
|
|
keywords = [
|
|
|
|
"if"
|
|
|
|
"then"
|
|
|
|
"else"
|
|
|
|
"do"
|
|
|
|
"for"
|
|
|
|
"local"
|
|
|
|
""
|
|
|
|
];
|
2023-12-24 19:19:12 +01:00
|
|
|
in
|
2024-04-23 08:33:17 +02:00
|
|
|
if builtins.match "([a-zA-Z_][a-zA-Z_0-9]*)" s != [ s ] || lib.elem s keywords then
|
2023-12-25 14:14:33 +01:00
|
|
|
"[${helpers.encodeString s}]"
|
2024-09-11 15:59:50 +02:00
|
|
|
else
|
|
|
|
s;
|
2023-12-25 14:14:33 +01:00
|
|
|
# }}}
|
2023-12-24 19:19:12 +01:00
|
|
|
};
|
|
|
|
# }}}
|
2024-04-23 08:33:17 +02:00
|
|
|
# {{{ Encoding
|
2023-12-25 14:14:33 +01:00
|
|
|
isLuaLiteral = given: lib.isAttrs given && given.__luaEncoderTag or null == "lua";
|
2024-09-11 15:59:50 +02:00
|
|
|
encodeWithDepth =
|
|
|
|
depth: given:
|
|
|
|
let
|
|
|
|
recurse = encodeWithDepth depth;
|
|
|
|
in
|
2023-12-25 14:14:33 +01:00
|
|
|
if lib.isString given || lib.isDerivation given || lib.isPath given then
|
|
|
|
helpers.encodeString given
|
|
|
|
else if lib.isInt given || lib.isFloat given then
|
|
|
|
toString given
|
|
|
|
else if lib.isBool given then
|
|
|
|
if given then "true" else "false"
|
|
|
|
else if given == null then
|
|
|
|
"nil"
|
|
|
|
else if lib.isList given then
|
|
|
|
helpers.mkRawLuaObject (lib.lists.map recurse given)
|
|
|
|
else if lib.isAttrs given && given.__luaEncoderTag or null == "foldedList" then
|
|
|
|
let
|
|
|
|
makeFold = name: value: ''
|
|
|
|
-- {{{ ${name}
|
|
|
|
${recurse value},
|
|
|
|
-- }}}'';
|
|
|
|
folds = lib.mapAttrsToList makeFold given.value;
|
2023-12-24 19:19:12 +01:00
|
|
|
in
|
2023-12-25 14:14:33 +01:00
|
|
|
''
|
|
|
|
{
|
|
|
|
${lib.concatStringsSep "\n" folds}
|
|
|
|
}''
|
|
|
|
else if isLuaLiteral given then
|
|
|
|
given.value
|
|
|
|
else if lib.isAttrs given then
|
2024-09-11 15:59:50 +02:00
|
|
|
helpers.mkRawLuaObject (
|
|
|
|
lib.mapAttrsToList (
|
|
|
|
name: value:
|
|
|
|
let
|
|
|
|
result = recurse value;
|
|
|
|
in
|
|
|
|
lib.optionalString (result != "nil") "${helpers.mkAttrName name} = ${result}"
|
|
|
|
) given
|
|
|
|
)
|
2023-12-25 14:14:33 +01:00
|
|
|
else if lib.isFunction given then
|
|
|
|
let
|
2024-09-11 15:59:50 +02:00
|
|
|
argNames = [
|
|
|
|
"context"
|
|
|
|
"inner_context"
|
|
|
|
"local_context"
|
|
|
|
];
|
2023-12-25 14:14:33 +01:00
|
|
|
secretArg =
|
2024-09-11 15:59:50 +02:00
|
|
|
if depth >= builtins.length argNames then "arg_${depth}" else builtins.elemAt argNames depth;
|
2023-12-25 14:14:33 +01:00
|
|
|
body = given secretArg;
|
|
|
|
encoded = encodeWithDepth (depth + 1) body;
|
2024-09-11 15:59:50 +02:00
|
|
|
encodedBody = if isLuaLiteral body then encoded else "return ${encoded}";
|
2023-12-25 14:14:33 +01:00
|
|
|
in
|
2024-09-11 15:59:50 +02:00
|
|
|
if lib.hasInfix secretArg encoded then
|
|
|
|
''
|
|
|
|
function(${secretArg})
|
|
|
|
${encodedBody}
|
|
|
|
end''
|
|
|
|
else
|
|
|
|
''
|
|
|
|
function()
|
|
|
|
${encodedBody}
|
|
|
|
end''
|
|
|
|
else
|
|
|
|
builtins.throw "Cannot encode value ${helpers.toPretty given}";
|
2023-12-24 19:19:12 +01:00
|
|
|
# }}}
|
2023-12-25 14:14:33 +01:00
|
|
|
encode = encodeWithDepth 0;
|
2023-12-24 19:19:12 +01:00
|
|
|
in
|
2024-09-11 15:59:50 +02:00
|
|
|
{
|
|
|
|
inherit encode helpers;
|
|
|
|
}
|