use std::rc::Rc;
use std::str::FromStr;
use crate::item::{Item, Node};
use crate::parser::combinators::alt::{alt2, alt3};
use crate::parser::combinators::delimited::delimited;
use crate::parser::combinators::many::many0;
use crate::parser::combinators::map::map;
use crate::parser::combinators::opt::opt;
use crate::parser::combinators::pair::pair;
use crate::parser::combinators::support::{digit0, digit1, none_of};
use crate::parser::combinators::tag::{anychar, tag};
use crate::parser::combinators::tuple::{tuple3, tuple4};
use crate::parser::{ParseError, ParseInput, StaticState};
use crate::transform::Transform;
use crate::value::Value;
use crate::xdmerror::ErrorKind;
use qualname::{NamespacePrefix, NamespaceUri};
use rust_decimal::Decimal;
pub(crate) fn literal<'a, N: Node + 'a, L>() -> Box<
dyn Fn(
ParseInput<'a, N>,
&mut StaticState<L>,
) -> Result<(ParseInput<'a, N>, Transform<N>), ParseError>
+ 'a,
>
where
L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
{
Box::new(alt2(numeric_literal::<N, L>(), string_literal::<N, L>()))
}
fn numeric_literal<'a, N: Node + 'a, L>() -> Box<
dyn Fn(
ParseInput<'a, N>,
&mut StaticState<L>,
) -> Result<(ParseInput<'a, N>, Transform<N>), ParseError>
+ 'a,
>
where
L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
{
Box::new(alt3(
double_literal::<N, L>(),
decimal_literal::<N, L>(),
integer_literal::<N, L>(),
))
}
fn integer_literal<'a, N: Node + 'a, L>() -> Box<
dyn Fn(
ParseInput<'a, N>,
&mut StaticState<L>,
) -> Result<(ParseInput<'a, N>, Transform<N>), ParseError>
+ 'a,
>
where
L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
{
Box::new(map(digit1(), |s: String| {
s.parse::<i64>().map_or(
Transform::Error(ErrorKind::ParseError, String::from("not a valid integer")),
|n| Transform::Literal(Item::Value(Rc::new(Value::from(n)))),
)
}))
}
fn decimal_literal<'a, N: Node + 'a, L>() -> Box<
dyn Fn(
ParseInput<'a, N>,
&mut StaticState<L>,
) -> Result<(ParseInput<'a, N>, Transform<N>), ParseError>
+ 'a,
>
where
L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
{
Box::new(alt2(
decimal_literal_frac::<N, L>(),
decimal_literal_comp::<N, L>(),
))
}
fn decimal_literal_frac<'a, N: Node + 'a, L>() -> Box<
dyn Fn(
ParseInput<'a, N>,
&mut StaticState<L>,
) -> Result<(ParseInput<'a, N>, Transform<N>), ParseError>
+ 'a,
>
where
L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
{
Box::new(map(pair(tag("."), digit1()), |(_, mut f)| {
f.insert(0, '.');
let n = f.parse::<f64>();
let i = match n {
Ok(m) => Value::from(m),
Err(_) => {
f.insert(0, '0');
Value::from(Decimal::from_str(&f).unwrap())
}
};
Transform::Literal(Item::Value(Rc::new(i)))
}))
}
fn decimal_literal_comp<'a, N: Node + 'a, L>() -> Box<
dyn Fn(
ParseInput<'a, N>,
&mut StaticState<L>,
) -> Result<(ParseInput<'a, N>, Transform<N>), ParseError>
+ 'a,
>
where
L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
{
Box::new(map(tuple3(digit1(), tag("."), digit0()), |(w, _, f)| {
let s = format!("{}.{}", w, f);
let n = s.parse::<f64>();
let i = match n {
Ok(m) => Value::from(m),
Err(_) => Value::from(Decimal::from_str(&s).unwrap()),
};
Transform::Literal(Item::Value(Rc::new(i)))
}))
}
fn double_literal<'a, N: Node + 'a, L>() -> Box<
dyn Fn(
ParseInput<'a, N>,
&mut StaticState<L>,
) -> Result<(ParseInput<'a, N>, Transform<N>), ParseError>
+ 'a,
>
where
L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
{
Box::new(alt2(
double_literal_frac::<N, L>(),
double_literal_comp::<N, L>(),
))
}
fn double_literal_frac<'a, N: Node + 'a, L>() -> Box<
dyn Fn(
ParseInput<'a, N>,
&mut StaticState<L>,
) -> Result<(ParseInput<'a, N>, Transform<N>), ParseError>
+ 'a,
>
where
L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
{
Box::new(map(
tuple4(
pair(tag("."), digit1()),
alt2(tag("e"), tag("E")),
opt(alt2(map(tag("+"), |_| "+"), map(tag("-"), |_| "-"))),
digit1(),
"xpath literal",
),
|((_, f), _, s, e)| {
let n = format!("0.{}e{}{}", f, s.unwrap_or(""), e).parse::<f64>();
let i = match n {
Ok(m) => Value::from(m),
Err(_) => panic!("unable to convert to double"),
};
Transform::Literal(Item::Value(Rc::new(i)))
},
))
}
fn double_literal_comp<'a, N: Node + 'a, L>() -> Box<
dyn Fn(
ParseInput<'a, N>,
&mut StaticState<L>,
) -> Result<(ParseInput<'a, N>, Transform<N>), ParseError>
+ 'a,
>
where
L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
{
Box::new(map(
tuple4(
tuple3(digit1(), tag("."), digit1()),
alt2(tag("e"), tag("E")),
opt(alt2(map(tag("+"), |_| "+"), map(tag("-"), |_| "-"))),
digit1(),
"xpath literal",
),
|((c, _, f), _, s, e)| {
let n = format!("{}.{}e{}{}", c, f, s.unwrap_or(""), e).parse::<f64>();
let i = match n {
Ok(m) => Value::from(m),
Err(_) => panic!("unable to convert to double"),
};
Transform::Literal(Item::Value(Rc::new(i)))
},
))
}
fn string_literal_double<'a, N: Node + 'a, L>() -> Box<
dyn Fn(
ParseInput<'a, N>,
&mut StaticState<L>,
) -> Result<(ParseInput<'a, N>, Transform<N>), ParseError>
+ 'a,
>
where
L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
{
Box::new(map(
delimited(
anychar('"'),
map(
many0(alt2(map(tag("\"\""), |_| "\""), none_of("\""))),
|v| {
v.iter().fold(String::new(), |mut t, w| {
t.push_str(w);
t
})
},
),
anychar('"'),
),
|s| Transform::Literal(Item::Value(Rc::new(Value::from(s)))),
))
}
fn string_literal_single<'a, N: Node + 'a, L>() -> Box<
dyn Fn(
ParseInput<'a, N>,
&mut StaticState<L>,
) -> Result<(ParseInput<'a, N>, Transform<N>), ParseError>
+ 'a,
>
where
L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
{
Box::new(map(
delimited(
anychar('\''),
map(many0(alt2(map(tag("''"), |_| "'"), none_of("'"))), |v| {
v.iter().fold(String::new(), |mut t, w| {
t.push_str(w);
t
})
}),
anychar('\''),
),
|s| Transform::Literal(Item::Value(Rc::new(Value::from(s)))),
))
}
fn string_literal<'a, N: Node + 'a, L>() -> Box<
dyn Fn(
ParseInput<'a, N>,
&mut StaticState<L>,
) -> Result<(ParseInput<'a, N>, Transform<N>), ParseError>
+ 'a,
>
where
L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
{
Box::new(alt2(
string_literal_double::<N, L>(),
string_literal_single::<N, L>(),
))
}