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 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::::parser() .separated_by(ascii_whitespace1()) .many() .check_finished(it) .unwrap(); } }