summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Turner <jturner.usa@gmail.com>2025-10-28 09:44:32 +0000
committerJohn Turner <jturner.usa@gmail.com>2025-10-28 09:47:41 +0000
commitb54616a6dd4f512b4ec87c3780c36257e5c322b3 (patch)
tree9467d9a2292b0e3ca3587c895e158a1cc372787d
parent92a8e46082a4a3f252cc5fa5afb59716d0657254 (diff)
downloadgentoo-utils-b54616a6dd4f512b4ec87c3780c36257e5c322b3.tar.gz
impl Parseable trait
-rw-r--r--src/atom/mod.rs10
-rw-r--r--src/atom/parsers.rs471
-rw-r--r--src/depend/parsers.rs92
-rw-r--r--src/lib.rs9
-rw-r--r--src/useflag/parsers.rs49
-rw-r--r--tests/depend.rs10
6 files changed, 368 insertions, 273 deletions
diff --git a/src/atom/mod.rs b/src/atom/mod.rs
index 11c56f8..ffb9c61 100644
--- a/src/atom/mod.rs
+++ b/src/atom/mod.rs
@@ -334,14 +334,14 @@ impl fmt::Display for Atom {
mod test {
use mon::{Parser, input::InputIter};
- use crate::atom::parsers;
+ use super::*;
+
+ use crate::Parseable;
#[test]
fn test_version_display() {
let s = "1.0.0_alpha1_beta1-r1";
- let version = parsers::version()
- .parse_finished(InputIter::new(s))
- .unwrap();
+ let version = Version::parser().parse_finished(InputIter::new(s)).unwrap();
assert_eq!(version.to_string().as_str(), s);
}
@@ -349,7 +349,7 @@ mod test {
#[test]
fn test_display_atom() {
let s = "!!>=foo/bar-1.0.0v_alpha1_beta1-r1:slot/sub=[a,b,c]";
- let atom = parsers::atom().parse_finished(InputIter::new(s)).unwrap();
+ let atom = Atom::parser().parse_finished(InputIter::new(s)).unwrap();
assert_eq!(atom.to_string().as_str(), s);
}
diff --git a/src/atom/parsers.rs b/src/atom/parsers.rs
index 3096e67..35df5f8 100644
--- a/src/atom/parsers.rs
+++ b/src/atom/parsers.rs
@@ -3,232 +3,291 @@ use core::option::Option::None;
use mon::{Parser, r#if, numeric1, one_of, tag};
use crate::{
+ Parseable,
atom::{
Atom, Blocker, Category, Name, Slot, SlotName, SlotOperator, UseDep, UseDepCondition,
UseDepNegate, UseDepSign, Version, VersionNumber, VersionOperator, VersionSuffix,
VersionSuffixKind,
},
- useflag::parsers::useflag,
+ useflag::UseFlag,
};
-pub fn blocker<'a>() -> impl Parser<&'a str, Output = Blocker> {
- tag("!!")
- .map(|_| Blocker::Strong)
- .or(tag("!").map(|_| Blocker::Weak))
+impl<'a> Parseable<'a, &'a str> for Blocker {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ tag("!!")
+ .map(|_| Blocker::Strong)
+ .or(tag("!").map(|_| Blocker::Weak))
+ }
}
-pub fn version_operator<'a>() -> impl Parser<&'a str, Output = VersionOperator> {
- tag("<=")
- .map(|_| VersionOperator::LtEq)
- .or(tag(">=").map(|_| VersionOperator::GtEq))
- .or(tag("<").map(|_| VersionOperator::Lt))
- .or(tag(">").map(|_| VersionOperator::Gt))
- .or(tag("=").map(|_| VersionOperator::Eq))
- .or(tag("~").map(|_| VersionOperator::Roughly))
+impl<'a> Parseable<'a, &'a str> for VersionOperator {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ tag("<=")
+ .map(|_| VersionOperator::LtEq)
+ .or(tag(">=").map(|_| VersionOperator::GtEq))
+ .or(tag("<").map(|_| VersionOperator::Lt))
+ .or(tag(">").map(|_| VersionOperator::Gt))
+ .or(tag("=").map(|_| VersionOperator::Eq))
+ .or(tag("~").map(|_| VersionOperator::Roughly))
+ }
}
-pub fn version_number<'a>() -> impl Parser<&'a str, Output = VersionNumber> {
- numeric1()
- .followed_by(tag("*").opt())
- .recognize()
- .map(|output: &str| VersionNumber(output.to_string()))
+impl<'a> Parseable<'a, &'a str> for VersionNumber {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ numeric1()
+ .followed_by(tag("*").opt())
+ .recognize()
+ .map(|output: &str| VersionNumber(output.to_string()))
+ }
}
-pub fn version_suffix_kind<'a>() -> impl Parser<&'a str, Output = VersionSuffixKind> {
- tag("alpha")
- .map(|_| VersionSuffixKind::Alpha)
- .or(tag("beta").map(|_| VersionSuffixKind::Beta))
- .or(tag("pre").map(|_| VersionSuffixKind::Pre))
- .or(tag("rc").map(|_| VersionSuffixKind::Rc))
- .or(tag("p").map(|_| VersionSuffixKind::P))
+impl<'a> Parseable<'a, &'a str> for VersionSuffixKind {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ tag("alpha")
+ .map(|_| VersionSuffixKind::Alpha)
+ .or(tag("beta").map(|_| VersionSuffixKind::Beta))
+ .or(tag("pre").map(|_| VersionSuffixKind::Pre))
+ .or(tag("rc").map(|_| VersionSuffixKind::Rc))
+ .or(tag("p").map(|_| VersionSuffixKind::P))
+ }
}
-pub fn version_suffix<'a>() -> impl Parser<&'a str, Output = VersionSuffix> {
- version_suffix_kind()
- .and(version_number().opt())
- .map(|(kind, number)| VersionSuffix { kind, number })
+impl<'a> Parseable<'a, &'a str> for VersionSuffix {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ VersionSuffixKind::parser()
+ .and(VersionNumber::parser().opt())
+ .map(|(kind, number)| VersionSuffix { kind, number })
+ }
}
-pub fn version<'a>() -> impl Parser<&'a str, Output = Version> {
- let numbers = version_number().separated_list(tag("."), 1..);
- let suffixes = version_suffix().separated_list(tag("_"), 0..);
- let rev = version_number().preceded_by(tag("-r"));
-
- numbers
- .and(r#if(|c: &char| c.is_ascii_alphabetic() && c.is_ascii_lowercase()).opt())
- .and(suffixes.preceded_by(tag("_")).opt())
- .and(rev.opt())
- .map(|(((numbers, letter), suffixes), rev)| Version {
- numbers,
- letter,
- suffixes: suffixes.unwrap_or(Vec::new()),
- rev,
- })
+impl<'a> Parseable<'a, &'a str> for Version {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ let numbers = VersionNumber::parser().separated_list(tag("."), 1..);
+ let suffixes = VersionSuffix::parser().separated_list(tag("_"), 0..);
+ let rev = VersionNumber::parser().preceded_by(tag("-r"));
+
+ numbers
+ .and(r#if(|c: &char| c.is_ascii_alphabetic() && c.is_ascii_lowercase()).opt())
+ .and(suffixes.preceded_by(tag("_")).opt())
+ .and(rev.opt())
+ .map(|(((numbers, letter), suffixes), rev)| Version {
+ numbers,
+ letter,
+ suffixes: suffixes.unwrap_or(Vec::new()),
+ rev,
+ })
+ }
}
-pub fn category<'a>() -> impl Parser<&'a str, Output = Category> {
- let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_');
- let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_.-".contains(*c)).list(0..);
+impl<'a> Parseable<'a, &'a str> for Category {
+ type Parser = impl Parser<&'a str, Output = Self>;
- start
- .and(rest)
- .recognize()
- .map(|output: &str| Category(output.to_string()))
+ fn parser() -> Self::Parser {
+ let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_');
+ let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_.-".contains(*c)).list(0..);
+
+ start
+ .and(rest)
+ .recognize()
+ .map(|output: &str| Category(output.to_string()))
+ }
}
-pub fn name<'a>() -> impl Parser<&'a str, Output = Name> {
- let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_');
- let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "_+".contains(*c))
- .or(one_of("-".chars()).and_not(
- version().preceded_by(tag("-")).followed_by(
- r#if(|c: &char| c.is_ascii_alphanumeric() || "_+-".contains(*c)).not(),
- ),
- ))
- .list(0..);
-
- start
- .and(rest)
- .recognize()
- .map(|output: &str| Name(output.to_string()))
+impl<'a> Parseable<'a, &'a str> for Name {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_');
+ let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "_+".contains(*c))
+ .or(
+ one_of("-".chars()).and_not(Version::parser().preceded_by(tag("-")).followed_by(
+ r#if(|c: &char| c.is_ascii_alphanumeric() || "_+-".contains(*c)).not(),
+ )),
+ )
+ .list(0..);
+
+ start
+ .and(rest)
+ .recognize()
+ .map(|output: &str| Name(output.to_string()))
+ }
}
-pub fn slot_operator<'a>() -> impl Parser<&'a str, Output = SlotOperator> {
- tag("=")
- .map(|_| SlotOperator::Eq)
- .or(tag("*").map(|_| SlotOperator::Star))
+impl<'a> Parseable<'a, &'a str> for SlotOperator {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ tag("=")
+ .map(|_| SlotOperator::Eq)
+ .or(tag("*").map(|_| SlotOperator::Star))
+ }
}
-pub fn slotname<'a>() -> impl Parser<&'a str, Output = SlotName> {
- let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_');
- let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_.-".contains(*c)).list(0..);
+impl<'a> Parseable<'a, &'a str> for SlotName {
+ type Parser = impl Parser<&'a str, Output = Self>;
- start
- .and(rest)
- .recognize()
- .map(|output: &str| SlotName(output.to_string()))
+ fn parser() -> Self::Parser {
+ let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_');
+ let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_.-".contains(*c)).list(0..);
+
+ start
+ .and(rest)
+ .recognize()
+ .map(|output: &str| SlotName(output.to_string()))
+ }
}
-pub fn slot<'a>() -> impl Parser<&'a str, Output = Slot> {
- slotname()
- .opt()
- .and(slotname().preceded_by(tag("/")).opt())
- .and(slot_operator().opt())
- .map(|((slot, sub), operator)| Slot {
- slot,
- sub,
- operator,
- })
+impl<'a> Parseable<'a, &'a str> for Slot {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ SlotName::parser()
+ .opt()
+ .and(SlotName::parser().preceded_by(tag("/")).opt())
+ .and(SlotOperator::parser().opt())
+ .map(|((slot, sub), operator)| Slot {
+ slot,
+ sub,
+ operator,
+ })
+ }
}
-pub fn usedep_sign<'a>() -> impl Parser<&'a str, Output = UseDepSign> {
- tag("(-)")
- .map(|_| UseDepSign::Disabled)
- .or(tag("(+)").map(|_| UseDepSign::Enabled))
+impl<'a> Parseable<'a, &'a str> for UseDepSign {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ tag("(-)")
+ .map(|_| UseDepSign::Disabled)
+ .or(tag("(+)").map(|_| UseDepSign::Enabled))
+ }
}
-pub fn usedep<'a>() -> impl Parser<&'a str, Output = UseDep> {
- let a = useflag()
- .and(usedep_sign().opt())
- .preceded_by(tag("-"))
- .map(|(flag, sign)| UseDep {
- negate: Some(UseDepNegate::Minus),
- flag,
- sign,
- condition: None,
- });
-
- let b = useflag()
- .and(usedep_sign().opt())
- .preceded_by(tag("!"))
- .followed_by(tag("?"))
- .map(|(flag, sign)| UseDep {
- negate: Some(UseDepNegate::Exclamation),
- flag,
- sign,
- condition: Some(UseDepCondition::Question),
- });
-
- let c = useflag()
- .and(usedep_sign().opt())
- .followed_by(tag("?"))
- .map(|(flag, sign)| UseDep {
- negate: None,
- flag,
- sign,
- condition: Some(UseDepCondition::Question),
- });
-
- let d = useflag()
- .and(usedep_sign().opt())
- .preceded_by(tag("!"))
- .followed_by(tag("="))
- .map(|(flag, sign)| UseDep {
- negate: Some(UseDepNegate::Exclamation),
- flag,
- sign,
- condition: Some(UseDepCondition::Eq),
- });
-
- let e = useflag()
- .and(usedep_sign().opt())
- .followed_by(tag("="))
- .map(|(flag, sign)| UseDep {
- negate: None,
- flag,
- sign,
- condition: Some(UseDepCondition::Eq),
- });
-
- let f = useflag()
- .and(usedep_sign().opt())
- .map(|(flag, sign)| UseDep {
- negate: None,
- flag,
- sign,
- condition: None,
- });
-
- a.or(b).or(c).or(d).or(e).or(f)
+impl<'a> Parseable<'a, &'a str> for UseDep {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ let a = UseFlag::parser()
+ .and(UseDepSign::parser().opt())
+ .preceded_by(tag("-"))
+ .map(|(flag, sign)| UseDep {
+ negate: Some(UseDepNegate::Minus),
+ flag,
+ sign,
+ condition: None,
+ });
+
+ let b = UseFlag::parser()
+ .and(UseDepSign::parser().opt())
+ .preceded_by(tag("!"))
+ .followed_by(tag("?"))
+ .map(|(flag, sign)| UseDep {
+ negate: Some(UseDepNegate::Exclamation),
+ flag,
+ sign,
+ condition: Some(UseDepCondition::Question),
+ });
+
+ let c = UseFlag::parser()
+ .and(UseDepSign::parser().opt())
+ .followed_by(tag("?"))
+ .map(|(flag, sign)| UseDep {
+ negate: None,
+ flag,
+ sign,
+ condition: Some(UseDepCondition::Question),
+ });
+
+ let d = UseFlag::parser()
+ .and(UseDepSign::parser().opt())
+ .preceded_by(tag("!"))
+ .followed_by(tag("="))
+ .map(|(flag, sign)| UseDep {
+ negate: Some(UseDepNegate::Exclamation),
+ flag,
+ sign,
+ condition: Some(UseDepCondition::Eq),
+ });
+
+ let e = UseFlag::parser()
+ .and(UseDepSign::parser().opt())
+ .followed_by(tag("="))
+ .map(|(flag, sign)| UseDep {
+ negate: None,
+ flag,
+ sign,
+ condition: Some(UseDepCondition::Eq),
+ });
+
+ let f = UseFlag::parser()
+ .and(UseDepSign::parser().opt())
+ .map(|(flag, sign)| UseDep {
+ negate: None,
+ flag,
+ sign,
+ condition: None,
+ });
+
+ a.or(b).or(c).or(d).or(e).or(f)
+ }
}
-pub fn atom<'a>() -> impl Parser<&'a str, Output = Atom> {
- blocker()
- .opt()
- .and(version_operator().opt())
- .and(category())
- .and(name().preceded_by(tag("/")))
- .and(version().preceded_by(tag("-")).opt())
- .and(slot().preceded_by(tag(":")).opt())
- .and(
- usedep()
- .separated_list(tag(","), 0..)
- .delimited_by(tag("["), tag("]"))
- .opt(),
- )
- .map(
- |((((((blocker, version_operator), category), name), version), slot), usedeps)| Atom {
- blocker,
- version_operator,
- category,
- name,
- version,
- slot,
- usedeps: usedeps.unwrap_or(Vec::new()),
- },
- )
- .verify_output(|atom| match (&atom.version_operator, &atom.version) {
- (Some(VersionOperator::Eq), Some(_)) => true,
- (Some(_), Some(version))
- if !version
- .numbers()
- .iter()
- .any(|number| number.get().contains("*")) =>
- {
- true
- }
- (None, None) => true,
- _ => false,
- })
+impl<'a> Parseable<'a, &'a str> for Atom {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ Blocker::parser()
+ .opt()
+ .and(VersionOperator::parser().opt())
+ .and(Category::parser())
+ .and(Name::parser().preceded_by(tag("/")))
+ .and(Version::parser().preceded_by(tag("-")).opt())
+ .and(Slot::parser().preceded_by(tag(":")).opt())
+ .and(
+ UseDep::parser()
+ .separated_list(tag(","), 0..)
+ .delimited_by(tag("["), tag("]"))
+ .opt(),
+ )
+ .map(
+ |((((((blocker, version_operator), category), name), version), slot), usedeps)| {
+ Atom {
+ blocker,
+ version_operator,
+ category,
+ name,
+ version,
+ slot,
+ usedeps: usedeps.unwrap_or(Vec::new()),
+ }
+ },
+ )
+ .verify_output(|atom| match (&atom.version_operator, &atom.version) {
+ (Some(VersionOperator::Eq), Some(_)) => true,
+ (Some(_), Some(version))
+ if !version
+ .numbers()
+ .iter()
+ .any(|number| number.get().contains("*")) =>
+ {
+ true
+ }
+ (None, None) => true,
+ _ => false,
+ })
+ }
}
#[cfg(test)]
@@ -242,14 +301,14 @@ mod test {
fn test_version() {
let it = InputIter::new("1.0.0v_alpha1_beta1-r1");
- version().check_finished(it).unwrap();
+ Version::parser().check_finished(it).unwrap();
}
#[test]
fn test_name() {
let it = InputIter::new("foo-1-bar-1.0.0");
- match name().parse(it) {
+ match Name::parser().parse(it) {
Ok((_, output)) => {
assert_eq!(output.0.as_str(), "foo-1-bar");
}
@@ -263,7 +322,7 @@ mod test {
"!!>=cat/pkg-1-foo-1.0.0v_alpha1_p20250326-r1:primary/sub=[use,use=,!use=,use?,!use?,-use,use(+),use(-)]",
);
- atom().check_finished(it).unwrap();
+ Atom::parser().check_finished(it).unwrap();
}
#[test]
@@ -272,83 +331,83 @@ mod test {
"!!>=_.+-0-/_-test-T-123_beta1_-4a-6+-_p--1.00.02b_alpha3_pre_p4-r5:slot/_-+6-9=[test(+),test(-)]",
);
- atom().check_finished(it).unwrap();
+ Atom::parser().check_finished(it).unwrap();
}
#[test]
fn test_atom_with_star_in_non_empty_slot() {
let it = InputIter::new("foo/bar:*/subslot");
- assert!(atom().check_finished(it).is_err());
+ assert!(Atom::parser().check_finished(it).is_err());
}
#[test]
fn test_invalid_usedep() {
let it = InputIter::new("foo-bar:slot/sub=[!use]");
- assert!(atom().check_finished(it).is_err())
+ assert!(Atom::parser().check_finished(it).is_err())
}
#[test]
fn test_empty_slot() {
let it = InputIter::new("foo/bar:=");
- atom().check_finished(it).unwrap();
+ Atom::parser().check_finished(it).unwrap();
}
#[test]
fn test_usedep_with_underscore() {
let it = InputIter::new("foo/bar[use_dep]");
- atom().check_finished(it).unwrap();
+ Atom::parser().check_finished(it).unwrap();
}
#[test]
fn test_version_with_uppercase_letter() {
let it = InputIter::new("=foo/bar-1.0.0V");
- assert!(atom().check_finished(it).is_err());
+ assert!(Atom::parser().check_finished(it).is_err());
}
#[test]
fn test_version_with_version_operator_without_version() {
let it = InputIter::new("=foo/bar");
- assert!(atom().check_finished(it).is_err());
+ assert!(Atom::parser().check_finished(it).is_err());
}
#[test]
fn test_version_with_version_without_version_operator() {
let it = InputIter::new("foo/bar-1.0.0");
- assert!(atom().check_finished(it).is_err());
+ assert!(Atom::parser().check_finished(it).is_err());
}
#[test]
fn test_atom_with_eq_version_operator() {
let it = InputIter::new("=foo/bar-1.0.0");
- atom().check_finished(it).unwrap();
+ Atom::parser().check_finished(it).unwrap();
}
#[test]
fn test_atom_with_star_in_version() {
let it = InputIter::new("=foo/bar-1.2*");
- atom().check_finished(it).unwrap();
+ Atom::parser().check_finished(it).unwrap();
}
#[test]
fn test_atom_with_star_in_version_without_eq_version_operator() {
let it = InputIter::new(">=foo/bar-1.2*");
- assert!(atom().check_finished(it).is_err());
+ assert!(Atom::parser().check_finished(it).is_err());
}
#[test]
fn test_atom_with_trailing_dash_and_letter() {
let it = InputIter::new("dev-db/mysql-connector-c");
- atom().check_finished(it).unwrap();
+ Atom::parser().check_finished(it).unwrap();
}
}
diff --git a/src/depend/parsers.rs b/src/depend/parsers.rs
index 10da058..7a69fee 100644
--- a/src/depend/parsers.rs
+++ b/src/depend/parsers.rs
@@ -1,61 +1,73 @@
-use mon::{Parser, ParserResult, input::InputIter, tag, whitespace1};
+use mon::{Parser, tag, whitespace1};
use crate::{
- atom,
+ Parseable,
+ atom::Atom,
depend::{Conditional, Expr},
- useflag,
+ useflag::UseFlag,
};
-fn expr(it: InputIter<&str>) -> ParserResult<&str, Expr> {
- let all_of = expr
- .separated_list(whitespace1(), 1..)
- .delimited_by(tag("(").followed_by(whitespace1()), tag(")"))
- .map(|exprs| Expr::AllOf(exprs));
-
- let any_of = expr
- .separated_list(whitespace1(), 1..)
- .delimited_by(tag("(").followed_by(whitespace1()), tag(")"))
- .preceded_by(tag("||").followed_by(whitespace1()))
- .map(|exprs| Expr::AnyOf(exprs));
-
- let one_of = expr
- .separated_list(whitespace1(), 1..)
- .delimited_by(tag("(").followed_by(whitespace1()), tag(")"))
- .preceded_by(tag("^^").followed_by(whitespace1()))
- .map(|exprs| Expr::OneOf(exprs));
-
- atom::parsers::atom()
- .map(|atom| Expr::Atom(atom))
- .or(conditional().map(|conditional| Expr::Conditional(conditional)))
- .or(any_of)
- .or(all_of)
- .or(one_of)
- .parse(it)
-}
+impl<'a> Parseable<'a, &'a str> for Expr {
+ type Parser = impl Parser<&'a str, Output = Self>;
-fn conditional<'a>() -> impl Parser<&'a str, Output = Conditional> {
- useflag::parsers::useflag()
- .preceded_by(tag("!"))
- .followed_by(tag("?"))
- .map(|flag| Conditional::Negative(flag))
- .or(useflag::parsers::useflag()
- .followed_by(tag("?"))
- .map(|flag| Conditional::Positive(flag)))
+ fn parser() -> Self::Parser {
+ |it| {
+ let all_of = Expr::parser()
+ .separated_list(whitespace1(), 1..)
+ .delimited_by(tag("(").followed_by(whitespace1()), tag(")"))
+ .map(|exprs| Expr::AllOf(exprs));
+
+ let any_of = Expr::parser()
+ .separated_list(whitespace1(), 1..)
+ .delimited_by(tag("(").followed_by(whitespace1()), tag(")"))
+ .preceded_by(tag("||").followed_by(whitespace1()))
+ .map(|exprs| Expr::AnyOf(exprs));
+
+ let one_of = Expr::parser()
+ .separated_list(whitespace1(), 1..)
+ .delimited_by(tag("(").followed_by(whitespace1()), tag(")"))
+ .preceded_by(tag("^^").followed_by(whitespace1()))
+ .map(|exprs| Expr::OneOf(exprs));
+
+ Atom::parser()
+ .map(|atom| Expr::Atom(atom))
+ .or(Conditional::parser().map(|conditional| Expr::Conditional(conditional)))
+ .or(any_of)
+ .or(all_of)
+ .or(one_of)
+ .parse(it)
+ }
+ }
}
-pub fn exprs<'a>() -> impl Parser<&'a str, Output = Vec<Expr>> {
- expr.separated_list(whitespace1(), 0..)
+impl<'a> Parseable<'a, &'a str> for Conditional {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ UseFlag::parser()
+ .preceded_by(tag("!"))
+ .followed_by(tag("?"))
+ .map(|flag| Conditional::Negative(flag))
+ .or(UseFlag::parser()
+ .followed_by(tag("?"))
+ .map(|flag| Conditional::Positive(flag)))
+ }
}
#[cfg(test)]
mod test {
+ use mon::input::InputIter;
+
use super::*;
#[test]
fn test_expr() {
let it = InputIter::new("flag? ( || ( foo/bar foo/bar ) )");
- exprs().check_finished(it).unwrap();
+ Expr::parser()
+ .separated_list(whitespace1(), 0..)
+ .check_finished(it)
+ .unwrap();
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 4a1364e..d986f3e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,14 @@
#![deny(clippy::pedantic)]
#![allow(dead_code, unstable_name_collisions)]
+#![feature(impl_trait_in_assoc_type)]
+
+use mon::{Parser, input::Input};
+
+pub trait Parseable<'a, I: Input + 'a> {
+ type Parser: Parser<I, Output = Self>;
+
+ fn parser() -> Self::Parser;
+}
pub mod atom;
pub mod depend;
diff --git a/src/useflag/parsers.rs b/src/useflag/parsers.rs
index 49000d9..6b13a64 100644
--- a/src/useflag/parsers.rs
+++ b/src/useflag/parsers.rs
@@ -1,26 +1,37 @@
use mon::{Parser, r#if, tag};
-use crate::useflag::{IUseFlag, UseFlag};
+use crate::{
+ Parseable,
+ useflag::{IUseFlag, UseFlag},
+};
-pub fn useflag<'a>() -> impl Parser<&'a str, Output = UseFlag> {
- let start = r#if(|c: &char| c.is_ascii_alphanumeric());
- let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_@-".contains(*c)).list(0..);
+impl<'a> Parseable<'a, &'a str> for UseFlag {
+ type Parser = impl Parser<&'a str, Output = Self>;
- start
- .and(rest)
- .recognize()
- .map(|output: &str| UseFlag(output.to_string()))
+ fn parser() -> Self::Parser {
+ let start = r#if(|c: &char| c.is_ascii_alphanumeric());
+ let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_@-".contains(*c)).list(0..);
+
+ start
+ .and(rest)
+ .recognize()
+ .map(|output: &str| UseFlag(output.to_string()))
+ }
}
-pub fn iuseflag<'a>() -> impl Parser<&'a str, Output = IUseFlag> {
- useflag()
- .preceded_by(tag("+"))
- .map(|flag| IUseFlag {
- default: true,
- flag,
- })
- .or(useflag().map(|flag| IUseFlag {
- default: false,
- flag,
- }))
+impl<'a> Parseable<'a, &'a str> for IUseFlag {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ UseFlag::parser()
+ .preceded_by(tag("+"))
+ .map(|flag| IUseFlag {
+ default: true,
+ flag,
+ })
+ .or(UseFlag::parser().map(|flag| IUseFlag {
+ default: false,
+ flag,
+ }))
+ }
}
diff --git a/tests/depend.rs b/tests/depend.rs
index 085d739..12d6e75 100644
--- a/tests/depend.rs
+++ b/tests/depend.rs
@@ -1,5 +1,8 @@
-use gentoo_utils::depend;
-use mon::{Parser, eof, input::InputIter, tag};
+use gentoo_utils::{
+ Parseable,
+ depend::{self, Expr},
+};
+use mon::{Parser, eof, input::InputIter, tag, whitespace1};
use std::fs;
#[test]
@@ -14,7 +17,8 @@ fn parse_md5_cache() {
if line.starts_with("DEPEND=") {
eprintln!("{line}");
eprintln!();
- depend::parsers::exprs()
+ Expr::parser()
+ .separated_list(whitespace1(), 0..)
.ignore()
.or(eof())
.preceded_by(tag("DEPEND="))