use core::fmt;
use crate::ast::core::Template;
use crate::ast::{Expr, TemplateElement};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Value {
Expression(Expr),
Number(String),
String(Template),
Boolean(bool),
List {
space0: String,
elements: Vec<ListElement>,
},
Object {
space0: String,
elements: Vec<ObjectElement>,
},
Null,
}
impl Value {
pub fn _type(&self) -> String {
match self {
Value::Number(_) => "number".to_string(),
Value::Null => "null".to_string(),
Value::Boolean(_) => "boolean".to_string(),
Value::List { .. } => "list".to_string(),
Value::Object { .. } => "object".to_string(),
Value::String(_) => "string".to_string(),
Value::Expression(_) => "expression".to_string(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ListElement {
pub space0: String,
pub value: Value,
pub space1: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ObjectElement {
pub space0: String,
pub name: Template,
pub space1: String,
pub space2: String,
pub value: Value,
pub space3: String,
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
Value::Expression(expr) => format!("{{{{{expr}}}}}"),
Value::Number(s) => s.to_string(),
Value::String(template) => format!("\"{template}\""),
Value::Boolean(value) => {
if *value {
"true".to_string()
} else {
"false".to_string()
}
}
Value::List { space0, elements } => {
let elements = elements
.iter()
.map(|e| e.to_string())
.collect::<Vec<String>>();
format!("[{}{}]", space0, elements.join(","))
}
Value::Object { space0, elements } => {
let elements = elements
.iter()
.map(|e| e.to_string())
.collect::<Vec<String>>();
format!("{{{}{}}}", space0, elements.join(","))
}
Value::Null => "null".to_string(),
};
write!(f, "{s}")
}
}
impl fmt::Display for ListElement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = String::new();
s.push_str(self.space0.as_str());
s.push_str(self.value.to_string().as_str());
s.push_str(self.space1.as_str());
write!(f, "{s}")
}
}
impl fmt::Display for ObjectElement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = String::new();
s.push_str(self.space0.as_str());
s.push('"');
s.push_str(self.name.to_string().as_str());
s.push('"');
s.push_str(self.space1.as_str());
s.push(':');
s.push_str(self.space2.as_str());
s.push_str(self.value.to_string().as_str());
s.push_str(self.space3.as_str());
write!(f, "{s}")
}
}
impl Value {
pub fn encoded(&self) -> String {
match self {
Value::Expression(expr) => format!("{{{{{expr}}}}}"),
Value::Number(s) => s.to_string(),
Value::String(template) => template.encoded(),
Value::Boolean(value) => {
if *value {
"true".to_string()
} else {
"false".to_string()
}
}
Value::List { space0, elements } => {
let elements = elements
.iter()
.map(|e| e.encoded())
.collect::<Vec<String>>();
format!("[{}{}]", space0, elements.join(","))
}
Value::Object { space0, elements } => {
let elements = elements
.iter()
.map(|e| e.encoded())
.collect::<Vec<String>>();
format!("{{{}{}}}", space0, elements.join(","))
}
Value::Null => "null".to_string(),
}
}
}
impl ListElement {
fn encoded(&self) -> String {
let mut s = String::new();
s.push_str(self.space0.as_str());
s.push_str(self.value.encoded().as_str());
s.push_str(self.space1.as_str());
s
}
}
impl ObjectElement {
fn encoded(&self) -> String {
let mut s = String::new();
s.push_str(self.space0.as_str());
s.push_str(self.name.encoded().as_str());
s.push_str(self.space1.as_str());
s.push(':');
s.push_str(self.space2.as_str());
s.push_str(self.value.encoded().as_str());
s.push_str(self.space3.as_str());
s
}
}
impl Template {
pub fn encoded(&self) -> String {
let mut s = String::new();
if let Some(d) = self.delimiter {
s.push(d)
}
let elements: Vec<String> = self.elements.iter().map(|e| e.encoded()).collect();
s.push_str(elements.join("").as_str());
if let Some(d) = self.delimiter {
s.push(d)
}
s
}
}
impl TemplateElement {
fn encoded(&self) -> String {
match self {
TemplateElement::String { encoded, .. } => encoded.to_string(),
TemplateElement::Expression(expr) => format!("{{{{{expr}}}}}"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{Pos, SourceInfo, TemplateElement, Variable, Whitespace};
#[test]
fn test_to_string() {
assert_eq!(
"{{x}}".to_string(),
Value::Expression(Expr {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
},
variable: Variable {
name: "x".to_string(),
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
},
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
},
})
.to_string()
);
assert_eq!("1".to_string(), Value::Number("1".to_string()).to_string());
assert_eq!(
"\"hello\"".to_string(),
Value::String(Template {
delimiter: None,
elements: vec![TemplateElement::String {
value: "hello".to_string(),
encoded: "hello".to_string(),
}],
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
})
.to_string()
);
assert_eq!("true".to_string(), Value::Boolean(true).to_string());
assert_eq!(
"[]".to_string(),
Value::List {
space0: String::new(),
elements: vec![],
}
.to_string()
);
assert_eq!(
"[1, 2, 3]".to_string(),
Value::List {
space0: String::new(),
elements: vec![
ListElement {
space0: String::new(),
value: Value::Number("1".to_string()),
space1: String::new(),
},
ListElement {
space0: " ".to_string(),
value: Value::Number("2".to_string()),
space1: String::new(),
},
ListElement {
space0: " ".to_string(),
value: Value::Number("3".to_string()),
space1: String::new(),
}
],
}
.to_string()
);
assert_eq!(
"{}".to_string(),
Value::Object {
space0: String::new(),
elements: vec![],
}
.to_string()
);
assert_eq!(
"{ \"id\": 123 }".to_string(),
Value::Object {
space0: String::new(),
elements: vec![ObjectElement {
space0: " ".to_string(),
name: Template {
delimiter: Some('"'),
elements: vec![TemplateElement::String {
value: "id".to_string(),
encoded: "id".to_string(),
}],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
},
space1: String::new(),
space2: " ".to_string(),
value: Value::Number("123".to_string()),
space3: " ".to_string(),
}],
}
.to_string()
);
assert_eq!("null".to_string(), Value::Null.to_string());
}
#[test]
fn test_encoded() {
assert_eq!(
TemplateElement::Expression(Expr {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
},
variable: Variable {
name: "name".to_string(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
},
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
},
})
.encoded(),
"{{name}}".to_string()
);
assert_eq!(
Template {
delimiter: None,
elements: vec![TemplateElement::Expression(Expr {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
},
variable: Variable {
name: "name".to_string(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
},
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
},
})],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
}
.encoded(),
"{{name}}".to_string()
);
}
}