push sheeet
Some checks failed
Periodic Merges (6h) / master → staging-nixos (push) Failing after 12m50s
Periodic Merges (6h) / master → staging-next (push) Failing after 12m54s
Periodic Merges (24h) / merge-base(master,staging) → haskell-updates (push) Failing after 11m54s
Periodic Merges (6h) / staging-next → staging (push) Failing after 12m13s
Periodic Merges (24h) / staging-next-25.05 → staging-25.05 (push) Failing after 13m24s
Periodic Merges (24h) / release-25.05 → staging-next-25.05 (push) Failing after 14m28s

This commit is contained in:
Dark Steveneq
2025-10-09 14:15:47 +02:00
commit 646b892680
49168 changed files with 5897842 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
{
lib,
pkgs,
}:
let
inherit (pkgs) buildPackages callPackage;
libconfig-generator = buildPackages.rustPlatform.buildRustPackage {
name = "libconfig-generator";
version = "0.1.0";
src = ./src;
passthru.updateScript = ./update.sh;
cargoLock.lockFile = ./src/Cargo.lock;
};
libconfig-validator =
buildPackages.runCommandCC "libconfig-validator"
{
buildInputs = with buildPackages; [ libconfig ];
}
''
mkdir -p "$out/bin"
$CC -lconfig -x c - -o "$out/bin/libconfig-validator" ${./validator.c}
'';
in
{
format =
{
generator ? libconfig-generator,
validator ? libconfig-validator,
}:
{
inherit generator;
type =
with lib.types;
let
valueType =
(oneOf [
bool
int
float
str
path
(attrsOf valueType)
(listOf valueType)
])
// {
description = "libconfig value";
};
in
attrsOf valueType;
lib = {
mkHex = value: {
_type = "hex";
inherit value;
};
mkOctal = value: {
_type = "octal";
inherit value;
};
mkFloat = value: {
_type = "float";
inherit value;
};
mkArray = value: {
_type = "array";
inherit value;
};
mkList = value: {
_type = "list";
inherit value;
};
};
generate =
name: value:
callPackage
(
{
stdenvNoCC,
libconfig-generator,
libconfig-validator,
writeText,
}:
stdenvNoCC.mkDerivation rec {
inherit name;
dontUnpack = true;
preferLocalBuild = true;
json = builtins.toJSON value;
passAsFile = [ "json" ];
strictDeps = true;
nativeBuildInputs = [ libconfig-generator ];
buildPhase = ''
runHook preBuild
libconfig-generator < $jsonPath > output.cfg
runHook postBuild
'';
doCheck = true;
nativeCheckInputs = [ libconfig-validator ];
checkPhase = ''
runHook preCheck
libconfig-validator output.cfg
runHook postCheck
'';
installPhase = ''
runHook preInstall
mv output.cfg $out
runHook postInstall
'';
passthru.json = writeText "${name}.json" json;
}
)
{
libconfig-generator = generator;
libconfig-validator = validator;
};
};
}

View File

@@ -0,0 +1,40 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "libconfig-generator"
version = "0.1.0"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "serde"
version = "1.0.183"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c"
[[package]]
name = "serde_json"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
dependencies = [
"itoa",
"ryu",
"serde",
]

View File

@@ -0,0 +1,10 @@
[package]
name = "libconfig-generator"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = "1.0.178"
serde_json = "1.0.104"

View File

