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 | |
| parent | bffc1e88b0c84de24324442e6a17a88d90b3a63c (diff) | |
| download | gentoo-utils-f8149b43d463ec3248626fa2cba2ed6f8579bc47.tar.gz | |
rearrange modules
Diffstat (limited to 'src/repo/ebuild')
| -rw-r--r-- | src/repo/ebuild/mod.rs | 83 | ||||
| -rw-r--r-- | src/repo/ebuild/parsers.rs | 205 |
2 files changed, 288 insertions, 0 deletions
diff --git a/src/repo/ebuild/mod.rs b/src/repo/ebuild/mod.rs new file mode 100644 index 0000000..3f52db9 --- /dev/null +++ b/src/repo/ebuild/mod.rs @@ -0,0 +1,83 @@ +use get::Get; +use std::path::PathBuf; + +use crate::{ + atom::{Atom, Name, Slot, Version}, + useflag::{IUseFlag, UseFlag}, +}; + +mod parsers; + +#[derive(Clone, Debug)] +pub enum Conditional { + Negative(UseFlag), + Positive(UseFlag), +} + +#[derive(Clone, Debug)] +pub enum Depend<T> { + Element(T), + AllOf(Vec<Self>), + AnyOf(Vec<Self>), + OneOf(Vec<Self>), + ConditionalGroup(Conditional, Vec<Self>), +} + +#[derive(Debug, Clone)] +pub enum UriPrefix { + Mirror, + Fetch, +} + +#[derive(Debug, Clone, Get)] +pub struct Uri { + #[get(kind = "deref")] + protocol: String, + #[get(kind = "deref")] + path: String, +} + +#[derive(Debug, Clone)] +pub enum SrcUri { + Filename(PathBuf), + Uri { + prefix: Option<UriPrefix>, + uri: Uri, + filename: Option<PathBuf>, + }, +} + +#[derive(Debug, Clone, Get)] +pub struct License(#[get(method = "get", kind = "deref")] String); + +#[derive(Debug, Clone, Get)] +pub struct Eapi(#[get(method = "get", kind = "deref")] String); + +#[derive(Debug, Clone, Get)] +pub struct Eclass(#[get(method = "get", kind = "deref")] String); + +#[derive(Debug, Clone, Get)] +pub struct Ebuild { + pub(super) name: Name, + pub(super) version: Version, + pub(super) slot: Option<Slot>, + pub(super) homepage: Option<String>, + #[get(kind = "deref")] + pub(super) src_uri: Vec<Depend<SrcUri>>, + pub(super) eapi: Option<Eapi>, + #[get(kind = "deref")] + pub(super) inherit: Vec<Eclass>, + #[get(kind = "deref")] + pub(super) iuse: Vec<IUseFlag>, + #[get(kind = "deref")] + pub(super) license: Vec<Depend<License>>, + pub(super) description: Option<String>, + #[get(kind = "deref")] + pub(super) depend: Vec<Depend<Atom>>, + #[get(kind = "deref")] + pub(super) bdepend: Vec<Depend<Atom>>, + #[get(kind = "deref")] + pub(super) rdepend: Vec<Depend<Atom>>, + #[get(kind = "deref")] + pub(super) idepend: Vec<Depend<Atom>>, +} 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(); + } +} |
