76 releases

0.18.0 Jun 15, 2024
0.17.5 Dec 16, 2023
0.17.4 May 28, 2022
0.17.1 Feb 17, 2022
0.1.0 Nov 27, 2019

#12 in #edn

Download history 84/week @ 2024-07-20 350/week @ 2024-07-27 18/week @ 2024-08-03 22/week @ 2024-08-10 8/week @ 2024-08-17 17/week @ 2024-08-24 18/week @ 2024-08-31 23/week @ 2024-09-07 14/week @ 2024-09-14 150/week @ 2024-09-21 75/week @ 2024-09-28 16/week @ 2024-10-05 18/week @ 2024-10-12 15/week @ 2024-10-19 18/week @ 2024-10-26 36/week @ 2024-11-02

89 downloads per month
Used in 5 crates

MIT license

125KB
2.5K SLoC

DEPRECATION WARNING

Development is currently halted for this project. No new features are under development, but also no breaking changes. If you have a critical issue, we might take a look. You may find Grinkers/clojure-reader as a suitable alternative, it has been built by one of our maintainers @Grinkers.

edn-rs

Build Status codecov

A crate to parse and emit EDN (Extensible Data Notation)

  • 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)

Full integration examples:

Usage

Default

Includes features std and sets.

[dependencies]
edn-rs = "0.17.4"

no_std

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 }

Optional features

  • std: Implements (de)serialization for Hashmap and HashSet; Also some floating point functionality.
  • sets: Implements (de)serialization for EDN sets. Depends on ordered-float.
  • json: Implements json->edn and edn->json conversions. Depends on regex.

Quick reference

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 via edn-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 via edn-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 your Cargo.toml dependencies the following line edn-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 feature json to be activated. To enable this feature add to your Cargo.toml dependencies the following line edn-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(())
}

Edn-rs Current Features

  • Define struct to map EDN info EdnNode
  • Define EDN types, EdnType
    • Edn Type into primitive: Edn::Bool(true).into() -> true. This was done by to_float, to_bool, to_int, to_vec.
    • implement futures::Future trait to Edn
    • to_string() for Edn.
    • to_debug() for Edn.
  • Parse EDN data from_str:
    • nil ""
    • String "\"string\""
    • Numbers "324352", "3442.234", "3/4"
    • Keywords :a
    • Symbol sym-bol-s
    • 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}}"
  • 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\"]}"
  • 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
  • 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

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:

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

Examples

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(())
}

Current Features

  • derive Serialize
  • edn_rs::to_string
  • derive Deserialize
  • let val: YourStruct = edn_rs::from_str(&str)

Dependencies

~0–540KB