Plugx Input (work-in-progress)

A simple and flexible data-structure for configuration and state manipulation of plugins.

Package | Documentation | Repository


Demo

```rust use plugx_input::Input;

let mut map = Input::newmap(); map.mapmut().unwrap().insert("key".into(), Input::from([1, 2, 3])); let innerlist = map .mapmut() // Option<&mut Hashmap> .unwrap() // &mut Hashmap .getmut("key") // Option<&mut Input> .unwrap() // &mut Input (which is a list) .listmut() // Option<&mut Vec> .unwrap(); // &mut Vec *innerlist.getmut(0).unwrap() = 3.14.into(); *innerlist.getmut(1).unwrap() = true.into(); *innerlist.getmut(2).unwrap() = "hello world".into(); asserteq!(format!("{map}"), "{\"key\":[3.14, true, \"hello world\"]}".tostring()); ```

Features

Diff

```rust use plugxinput::Input; use plugxinput::diff::{diff, InputDiff};

let mut map = Input::newmap(); // {} map.mapmut().unwrap().insert("foo".into(), Input::newmap()); // {"foo": {}} map // {"foo": {"bar": [50, 60, 70]}} .mapmut().unwrap() .getmut("foo").unwrap() .mapmut().unwrap() .insert("bar".into(), Input::from([50, 60, 70]));

let mut map2 = map.clone(); // {"foo": {"bar": [50, 60, 70]}} *map2 // {"foo": {"bar": [100, 60, 70]}} .mapmut().unwrap() .getmut("foo").unwrap() .mapmut().unwrap() .getmut("bar").unwrap() .listmut().unwrap() .getmut(0).unwrap() .int_mut().unwrap() = 100;

diff( &map, &map2, &mut |diff: InputDiff| { println!("value {} {}", diff.position(), diff.action()) } ); // prints: // value [foo][bar][0] increased by 50 ```

Merge

```rust use plugxinput::Input; use plugxinput::merge::merge;

let mut map = Input::newmap(); // {} map.mapmut().unwrap().insert("foo".into(), Input::newmap()); // {"foo": {}} map // {"foo": {"bar": false}} .mapmut().unwrap() .getmut("foo").unwrap() .mapmut().unwrap() .insert("bar".into(), false.into());

let mut map2 = Input::newmap(); // {} map2.mapmut().unwrap().insert("foo".into(), Input::newmap()); // {"foo": {}} map2 // {"foo": {"baz": true}} .mapmut().unwrap() .getmut("foo").unwrap() .mapmut().unwrap() .insert("baz".into(), true.into());

merge(&mut map, &map2); println!("{map}"); // prints: // {"foo": {"bar": false, "baz": true}} ```

Validation with human-readable errors

```rust use plugxinput::Input; use plugxinput::validation::validate; use plugx_input::definition::{InputDefinition, InputDefinitionType};

let rules_json = r#"
    {
        "type": "static_map",
        "definitions": {
            "foo": {"definition": {"type": "boolean"}},
            "bar": {"definition": {"type": "string"}, "default": "hello world"},
            "baz": {"definition": {"type": "enum", "items": ["x", "y", "z"]}, "default": "y"},
            "qux": {
                "definition": {
                    "type": "either",
                    "definitions": [
                        {"type": "enum", "items": ["yes", "y", "no", "n"]},
                        {"type": "boolean"}
                    ]
                }
            }
        }
    }
"#;
// Also we could programmatically build `rules`:
let rules: InputDefinitionType = serde_json::from_str(&rules_json).unwrap();

let mut map = Input::new_map();
let error = validate(&mut map, &rules.clone().into(), None).err().unwrap();
println!("{error}");
// prints:
//    qux is not set (expected a value that must be enum with possible values ["yes", "y", "no", "n"] or boolean)

map.map_mut().unwrap().insert("qux".into(), "yes".into());
let error = validate(&mut map, &rules.clone().into(), None).err().unwrap();
println!("{error}");
// prints:
//    foo is not set (expected boolean)

map.map_mut().unwrap().insert("foo".into(), false.into());
assert!(validate(&mut map, &rules.clone().into(), None).is_ok());

// Default values:
assert_eq!(
    map.map_ref().unwrap().get("bar").unwrap().str_ref().unwrap(),
    "hello world"
);
assert_eq!(
    map.map_ref().unwrap().get("baz").unwrap().str_ref().unwrap(),
    "y"
);

```

Cargo features

To contributors

I ❤️ PR from everyone, and I appreciate your help but before opening a PR, file an issue and describe your feature, fix, etc.