rearrange modules
This commit is contained in:
83
src/repo/ebuild/mod.rs
Normal file
83
src/repo/ebuild/mod.rs
Normal file
@@ -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>>,
|
||||
}
|
||||
205
src/repo/ebuild/parsers.rs
Normal file
205
src/repo/ebuild/parsers.rs
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
318
src/repo/mod.rs
Normal file
318
src/repo/mod.rs
Normal file
@@ -0,0 +1,318 @@
|
||||
use std::{
|
||||
fs, io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use get::Get;
|
||||
|
||||
use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter, tag};
|
||||
|
||||
use crate::{
|
||||
Parseable,
|
||||
atom::{self, Atom},
|
||||
repo::ebuild::{Depend, Eapi, Ebuild, Eclass, License, SrcUri},
|
||||
useflag::IUseFlag,
|
||||
};
|
||||
|
||||
pub mod ebuild;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("io error: {0}")]
|
||||
Io(PathBuf, io::Error),
|
||||
#[error("error while reading directory: {0:?}: {1}")]
|
||||
ReadDir(PathBuf, io::Error),
|
||||
#[error("failed to decode path: {0}")]
|
||||
Unicode(PathBuf),
|
||||
#[error("parser error: {0}")]
|
||||
Parser(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Get)]
|
||||
pub struct Repo {
|
||||
#[get(kind = "deref")]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Get)]
|
||||
pub struct Category {
|
||||
name: atom::Category,
|
||||
#[get(kind = "deref")]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Categories(PathBuf, fs::ReadDir);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ebuilds(PathBuf, fs::ReadDir);
|
||||
|
||||
impl Repo {
|
||||
pub fn new<P: AsRef<Path>>(path: P) -> Self {
|
||||
Self {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn categories(&self) -> Result<Categories, Error> {
|
||||
let path = self.path.as_path().join("metadata/md5-cache");
|
||||
|
||||
Ok(Categories(
|
||||
path.clone(),
|
||||
fs::read_dir(&path).map_err(|e| Error::Io(path, e))?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Category {
|
||||
pub fn ebuilds(&self) -> Result<Ebuilds, Error> {
|
||||
Ok(Ebuilds(
|
||||
self.path.clone(),
|
||||
fs::read_dir(&self.path).map_err(|e| Error::Io(self.path.clone(), e))?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Categories {
|
||||
type Item = Result<Category, Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.1.next()? {
|
||||
Ok(entry) => match read_category(entry.path()) {
|
||||
Ok(category) => Some(Ok(category)),
|
||||
Err(e) => Some(Err(e)),
|
||||
},
|
||||
Err(e) => Some(Err(Error::ReadDir(self.0.clone(), e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Ebuilds {
|
||||
type Item = Result<Ebuild, Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.1.next()? {
|
||||
Ok(entry) => match read_ebuild(entry.path()) {
|
||||
Ok(ebuild) => Some(Ok(ebuild)),
|
||||
Err(e) => Some(Err(e)),
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_category(path: PathBuf) -> Result<Category, Error> {
|
||||
let file_name = path
|
||||
.as_path()
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.ok_or(Error::Unicode(path.clone()))?;
|
||||
|
||||
let name = atom::Category::parser()
|
||||
.parse_finished(InputIter::new(file_name))
|
||||
.map_err(|_| Error::Parser(file_name.to_string()))?;
|
||||
|
||||
Ok(Category { name, path })
|
||||
}
|
||||
|
||||
fn read_ebuild(path: PathBuf) -> Result<Ebuild, Error> {
|
||||
let file_name = path
|
||||
.as_path()
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.ok_or(Error::Unicode(path.clone()))?;
|
||||
|
||||
let (name, version) = atom::Name::parser()
|
||||
.and(atom::Version::parser().preceded_by(tag("-")))
|
||||
.parse_finished(InputIter::new(file_name))
|
||||
.map_err(|_| Error::Parser(file_name.to_string()))?;
|
||||
|
||||
let metadata = fs::read_to_string(path.as_path()).map_err(|e| Error::Io(path, e))?;
|
||||
|
||||
Ok(Ebuild {
|
||||
name,
|
||||
version,
|
||||
slot: match read_slot(&metadata) {
|
||||
Some(Ok(slot)) => Some(slot),
|
||||
Some(Err(e)) => return Err(e),
|
||||
None => None,
|
||||
},
|
||||
homepage: read_homepage(&metadata),
|
||||
src_uri: match read_src_uri(&metadata) {
|
||||
Some(Ok(src_uri)) => src_uri,
|
||||
Some(Err(e)) => return Err(e),
|
||||
None => Vec::new(),
|
||||
},
|
||||
eapi: match read_eapi(&metadata) {
|
||||
Some(Ok(eapi)) => Some(eapi),
|
||||
Some(Err(e)) => return Err(e),
|
||||
None => None,
|
||||
},
|
||||
inherit: match read_inherit(&metadata) {
|
||||
Some(Ok(inherit)) => inherit,
|
||||
Some(Err(e)) => return Err(e),
|
||||
None => Vec::new(),
|
||||
},
|
||||
iuse: match read_iuse(&metadata) {
|
||||
Some(Ok(iuse)) => iuse,
|
||||
Some(Err(e)) => return Err(e),
|
||||
None => Vec::new(),
|
||||
},
|
||||
license: match read_license(&metadata) {
|
||||
Some(Ok(license)) => license,
|
||||
Some(Err(e)) => return Err(e),
|
||||
None => Vec::new(),
|
||||
},
|
||||
description: read_description(&metadata),
|
||||
depend: match read_depend(&metadata) {
|
||||
Some(Ok(depend)) => depend,
|
||||
Some(Err(e)) => return Err(e),
|
||||
None => Vec::new(),
|
||||
},
|
||||
bdepend: match read_bdepend(&metadata) {
|
||||
Some(Ok(depend)) => depend,
|
||||
Some(Err(e)) => return Err(e),
|
||||
None => Vec::new(),
|
||||
},
|
||||
rdepend: match read_rdepend(&metadata) {
|
||||
Some(Ok(depend)) => depend,
|
||||
Some(Err(e)) => return Err(e),
|
||||
None => Vec::new(),
|
||||
},
|
||||
idepend: match read_idepend(&metadata) {
|
||||
Some(Ok(depend)) => depend,
|
||||
Some(Err(e)) => return Err(e),
|
||||
None => Vec::new(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn read_slot(input: &str) -> Option<Result<atom::Slot, Error>> {
|
||||
let line = input.lines().find_map(|line| line.strip_prefix("SLOT="))?;
|
||||
|
||||
match atom::Slot::parser().parse_finished(InputIter::new(line)) {
|
||||
Ok(slot) => Some(Ok(slot)),
|
||||
Err(_) => Some(Err(Error::Parser(line.to_string()))),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_homepage(input: &str) -> Option<String> {
|
||||
input
|
||||
.lines()
|
||||
.find_map(|line| line.strip_prefix("HOMEPAGE=").map(str::to_string))
|
||||
}
|
||||
|
||||
fn read_src_uri(input: &str) -> Option<Result<Vec<Depend<SrcUri>>, Error>> {
|
||||
let line = input
|
||||
.lines()
|
||||
.find_map(|line| line.strip_prefix("SRC_URI="))?;
|
||||
|
||||
match Depend::<SrcUri>::parser()
|
||||
.separated_by(ascii_whitespace1())
|
||||
.many()
|
||||
.parse_finished(InputIter::new(line))
|
||||
{
|
||||
Ok(slot) => Some(Ok(slot)),
|
||||
Err(_) => Some(Err(Error::Parser(line.to_string()))),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_eapi(input: &str) -> Option<Result<Eapi, Error>> {
|
||||
let line = input.lines().find_map(|line| line.strip_prefix("EAPI="))?;
|
||||
|
||||
match Eapi::parser().parse_finished(InputIter::new(line)) {
|
||||
Ok(slot) => Some(Ok(slot)),
|
||||
Err(_) => Some(Err(Error::Parser(line.to_string()))),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_inherit(input: &str) -> Option<Result<Vec<Eclass>, Error>> {
|
||||
let line = input
|
||||
.lines()
|
||||
.find_map(|line| line.strip_prefix("INHERIT="))?;
|
||||
|
||||
match Eclass::parser()
|
||||
.separated_by(ascii_whitespace1())
|
||||
.many()
|
||||
.parse_finished(InputIter::new(line))
|
||||
{
|
||||
Ok(inherit) => Some(Ok(inherit)),
|
||||
Err(_) => Some(Err(Error::Parser(line.to_string()))),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_iuse(input: &str) -> Option<Result<Vec<IUseFlag>, Error>> {
|
||||
let line = input.lines().find_map(|line| line.strip_prefix("IUSE="))?;
|
||||
|
||||
match IUseFlag::parser()
|
||||
.separated_by(ascii_whitespace1())
|
||||
.many()
|
||||
.parse_finished(InputIter::new(line))
|
||||
{
|
||||
Ok(iuse) => Some(Ok(iuse)),
|
||||
Err(_) => Some(Err(Error::Parser(line.to_string()))),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_license(input: &str) -> Option<Result<Vec<Depend<License>>, Error>> {
|
||||
let line = input
|
||||
.lines()
|
||||
.find_map(|line| line.strip_suffix("LICENSE="))?;
|
||||
|
||||
match Depend::<License>::parser()
|
||||
.separated_by(ascii_whitespace1())
|
||||
.many()
|
||||
.parse_finished(InputIter::new(line))
|
||||
{
|
||||
Ok(license) => Some(Ok(license)),
|
||||
Err(_) => Some(Err(Error::Parser(line.to_string()))),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_description(input: &str) -> Option<String> {
|
||||
input
|
||||
.lines()
|
||||
.find_map(|line| line.strip_prefix("DESCRIPTION=").map(str::to_string))
|
||||
}
|
||||
|
||||
fn read_depend(input: &str) -> Option<Result<Vec<Depend<Atom>>, Error>> {
|
||||
let line = input
|
||||
.lines()
|
||||
.find_map(|line| line.strip_prefix("DEPEND="))?;
|
||||
|
||||
Some(parse_depends(line))
|
||||
}
|
||||
|
||||
fn read_bdepend(input: &str) -> Option<Result<Vec<Depend<Atom>>, Error>> {
|
||||
let line = input
|
||||
.lines()
|
||||
.find_map(|line| line.strip_prefix("BDEPEND="))?;
|
||||
|
||||
Some(parse_depends(line))
|
||||
}
|
||||
|
||||
fn read_rdepend(input: &str) -> Option<Result<Vec<Depend<Atom>>, Error>> {
|
||||
let line = input
|
||||
.lines()
|
||||
.find_map(|line| line.strip_prefix("RDEPEND="))?;
|
||||
|
||||
Some(parse_depends(line))
|
||||
}
|
||||
|
||||
fn read_idepend(input: &str) -> Option<Result<Vec<Depend<Atom>>, Error>> {
|
||||
let line = input
|
||||
.lines()
|
||||
.find_map(|line| line.strip_prefix("IDEPEND="))?;
|
||||
|
||||
Some(parse_depends(line))
|
||||
}
|
||||
|
||||
fn parse_depends(line: &str) -> Result<Vec<Depend<Atom>>, Error> {
|
||||
Depend::<Atom>::parser()
|
||||
.separated_by(ascii_whitespace1())
|
||||
.many()
|
||||
.parse_finished(InputIter::new(line))
|
||||
.map_err(|_| Error::Parser(line.to_string()))
|
||||
}
|
||||
Reference in New Issue
Block a user