tiny_json_rs/mapper/
mod.rs

1use crate::lexer::{StringType, Token, TokenType};
2use alloc::collections::BTreeMap;
3use alloc::string::String;
4use alloc::vec::Vec;
5use core::fmt::Display;
6
7pub type Object = BTreeMap<String, Value>;
8
9#[derive(Debug, PartialEq, Clone)]
10pub enum Value {
11    Token(Token),
12    Object(Object),
13    Array(Vec<Value>),
14}
15
16impl Display for Value {
17    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
18        match self {
19            Value::Token(token) => write!(f, "{}", token.literal),
20            Value::Object(object) => write!(f, "{:?}", object),
21            Value::Array(array) => write!(f, "{:?}", array),
22        }
23    }
24}
25
26#[derive(Debug, PartialEq)]
27pub enum MapperError {
28    UnexpectedToken(Token),
29}
30
31pub struct Mapper {
32    pub token_list: Vec<Token>,
33    pub position: usize,
34}
35
36impl Mapper {
37    pub fn new(token_list: Vec<Token>) -> Self {
38        Mapper {
39            token_list,
40            position: 0,
41        }
42    }
43
44    fn read_token(&mut self) -> Token {
45        let token = self.token_list[self.position].clone();
46        self.position += 1;
47        token
48    }
49
50    fn peek_token(&mut self) -> Token {
51        self.token_list[self.position].clone()
52    }
53
54    fn expect(&mut self, token_type: TokenType) -> Result<Token, MapperError> {
55        let token = self.read_token();
56        if token.token_type != token_type {
57            return Err(MapperError::UnexpectedToken(token));
58        }
59        Ok(token)
60    }
61
62    fn parse_array(&mut self) -> Result<Vec<Value>, MapperError> {
63        let mut array = Vec::new();
64        self.expect(TokenType::LBracket)?; // [
65        loop {
66            let token = self.peek_token();
67            if token.token_type == TokenType::RBracket {
68                break;
69            }
70
71            if token.token_type == TokenType::LBrace {
72                let object = self.parse_object()?;
73                array.push(Value::Object(object));
74            } else {
75                let token = self.read_token();
76                array.push(Value::Token(token));
77            }
78
79            let token = self.read_token();
80            match token.token_type {
81                TokenType::Comma => continue,
82                TokenType::RBracket => break,
83                _ => return Err(MapperError::UnexpectedToken(token)),
84            }
85        }
86        Ok(array)
87    }
88
89    fn parse_value(&mut self) -> Result<(String, Value), MapperError> {
90        let key_token = self.expect(TokenType::String(StringType::SimpleString))?;
91        self.expect(TokenType::Colon)?;
92        let value_token = self.read_token();
93        return match value_token.token_type {
94            TokenType::String(_) => {
95                let value = Value::Token(value_token);
96                Ok((key_token.literal, value))
97            }
98            TokenType::Int | TokenType::Float | TokenType::ReservedString => {
99                let value = Value::Token(value_token);
100                Ok((key_token.literal, value))
101            }
102            TokenType::LBrace => {
103                self.position -= 1;
104                let value = Value::Object(self.parse_object()?);
105                Ok((key_token.literal, value))
106            }
107            TokenType::LBracket => {
108                self.position -= 1;
109                let value = Value::Array(self.parse_array()?);
110                Ok((key_token.literal, value))
111            }
112            _ => Err(MapperError::UnexpectedToken(value_token)),
113        };
114    }
115
116    pub fn parse_object(&mut self) -> Result<Object, MapperError> {
117        let mut object = BTreeMap::new();
118        self.expect(TokenType::LBrace)?;
119        loop {
120            let token = self.peek_token();
121            if token.token_type == TokenType::RBrace {
122                break;
123            }
124
125            let (key, value) = self.parse_value()?;
126            object.insert(key, value);
127            let token = self.read_token();
128            match token.token_type {
129                TokenType::Comma => continue,
130                TokenType::RBrace => break,
131                _ => return Err(MapperError::UnexpectedToken(token)),
132            }
133        }
134        Ok(object)
135    }
136}
137
138#[cfg(test)]
139pub mod test {
140    use alloc::string::ToString;
141
142    #[test]
143    pub fn test_mapper() {
144        let input = r#"
145        {
146            "name": "John",
147            "age": 30,
148            "isActive": True,
149            "cars": [
150                {
151                    "name": "Ford",
152                    "plate": "20-13f"
153                },
154                {
155                    "name": "Fiat",
156                    "plate": "20-13f"
157                }
158            ]
159        }
160        "#
161        .to_string();
162
163        let token_list = crate::lexer::Lexer::new(input).tokenize().unwrap();
164        let mut mapper = crate::mapper::Mapper::new(token_list);
165        let object = mapper.parse_object().unwrap();
166        assert_eq!(object["name"].to_string(), "John");
167        assert_eq!(object["age"].to_string(), "30");
168        assert_eq!(object["isActive"].to_string(), "True");
169
170        let cars = match object["cars"] {
171            crate::mapper::Value::Array(ref cars) => cars,
172            _ => panic!("Expected array"),
173        };
174
175        let car1 = match cars[0] {
176            crate::mapper::Value::Object(ref car) => car,
177            _ => panic!("Expected object"),
178        };
179
180        let car2 = match cars[1] {
181            crate::mapper::Value::Object(ref car) => car,
182            _ => panic!("Expected object"),
183        };
184
185        assert_eq!(car1["name"].to_string(), "Ford");
186        assert_eq!(car1["plate"].to_string(), "20-13f");
187        assert_eq!(car2["name"].to_string(), "Fiat");
188        assert_eq!(car2["plate"].to_string(), "20-13f");
189    }
190}