use std::fmt;
use std::fmt::Formatter;
use crate::reader::Pos;
use crate::types::{SourceString, ToSource};
use super::json::JsonValue;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct KeyValue {
pub line_terminators: Vec<LineTerminator>,
pub space0: Whitespace,
pub key: Template,
pub space1: Whitespace,
pub space2: Whitespace,
pub value: Template,
pub line_terminator0: LineTerminator,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MultilineString {
pub space: Whitespace,
pub newline: Whitespace,
pub kind: MultilineStringKind,
}
impl fmt::Display for MultilineString {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match &self.kind {
MultilineStringKind::Text(value)
| MultilineStringKind::Json(value)
| MultilineStringKind::Xml(value) => write!(f, "{value}"),
MultilineStringKind::Raw(value) => write!(f, "{value}"),
MultilineStringKind::GraphQl(value) => write!(f, "{value}"),
}
}
}
impl ToSource for MultilineString {
fn to_source(&self) -> SourceString {
let mut source = SourceString::new();
source.push_str("```");
source.push_str(self.lang());
source.push_str(self.space.as_str());
source.push_str(self.newline.as_str());
source.push_str(self.kind.to_source().as_str());
source.push_str("```");
source
}
}
impl MultilineString {
pub fn lang(&self) -> &'static str {
match self.kind {
MultilineStringKind::Text(_) => "",
MultilineStringKind::Json(_) => "json",
MultilineStringKind::Xml(_) => "xml",
MultilineStringKind::Raw(_) => "raw",
MultilineStringKind::GraphQl(_) => "graphql",
}
}
pub fn value(&self) -> Template {
match &self.kind {
MultilineStringKind::Text(text)
| MultilineStringKind::Json(text)
| MultilineStringKind::Xml(text) => text.clone(),
MultilineStringKind::Raw(text) => text.clone(),
MultilineStringKind::GraphQl(text) => text.value.clone(),
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MultilineStringKind {
Text(Template),
Json(Template),
Xml(Template),
Raw(Template),
GraphQl(GraphQl),
}
impl ToSource for MultilineStringKind {
fn to_source(&self) -> SourceString {
match self {
MultilineStringKind::Text(value)
| MultilineStringKind::Json(value)
| MultilineStringKind::Xml(value) => value.to_source(),
MultilineStringKind::Raw(value) => value.to_source(),
MultilineStringKind::GraphQl(value) => value.to_source(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GraphQl {
pub value: Template,
pub variables: Option<GraphQlVariables>,
}
impl fmt::Display for GraphQl {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)?;
if let Some(vars) = &self.variables {
write!(f, "{}", vars.to_source())?;
}
Ok(())
}
}
impl ToSource for GraphQl {
fn to_source(&self) -> SourceString {
let mut source = SourceString::new();
source.push_str(self.value.to_source().as_str());
if let Some(vars) = &self.variables {
source.push_str(vars.to_source().as_str());
}
source
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GraphQlVariables {
pub space: Whitespace,
pub value: JsonValue,
pub whitespace: Whitespace,
}
impl ToSource for GraphQlVariables {
fn to_source(&self) -> SourceString {
let mut source = "variables".to_source();
source.push_str(self.space.as_str());
source.push_str(self.value.to_source().as_str());
source.push_str(self.whitespace.as_str());
source
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Base64 {
pub space0: Whitespace,
pub value: Vec<u8>,
pub source: SourceString,
pub space1: Whitespace,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct File {
pub space0: Whitespace,
pub filename: Template,
pub space1: Whitespace,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Template {
pub delimiter: Option<char>,
pub elements: Vec<TemplateElement>,
pub source_info: SourceInfo,
}
impl fmt::Display for Template {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut buffer = String::new();
for element in self.elements.iter() {
buffer.push_str(element.to_string().as_str());
}
write!(f, "{buffer}")
}
}
impl ToSource for Template {
fn to_source(&self) -> SourceString {
let mut s = SourceString::new();
if let Some(d) = self.delimiter {
s.push(d);
}
let elements: Vec<SourceString> = self.elements.iter().map(|e| e.to_source()).collect();
s.push_str(elements.join("").as_str());
if let Some(d) = self.delimiter {
s.push(d);
}
s
}
}
impl Template {
pub fn new(
delimiter: Option<char>,
elements: Vec<TemplateElement>,
source_info: SourceInfo,
) -> Template {
Template {
delimiter,
elements,
source_info,
}
}
pub fn is_empty(&self) -> bool {
self.elements.is_empty()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TemplateElement {
String { value: String, source: SourceString },
Placeholder(Placeholder),
}
impl fmt::Display for TemplateElement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let s = match self {
TemplateElement::String { value, .. } => value.clone(),
TemplateElement::Placeholder(value) => format!("{{{{{value}}}}}"),
};
write!(f, "{s}")
}
}
impl ToSource for TemplateElement {
fn to_source(&self) -> SourceString {
match self {
TemplateElement::String { source, .. } => source.clone(),
TemplateElement::Placeholder(value) => value.to_source(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Comment {
pub value: String,
pub source_info: SourceInfo,
}
impl ToSource for Comment {
fn to_source(&self) -> SourceString {
format!("#{}", self.value).to_source()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Whitespace {
pub value: String,
pub source_info: SourceInfo,
}
impl fmt::Display for Whitespace {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl Whitespace {
pub fn as_str(&self) -> &str {
&self.value
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Number {
Float(Float),
Integer(I64),
BigInteger(String),
}
impl fmt::Display for Number {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Number::Float(value) => write!(f, "{value}"),
Number::Integer(value) => write!(f, "{value}"),
Number::BigInteger(value) => write!(f, "{value}"),
}
}
}
impl ToSource for Number {
fn to_source(&self) -> SourceString {
match self {
Number::Float(value) => value.to_source(),
Number::Integer(value) => value.to_source(),
Number::BigInteger(value) => value.to_source(),
}
}
}
#[derive(Clone, Debug)]
pub struct Float {
value: f64,
source: SourceString,
}
impl Float {
pub fn new(value: f64, source: SourceString) -> Float {
Float { value, source }
}
pub fn as_f64(&self) -> f64 {
self.value
}
}
impl fmt::Display for Float {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl ToSource for Float {
fn to_source(&self) -> SourceString {
self.source.clone()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct U64 {
value: u64,
source: SourceString,
}
impl U64 {
pub fn new(value: u64, source: SourceString) -> U64 {
U64 { value, source }
}
pub fn as_u64(&self) -> u64 {
self.value
}
}
impl fmt::Display for U64 {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl ToSource for U64 {
fn to_source(&self) -> SourceString {
self.source.clone()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct I64 {
value: i64,
source: SourceString,
}
impl I64 {
pub fn new(value: i64, source: SourceString) -> I64 {
I64 { value, source }
}
pub fn as_i64(&self) -> i64 {
self.value
}
}
impl fmt::Display for I64 {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl ToSource for I64 {
fn to_source(&self) -> SourceString {
self.source.clone()
}
}
impl PartialEq for Float {
fn eq(&self, other: &Self) -> bool {
self.source == other.source
}
}
impl Eq for Float {}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LineTerminator {
pub space0: Whitespace,
pub comment: Option<Comment>,
pub newline: Whitespace,
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Bytes {
Json(JsonValue),
Xml(String),
MultilineString(MultilineString),
OnelineString(Template),
Base64(Base64),
File(File),
Hex(Hex),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Hex {
pub space0: Whitespace,
pub value: Vec<u8>,
pub source: SourceString,
pub space1: Whitespace,
}
impl fmt::Display for Hex {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "hex,{}{}{};", self.space0, self.source, self.space1)
}
}
#[derive(Clone, Debug)]
pub struct Regex {
pub inner: regex::Regex,
pub source: SourceString,
}
impl ToSource for Regex {
fn to_source(&self) -> SourceString {
self.source.clone()
}
}
impl fmt::Display for Regex {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl PartialEq for Regex {
fn eq(&self, other: &Self) -> bool {
self.inner.to_string() == other.inner.to_string()
}
}
impl Eq for Regex {}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct SourceInfo {
pub start: Pos,
pub end: Pos,
}
impl SourceInfo {
pub fn new(start: Pos, end: Pos) -> SourceInfo {
SourceInfo { start, end }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Placeholder {
pub space0: Whitespace,
pub expr: Expr,
pub space1: Whitespace,
}
impl fmt::Display for Placeholder {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.expr)
}
}
impl ToSource for Placeholder {
fn to_source(&self) -> SourceString {
let mut source = SourceString::new();
source.push_str("{{");
source.push_str(self.space0.as_str());
source.push_str(self.expr.to_source().as_str());
source.push_str(self.space1.as_str());
source.push_str("}}");
source
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Expr {
pub source_info: SourceInfo,
pub kind: ExprKind,
}
impl fmt::Display for Expr {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.kind)
}
}
impl ToSource for Expr {
fn to_source(&self) -> SourceString {
self.kind.to_string().to_source()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ExprKind {
Variable(Variable),
Function(Function),
}
impl fmt::Display for ExprKind {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
ExprKind::Variable(variable) => write!(f, "{variable}"),
ExprKind::Function(function) => write!(f, "{function}"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Variable {
pub name: String,
pub source_info: SourceInfo,
}
impl fmt::Display for Variable {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Function {
NewDate,
NewUuid,
}
impl fmt::Display for Function {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Function::NewDate => write!(f, "newDate"),
Function::NewUuid => write!(f, "newUuid"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::json::{JsonListElement, JsonObjectElement, JsonValue};
use crate::types::ToSource;
#[test]
fn test_float() {
assert_eq!(
Float {
value: 1.0,
source: "1.0".to_source()
}
.to_source()
.as_str(),
"1.0"
);
assert_eq!(
Float {
value: 1.0,
source: "1.0".to_source()
}
.to_string(),
"1"
);
assert_eq!(
Float {
value: 1.01,
source: "1.01".to_source()
}
.to_source()
.as_str(),
"1.01"
);
assert_eq!(
Float {
value: 1.01,
source: "1.01".to_source()
}
.to_string(),
"1.01"
);
assert_eq!(
Float {
value: 1.01,
source: "1.010".to_source()
}
.to_source()
.as_str(),
"1.010"
);
assert_eq!(
Float {
value: 1.01,
source: "1.010".to_source()
}
.to_string(),
"1.01"
);
assert_eq!(
Float {
value: -1.333,
source: "-1.333".to_source()
}
.to_source()
.as_str(),
"-1.333"
);
assert_eq!(
Float {
value: -1.333,
source: "-1.333".to_source()
}
.to_string(),
"-1.333"
);
}
fn whitespace() -> Whitespace {
Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
}
}
fn variable_placeholder() -> Placeholder {
Placeholder {
space0: whitespace(),
expr: Expr {
kind: ExprKind::Variable(Variable {
name: "name".to_string(),
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
}),
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
},
space1: whitespace(),
}
}
fn hello_template() -> Template {
Template::new(
None,
vec![
TemplateElement::String {
value: "Hello ".to_string(),
source: "Hello ".to_source(),
},
TemplateElement::Placeholder(variable_placeholder()),
TemplateElement::String {
value: "!".to_string(),
source: "!".to_source(),
},
],
SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
)
}
#[test]
fn test_template() {
assert_eq!(hello_template().to_string(), "Hello {{name}}!");
}
#[test]
fn test_template_to_source() {
assert_eq!(
"{{x}}",
JsonValue::Placeholder(Placeholder {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
},
expr: Expr {
kind: ExprKind::Variable(Variable {
name: "x".to_string(),
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
}),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
},
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
},
})
.to_source()
.as_str()
);
assert_eq!("1", JsonValue::Number("1".to_string()).to_source().as_str());
assert_eq!(
"\"hello\"",
JsonValue::String(Template::new(
Some('"'),
vec![TemplateElement::String {
value: "hello".to_string(),
source: "hello".to_source(),
}],
SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0))
))
.to_source()
.as_str()
);
assert_eq!("true", JsonValue::Boolean(true).to_source().as_str());
assert_eq!(
"[]",
JsonValue::List {
space0: String::new(),
elements: vec![],
}
.to_source()
.as_str()
);
assert_eq!(
"[1, 2, 3]",
JsonValue::List {
space0: String::new(),
elements: vec![
JsonListElement {
space0: String::new(),
value: JsonValue::Number("1".to_string()),
space1: String::new(),
},
JsonListElement {
space0: " ".to_string(),
value: JsonValue::Number("2".to_string()),
space1: String::new(),
},
JsonListElement {
space0: " ".to_string(),
value: JsonValue::Number("3".to_string()),
space1: String::new(),
}
],
}
.to_source()
.as_str()
);
assert_eq!(
"{}",
JsonValue::Object {
space0: String::new(),
elements: vec![],
}
.to_source()
.as_str()
);
assert_eq!(
"{ \"id\": 123 }",
JsonValue::Object {
space0: String::new(),
elements: vec![JsonObjectElement {
space0: " ".to_string(),
name: Template::new(
Some('"'),
vec![TemplateElement::String {
value: "id".to_string(),
source: "id".to_source(),
}],
SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1))
),
space1: String::new(),
space2: " ".to_string(),
value: JsonValue::Number("123".to_string()),
space3: " ".to_string(),
}],
}
.to_source()
.as_str()
);
assert_eq!("null", JsonValue::Null.to_source().as_str());
assert_eq!(
"{{name}}",
TemplateElement::Placeholder(Placeholder {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
},
expr: Expr {
kind: ExprKind::Variable(Variable {
name: "name".to_string(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
}),
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)),
},
})
.to_source()
.as_str(),
);
assert_eq!(
"{{name}}",
Template::new(
None,
vec![TemplateElement::Placeholder(Placeholder {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
},
expr: Expr {
kind: ExprKind::Variable(Variable {
name: "name".to_string(),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
}),
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)),
},
})],
SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1))
)
.to_source()
.as_str(),
);
}
}