use super::*;
fn kvp_kserdstr_to_kserd<'a, E: CxErr<'a>>(
force_inline: bool,
) -> impl FnMut(&'a str) -> IResult<&'a str, (Kstr<'a>, Kserd<'a>), E> {
context(
"name-kserd key value pair",
separated_pair(
field_name,
ignore_inline_whitespace(char('=')),
ignore_inline_whitespace(cut(if force_inline {
kserd_inline
} else {
kserd_concise
})),
),
)
}
fn inline_cntr_kserds<'a, E: CxErr<'a>>(
i: &'a str,
) -> IResult<&'a str, Vec<(Kstr<'a>, Kserd<'a>)>, E> {
context(
"comma separated kserdstr-kserd pair",
separated_list0(
ignore_inline_whitespace(char(',')),
ignore_inline_whitespace(kvp_kserdstr_to_kserd(true)),
),
)(i)
}
fn concise_cntr_kserds<'a, E: CxErr<'a>>(
i: &'a str,
) -> IResult<&'a str, Vec<(Kstr<'a>, Kserd<'a>)>, E> {
context(
"newline separated (concise) kserdstr-kserd pair",
preceded(
multiline_whitespace,
cut(terminated(
separated_list0(multiline_whitespace, kvp_kserdstr_to_kserd(false)),
multiline_whitespace,
)),
),
)(i)
}
pub(super) fn delimited<'a, E: CxErr<'a>>(
force_inline: bool,
) -> impl Fn(&'a str) -> IResult<&'a str, Kserd<'a>, E> {
use std::iter::FromIterator;
move |i: &'a str| {
let (i, ident) = opt(ident(false))(i)?;
let (i, _) = ignore_inline_whitespace(char('('))(i)?;
let concise = recognise_concise(i) && !force_inline;
let parser = if concise {
concise_cntr_kserds
} else {
inline_cntr_kserds
};
let ctx = if concise {
"concise container"
} else {
"inline container"
};
let (i, value) = context(ctx, terminated(parser, ignore_inline_whitespace(char(')'))))(i)?;
let value = Value::Cntr(BTreeMap::from_iter(value));
Ok((i, kserd_ctor(ident, value)))
}
}
pub(super) fn verbose<'a, E: CxErr<'a>>(
indents: usize,
) -> impl Fn(&'a str) -> IResult<&'a str, Kserd<'a>, E> {
move |i: &'a str| {
let (i, fields) = many0(verbose_cntr_field(indents))(i)?;
let mut seqs = BTreeMap::new();
let mut maps = BTreeMap::new();
let mut rfields = BTreeMap::new();
let mut identity = None;
for field in fields {
match field {
ContainerField::Id(id) => identity = Some(id),
ContainerField::Direct(name, value) => {
rfields.insert(name, value);
}
ContainerField::NestedVerbose(name, value) => {
rfields.insert(name, value);
}
ContainerField::SeqEntry {
id,
field_name,
value,
} => {
let (x, seq) = seqs.entry(field_name).or_insert_with(|| (None, Vec::new()));
*x = id.or_else(|| x.take());
seq.push(value);
}
ContainerField::MapEntry {
id,
field_name,
key,
value,
} => {
let (x, map) = maps
.entry(field_name)
.or_insert_with(|| (None, BTreeMap::new()));
*x = id.or_else(|| x.take());
map.insert(key, value);
}
}
}
for (name, (id, seq)) in seqs {
let mut val = Kserd::new(Value::Seq(seq));
val.id = id;
rfields.insert(name, val);
}
for (name, (id, map)) in maps {
let mut val = Kserd::new(Value::Map(map));
val.id = id;
rfields.insert(name, val);
}
let mut kserd = Kserd::new(Value::Cntr(rfields));
kserd.id = identity;
Ok((i, kserd))
}
}
#[derive(Debug)]
enum ContainerField<'a> {
Id(Kstr<'a>),
Direct(Kstr<'a>, Kserd<'a>),
NestedVerbose(Kstr<'a>, Kserd<'a>),
SeqEntry {
id: Option<Kstr<'a>>,
field_name: Kstr<'a>,
value: Kserd<'a>,
},
MapEntry {
id: Option<Kstr<'a>>,
field_name: Kstr<'a>,
key: Kserd<'a>,
value: Kserd<'a>,
},
}
fn verbose_cntr_field<'a, E: CxErr<'a>>(
indents: usize,
) -> impl Fn(&'a str) -> IResult<&'a str, ContainerField<'a>, E> {
move |i: &'a str| {
let i = {
let mut tmp = i;
loop {
let (n, yes) = opt(ignore_inline_whitespace(line_ending))(tmp)?;
if yes.is_some() {
tmp = n; } else {
break;
}
}
tmp
};
let (i, _) = expect_indents(indents)(i)?;
let (i, _) = inline_whitespace(i)?;
if i.is_empty() || (ignore_inline_whitespace::<_, E, _>(line_ending)(i)).is_ok() {
return Err(Err::Error(error::make_error(i, ErrorKind::Eof)));
}
if let Some(rem) = i.strip_prefix(':') {
let (i, identity) = ignore_inline_whitespace(ident(false))(rem)?;
Ok((i, ContainerField::Id(identity)))
} else if i.starts_with("[[") {
let (i, (field_name, id)) = context(
"field name. expecting 'field_name[:identity]'",
cut(preceded(
tag("[["),
terminated(
verbose_field_name_and_identity,
ignore_inline_whitespace(tag("]]")),
),
)),
)(i)?;
let (i, _) = ignore_inline_whitespace(line_ending)(i)?;
let (i, key) = opt(terminated(
ignore_inline_whitespace(uncut(kserd_inline)),
ignore_inline_whitespace(char(':')),
))(i)?;
if let Some(key) = key {
let (i, value) = context(
"verbose map entry",
alt((
ignore_inline_whitespace(kserd_concise),
preceded(
ignore_inline_whitespace(line_ending),
kserd_nested(indents + 1),
),
)),
)(i)?;
let entry = ContainerField::MapEntry {
id,
field_name,
key,
value,
};
Ok((i, entry))
} else {
let (i, value) = context("verbose sequence entry", kserd_nested(indents + 1))(i)?;
Ok((
i,
ContainerField::SeqEntry {
id,
field_name,
value,
},
))
}
} else if i.starts_with('[') {
let (i, (fname, identity)) = preceded(
tag("["),
terminated(
verbose_field_name_and_identity,
ignore_inline_whitespace(tag("]")),
),
)(i)?;
let (i, _) = ignore_inline_whitespace(line_ending)(i)?;
let (i, mut value) = context("nested container", kserd_nested(indents + 1))(i)?;
value.id = value.id.or(identity);
Ok((i, ContainerField::NestedVerbose(fname, value)))
} else {
let (i, (fname, val)) = cut(kvp_kserdstr_to_kserd(false))(i)?;
Ok((i, ContainerField::Direct(fname, val)))
}
}
}
fn expect_indents<'a, E: CxErr<'a>>(indents: usize) -> impl Fn(&'a str) -> IResult<&'a str, (), E> {
move |i: &'a str| {
let mut r = i;
for _ in 0..indents {
let (r_, _) = alt((tag(" "), tag("\t")))(r)?;
r = r_;
}
Ok((r, ()))
}
}
fn verbose_field_name_and_identity<'a, E: CxErr<'a>>(
i: &'a str,
) -> IResult<&'a str, (Kstr<'a>, Option<Kstr<'a>>), E> {
context("verbose container field name", |i| {
let (i, fname) = ignore_inline_whitespace(field_name)(i)?;
match ignore_inline_whitespace::<_, E, _>(char(':'))(i) {
Ok((i, _)) => {
let (i, id) = ignore_inline_whitespace(ident(false))(i)?;
Ok((i, (fname, Some(id))))
}
Err(_) => Ok((i, (fname, None))),
}
})(i)
}
#[cfg(test)]
mod tests {
use super::*;
fn kserdit<'a, K, V>(v: Vec<(K, V)>) -> impl Iterator<Item = (Kserd<'a>, Kserd<'a>)>
where
K: ToKserd<'a>,
V: ToKserd<'a>,
{
v.into_iter()
.map(|(k, v)| (k.into_kserd().unwrap(), v.into_kserd().unwrap()))
}
#[test]
fn verbose_cntr_map_entries() {
let s = r#"
[[map-1]]
"a":
100
[[map-1]]
"b":
200
[[map-2]]
(1,2):
(
3
4
)
[[map-2]]
(5,6):
(7,8)
"#;
let expected = Kserd::new_cntr(vec![
(
"map-1",
Kserd::new_map(kserdit(vec![("a", 100), ("b", 200)])),
),
(
"map-2",
Kserd::new_map(kserdit(vec![((1, 2), (3, 4)), ((5, 6), (7, 8))])),
),
])
.unwrap();
assert_eq!(parse(s), Ok(expected));
}
}