@@ -0,0 +1,271 @@
use serde_json::Value;
use std::mem::discriminant;
#[derive(Debug)]
enum LibConfigIntNumber {
Oct(i64),
Hex(i64),
Int(i64),
}
#[derive(Debug)]
enum LibConfigValue {
Bool(bool),
Int(LibConfigIntNumber),
Float(f64),
String(String),
Array(Vec<LibConfigValue>),
List(Vec<LibConfigValue>),
Group(Vec<String>, Vec<(String, LibConfigValue)>),
}
fn validate_setting_name(key: &str) -> bool {
let first_char = key.chars().next().expect("Empty setting name");
(first_char.is_alphabetic() || first_char == '*')
&& key[1..]
.chars()
.all(|c| c.is_alphanumeric() || c == '_' || c == '*' || c == '-')
}
const SPECIAL_TYPES: [&str; 5] = ["octal", "hex", "float", "list", "array"];
fn object_is_special_type(o: &serde_json::Map<String, Value>) -> Option<&str> {
o.get("_type").and_then(|x| x.as_str()).and_then(|x| {
if SPECIAL_TYPES.contains(&x) {
Some(x)
} else {
None
}
})
}
fn vec_is_array(v: &Vec<LibConfigValue>) -> bool {
if v.is_empty() {
return true;
}
let first_item = v.first().unwrap();
if match first_item {
LibConfigValue::Array(_) => true,
LibConfigValue::List(_) => true,
LibConfigValue::Group(_, _) => true,
_ => false,
} {
return false;
};
v[1..]
.iter()
.all(|item| discriminant(first_item) == discriminant(item))
}
fn json_to_libconfig(v: &Value) -> LibConfigValue {
match v {
Value::Null => panic!("Null value not allowed in libconfig"),
Value::Bool(b) => LibConfigValue::Bool(b.clone()),
Value::Number(n) => {
if n.is_i64() {
LibConfigValue::Int(LibConfigIntNumber::Int(n.as_i64().unwrap()))
} else if n.is_f64() {
LibConfigValue::Float(n.as_f64().unwrap())
} else {
panic!("{} is not i64 or f64, cannot be represented as number in libconfig", n);
}
}
Value::String(s) => LibConfigValue::String(s.to_string()),
Value::Array(a) => {
let items = a
.iter()
.map(|item| json_to_libconfig(item))
.collect::<Vec<LibConfigValue>>();
LibConfigValue::List(items)
}
Value::Object(o) => {
if let Some(_type) = object_is_special_type(o) {
let value = o
.get("value")
.expect(format!("Missing value for special type: {}", &_type).as_str());
return match _type {
"octal" => {
let str_value = value
.as_str()
.expect(
format!("Value is not a string for special type: {}", &_type)
.as_str(),
)
.to_owned();
LibConfigValue::Int(LibConfigIntNumber::Oct(
i64::from_str_radix(&str_value, 8)
.expect(format!("Invalid octal value: {}", value).as_str()),
))
}
"hex" => {
let str_value = value
.as_str()
.expect(
format!("Value is not a string for special type: {}", &_type)
.as_str(),
)
.to_owned();
LibConfigValue::Int(LibConfigIntNumber::Hex(
i64::from_str_radix(&str_value[2..], 16)
.expect(format!("Invalid hex value: {}", value).as_str()),
))
}
"float" => {
let str_value = value
.as_str()
.expect(
format!("Value is not a string for special type: {}", &_type)
.as_str(),
)
.to_owned();
LibConfigValue::Float(
str_value
.parse::<f64>()
.expect(format!("Invalid float value: {}", value).as_str()),
)
}
"list" => {
let items = value
.as_array()
.expect(
format!("Value is not an array for special type: {}", &_type)
.as_str(),
)
.to_owned()
.iter()
.map(|item| json_to_libconfig(item))
.collect::<Vec<LibConfigValue>>();
LibConfigValue::List(items)
}
"array" => {
let items = value
.as_array()
.expect(
format!("Value is not an array for special type: {}", &_type)
.as_str(),
)
.to_owned()
.iter()
.map(|item| json_to_libconfig(item))
.collect::<Vec<LibConfigValue>>();
if !vec_is_array(&items) {
panic!(
"This can not be an array because of its contents: {:#?}",
items
);
}
LibConfigValue::Array(items)
}
_ => panic!("Invalid type: {}", _type),
};
}
let mut items = o
.iter()
.filter(|(key, _)| key.as_str() != "_includes")
.map(|(key, value)| (key.clone(), json_to_libconfig(value)))
.collect::<Vec<(String, LibConfigValue)>>();
items.sort_by(|(a,_),(b,_)| a.partial_cmp(b).unwrap());
let includes = o
.get("_includes")
.map(|x| {
x.as_array()
.expect("_includes is not an array")
.iter()
.map(|x| {
x.as_str()
.expect("_includes item is not a string")
.to_owned()
})
.collect::<Vec<String>>()
})
.unwrap_or(vec![]);
for (key,_) in items.iter() {
if !validate_setting_name(key) {
panic!("Invalid setting name: {}", key);
}
}
LibConfigValue::Group(includes, items)
}
}
}
impl ToString for LibConfigValue {
fn to_string(&self) -> String {
match self {
LibConfigValue::Bool(b) => b.to_string(),
LibConfigValue::Int(i) => match i {
LibConfigIntNumber::Oct(n) => format!("0{:o}", n),
LibConfigIntNumber::Hex(n) => format!("0x{:x}", n),
LibConfigIntNumber::Int(n) => n.to_string(),
},
LibConfigValue::Float(n) => format!("{:?}", n),
LibConfigValue::String(s) => {
format!("\"{}\"", s.replace("\\", "\\\\").replace("\"", "\\\""))
}
LibConfigValue::Array(a) => {
let items = a
.iter()
.map(|item| item.to_string())
.collect::<Vec<String>>()
.join(", ");
format!("[{}]", items)
}
LibConfigValue::List(a) => {
let items = a
.iter()
.map(|item| item.to_string())
.collect::<Vec<String>>()
.join(", ");
format!("({})", items)
}
LibConfigValue::Group(i, o) => {
let includes = i
.iter()
.map(|x| x.replace("\\", "\\\\").replace("\"", "\\\""))
.map(|x| format!("@include \"{}\"", x))
.collect::<Vec<String>>()
.join("\n");
let items = o
.iter()
.map(|(key, value)| format!("{}={};", key, value.to_string()))
.collect::<Vec<String>>()
.join("");
if includes.is_empty() {
format!("{{{}}}", items)
} else {
format!("{{\n{}\n{}}}", includes, items)
}
}
}
}
}
fn main() {
let stdin = std::io::stdin().lock();
let json = serde_json::Deserializer::from_reader(stdin)
.into_iter::<Value>()
.next()
.expect("Could not read content from stdin")
.expect("Could not parse JSON from stdin");
for (key, value) in json
.as_object()
.expect("Top level of JSON file is not an object")
{
print!("{}={};", key, json_to_libconfig(value).to_string());
}
print!("\n\n");
}

