formats.javaProperties: init

This commit is contained in:
Robert Hensing 2022-03-28 08:51:09 +02:00
parent 6febb906a8
commit 4f8e44394c
6 changed files with 260 additions and 5 deletions

View File

@ -31,6 +31,9 @@ rec {
*/
inherit (import ./formats/java-properties/default.nix { inherit lib pkgs; })
javaProperties;
json = {}: {
type = with lib.types; let

View File

@ -0,0 +1,85 @@
{ lib, pkgs }:
{
javaProperties = {}: {
type = lib.types.attrsOf lib.types.str;
generate = name: value:
pkgs.runCommandLocal name
{
# Requirements
# ============
#
# 1. Strings in Nix carry over to the same
# strings in Java => need proper escapes
# 2. Generate files quickly
# - A JVM would have to match the app's
# JVM to avoid build closure bloat
# - Even then, JVM startup would slow
# down config generation.
#
#
# Implementation
# ==============
#
# Escaping has two steps
#
# 1. jq
# Escape known separators, in order not
# to break up the keys and values.
# This handles typical whitespace correctly,
# but may produce garbage for other control
# characters.
#
# 2. iconv
# Escape >ascii code points to java escapes,
# as .properties files are supposed to be
# encoded in ISO 8859-1. It's an old format.
# UTF-8 behavior may exist in some apps and
# libraries, but we can't rely on this in
# general.
passAsFile = [ "value" ];
value = builtins.toJSON value;
nativeBuildInputs = [
pkgs.jq
pkgs.libiconvReal
];
jqCode =
let
main = ''
to_entries
| .[]
| "\(
.key
| ${commonEscapes}
| gsub(" "; "\\ ")
| gsub("="; "\\=")
) = \(
.value
| ${commonEscapes}
| gsub("^ "; "\\ ")
| gsub("\\n "; "\n\\ ")
)"
'';
# Most escapes are equal for both keys and values.
commonEscapes = ''
gsub("\\\\"; "\\\\")
| gsub("\\n"; "\\n\\\n")
| gsub("#"; "\\#")
| gsub("!"; "\\!")
| gsub("\\t"; "\\t")
| gsub("\r"; "\\r")
'';
in
main;
inputEncoding = "UTF-8";
} ''
jq -r --arg hash '#' "$jqCode" "$valuePath" \
| iconv --from-code "$inputEncoding" --to-code JAVA \
> "$out"
'';
};
}

View File

@ -0,0 +1,27 @@
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;
import java.util.SortedSet;
import java.util.TreeSet;
class Main {
public static void main (String args[]) {
try {
InputStream input = new FileInputStream(args[0]);
Properties prop = new Properties();
prop.load(input);
SortedSet<String> keySet = new TreeSet(prop.keySet());
for (String key : keySet) {
System.out.println("KEY");
System.out.println(key);
System.out.println("VALUE");
System.out.println(prop.get(key));
System.out.println("");
}
} catch (Exception e) {
e.printStackTrace();
System.err.println(e.toString());
System.exit(1);
}
}
}

View File

@ -0,0 +1,85 @@
{ fetchurl
, formats
, glibcLocales
, jdk
, lib
, stdenv
}:
let
inherit (lib) concatStrings attrValues mapAttrs;
javaProperties = formats.javaProperties { };
input = {
foo = "bar";
"empty value" = "";
"typical.dot.syntax" = "com.sun.awt";
"" = "empty key's value";
"1" = "2 3";
"#" = "not a comment # still not";
"!" = "not a comment!";
"!a" = "still not! a comment";
"!b" = "still not ! a comment";
"dos paths" = "C:\\Program Files\\Nix For Windows\\nix.exe";
"a \t\nb" = " c";
"angry \t\nkey" = ''
multi
${"\tline\r"}
space-
indented
trailing-space${" "}
trailing-space${" "}
value
'';
"this=not" = "bad";
"nor = this" = "bad";
"all stuff" = "foo = bar";
"unicode big brain" = "e = mc";
"ütf-8" = "dûh";
# NB: Some editors (vscode) show this _whole_ line in right-to-left order
"الجبر" = "أكثر من مجرد أرقام";
};
in
stdenv.mkDerivation {
name = "pkgs.formats.javaProperties-test-${jdk.name}";
nativeBuildInputs = [
jdk
glibcLocales
];
# technically should go through the type.merge first, but that's tested
# in tests/formats.nix.
properties = javaProperties.generate "example.properties" input;
# Expected output as printed by Main.java
passAsFile = [ "expected" ];
expected = concatStrings (attrValues (
mapAttrs
(key: value:
''
KEY
${key}
VALUE
${value}
''
)
input
));
src = lib.sourceByRegex ./. [
".*\.java"
];
LANG = "C.UTF-8";
buildPhase = ''
javac Main.java
'';
doCheck = true;
checkPhase = ''
cat -v $properties
java Main $properties >actual
diff -U3 $expectedPath actual
'';
installPhase = "touch $out";
}

View File

@ -1,7 +1,45 @@
# Call nix-build on this file to run all tests in this directory
{ pkgs ? import ../../.. {} }:
# This produces a link farm derivation with the original attrs
# merged on top of it.
# You can run parts of the "hierarchy" with for example:
# nix-build -A java-properties
# See `structured` below.
{ pkgs ? import ../../.. { } }:
let
formats = import ./formats.nix { inherit pkgs; };
in pkgs.linkFarm "nixpkgs-pkgs-lib-tests" [
{ name = "formats"; path = import ./formats.nix { inherit pkgs; }; }
]
inherit (pkgs.lib) mapAttrs mapAttrsToList isDerivation mergeAttrs foldl' attrValues recurseIntoAttrs;
structured = {
formats = import ./formats.nix { inherit pkgs; };
java-properties = recurseIntoAttrs {
jdk8 = pkgs.callPackage ../formats/java-properties/test { jdk = pkgs.jdk8; };
jdk11 = pkgs.callPackage ../formats/java-properties/test { jdk = pkgs.jdk11_headless; };
jdk17 = pkgs.callPackage ../formats/java-properties/test { jdk = pkgs.jdk17_headless; };
};
};
flatten = prefix: as:
foldl'
mergeAttrs
{ }
(attrValues
(mapAttrs
(k: v:
if isDerivation v
then { "${prefix}${k}" = v; }
else if v?recurseForDerivations
then flatten "${prefix}${k}-" (removeAttrs v [ "recurseForDerivations" ])
else builtins.trace v throw "expected derivation or recurseIntoAttrs")
as
)
);
in
# It has to be a link farm for inclusion in the hydra unstable jobset.
pkgs.linkFarm "pkgs-lib-formats-tests"
(mapAttrsToList
(k: v: { name = k; path = v; })
(flatten "" structured)
)
// structured

View File

@ -168,4 +168,21 @@ in runBuildTests {
level4 = "deep"
'';
};
# See also java-properties/default.nix for more complete tests
testJavaProperties = {
drv = evalFormat formats.javaProperties {} {
foo = "bar";
"1" = "2";
"ütf 8" = "dûh";
# NB: Some editors (vscode) show this _whole_ line in right-to-left order
"الجبر" = "أكثر من مجرد أرقام";
};
expected = ''
1 = 2
foo = bar
\u00fctf\ 8 = d\u00fbh
\u0627\u0644\u062c\u0628\u0631 = \u0623\u0643\u062b\u0631 \u0645\u0646 \u0645\u062c\u0631\u062f \u0623\u0631\u0642\u0627\u0645
'';
};
}