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.
- MSRV (minimal supported rust version) is stable minus 2 versions. Once stable (1.0.0), the plan is to indefinitely maintain the MSRV.
- Current library maintainer is Kevin Nakamura (@Grinkers)
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:
Includes features std
and sets
.
[dependencies]
edn-rs = "0.17.4"
To use edn-rs
without any additional dependencies, disable default features.
edn-rs
still relies on alloc
. In no_std environments, you must supply your own #[global_allocator]
[dependencies]
edn-rs = { version = "0.17.4", default-features = false }
std
: Implements (de)serialization for Hashmap and HashSet; Also some floating point functionality.sets
: Implements (de)serialization for EDN sets. Depends onordered-float
.json
: Implements json->edn and edn->json conversions. Depends onregex
.
- Link to benchmarks implementation here
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 |
Parse an EDN token into a Edn
with edn!
macro:
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".to_string()),
Edn::Double(1.2.into()),
Edn::Int(3),
Edn::Bool(false),
Edn::Key(":f".to_string()),
Edn::Nil,
Edn::Rational("3/4".to_string())
]
)
);
println!("{:?}", edn);
assert_eq!(edn, expected);
}
Parse an EDN String with Edn::from_str
:
use edn_rs::{
set, map,
Edn, Map, Vector, Set,
};
use std::str::FromStr;
fn main() -> Result<(), String> {
let edn_str = "{:a \"2\" :b [true false] :c #{:A {:a :b} nil}}";
// std::str::FromStr
let edn: Edn = Edn::from_str(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
:
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
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use edn_rs::{
map, set, hmap, hset
};
use edn_derive::Serialize;
#[derive(Debug, Clone, Serialize)]
struct ExampleEdn {
btreemap: BTreeMap<String, Vec<String>>,
btreeset: BTreeSet<i64>,
hashmap: HashMap<String, Vec<String>>,
hashset: HashSet<i64>,
tuples: (i32, bool, char),
nothing: (),
}
fn main() {
let edn = ExampleEdn {
btreemap: map!{"this is a key".to_string() => vec!["with".to_string(), "many".to_string(), "keys".to_string()]},
btreeset: set!{3i64, 4i64, 5i64},
hashmap: hmap!{"this is a key".to_string() => vec!["with".to_string(), "many".to_string(), "keys".to_string()]},
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 viaedn-derive
crate.
use edn_rs::{Deserialize, Edn, EdnError};
#[derive(Debug, PartialEq)]
struct Person {
name: String,
age: u64,
}
impl Deserialize for Person {
fn deserialize(edn: &Edn) -> Result<Self, EdnError> {
Ok(Self {
name: edn_rs::from_edn(&edn[":name"])?,
age: edn_rs::from_edn(&edn[":age"])?,
})
}
}
fn main() -> Result<(), EdnError> {
let edn_str = "{:name \"rose\" :age 66}";
let person: Person = edn_rs::from_str(edn_str)?;
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 viaedn-derive
crate.
use edn_rs::{map, Deserialize, Edn, EdnError, Map};
#[derive(Debug, PartialEq)]
struct Person {
name: String,
age: u64,
}
impl Deserialize for Person {
fn deserialize(edn: &Edn) -> Result<Self, EdnError> {
Ok(Self {
name: edn_rs::from_edn(&edn[":name"])?,
age: edn_rs::from_edn(&edn[":age"])?,
})
}
}
fn main() -> Result<(), EdnError> {
let edn = Edn::Map(Map::new(map! {
":name".to_string() => Edn::Str("rose".to_string()),
":age".to_string() => Edn::UInt(66)
}));
let person: Person = edn_rs::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 yourCargo.toml
dependencies the following lineedn-rs = { version = 0.17.4", features = ["json"] }
.
use edn_rs::json_to_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": "eva",
"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 \"eva\", :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 featurejson
to be activated. To enable this feature add to yourCargo.toml
dependencies the following lineedn-rs = { version = 0.17.4", features = ["json"] }
.
use std::str::FromStr;
fn complex_json() {
let edn = "{
:people-list [
{ :first-name \"eva\", :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\": \"eva\"},
{\"age\": 32.0, \"firstName\": \"Julia\"}
],
\"queerentener\": true}",
);
assert_eq!(
actual_json,
expected
);
}
to_string/to_debug
to_debug
emits a Debug version of Edn
type.
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)]))";
assert_eq!(edn.to_debug(), expected);
to_string
emits a valid edn.
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, ]";
assert_eq!(edn.to_string(), expected);
Larger to_string
example:
fn complex_ok() -> Result<(), EdnError> {
use std::str::FromStr;
let edn_str = "{ :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(())
}
- Define
struct
to map EDN infoEdnNode
- Define EDN types,
EdnType
- Edn Type into primitive:
Edn::Bool(true).into() -> true
. This was done byto_float
,to_bool
,to_int
,to_vec
. - implement
futures::Future
trait toEdn
-
to_string()
forEdn
. -
to_debug()
forEdn
.
- Edn Type into primitive:
- Parse EDN data
from_str
:- nil
""
- String
"\"string\""
- Numbers
"324352"
,"3442.234"
,"3/4"
- Keywords
:a
- Symbol
sym-bol-s
with a maximum of 200 chars - Vector
"[1 :2 \"d\"]"
- List
"(1 :2 \"d\")"
- Set
"#{1 2 3}"
- Map
"{:a 1 :b 2 }"
- Tag
#inst \"yyyy-mm-ddTHH:MM:ss\"
,#uuid \"<some-uuid>\"
as string data (no custom reader support) - Nested structures
"{:a \"2\" :b [true false] :c #{:A {:a :b} nil}}"
- nil
- Simple data structures in one another
edn!
:- Vec in Vec
"[1 2 [:3 \"4\"]]"
- Set in Vec
"[1 2 #{:3 \"4\"}]"
- List in List
"(1 2 (:3 \"4\"))"
- List in Set
"'#{1 2 (:3 \"4\")}"
- Maps in general
"{:a 2 :b {:3 \"4\"}}"
,"{:a 2 :b [:3 \"4\"]}"
- Vec in Vec
- Multiple simple data structures in one another (Map and Set in a vector)
- Multi deepen data structures (Map in a Set in a List in a Vec in a Vec)
- Navigate through Edn Data
- Navigate through Sets. DOne by
set_iter
- Navigate through Sets. DOne by
- Json to Edn
- Json String to EDN String
- macro to process Structs and Enums to EDN
- trait Deserialize EDN to Struct
- trait Serialize struct to EDN
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
.
Just add to your Cargo.toml
the following:
[dependencies]
edn-derive = "<version>"
edn-rs = "0.17.4"
Serialize
use edn_derive::Serialize;
#[derive(Serialize)]
pub struct Person {
name: String,
age: u64,
}
fn main() {
let person = Person {
name: "joana".to_string(),
age: 290000,
};
assert_eq!(
edn_rs::to_string(person),
"{ :name \"joana\", :age 290000, }"
);
}
Deserialization
use edn_derive::Deserialize;
use edn_rs::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: u64,
}
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(())
}
-
derive Serialize
-
edn_rs::to_string
-
derive Deserialize
-
let val: YourStruct = edn_rs::from_str(&str)