diff options
| author | John Turner <jturner.usa@gmail.com> | 2025-11-22 02:11:52 +0000 |
|---|---|---|
| committer | John Turner <jturner.usa@gmail.com> | 2025-11-23 02:49:53 +0000 |
| commit | f8149b43d463ec3248626fa2cba2ed6f8579bc47 (patch) | |
| tree | 7039d4e7d89b87fdc58f7d1b11fb332636449622 /src/repo/ebuild/parsers.rs | |
| parent | bffc1e88b0c84de24324442e6a17a88d90b3a63c (diff) | |
| download | gentoo-utils-f8149b43d463ec3248626fa2cba2ed6f8579bc47.tar.gz | |
rearrange modules
Diffstat (limited to 'src/repo/ebuild/parsers.rs')
| -rw-r--r-- | src/repo/ebuild/parsers.rs | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/repo/ebuild/parsers.rs b/src/repo/ebuild/parsers.rs new file mode 100644 index 0000000..c80cdf2 --- /dev/null +++ b/src/repo/ebuild/parsers.rs @@ -0,0 +1,205 @@ +use std::path::PathBuf; + +use mon::{ + Parser, ParserIter, ascii_alpha1, ascii_alphanumeric, ascii_whitespace1, r#if, one_of, tag, +}; + +use crate::{ + Parseable, + repo::ebuild::{Conditional, Depend, Eapi, Eclass, License, SrcUri, Uri, UriPrefix}, + useflag::UseFlag, +}; + +impl<'a> Parseable<'a, &'a str> for UriPrefix { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + tag("+mirror") + .map(|_| UriPrefix::Mirror) + .or(tag("+fetch").map(|_| UriPrefix::Fetch)) + } +} + +impl<'a> Parseable<'a, &'a str> for Uri { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + let protocol = ascii_alpha1::<&str>() + .followed_by(tag("://")) + .map(|output: &str| output.to_string()); + let path = r#if(|c: &char| !c.is_ascii_whitespace()) + .repeated() + .at_least(1) + .recognize() + .map(|output: &str| output.to_string()); + + protocol + .and(path) + .map(|(protocol, path)| Uri { protocol, path }) + } +} + +impl<'a> Parseable<'a, &'a str> for SrcUri { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + let filename = || { + r#if(|c: &char| !c.is_ascii_whitespace()) + .repeated() + .at_least(1) + .recognize() + .map(|output: &str| PathBuf::from(output)) + }; + + let uri = UriPrefix::parser() + .opt() + .and(Uri::parser()) + .and(filename().preceded_by(tag(" -> ")).opt()) + .map(|((prefix, uri), filename)| SrcUri::Uri { + prefix, + uri, + filename, + }); + + uri.or(filename().map(|path: PathBuf| SrcUri::Filename(path))) + } +} + +impl<'a> Parseable<'a, &'a str> for License { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + let start = ascii_alphanumeric().or(one_of("_".chars())); + let rest = ascii_alphanumeric() + .or(one_of("+_.-".chars())) + .repeated() + .many(); + + start + .and(rest) + .recognize() + .map(|output: &str| License(output.to_string())) + } +} + +impl<'a> Parseable<'a, &'a str> for Eapi { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + let start = ascii_alphanumeric().or(one_of("_".chars())); + let rest = ascii_alphanumeric() + .or(one_of("+_.-".chars())) + .repeated() + .many(); + + start + .and(rest) + .recognize() + .map(|output: &str| Eapi(output.to_string())) + } +} + +// TODO: +// Cant find information about eclass names in pms so we allow anything except +// for whitespace. +impl<'a> Parseable<'a, &'a str> for Eclass { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + r#if(|c: &char| !c.is_ascii_whitespace()) + .repeated() + .at_least(1) + .recognize() + .map(|output: &str| Eclass(output.to_string())) + } +} + +impl<'a, T> Parseable<'a, &'a str> for Depend<T> +where + T: Parseable<'a, &'a str>, +{ + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + |it| { + let exprs = || { + Depend::parser() + .separated_by_with_trailing(ascii_whitespace1()) + .at_least(1) + .delimited_by(tag("(").followed_by(ascii_whitespace1()), tag(")")) + }; + + let all_of_group = exprs().map(|exprs| Depend::AllOf(exprs)); + + let any_of_group = exprs() + .preceded_by(tag("||").followed_by(ascii_whitespace1())) + .map(|exprs| Depend::AnyOf(exprs)); + + let one_of_group = exprs() + .preceded_by(tag("^^").followed_by(ascii_whitespace1())) + .map(|exprs| Depend::OneOf(exprs)); + + let conditional_group = Conditional::parser() + .followed_by(ascii_whitespace1()) + .and(exprs()) + .map(|(conditional, exprs)| Depend::ConditionalGroup(conditional, exprs)); + + T::parser() + .map(|e| Depend::Element(e)) + .or(conditional_group) + .or(any_of_group) + .or(all_of_group) + .or(one_of_group) + .parse(it) + } + } +} + +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(Conditional::Negative) + .or(UseFlag::parser() + .followed_by(tag("?")) + .map(Conditional::Positive)) + } +} + +#[cfg(test)] +mod test { + + use mon::{ParserIter, input::InputIter}; + + use crate::{atom::Atom, repo::ebuild::Depend}; + + use super::*; + + #[test] + fn test_src_uri() { + let tests = [ + "https://example.com/foo/bar.tar.gz", + "https://example.com/foo/bar.tar.gz -> bar.tar.gz", + ]; + + for test in tests { + SrcUri::parser() + .check_finished(InputIter::new(test)) + .unwrap(); + } + } + + #[test] + fn test_expr() { + let it = InputIter::new("flag? ( || ( foo/bar foo/bar ) )"); + + Depend::<Atom>::parser() + .separated_by(ascii_whitespace1()) + .many() + .check_finished(it) + .unwrap(); + } +} |
