edn_rs/
lib.rs

1#![no_std]
2#![recursion_limit = "512"]
3
4#[macro_use]
5mod macros;
6
7extern crate alloc;
8#[cfg(feature = "std")]
9extern crate std;
10
11use alloc::string::String;
12
13/// Edn type implementation
14pub mod edn;
15
16/// Serialization module for most possible types.
17///
18/// Tuples are limited between `(A, B)` and `(A, B, C, D, E, F)`, any other tuple needs to be implemented by the `trait Serialize`.
19/// This module requires `#[macro_use]` for `structs`.
20///
21/// Example:
22/// ```rust
23/// use std::collections::{BTreeMap, BTreeSet};
24/// use edn_derive::Serialize;
25/// use edn_rs::{set, map, edn::Edn};
26///
27/// #[derive(Serialize)]
28/// struct ExampleEdn {
29///     map: BTreeMap<String, Vec<String>>,
30///     set: BTreeSet<i64>,
31///     tuples: (i32, bool, char),
32/// }
33/// fn main() {
34///     let edn = ExampleEdn {
35///         map: map!{"this is a key".to_string() => vec!["with".to_string(), "many".to_string(), "keys".to_string()]},
36///         set: set!{3i64, 4i64, 5i64},
37///         tuples: (3i32, true, 'd')
38///     };
39///     println!("{}", edn_rs::to_string(&edn));
40///     // { :map {:this-is-a-key ["with", "many", "keys"]}, :set #{3, 4, 5}, :tuples (3, true, \d), }
41/// }
42///```
43#[allow(clippy::needless_doctest_main)]
44pub mod serialize;
45
46#[cfg(feature = "json")]
47pub(crate) mod json;
48
49#[cfg(feature = "json")]
50use alloc::borrow::Cow;
51#[cfg(feature = "json")]
52#[allow(unused_imports)]
53use alloc::string::ToString;
54
55mod deserialize;
56/// `json_to_edn` receives a json string and parses its common key-values to a regular EDN format. It requires feature `json`
57/// tested examples are:
58/// 1. `"{\"hello world\": \"julia\"}"` becomes `"{:hello-world \"julia\"}"`
59/// 2. `"{\"hello\": null}"` becomes `"{:hello nil}"`
60/// 3. `{\"hello\": 'c'}` becomes `"{:hello \\c}"`
61/// 4. `"{\"multi_string with underscore\": 545643}"` becomes `"{:multi-string-with-underscore 545643}"`
62///
63/// ```
64/// use edn_rs::json_to_edn;
65///
66/// fn emits_helloworld_edn() {
67///     let json = String::from("{\"hello\": \"world\"}");
68///     let edn = String::from("{:hello \"world\"}");
69///
70///     assert_eq!(edn, json_to_edn(json));
71/// }
72///
73/// fn emits_vec_of_map_edn() {
74///     let json = String::from("[{\"hello\": \"world\"}, {\"hello\": \"julia\"}, {\"hello\": \"serde\"}");
75///     let edn = String::from("[{:hello \"world\"} {:hello \"julia\"} {:hello \"serde\"}]");
76///
77///     assert_eq!(edn, json_to_edn(json));
78/// }
79/// ```
80#[cfg(feature = "json")]
81#[allow(clippy::missing_panics_doc)] // Our regex's don't rely on user-input
82pub fn json_to_edn<'a>(json: impl AsRef<str>) -> Cow<'a, str> {
83    use regex::{Captures, Regex};
84
85    // Convert string keys to EDN keywords
86    let re = Regex::new(r#""\w*(\s\w*)*":"#).unwrap();
87    let json = re.replace_all(json.as_ref(), |caps: &Captures<'_>| {
88        let mut rcap = caps[0].replace(['\"', ':'], "").replace(['_', ' '], "-");
89        rcap.insert(0, ':');
90        rcap.clone()
91    });
92
93    // Convert chars
94    let c_re = Regex::new(r"'.'").unwrap();
95    let json = c_re.replace_all(&json[..], |caps: &Captures<'_>| {
96        let mut rcap = caps[0].replace('\'', "");
97        rcap.insert(0, '\\');
98        rcap.clone()
99    });
100
101    json.replace("null", "nil").into()
102}
103
104pub use deserialize::{Deserialize, from_edn, from_str};
105pub use edn::Error as EdnError;
106#[cfg(feature = "sets")]
107pub use edn::Set;
108pub use edn::{Edn, List, Map, Vector};
109pub use serialize::Serialize;
110
111/// Function for converting Rust types into EDN Strings.
112/// For it to work, the type must implement the Serialize trait.
113/// Use `#[derive(Serialize)]` from `edn-derive` crate.
114///
115/// Example:
116/// ```rust
117/// use std::collections::{BTreeMap, BTreeSet};
118/// use edn_derive::Serialize;
119/// use edn_rs::{set, map, edn::Edn};
120///
121/// #[derive(Debug, Serialize)]
122/// struct ExampleEdn {
123///     map: BTreeMap<String, Vec<String>>,
124///     set: BTreeSet<i64>,
125///     tuples: (i32, bool, char),
126/// }
127///
128/// fn main() {
129///     let edn = ExampleEdn {
130///         map: map!{"this is a key".to_string() => vec!["with".to_string(), "many".to_string(), "keys".to_string()]},
131///         set: set!{3i64, 4i64, 5i64},
132///         tuples: (3i32, true, 'd')
133///     };
134///     println!("{}", edn_rs::to_string(&edn));
135///     // { :map {:this-is-a-key ["with", "many", "keys"]}, :set #{3, 4, 5}, :tuples (3, true, \d), }
136/// }
137///```
138#[allow(clippy::needless_doctest_main)]
139pub fn to_string<T: Serialize>(t: &T) -> String {
140    t.serialize()
141}