edn-rs

Build Status codecov

Crate to parse and emit EDN * This lib does not make effort to conform the EDN received to EDN Spec. The lib that generated this EDN should be responsible for this. For more information on Edn Spec please visit: https://github.com/edn-format/edn. * Minimal Rust Version is 1.46+.

Our MTTA (Mean time to acknowledge) is around one day; and our TTR (Time To Resolve) can vary from a few days to a couple of weeks depending on the number of issues.

Current example usage in: * crate transistor; * atm-crux; * Rust/Clojure FFI. Deserialize Clojure Edn into Rust Struct;

Usage

Cargo.toml toml [dependencies] edn-rs = "0.17.2"

Simple time-only benchmarks of edn-rs against Clojure Edn

| Method\Lang | Rust --release | Rust --debug | Clojure | |- |- |- |- | | parse string | 77.57µs | 266.479µs | 4.712235 milis | | get-in/navigate (3 blocks) | 4.224µs | 22.861µs | 26.333 µs | | Deserialize to struct | 110.358µs | 357.054µs | 4.712235 milis | | parse with criterium | 11.348µs | - | 23.230µs|

Quick reference

Parse an EDN token into a Edn with edn! macro: ```rust use edn_rs::{ edn, Edn, List };

fn main() { let edn = edn!((sym 1.2 3 false :f nil 3/4)); let expected = Edn::List( List::new( vec![ Edn::Symbol("sym".tostring()), Edn::Double(1.2.into()), Edn::Int(3), Edn::Bool(false), Edn::Key(":f".tostring()), Edn::Nil, Edn::Rational("3/4".to_string()) ] ) );

println!("{:?}", edn);
assert_eq!(edn, expected);

} ```

Parse an EDN String with Edn::from_str: ```rust use edn_rs::{ set, map, Edn, Map, Vector, Set, }; use std::str::FromStr;

fn main() -> Result<(), String> { let ednstr = "{:a \"2\" :b [true false] :c #{:A {:a :b} nil}}"; // std::str::FromStr let edn: Edn = Edn::fromstr(edn_str);

assert_eq!(
    edn,
    Edn::Map(Map::new(
        map!{
            ":a".to_string() => Edn::Str("2".to_string()),
            ":b".to_string() => Edn::Vector(Vector::new(vec![Edn::Bool(true), Edn::Bool(false)])),
            ":c".to_string() => Edn::Set(Set::new(
                set!{
                    Edn::Map(Map::new(map!{":a".to_string() => Edn::Key(":b".to_string())})),
                    Edn::Key(":A".to_string()),
                    Edn::Nil}))}
    ))
);

assert_eq!(edn[":b"][0], Edn::Bool(true));

Ok(())

} ```

To navigate through Edn data you can just use get and get_mut:

```rust use edn_rs::{ edn, Edn, List, Map };

fn main() { let edn = edn!((sym 1.2 3 {false :f nil 3/4}));

println!("{:?}", edn);
assert_eq!(edn[1], edn!(1.2));
assert_eq!(edn[1], Edn::Double(1.2f64.into()));
assert_eq!(edn[3]["false"], edn!(:f));
assert_eq!(edn[3]["false"], Edn::Key(":f".to_string()));

} ```

Serializes Rust Types into EDN with edn-derive::Serialize ```rust use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use ednrs::{ map, set, hmap, hset }; use ednderive::Serialize;

[derive(Debug, Clone, Serialize)]

struct ExampleEdn { btreemap: BTreeMap>, btreeset: BTreeSet, hashmap: HashMap>, hashset: HashSet, tuples: (i32, bool, char), nothing: (), }

fn main() { let edn = ExampleEdn { btreemap: map!{"this is a key".tostring() => vec!["with".tostring(), "many".tostring(), "keys".tostring()]}, btreeset: set!{3i64, 4i64, 5i64}, hashmap: hmap!{"this is a key".tostring() => vec!["with".tostring(), "many".tostring(), "keys".tostring()]}, hashset: hset!{3i64}, tuples: (3i32, true, 'd'), nothing: (), };

println!("{}", edn_rs::to_string(edn));
// { :btreemap {:this-is-a-key [\"with\", \"many\", \"keys\"]}, :btreeset #{3, 4, 5}, :hashmap {:this-is-a-key [\"with\", \"many\", \"keys\"]}, :hashset #{3}, :tuples (3, true, \\d), :nothing nil, }

} ```

