128 lines
4.6 KiB
Nix
128 lines
4.6 KiB
Nix
{
|
||
config,
|
||
lib,
|
||
pkgs,
|
||
vaculib,
|
||
...
|
||
}:
|
||
let
|
||
inherit (lib) mkOption types;
|
||
unitNameRegex =
|
||
let
|
||
# Unit names cannot begin or end with an underscore (‘_’), a comma (‘,’) or a decimal point (‘.’). Names must not contain any of the operator characters ‘+’, ‘-’, ‘*’, ‘/’, ‘|’, ‘^’, ‘;’, ‘~’, the comment character ‘#’, or parentheses. To facilitate copying and pasting from documents, several typographical characters are converted to operators: the figure dash (U+2012), minus (‘-’; U+2212), and en dash (‘–’; U+2013) are converted to the operator ‘-’; the multiplication sign (‘×’; U+00D7), N-ary times operator (U+2A09), dot operator (‘⋅’; U+22C5), and middle dot (‘·’; U+00B7) are converted to the operator ‘*’; the division sign (‘÷’; U+00F7) is converted to the operator ‘/’; and the fraction slash (U+2044) is converted to the operator ‘|’; accordingly, none of these characters can appear in unit names.
|
||
disallowedAnywhere =
|
||
"+*/|^;~#()" + (builtins.fromJSON ''"\u2012\u2212\u2013\u00d7\u2a09\u22c5\u00b7\u00f7\u2044"'');
|
||
disallowedMiddle = "-" + disallowedAnywhere;
|
||
disallowedAtEnd = "23456789_,." + disallowedAnywhere;
|
||
disallowedAtBegin = "-01" + disallowedAtEnd;
|
||
anyExcept = chars: "[^${lib.escapeRegex chars}]";
|
||
singleChar = anyExcept disallowedAtBegin;
|
||
multiChar = "${anyExcept disallowedAtBegin}${anyExcept disallowedMiddle}*${anyExcept disallowedAtEnd}";
|
||
numberSuffix = regex: "${regex}_[0-9\\.,]+";
|
||
fullRegex = "${singleChar}|${multiChar}|${numberSuffix singleChar}|${numberSuffix multiChar}";
|
||
in
|
||
fullRegex;
|
||
unitsAttrsType = types.addCheck (types.attrsOf types.str) (
|
||
attrs: builtins.all (name: (builtins.match unitNameRegex name) != null) (builtins.attrNames attrs)
|
||
);
|
||
unitsDir = pkgs.stdenvNoCC.mkDerivation {
|
||
name = "vacu-units-files";
|
||
|
||
src = pkgs.units.src;
|
||
|
||
phases = [
|
||
"unpackPhase"
|
||
"installPhase"
|
||
];
|
||
|
||
installPhase = ''
|
||
mkdir -p "$out"
|
||
cp {definitions,elements}.units "$out"
|
||
ln -s ${../units/currency.units} "$out"/currency.units
|
||
ln -s ${../units/cpi.units} "$out"/cpi.units
|
||
printf '%s' ${lib.escapeShellArg config.vacu.units.lines} > "$out"/vacu.units
|
||
'';
|
||
};
|
||
in
|
||
{
|
||
options.vacu.units = {
|
||
originalPackage = mkOption {
|
||
type = types.package;
|
||
default = pkgs.units.override { enableCurrenciesUpdater = false; };
|
||
defaultText = "pkgs.units.override { ... }";
|
||
};
|
||
finalPackage = mkOption {
|
||
type = types.package;
|
||
readOnly = true;
|
||
};
|
||
check = mkOption {
|
||
type = types.package;
|
||
readOnly = true;
|
||
};
|
||
generatedConfigDir = mkOption {
|
||
readOnly = true;
|
||
type = types.package;
|
||
};
|
||
generatedConfigFile = mkOption {
|
||
readOnly = true;
|
||
type = types.pathInStore;
|
||
};
|
||
lines = mkOption {
|
||
default = "";
|
||
type = types.lines;
|
||
};
|
||
extraUnits = mkOption {
|
||
type = unitsAttrsType;
|
||
default = { };
|
||
};
|
||
};
|
||
config = lib.mkMerge [
|
||
{
|
||
vacu.units = {
|
||
finalPackage = vaculib.makeWrapper {
|
||
original = config.vacu.units.originalPackage;
|
||
new = "units";
|
||
prepend_flags = [
|
||
"--file"
|
||
config.vacu.units.generatedConfigFile
|
||
];
|
||
};
|
||
generatedConfigDir = unitsDir;
|
||
generatedConfigFile = "${unitsDir}/vacu.units";
|
||
lines = lib.mkOrder 750 ''
|
||
# default units file, includes elements.units, currency.units, cpi.units
|
||
!include definitions.units
|
||
'';
|
||
};
|
||
vacu.textChecks.units-config = ''
|
||
# `units --check` returns success (exit code 0) regardless of success >:(
|
||
# example output:
|
||
|
||
# $ result/bin/units --check
|
||
# Currency exchange rates from exchangerate-api.com (USD base) on 2024-11-14
|
||
# Consumer price index data from US BLS, 2024-02-18
|
||
# 7247 units, 125 prefixes, 134 nonlinear units
|
||
#
|
||
|
||
output="$(${lib.getExe config.vacu.units.finalPackage} --check)"
|
||
printf '%s' "$output"
|
||
filteredLines="$(printf '%s' "$output" \
|
||
| grep -v '^\s*$' \
|
||
| grep -v 'Currency exchange rates from' \
|
||
| grep -v 'Consumer price index data from' \
|
||
| grep -vE '[0-9]+ units, [0-9]+ prefixes, [0-9]+ nonlinear units' || true
|
||
)"
|
||
if [[ -n "$filteredLines" ]]; then
|
||
exit 1
|
||
fi
|
||
touch "$out"
|
||
'';
|
||
}
|
||
{
|
||
vacu.units.lines = lib.concatStringsSep "\n" (
|
||
lib.mapAttrsToList (name: value: "+${name} ${value}") config.vacu.units.extraUnits
|
||
);
|
||
}
|
||
];
|
||
}
|