json_decode/
lib.rs

1mod decoders;
2mod map_fns;
3
4pub use decoders::{
5    and_then, boolean, fail, field, float, integer, json, list, map, option, serde, string,
6    succeed, unsigned_integer, BoxDecoder,
7};
8pub use map_fns::*;
9
10pub trait Decoder<'a, DecodesTo> {
11    // OK, so theoretically this needs to store some functions & some collection of arguments.
12    // Since functions need to be of differing lengths we probably need a trait rather than a struct
13    // with different implementations for lenghts of arguments.
14    //
15    // Structs could probably be generic over the types of the arguments?
16    //
17    // Or alternatively all functions have to take a JSON.Value enum and do the decoding based on that.
18    fn decode(&self, value: &serde_json::Value) -> Result<DecodesTo, DecodeError>;
19}
20
21#[derive(thiserror::Error, Debug, PartialEq)]
22pub enum DecodeError {
23    #[error("Could not find field {0} in {1}")]
24    MissingField(String, String),
25    #[error("Expected a {0} but found a {1}")]
26    IncorrectType(String, String),
27    #[error("Invalid integer: {0}")]
28    InvalidInteger(String),
29    #[error("Integer {0} was too big to decode as {1}")]
30    IntegerOverflow(String, &'static str),
31    #[error("Serde error: {0}")]
32    SerdeError(String),
33    #[error("Error: {0}")]
34    Other(String),
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[derive(Debug, PartialEq)]
42    struct TestStruct {
43        field_one: String,
44    }
45
46    impl TestStruct {
47        fn new(field_one: String) -> Self {
48            TestStruct { field_one }
49        }
50    }
51
52    #[test]
53    fn decode_a_struct() {
54        let decoder = map(TestStruct::new, field("field_one", string()));
55
56        let json = serde_json::from_str(r#"{"field_one": "test"}"#).unwrap();
57
58        assert_eq!(
59            decoder.decode(&json),
60            Ok(TestStruct {
61                field_one: "test".to_string()
62            })
63        )
64    }
65
66    #[derive(Debug, PartialEq)]
67    struct Test4Struct {
68        field_one: String,
69        field_two: i64,
70        field_three: bool,
71        field_four: f64,
72    }
73
74    impl Test4Struct {
75        fn new(field_one: String, field_two: i64, field_three: bool, field_four: f64) -> Self {
76            Test4Struct {
77                field_one,
78                field_two,
79                field_three,
80                field_four,
81            }
82        }
83    }
84
85    // TODO: HashMaps, Arrays etc.
86    // TODO: failure cases.
87
88    #[test]
89    fn one_of_the_macro_map_fns() {
90        let decoder = map4(
91            Test4Struct::new,
92            field("field_one", string()),
93            field("field_two", integer()),
94            field("field_three", boolean()),
95            field("field_four", float()),
96        );
97
98        let json = serde_json::json!({"field_one": "test", "field_two": 10000, "field_three": true, "field_four": 1.0});
99
100        assert_eq!(
101            decoder.decode(&json),
102            Ok(Test4Struct {
103                field_one: "test".to_string(),
104                field_two: 10000,
105                field_three: true,
106                field_four: 1.0
107            })
108        )
109    }
110
111    #[test]
112    fn decoding_a_list() {
113        let decoder = list::<_, Vec<_>>(string());
114
115        let json = serde_json::json!(["one", "two", "three", "four"]);
116
117        assert_eq!(
118            decoder.decode(&json),
119            Ok(vec![
120                "one".to_string(),
121                "two".to_string(),
122                "three".to_string(),
123                "four".to_string()
124            ])
125        )
126    }
127
128    #[test]
129    fn decoding_opt_vec_opt() {
130        let decoder = option(list::<_, Vec<_>>(option(string())));
131
132        assert_eq!(
133            decoder.decode(&serde_json::json!(["hello", null])),
134            Ok(Some(vec![Some("hello".to_string()), None]))
135        );
136        assert_eq!(decoder.decode(&serde_json::json!(null)), Ok(None))
137    }
138
139    #[test]
140    fn decode_using_serde() {}
141
142    #[test]
143    fn test_and_then() {
144        let decoder = and_then(
145            |s| {
146                if s == "ok" {
147                    succeed(Some(s))
148                } else {
149                    fail("Go Away")
150                }
151            },
152            string(),
153        );
154
155        assert_eq!(
156            decoder.decode(&serde_json::json!("ok")),
157            Ok(Some("ok".to_string()))
158        );
159
160        assert_eq!(
161            decoder.decode(&serde_json::json!("fail")),
162            Err(DecodeError::Other("Go Away".into()))
163        );
164    }
165
166    #[test]
167    #[allow(clippy::unnecessary_cast)]
168    fn decoding_integers() {
169        assert_eq!(integer().decode(&serde_json::json!(1)), Ok(1 as i32));
170        assert_eq!(integer().decode(&serde_json::json!(1)), Ok(1 as i64));
171        assert_eq!(
172            integer::<i8>().decode(&serde_json::json!(512)),
173            Err(DecodeError::IntegerOverflow("512".to_string(), "i8"))
174        );
175
176        assert_eq!(
177            unsigned_integer().decode(&serde_json::json!(1)),
178            Ok(1 as u32)
179        );
180        assert_eq!(
181            unsigned_integer().decode(&serde_json::json!(1)),
182            Ok(1 as u64)
183        );
184        assert_eq!(
185            unsigned_integer::<u8>().decode(&serde_json::json!(512)),
186            Err(DecodeError::IntegerOverflow("512".to_string(), "u8"))
187        );
188    }
189}