use std::path::PathBuf; use mon::{Parser, alpha1, r#if, tag, whitespace1}; use crate::{ Parseable, 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 = alpha1::<&str>() .followed_by(tag("://")) .map(|output: &str| output.to_string()); let path = r#if(|c: &char| !c.is_ascii_whitespace()) .repeated(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(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 = r#if(|c: &char| c.is_ascii_alphanumeric() || "_".contains(*c)); let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_.-".contains(*c)).repeated(0..); 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 = r#if(|c: &char| c.is_ascii_alphanumeric() || "_".contains(*c)); let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_.-".contains(*c)).repeated(0..); 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(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 all_of_group = Depend::parser() .separated_by(whitespace1(), 1..) .delimited_by(tag("(").followed_by(whitespace1()), tag(")")) .map(|exprs| Depend::AllOf(exprs)); let any_of_group = Depend::parser() .separated_by(whitespace1(), 1..) .delimited_by(tag("(").followed_by(whitespace1()), tag(")")) .preceded_by(tag("||").followed_by(whitespace1())) .map(|exprs| Depend::AnyOf(exprs)); let one_of_group = Depend::parser() .separated_by(whitespace1(), 1..) .delimited_by(tag("(").followed_by(whitespace1()), tag(")")) .preceded_by(tag("^^").followed_by(whitespace1())) .map(|exprs| Depend::OneOf(exprs)); let conditional_group = Conditional::parser() .followed_by(whitespace1()) .and( Depend::parser() .separated_by(whitespace1(), 1..) .delimited_by(tag("(").followed_by(whitespace1()), tag(")")), ) .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(|flag| Conditional::Negative(flag)) .or(UseFlag::parser() .followed_by(tag("?")) .map(|flag| Conditional::Positive(flag))) } } #[cfg(test)] mod test { use mon::input::InputIter; use crate::{atom::Atom, 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(whitespace1(), 0..) .check_finished(it) .unwrap(); } }