diff options
| author | John Turner <jturner.usa@gmail.com> | 2025-10-28 09:44:32 +0000 |
|---|---|---|
| committer | John Turner <jturner.usa@gmail.com> | 2025-10-28 09:47:41 +0000 |
| commit | b54616a6dd4f512b4ec87c3780c36257e5c322b3 (patch) | |
| tree | 9467d9a2292b0e3ca3587c895e158a1cc372787d /src/atom | |
| parent | 92a8e46082a4a3f252cc5fa5afb59716d0657254 (diff) | |
| download | gentoo-utils-b54616a6dd4f512b4ec87c3780c36257e5c322b3.tar.gz | |
impl Parseable trait
Diffstat (limited to 'src/atom')
| -rw-r--r-- | src/atom/mod.rs | 10 | ||||
| -rw-r--r-- | src/atom/parsers.rs | 471 |
2 files changed, 270 insertions, 211 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(); } } |
