From 8937e096a4b7eba649817d12d23d3e369a4a4f1d Mon Sep 17 00:00:00 2001 From: John Turner Date: Wed, 29 Oct 2025 16:24:25 +0000 Subject: create ebuild module --- src/ebuild/mod.rs | 57 +++++++++++++++++++ src/ebuild/parsers.rs | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 src/ebuild/mod.rs create mode 100644 src/ebuild/parsers.rs (limited to 'src/ebuild') diff --git a/src/ebuild/mod.rs b/src/ebuild/mod.rs new file mode 100644 index 0000000..2558cf4 --- /dev/null +++ b/src/ebuild/mod.rs @@ -0,0 +1,57 @@ +use get::Get; +use std::path::PathBuf; + +use crate::{ + atom::{Atom, Name, Slot, Version}, + useflag::{IUseFlag, UseFlag}, +}; + +pub mod parsers; + +#[derive(Clone, Debug)] +pub enum Conditional { + Negative(UseFlag), + Positive(UseFlag), +} + +#[derive(Clone, Debug)] +pub enum Depend { + Element(T), + AllOf(Vec), + AnyOf(Vec), + OneOf(Vec), + ConditionalGroup(Conditional, Vec), +} + +#[derive(Debug, Clone, Get)] +pub struct SrcUri { + uri: String, + file_name: Option, +} + +#[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 { + name: Name, + version: Version, + slot: Option, + homepage: Option, + src_uri: Vec>, + eapi: Option, + inherit: Vec, + iuse: Vec, + license: Vec>, + description: Option, + depend: Vec>, + bdepend: Vec>, + rdpened: Vec>, + idepend: Vec>, +} diff --git a/src/ebuild/parsers.rs b/src/ebuild/parsers.rs new file mode 100644 index 0000000..d079609 --- /dev/null +++ b/src/ebuild/parsers.rs @@ -0,0 +1,155 @@ +use std::path::PathBuf; + +use mon::{Parser, alpha1, r#if, tag, whitespace1}; + +use crate::{ + Parseable, + ebuild::{Conditional, Depend, Eapi, License, SrcUri}, + useflag::UseFlag, +}; + +impl<'a> Parseable<'a, &'a str> for SrcUri { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + let protocol = alpha1::<&str>().followed_by(tag("://")); + + let uri = r#if(|c: &char| !c.is_ascii_whitespace()) + .list(1..) + .recognize() + .map(|output: &str| output.to_string()); + + let name = r#if(|c: &char| !c.is_ascii_whitespace()) + .list(1..) + .recognize() + .map(|output: &str| PathBuf::from(output)); + + uri.preceded_by(protocol) + .and( + name.preceded_by(tag("->").delimited_by(whitespace1(), whitespace1())) + .opt(), + ) + .map(|(uri, file_name)| SrcUri { uri, file_name }) + } +} + +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)).list(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)).list(0..); + + start + .and(rest) + .recognize() + .map(|output: &str| Eapi(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_list(whitespace1(), 1..) + .delimited_by(tag("(").followed_by(whitespace1()), tag(")")) + .map(|exprs| Depend::AllOf(exprs)); + + let any_of_group = Depend::parser() + .separated_list(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_list(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_list(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_list(whitespace1(), 0..) + .check_finished(it) + .unwrap(); + } +} -- cgit v1.2.3