1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#![no_std]
#![recursion_limit = "512"]

#[macro_use]
mod macros;

extern crate alloc;
#[cfg(feature = "std")]
extern crate std;

use alloc::string::String;

/// Edn type implementation
pub mod edn;

/// Serialization module for most possible types.
/// Tuples are limited between `(A, B)` and `(A, B, C, D, E, F)`, any other tuple needs to be implemented by the `trait Serialize`.
/// This module requires `#[macro_use]` for `structs`.
///
/// Example:
/// ```rust
/// use std::collections::{BTreeMap, BTreeSet};
/// use edn_derive::Serialize;
/// use edn_rs::{set, map, edn::Edn};
///
/// #[derive(Serialize)]
/// struct ExampleEdn {
///     map: BTreeMap<String, Vec<String>>,
///     set: BTreeSet<i64>,
///     tuples: (i32, bool, char),
/// }
/// fn main() {
///     let edn = ExampleEdn {
///         map: map!{"this is a key".to_string() => vec!["with".to_string(), "many".to_string(), "keys".to_string()]},
///         set: set!{3i64, 4i64, 5i64},
///         tuples: (3i32, true, 'd')
///     };
///     println!("{}", edn_rs::to_string(&edn));
///     // { :map {:this-is-a-key ["with", "many", "keys"]}, :set #{3, 4, 5}, :tuples (3, true, \d), }
/// }
///```
#[allow(clippy::needless_doctest_main)]
pub mod serialize;

#[cfg(feature = "json")]
pub(crate) mod json;

#[cfg(feature = "json")]
use alloc::borrow::Cow;
#[cfg(feature = "json")]
use alloc::string::ToString;

mod deserialize;
/// `json_to_edn` receives a json string and parses its common key-values to a regular EDN format. It requires feature `json`
/// tested examples are:
/// 1. `"{\"hello world\": \"julia\"}"` becomes `"{:hello-world \"julia\"}"`
/// 2. `"{\"hello\": null}"` becomes `"{:hello nil}"`
/// 3. `{\"hello\": 'c'}` becomes `"{:hello \\c}"`
/// 4. `"{\"multi_string with underscore\": 545643}"` becomes `"{:multi-string-with-underscore 545643}"`
///
/// ```
/// use edn_rs::json_to_edn;
///
/// fn emits_helloworld_edn() {
///     let json = String::from("{\"hello\": \"world\"}");
///     let edn = String::from("{:hello \"world\"}");
///
///     assert_eq!(edn, json_to_edn(json));
/// }
///
/// fn emits_vec_of_map_edn() {
///     let json = String::from("[{\"hello\": \"world\"}, {\"hello\": \"julia\"}, {\"hello\": \"serde\"}");
///     let edn = String::from("[{:hello \"world\"} {:hello \"julia\"} {:hello \"serde\"}]");
///
///     assert_eq!(edn, json_to_edn(json));
/// }
/// ```
#[cfg(feature = "json")]
#[allow(clippy::missing_panics_doc)] // Our regex's don't rely on user-input
pub fn json_to_edn<'a>(json: impl AsRef<str>) -> Cow<'a, str> {
    use regex::{Captures, Regex};

    // Convert string keys to EDN keywords
    let re = Regex::new(r#""\w*(\s\w*)*":"#).unwrap();
    let json = re.replace_all(json.as_ref(), |caps: &Captures<'_>| {
        let mut rcap = caps[0].replace(['\"', ':'], "").replace(['_', ' '], "-");
        rcap.insert(0, ':');
        rcap.to_string()
    });

    // Convert chars
    let c_re = Regex::new(r"'.'").unwrap();
    let json = c_re.replace_all(&json[..], |caps: &Captures<'_>| {
        let mut rcap = caps[0].replace('\'', "");
        rcap.insert(0, '\\');
        rcap.to_string()
    });

    json.replace("null", "nil").into()
}

pub use deserialize::{from_edn, from_str, Deserialize};
pub use edn::Error as EdnError;
#[cfg(feature = "sets")]
pub use edn::Set;
pub use edn::{Edn, List, Map, Vector};
pub use serialize::Serialize;

/// Function for converting Rust types into EDN Strings.
/// For it to work, the type must implement the Serialize trait.
/// Use `#[derive(Serialize)]` from `edn-derive` crate.
///
/// Example:
/// ```rust
/// use std::collections::{BTreeMap, BTreeSet};
/// use edn_derive::Serialize;
/// use edn_rs::{set, map, edn::Edn};
///
/// #[derive(Debug, Serialize)]
/// struct ExampleEdn {
///     map: BTreeMap<String, Vec<String>>,
///     set: BTreeSet<i64>,
///     tuples: (i32, bool, char),
/// }
///
/// fn main() {
///     let edn = ExampleEdn {
///         map: map!{"this is a key".to_string() => vec!["with".to_string(), "many".to_string(), "keys".to_string()]},
///         set: set!{3i64, 4i64, 5i64},
///         tuples: (3i32, true, 'd')
///     };
///     println!("{}", edn_rs::to_string(&edn));
///     // { :map {:this-is-a-key ["with", "many", "keys"]}, :set #{3, 4, 5}, :tuples (3, true, \d), }
/// }
///```
#[allow(clippy::needless_doctest_main)]
pub fn to_string<T: Serialize>(t: &T) -> String {
    t.serialize()
}