mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-22 15:03:28 +00:00
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
@ -34,6 +34,8 @@ rec {
|
|||||||
inherit (import ./formats/java-properties/default.nix { inherit lib pkgs; })
|
inherit (import ./formats/java-properties/default.nix { inherit lib pkgs; })
|
||||||
javaProperties;
|
javaProperties;
|
||||||
|
|
||||||
|
libconfig = (import ./formats/libconfig/default.nix { inherit lib pkgs; }).format;
|
||||||
|
|
||||||
json = {}: {
|
json = {}: {
|
||||||
|
|
||||||
type = with lib.types; let
|
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 New Issue
Block a user