Deserializes Strings into Rust Types:

For now you have to implement the conversion yourself with the Deserialize trait. Soon you'll be able to have that implemented for you via edn-derive crate. ```rust use edn_rs::{Deserialize, Edn, EdnError};

[derive(Debug, PartialEq)]

struct Person { name: String, age: usize, }

impl Deserialize for Person { fn deserialize(edn: &Edn) -> Result { Ok(Self { name: ednrs::fromedn(&edn[":name"])?, age: ednrs::fromedn(&edn[":age"])?, }) } }

fn main() -> Result<(), EdnError> { let ednstr = "{:name \"rose\" :age 66}"; let person: Person = ednrs::fromstr(ednstr)?;

assert_eq!(
    person,
    Person {
        name: "rose".to_string(),
        age: 66,
    }
);

println!("{:?}", person);
// Person { name: "rose", age: 66 }

let bad_edn_str = "{:name \"rose\" :age \"some text\"}";
let person: Result<Person, EdnError> = edn_rs::from_str(bad_edn_str);

assert_eq!(
    person,
    Err(EdnError::Deserialize(
        "couldn't convert `some text` into `uint`".to_string()
    ))
);

Ok(())

} ```

Deserializes Edn types into Rust Types: * Deserialization to std::collection::* is currently unsafe.

For now you have to implement the conversion yourself with the Deserialize trait. Soon you'll be able to have that implemented for you via edn-derive crate. ```rust use edn_rs::{map, Deserialize, Edn, EdnError, Map};

[derive(Debug, PartialEq)]

struct Person { name: String, age: usize, }

impl Deserialize for Person { fn deserialize(edn: &Edn) -> Result { Ok(Self { name: ednrs::fromedn(&edn[":name"])?, age: ednrs::fromedn(&edn[":age"])?, }) } }

fn main() -> Result<(), EdnError> { let edn = Edn::Map(Map::new(map! { ":name".tostring() => Edn::Str("rose".tostring()), ":age".tostring() => Edn::UInt(66) })); let person: Person = ednrs::from_edn(&edn)?;

println!("{:?}", person);
// Person { name: "rose", age: 66 }

assert_eq!(
    person,
    Person {
        name: "rose".to_string(),
        age: 66,
    }
);

let bad_edn = Edn::Map(Map::new(map! {
    ":name".to_string() => Edn::Str("rose".to_string()),
    ":age".to_string() => Edn::Str("some text".to_string())
}));
let person: Result<Person, EdnError> = edn_rs::from_edn(&bad_edn);

assert_eq!(
    person,
    Err(EdnError::Deserialize(
        "couldn't convert `\"some text\"` into `uint`".to_string()
    ))
);

Ok(())

} ```

Emits EDN format from a Json: * This function requires feature json to be activated. To enable this feature add to your Cargo.toml dependencies the following line edn-rs = { version = 0.17.2", features = ["json"] }.

```rust use ednrs::jsonto_edn;

