-
Here some context: I try to develop a MIME Message template parser (something really close to what Emacs achieved with their MML). A template looks like this: From: from@localhost
To: to@localhost
Subject: subject
Hello from a text/plain part! See the invitation in attachment.
<#part type="application/pdf" filename="~/Documents/invitation.pdf" /> The pub fn char() -> impl Parser<char, char, Error = Simple<char>> {
choice((
just('\\').ignore_then(one_of(ESCAPABLE_CHARS)),
none_of(ESCAPABLE_CHARS),
))
}
pub fn quoted_string() -> impl Parser<char, String, Error = Simple<char>> {
char()
.repeated()
.delimited_by(just('"'), just('"'))
.collect()
}
pub fn part_prop(prop: &str) -> impl Parser<char, (String, String), Error = Simple<char>> {
just(prop.to_owned())
.then_ignore(just('=').padded())
.then(quoted_string())
.padded()
} Is there a way I can force input to contain at least once the given props (no matter the order)? Something like: pub fn attachment_part_props() -> impl Parser<char, Vec<(String, String)>, Error = Simple<char>> {
all_of((part_prop("type"), part_prop("filename")))
} Let me know if I am clear enough, I can provide more context if needed. Thank you for your help! And thank you for your beautiful work, it is a real pleasure to use, the API is so intuitive and smooth! Well done 🙂 |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
This is, unfortunately, not context-sensitive and isn't something that chumsky currently supports as a built-in thing (and I'm unconvinced I could come up with a sufficiently general API that covers most cases of this that I've seen). However, you can do it using // 'Remember' what kind of part this is by appending it to the output
let part_prop = |kind| part_prop(kind).map(|o| (o, kind));
choice((
part_prop("type"),
part_prop("filename"),
))
.repeated()
.try_map(|xs, span| {
// Search through the outputs for each kind of part, ensuring we have at least one
if let Some(missing) = ["type", "filename"]
.iter()
.find(|p0| xs.iter().all(|(_, p1)| p1 != p0))
{
Err(Simple::custom(span, format!("Missing a '{}' part!", missing)))
} else {
// Filter out the part kind again, we don't need it from now on
Ok(xs.into_iter().map(|(p, _)| p).collect::<Vec<_>>())
}
}) |
Beta Was this translation helpful? Give feedback.
This is, unfortunately, not context-sensitive and isn't something that chumsky currently supports as a built-in thing (and I'm unconvinced I could come up with a sufficiently general API that covers most cases of this that I've seen). However, you can do it using
try_map
without too much difficulty in a way that gives you quite a lot of control over the error message: