create ebuild module

This commit is contained in:
John Turner
2025-10-29 16:24:25 +00:00
parent b5765118fe
commit 8937e096a4
6 changed files with 215 additions and 111 deletions

View File

@@ -1,18 +0,0 @@
use crate::useflag::UseFlag;
pub mod parsers;
#[derive(Clone, Debug)]
pub enum Conditional {
Negative(UseFlag),
Positive(UseFlag),
}
#[derive(Clone, Debug)]
pub enum Expr<T> {
Element(T),
AllOf(Vec<Self>),
AnyOf(Vec<Self>),
OneOf(Vec<Self>),
ConditionalGroup(Conditional, Vec<Self>),
}

View File

@@ -1,86 +0,0 @@
use mon::{Parser, tag, whitespace1};
use crate::{
Parseable,
depend::{Conditional, Expr},
useflag::UseFlag,
};
impl<'a, T> Parseable<'a, &'a str> for Expr<T>
where
T: Parseable<'a, &'a str>,
{
type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
|it| {
let all_of_group = Expr::parser()
.separated_list(whitespace1(), 1..)
.delimited_by(tag("(").followed_by(whitespace1()), tag(")"))
.map(|exprs| Expr::AllOf(exprs));
let any_of_group = Expr::parser()
.separated_list(whitespace1(), 1..)
.delimited_by(tag("(").followed_by(whitespace1()), tag(")"))
.preceded_by(tag("||").followed_by(whitespace1()))
.map(|exprs| Expr::AnyOf(exprs));
let one_of_group = Expr::parser()
.separated_list(whitespace1(), 1..)
.delimited_by(tag("(").followed_by(whitespace1()), tag(")"))
.preceded_by(tag("^^").followed_by(whitespace1()))
.map(|exprs| Expr::OneOf(exprs));
let conditional_group = Conditional::parser()
.followed_by(whitespace1())
.and(
Expr::parser()
.separated_list(whitespace1(), 1..)
.delimited_by(tag("(").followed_by(whitespace1()), tag(")")),
)
.map(|(conditional, exprs)| Expr::ConditionalGroup(conditional, exprs));
T::parser()
.map(|e| Expr::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;
use super::*;
#[test]
fn test_expr() {
let it = InputIter::new("flag? ( || ( foo/bar foo/bar ) )");
Expr::<Atom>::parser()
.separated_list(whitespace1(), 0..)
.check_finished(it)
.unwrap();
}
}

57
src/ebuild/mod.rs Normal file
View File

@@ -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<T> {
Element(T),
AllOf(Vec<Self>),
AnyOf(Vec<Self>),
OneOf(Vec<Self>),
ConditionalGroup(Conditional, Vec<Self>),
}
#[derive(Debug, Clone, Get)]
pub struct SrcUri {
uri: String,
file_name: 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 {
name: Name,
version: Version,
slot: Option<Slot>,
homepage: Option<String>,
src_uri: Vec<Depend<SrcUri>>,
eapi: Option<Eapi>,
inherit: Vec<Eclass>,
iuse: Vec<IUseFlag>,
license: Vec<Depend<License>>,
description: Option<String>,
depend: Vec<Depend<Atom>>,
bdepend: Vec<Depend<Atom>>,
rdpened: Vec<Depend<Atom>>,
idepend: Vec<Depend<Atom>>,
}

155
src/ebuild/parsers.rs Normal file
View File

@@ -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<T>
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::<Atom>::parser()
.separated_list(whitespace1(), 0..)
.check_finished(it)
.unwrap();
}
}

View File

@@ -11,5 +11,5 @@ pub trait Parseable<'a, I: Input + 'a> {
}
pub mod atom;
pub mod depend;
pub mod ebuild;
pub mod useflag;