use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::{alpha1, alphanumeric1, multispace0};
use nom::combinator::{map, recognize};
use nom::error::context;
use nom::multi::many0;
use nom::sequence::{delimited, pair, preceded, tuple};
use nom::IResult;
use std::fmt::{Display, Formatter};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Variable {
Simple(String),
Backquote(String),
Brackets(String),
DoubleQuote(String),
}
impl Variable {
pub fn parse(input: &str) -> IResult<&str, Variable> {
alt((
parse_quoted_variable, map(parse_simple_variable, Variable::Simple), ))(input)
}
pub fn get_ident(&self) -> &str {
match self {
Variable::Simple(ident)
| Variable::Backquote(ident)
| Variable::Brackets(ident)
| Variable::DoubleQuote(ident) => ident,
}
}
}
impl Display for Variable {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Variable::Simple(ident) => write!(f, "{}", ident),
Variable::Backquote(ident) => write!(f, "`{}`", ident),
Variable::Brackets(ident) => write!(f, "[{}]", ident),
Variable::DoubleQuote(ident) => write!(f, "\"{}\"", ident),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VariableChain {
pub variables: Vec<Variable>,
}
impl VariableChain {
pub fn new(variables: Vec<Variable>) -> VariableChain {
VariableChain { variables }
}
pub fn parse(input: &str) -> IResult<&str, VariableChain> {
context(
"VariableChain",
map(
tuple((
Variable::parse, many0(preceded(
multispace0,
preceded(tag("."), preceded(multispace0, Variable::parse)),
)), )),
|(first, rest)| VariableChain {
variables: std::iter::once(first).chain(rest.into_iter()).collect(),
},
),
)(input)
}
}
impl Display for VariableChain {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut result = String::new();
for (index, variable) in self.variables.iter().enumerate() {
if index > 0 {
result.push('.');
}
result.push_str(&variable.to_string());
}
write!(f, "{}", result)
}
}
fn parse_simple_variable(input: &str) -> IResult<&str, String> {
map(
recognize(pair(
alpha1, many0(alt((alphanumeric1, tag("_")))), )),
|s: &str| s.to_string(),
)(input)
}
fn parse_quoted_variable(input: &str) -> IResult<&str, Variable> {
alt((
map(
delimited(
tag("`"),
preceded(multispace0, parse_simple_variable),
preceded(multispace0, tag("`")),
),
|var| Variable::Backquote(var.to_string()),
),
map(
delimited(
tag("["),
preceded(multispace0, parse_simple_variable),
preceded(multispace0, tag("]")),
),
|var| Variable::Brackets(var.to_string()),
),
map(
delimited(
tag("\""),
preceded(multispace0, parse_simple_variable),
preceded(multispace0, tag("\"")),
),
|var| Variable::DoubleQuote(var.to_string()),
),
))(input)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_variable_chain_spec_001() {
let template = "sdfs.[ sdf ].` uire123`.\"gsdg \". dfdsl";
let (_, variable_chain) = VariableChain::parse(template).unwrap();
assert_eq!(
variable_chain.variables,
vec![
Variable::Simple("sdfs".to_string()),
Variable::Brackets("sdf".to_string()),
Variable::Backquote("uire123".to_string()),
Variable::DoubleQuote("gsdg".to_string()),
Variable::Simple("dfdsl".to_string()),
]
);
}
#[test]
fn test_parse_variable_chain_spec_002() {
let template = "123";
let parse_result = VariableChain::parse(template);
assert!(parse_result.is_err());
let template = "'asdfv'";
let parse_result = VariableChain::parse(template);
assert!(parse_result.is_err());
}
}