use std::fmt::Display;
use nom::{
branch::alt, bytes::complete::tag, character::complete::char, combinator::opt, error::context,
multi::many0,
};
use crate::xpath::grammar::{
expressions::{
expr_single,
primary_expressions::enclosed_expressions::{enclosed_expr, EnclosedExpr},
ExprSingle,
},
recipes::Res,
whitespace_recipes::ws,
};
pub fn array_constructor(input: &str) -> Res<&str, ArrayConstructor> {
fn square_array_constructor_map(input: &str) -> Res<&str, ArrayConstructor> {
square_array_constructor(input)
.map(|(next_input, res)| (next_input, ArrayConstructor::SquareArrayConstructor(res)))
}
fn curly_array_constructor_map(input: &str) -> Res<&str, ArrayConstructor> {
curly_array_constructor(input)
.map(|(next_input, res)| (next_input, ArrayConstructor::CurlyArrayConstructor(res)))
}
context(
"array_constructor",
alt((square_array_constructor_map, curly_array_constructor_map)),
)(input)
}
#[derive(PartialEq, Debug, Clone)]
pub enum ArrayConstructor {
SquareArrayConstructor(SquareArrayConstructor),
CurlyArrayConstructor(CurlyArrayConstructor),
}
impl Display for ArrayConstructor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ArrayConstructor::SquareArrayConstructor(x) => write!(f, "{}", x),
ArrayConstructor::CurlyArrayConstructor(x) => write!(f, "{}", x),
}
}
}
fn square_array_constructor(input: &str) -> Res<&str, SquareArrayConstructor> {
context(
"square_array_constructor",
ws((
char('['),
opt(ws((expr_single, many0(ws((char(','), expr_single)))))),
char(']'),
)),
)(input)
.map(|(next_input, res)| {
let mut entries = Vec::new();
if let Some(res) = res.1 {
entries.push(res.0);
let extras = res.1.into_iter().map(|res| res.1);
entries.extend(extras);
}
(next_input, SquareArrayConstructor { entries })
})
}
#[derive(PartialEq, Debug, Clone)]
pub struct SquareArrayConstructor {
pub entries: Vec<ExprSingle>,
}
impl Display for SquareArrayConstructor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[")?;
for (i, entry) in self.entries.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", entry)?;
}
write!(f, "]")
}
}
fn curly_array_constructor(input: &str) -> Res<&str, CurlyArrayConstructor> {
context("curly_array_constructor", ws((tag("array"), enclosed_expr)))(input)
.map(|(next_input, res)| (next_input, CurlyArrayConstructor(res.1)))
}
#[derive(PartialEq, Debug, Clone)]
pub struct CurlyArrayConstructor(EnclosedExpr);
impl CurlyArrayConstructor {
pub fn enclosed_expr(&self) -> &EnclosedExpr {
&self.0
}
}
impl Display for CurlyArrayConstructor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "array {}", self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn square_array_constructor_should_parse_lots_of_whitespace() {
let input = "[ 1, 2, 5, 7 ]";
let (next_input, res) = square_array_constructor(input).unwrap();
assert_eq!(next_input, "");
assert_eq!(res.to_string(), "[1, 2, 5, 7]");
}
#[test]
fn square_array_constructor_should_parse_no_whitespace() {
let input = "[1,2,5,7]";
let (next_input, res) = square_array_constructor(input).unwrap();
assert_eq!(next_input, "");
assert_eq!(res.to_string(), "[1, 2, 5, 7]");
}
#[test]
fn curly_array_constructor_should_parse_lots_of_whitespace() {
let input = "array { $x }";
let (next_input, res) = curly_array_constructor(input).unwrap();
assert_eq!(next_input, "");
assert_eq!(res.to_string(), "array { $x }");
}
#[test]
fn curly_array_constructor_should_parse_no_whitespace() {
let input = "array{$x}";
let (next_input, res) = curly_array_constructor(input).unwrap();
assert_eq!(next_input, "");
assert_eq!(res.to_string(), "array { $x }");
}
}