fn main() { let json = String::from(r#"{"hello": "world"}"#); let edn = String::from(r#"{:hello "world"}"#);

println!("{:?}", json_to_edn(json.clone()));
assert_eq!(edn, json_to_edn(json));

let complex_json = String::from(r#"{
        "people": 
        [
            {
                "name": "otavio",
                "age": 22
            },
            {
                "name": "Julia",
                "age": 32.0
            }
        ],
        "country or origin": "Brazil",
        "queerentener": true,
        "brain": null
    }"#);

println!("{:?}", json_to_edn(complex_json.clone()).replace("  ", "").replace("\n", " "));
// "{ :people  [ { :name \"otavio\", :age 22 }, { :name \"Julia\", :age 32.0 } ], :country-or-origin \"Brazil\", :queerentener true, :brain nil }"

} ```

Emits a JSON from type edn_rs::Edn. * The associated emthod is to_json(&self) and it requires feature json to be activated. To enable this feature add to your Cargo.toml dependencies the following line edn-rs = { version = 0.17.2", features = ["json"] }.

rust use std::str::FromStr; fn complex_json() { let edn = "{ :people-list [ { :first-name \"otavio\", :age 22 }, { :first-name \"Julia\", :age 32.0 } ], :country-or-origin \"Brazil\", :queerentener true, :brain nil }"; let parsed_edn : edn_rs::Edn = edn_rs::Edn::from_str(edn).unwrap(); let actual_json = parsed_edn.to_json(); let expected = String::from( "{\"brain\": null, \"countryOrOrigin\": \"Brazil\", \"peopleList\": [ {\"age\": 22, \"firstName\": \"otavio\"}, {\"age\": 32.0, \"firstName\": \"Julia\"} ], \"queerentener\": true}", ); assert_eq!( actual_json, expected ); }

tostring/todebug

to_debug emits a Debug version of Edn type. ```rust use edn_rs::edn::{Edn, Vector};

let edn = Edn::Vector(Vector::new(vec![Edn::Int(5), Edn::Int(6), Edn::Int(7)])); let expected = "Vector(Vector([Int(5), Int(6), Int(7)]))";

asserteq!(edn.todebug(), expected); ```

to_string emits a valid edn. ```rust use edn_rs::edn::{Edn, Vector};

let edn = Edn::Vector(Vector::new(vec![Edn::Int(5), Edn::Int(6), Edn::Int(7)])); let expected = "[5, 6, 7, ]";

asserteq!(edn.tostring(), expected); ```

Larger to_string example: ```rust fn complexok() -> Result<(), EdnError> { use std::str::FromStr; let ednstr = "{ :list [{:name \"rose\" :age 66 :cool true}, {:name \"josh\" :age 33 :cool false}, {:name \"eva\" :age 296 :cool true}] }";

let edn = Edn::from_str(edn_str)?;
println!("{:?}", edn.to_string());

// "{:list: [{:age 66, :cool true, :name \"rose\", }, {:age 33, :cool false, :name \"josh\", }, {:age 296, :cool true, :name \"eva\", }, ], }"

Ok(())

} ```

Using async/await with Edn type

Edn supports futures by using the feature async. To enable this feature add to your Cargo.toml dependencies the following line edn-rs = { version = 0.17.2", features = ["async"] } and you can use futures as in the following example.

```rust use edn_rs::{edn, Double, Edn, Vector}; use futures::prelude::; use futures::Future; use tokio::prelude::;

async fn foo() -> impl Future + Send { edn!([1 1.5 "hello" :key]) }

[tokio::main]

async fn main() { let edn = foo().await.await;

println!("{}", edn.to_string());
assert_eq!(edn, edn!([1 1.5 "hello" :key]));

assert_eq!(edn[1].to_float(), Some(1.5f64));

} ```

The objective of foo is to show that Edn can be wrapped with a Future. If you want to return an Edn from an async function just use:

rust async fn foo() -> Edn { edn!([1 1.5 "hello" :key]) }

Edn-rs Current Features

edn-derive

edn-derive is a proc-macro crate to (De)serialize Edn values, currently it is beta and it can be found at crates.io or at github.

Usage

Just add to your Cargo.toml the following:

toml [dependencies] edn-derive = "<version>" edn-rs = "0.17.2"

Examples

Serialize ```rust use edn_derive::Serialize;

[derive(Serialize)]

pub struct Person { name: String, age: usize, }

fn main() { let person = Person { name: "joana".tostring(), age: 290000, }; asserteq!( ednrs::tostring(person), "{ :name \"joana\", :age 290000, }" ); } ```

Deserialization ```rust use ednderive::Deserialize; use ednrs::EdnError;

// The Debug and PartialEq are only necessary because of assert_eq, you don't need them

[derive(Deserialize, Debug, PartialEq)]

pub struct Person { name: String, age: usize, }

fn main() -> Result<(), EdnError> { let edn_person = "{ :name \"joana\", :age 290000, }";

let person: Person = edn_rs::from_str(edn_person)?;

assert_eq!(
    person,
    Person {
        name: "joana".to_string(),
        age: 290000,
    }
);

Ok(())

} ```

Current Features