use crate::lexer::{StringType, Token, TokenType};
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt::Display;
pub type Object = BTreeMap<String, Value>;
#[derive(Debug, PartialEq, Clone)]
pub enum Value {
Token(Token),
Object(Object),
Array(Vec<Value>),
}
impl Display for Value {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Value::Token(token) => write!(f, "{}", token.literal),
Value::Object(object) => write!(f, "{:?}", object),
Value::Array(array) => write!(f, "{:?}", array),
}
}
}
#[derive(Debug, PartialEq)]
pub enum MapperError {
UnexpectedToken(Token),
}
pub struct Mapper {
pub token_list: Vec<Token>,
pub position: usize,
}
impl Mapper {
pub fn new(token_list: Vec<Token>) -> Self {
Mapper {
token_list,
position: 0,
}
}
fn read_token(&mut self) -> Token {
let token = self.token_list[self.position].clone();
self.position += 1;
token
}
fn peek_token(&mut self) -> Token {
self.token_list[self.position].clone()
}
fn expect(&mut self, token_type: TokenType) -> Result<Token, MapperError> {
let token = self.read_token();
if token.token_type != token_type {
return Err(MapperError::UnexpectedToken(token));
}
Ok(token)
}
fn parse_array(&mut self) -> Result<Vec<Value>, MapperError> {
let mut array = Vec::new();
self.expect(TokenType::LBracket)?; loop {
let token = self.peek_token();
if token.token_type == TokenType::RBracket {
break;
}
if token.token_type == TokenType::LBrace {
let object = self.parse_object()?;
array.push(Value::Object(object));
} else {
let token = self.read_token();
array.push(Value::Token(token));
}
let token = self.read_token();
match token.token_type {
TokenType::Comma => continue,
TokenType::RBracket => break,
_ => return Err(MapperError::UnexpectedToken(token)),
}
}
Ok(array)
}
fn parse_value(&mut self) -> Result<(String, Value), MapperError> {
let key_token = self.expect(TokenType::String(StringType::SimpleString))?;
self.expect(TokenType::Colon)?;
let value_token = self.read_token();
return match value_token.token_type {
TokenType::String(_) => {
let value = Value::Token(value_token);
Ok((key_token.literal, value))
}
TokenType::Int | TokenType::Float | TokenType::ReservedString => {
let value = Value::Token(value_token);
Ok((key_token.literal, value))
}
TokenType::LBrace => {
self.position -= 1;
let value = Value::Object(self.parse_object()?);
Ok((key_token.literal, value))
}
TokenType::LBracket => {
self.position -= 1;
let value = Value::Array(self.parse_array()?);
Ok((key_token.literal, value))
}
_ => Err(MapperError::UnexpectedToken(value_token)),
};
}
pub fn parse_object(&mut self) -> Result<Object, MapperError> {
let mut object = BTreeMap::new();
self.expect(TokenType::LBrace)?;
loop {
let token = self.peek_token();
if token.token_type == TokenType::RBrace {
break;
}
let (key, value) = self.parse_value()?;
object.insert(key, value);
let token = self.read_token();
match token.token_type {
TokenType::Comma => continue,
TokenType::RBrace => break,
_ => return Err(MapperError::UnexpectedToken(token)),
}
}
Ok(object)
}
}
#[cfg(test)]
pub mod test {
use alloc::string::ToString;
#[test]
pub fn test_mapper() {
let input = r#"
{"RawData":{"Nonce":2,"Sender":"1BvsDtWX8HxAbPfdCwBhkiVvsxDf75NzMOlDrsW7p4c=","Contract":[{"Parameter":{"type_url":"type.googleapis.com/proto.TransferContract","value":"CiDfSWO7Nahu8tqpVPkMTvE2JjFI8WRp/MbE/2lp+8Po7xgB"}}],"KAppFee":500000,"BandwidthFee":1000000,"Version":1,"ChainID":"MTA4"}}
"#
.to_string();
let token_list = crate::lexer::Lexer::new(input).tokenize().unwrap();
let mut mapper = crate::mapper::Mapper::new(token_list);
let object = mapper.parse_object().unwrap();
assert_eq!(object["name"].to_string(), "John");
assert_eq!(object["age"].to_string(), "30");
assert_eq!(object["isActive"].to_string(), "True");
let cars = match object["cars"] {
crate::mapper::Value::Array(ref cars) => cars,
_ => panic!("Expected array"),
};
let car1 = match cars[0] {
crate::mapper::Value::Object(ref car) => car,
_ => panic!("Expected object"),
};
let car2 = match cars[1] {
crate::mapper::Value::Object(ref car) => car,
_ => panic!("Expected object"),
};
assert_eq!(car1["name"].to_string(), "Ford");
assert_eq!(car1["plate"].to_string(), "20-13f");
assert_eq!(car2["name"].to_string(), "Fiat");
assert_eq!(car2["plate"].to_string(), "20-13f");
}
}