taitan-orm-parser 0.1.12

Next Generation ORM based on sqlx
Documentation
use std::str::FromStr;
use crate::template_parser::structs::variable::VariableChain;
use crate::template_parser::to_sql::{SqlSegment, ToSqlSegment};
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::{digit1, multispace0};
use nom::combinator::map;
use nom::sequence::{delimited, preceded, tuple};
use nom::IResult;

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum RawPlaceholder {
    QuestionMark,
    Indexed(usize),
    Named(String),
}

impl RawPlaceholder {
    pub fn parse(input: &str) -> IResult<&str, RawPlaceholder> {
        alt((
            map(tag("?"), |_| RawPlaceholder::QuestionMark),
            map(preceded(tag("$"), digit1), |d| {
                RawPlaceholder::Indexed(usize::from_str(d).unwrap())
            }),
            map(
                preceded(tag(":"), preceded(multispace0, VariableChain::parse)),
                |v| RawPlaceholder::Named(v.to_string()),
            ),
        ))(input)
    }
}

impl ToSqlSegment for RawPlaceholder {
    fn gen_sql_segment(&self) -> SqlSegment {
        match self {
            RawPlaceholder::QuestionMark => SqlSegment::Simple("?".to_string()),
            RawPlaceholder::Indexed(i) => SqlSegment::Simple(format!("${}", i)),
            RawPlaceholder::Named(i) => SqlSegment::Simple(i.to_string()),
        }
    }
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Placeholder {
    Raw(RawPlaceholder),
    Dollar(VariableChain),
    Hash(VariableChain),
    At(VariableChain),
}
impl Placeholder {
    pub fn parse(input: &str) -> IResult<&str, Placeholder> {
        alt((
            map(RawPlaceholder::parse, Placeholder::Raw),
            delimited(
                preceded(tag("$"), preceded(multispace0, tag("{"))),
                preceded(multispace0, map(VariableChain::parse, Placeholder::Dollar)),
                preceded(multispace0, tag("}")),
            ),
            delimited(
                preceded(tag("#"), preceded(multispace0, tag("{"))),
                preceded(multispace0, map(VariableChain::parse, Placeholder::Hash)),
                preceded(multispace0, tag("}")),
            ),
            delimited(
                preceded(tag("@"), preceded(multispace0, tag("{"))),
                preceded(multispace0, map(VariableChain::parse, Placeholder::At)),
                preceded(multispace0, tag("}")),
            ),
        ))(input)
    }
}

impl ToSqlSegment for Placeholder {
    fn gen_sql_segment(&self) -> SqlSegment {
        match self {
            Placeholder::Dollar(p) => SqlSegment::Dollar(p.to_string()),
            Placeholder::Hash(p) => SqlSegment::Hash(p.to_string()),
            Placeholder::At(p) => SqlSegment::At(p.to_string()),
            Placeholder::Raw(p)=>p.gen_sql_segment(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::template_parser::structs::variable::Variable;
    #[test]
    fn placeholder_parser_spec_001() {
        let template = "@{ hello. name. attr1 }";
        let (_, placeholder) = Placeholder::parse(template).unwrap();
        let variable_chain = vec![
            Variable::Simple("hello".to_string()),
            Variable::Simple("name".to_string()),
            Variable::Simple("attr1".to_string()),
        ];
        assert_eq!(
            placeholder,
            Placeholder::At(VariableChain::new(variable_chain))
        );

        let template = "#{ [ hello ]. \" name \". ` attr1 ` }";
        let (_, placeholder) = Placeholder::parse(template).unwrap();
        let variable_chain = vec![
            Variable::Brackets("hello".to_string()),
            Variable::DoubleQuote("name".to_string()),
            Variable::Backquote("attr1".to_string()),
        ];
        assert_eq!(
            placeholder,
            Placeholder::Hash(VariableChain::new(variable_chain))
        );

        let template = "${ [ hello ]. name . ` attr1 ` }";
        let (_, placeholder) = Placeholder::parse(template).unwrap();
        let variable_chain = vec![
            Variable::Brackets("hello".to_string()),
            Variable::Simple("name".to_string()),
            Variable::Backquote("attr1".to_string()),
        ];
        assert_eq!(
            placeholder,
            Placeholder::Dollar(VariableChain::new(variable_chain))
        );
    }

    #[test]
    fn placeholder_parser_spec_002() {
        let template = "@{ hello. name. attr1. }";
        let parse_result = Placeholder::parse(template);
        assert!(parse_result.is_err());
        if let Err(nom::Err::Error(error)) = parse_result {
            assert_eq!(error.to_string(), "error Tag at: . }");
        }
    }
}