formats.libconfig: init
Co-authored-by: ckie <25263210+ckiee@users.noreply.github.com> Signed-off-by: h7x4 <h7x4@nani.wtf>
This commit is contained in:
parent
5e4c2ada4f
commit
3530342dcc
7 changed files with 469 additions and 0 deletions
|
@ -34,6 +34,8 @@ rec {
|
|||
inherit (import ./formats/java-properties/default.nix { inherit lib pkgs; })
|
||||
javaProperties;
|
||||
|
||||
libconfig = (import ./formats/libconfig/default.nix { inherit lib pkgs; }).format;
|
||||
|
||||
json = {}: {
|
||||
|
||||
type = with lib.types; let
|
||||
|
|
121
pkgs/pkgs-lib/formats/libconfig/default.nix
Normal file
121
pkgs/pkgs-lib/formats/libconfig/default.nix
Normal file
|
@ -0,0 +1,121 @@
|
|||
{ lib
|
||||
, pkgs
|
||||
}:
|
||||
let
|
||||
inherit (pkgs) buildPackages callPackage;
|
||||
# Implementation notes:
|
||||
# Libconfig spec: https://hyperrealm.github.io/libconfig/libconfig_manual.html
|
||||
#
|
||||
# Since libconfig does not allow setting names to start with an underscore,
|
||||
# this is used as a prefix for both special types and include directives.
|
||||
#
|
||||
# The difference between 32bit and 64bit values became optional in libconfig
|
||||
# 1.5, so we assume 64bit values for all numbers.
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
};
|
||||
};
|
||||
}
|
40
pkgs/pkgs-lib/formats/libconfig/src/Cargo.lock
generated
Normal file
40
pkgs/pkgs-lib/formats/libconfig/src/Cargo.lock
generated
Normal 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",
|
||||
]
|
10
pkgs/pkgs-lib/formats/libconfig/src/Cargo.toml
Normal file
10
pkgs/pkgs-lib/formats/libconfig/src/Cargo.toml
Normal 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"
|
271
pkgs/pkgs-lib/formats/libconfig/src/src/main.rs
Normal file
271
pkgs/pkgs-lib/formats/libconfig/src/src/main.rs
Normal 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 == '*')
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
4
pkgs/pkgs-lib/formats/libconfig/update.sh
Executable file
4
pkgs/pkgs-lib/formats/libconfig/update.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -p cargo -i bash
|
||||
cd "$(dirname "$0")"
|
||||
cargo update
|
21
pkgs/pkgs-lib/formats/libconfig/validator.c
Normal file
21
pkgs/pkgs-lib/formats/libconfig/validator.c
Normal 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");
|
||||
}
|
Loading…
Reference in a new issue