View File

@@ -0,0 +1,111 @@
{
lib,
formats,
stdenvNoCC,
writeText,
...
}:
let
libconfig = formats.libconfig { };
include_expr = {
val = 1;
};
include_file = writeText "libconfig-test-include" ''
val=1;
'';
expression = {
simple_top_level_attr = "1.0";
nested.attrset.has.a.integer.value = 100;
some_floaty = 29.95;
## dashes in key names
top-level-dash = "pass";
nested.level-dash = "pass";
## Same syntax here on these two, but they should get serialized differently:
# > A list may have zero or more elements, each of which can be a scalar value, an array, a group, or another list.
list1d = libconfig.lib.mkList [
1
"mixed!"
5
2
];
# You might also omit the mkList, as a list will be a list (in contrast to an array) by default.
list2d = [
1
[
1
1.2
"foo"
]
[
"bar"
1.2
1
]
];
# > An array may have zero or more elements, but the elements must all be scalar values of the same type.
array1d = libconfig.lib.mkArray [
1
5
2
];
array2d = [
(libconfig.lib.mkArray [
1
2
])
(libconfig.lib.mkArray [
2
1
])
];
nasty_string = "\"@\n\\\t^*bf\n0\";'''$";
weirderTypes = {
_includes = [ include_file ];
pi = 3.141592654;
bigint = 9223372036854775807;
hex = libconfig.lib.mkHex "0x1FC3";
octal = libconfig.lib.mkOctal "0027";
float = libconfig.lib.mkFloat "1.2E-3";
array_of_ints = libconfig.lib.mkArray [
(libconfig.lib.mkOctal "0732")
(libconfig.lib.mkHex "0xA3")
1234
];
list_of_weird_types = [
3.141592654
9223372036854775807
(libconfig.lib.mkHex "0x1FC3")
(libconfig.lib.mkOctal "0027")
(libconfig.lib.mkFloat "1.2E-32")
(libconfig.lib.mkFloat "1")
];
};
};
libconfig-test-cfg = libconfig.generate "libconfig-test.cfg" expression;
in
stdenvNoCC.mkDerivation {
name = "pkgs.formats.libconfig-test-comprehensive";
dontUnpack = true;
dontBuild = true;
doCheck = true;
checkPhase = ''
cp ${./expected.txt} expected.txt
substituteInPlace expected.txt \
--subst-var-by include_file "${include_file}"
diff -U3 ./expected.txt ${libconfig-test-cfg}
'';
installPhase = ''
mkdir $out
cp expected.txt $out
cp ${libconfig-test-cfg} $out/libconfig-test.cfg
cp ${libconfig-test-cfg.passthru.json} $out/libconfig-test.json
'';
}

View File

@@ -0,0 +1,6 @@
array1d=[1, 5, 2];array2d=([1, 2], [2, 1]);list1d=(1, "mixed!", 5, 2);list2d=(1, (1, 1.2, "foo"), ("bar", 1.2, 1));nasty_string="\"@
\\ ^*bf
0\";'''$";nested={attrset={has={a={integer={value=100;};};};};level-dash="pass";};simple_top_level_attr="1.0";some_floaty=29.95;top-level-dash="pass";weirderTypes={
@include "@include_file@"
array_of_ints=[0732, 0xa3, 1234];bigint=9223372036854775807;float=0.0012;hex=0x1fc3;list_of_weird_types=(3.141592654, 9223372036854775807, 0x1fc3, 027, 1.2e-32, 1.0);octal=027;pi=3.141592654;};

View File

@@ -0,0 +1,4 @@
{ pkgs, ... }:
{
comprehensive = pkgs.callPackage ./comprehensive { };
}

View File

@@ -0,0 +1,4 @@
#!/usr/bin/env nix-shell
#!nix-shell -p cargo -i bash
cd "$(dirname "$0")"
cargo update

View File

@@ -0,0 +1,21 @@
// Copyright (C) 2005-2023 Mark A Lindner, ckie
// SPDX-License-Identifier: LGPL-2.1-or-later
#include <stdio.h>
#include <libconfig.h>
int main(int argc, char **argv)
{
config_t cfg;
config_init(&cfg);
if (argc != 2)
{
fprintf(stderr, "USAGE: validator <path-to-validate>");
}
if(! config_read_file(&cfg, argv[1]))
{
fprintf(stderr, "[libconfig] %s:%d - %s\n", config_error_file(&cfg),
config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
return 1;
}
printf("[libconfig] validation ok\n");
}