forked from gentoo-utils/gentoo-utils
impl Repo and md5-cache reading
This commit is contained in:
@@ -7,6 +7,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub mod parsers;
|
pub mod parsers;
|
||||||
|
pub mod repo;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Conditional {
|
pub enum Conditional {
|
||||||
@@ -23,10 +24,28 @@ pub enum Depend<T> {
|
|||||||
ConditionalGroup(Conditional, Vec<Self>),
|
ConditionalGroup(Conditional, Vec<Self>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum UriPrefix {
|
||||||
|
Mirror,
|
||||||
|
Fetch,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Get)]
|
#[derive(Debug, Clone, Get)]
|
||||||
pub struct SrcUri {
|
pub struct Uri {
|
||||||
uri: String,
|
#[get(kind = "deref")]
|
||||||
file_name: Option<PathBuf>,
|
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)]
|
#[derive(Debug, Clone, Get)]
|
||||||
@@ -44,14 +63,22 @@ pub struct Ebuild {
|
|||||||
version: Version,
|
version: Version,
|
||||||
slot: Option<Slot>,
|
slot: Option<Slot>,
|
||||||
homepage: Option<String>,
|
homepage: Option<String>,
|
||||||
|
#[get(kind = "deref")]
|
||||||
src_uri: Vec<Depend<SrcUri>>,
|
src_uri: Vec<Depend<SrcUri>>,
|
||||||
eapi: Option<Eapi>,
|
eapi: Option<Eapi>,
|
||||||
|
#[get(kind = "deref")]
|
||||||
inherit: Vec<Eclass>,
|
inherit: Vec<Eclass>,
|
||||||
|
#[get(kind = "deref")]
|
||||||
iuse: Vec<IUseFlag>,
|
iuse: Vec<IUseFlag>,
|
||||||
|
#[get(kind = "deref")]
|
||||||
license: Vec<Depend<License>>,
|
license: Vec<Depend<License>>,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
|
#[get(kind = "deref")]
|
||||||
depend: Vec<Depend<Atom>>,
|
depend: Vec<Depend<Atom>>,
|
||||||
|
#[get(kind = "deref")]
|
||||||
bdepend: Vec<Depend<Atom>>,
|
bdepend: Vec<Depend<Atom>>,
|
||||||
rdpened: Vec<Depend<Atom>>,
|
#[get(kind = "deref")]
|
||||||
|
rdepend: Vec<Depend<Atom>>,
|
||||||
|
#[get(kind = "deref")]
|
||||||
idepend: Vec<Depend<Atom>>,
|
idepend: Vec<Depend<Atom>>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,32 +4,59 @@ use mon::{Parser, alpha1, r#if, tag, whitespace1};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Parseable,
|
Parseable,
|
||||||
ebuild::{Conditional, Depend, Eapi, License, SrcUri},
|
ebuild::{Conditional, Depend, Eapi, Eclass, License, SrcUri, Uri, UriPrefix},
|
||||||
useflag::UseFlag,
|
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())
|
||||||
|
.list(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 {
|
impl<'a> Parseable<'a, &'a str> for SrcUri {
|
||||||
type Parser = impl Parser<&'a str, Output = Self>;
|
type Parser = impl Parser<&'a str, Output = Self>;
|
||||||
|
|
||||||
fn parser() -> Self::Parser {
|
fn parser() -> Self::Parser {
|
||||||
let protocol = alpha1::<&str>().followed_by(tag("://"));
|
let filename = || {
|
||||||
|
r#if(|c: &char| !c.is_ascii_whitespace())
|
||||||
|
.list(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,
|
||||||
|
});
|
||||||
|
|
||||||
let uri = r#if(|c: &char| !c.is_ascii_whitespace())
|
uri.or(filename().map(|path: PathBuf| SrcUri::Filename(path)))
|
||||||
.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 })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +88,20 @@ impl<'a> Parseable<'a, &'a str> for Eapi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
.list(1..)
|
||||||
|
.recognize()
|
||||||
|
.map(|output: &str| Eclass(output.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, T> Parseable<'a, &'a str> for Depend<T>
|
impl<'a, T> Parseable<'a, &'a str> for Depend<T>
|
||||||
where
|
where
|
||||||
T: Parseable<'a, &'a str>,
|
T: Parseable<'a, &'a str>,
|
||||||
|
|||||||
303
src/ebuild/repo/mod.rs
Normal file
303
src/ebuild/repo/mod.rs
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
use std::{
|
||||||
|
fs, io,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use get::Get;
|
||||||
|
|
||||||
|
use mon::{Parser, input::InputIter, tag, whitespace1};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Parseable,
|
||||||
|
atom::{self, Atom},
|
||||||
|
ebuild::{Depend, Eapi, Ebuild, Eclass, License, SrcUri},
|
||||||
|
useflag::IUseFlag,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("io error: {0}")]
|
||||||
|
Io(#[from] 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(fs::ReadDir);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Ebuilds(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> {
|
||||||
|
Ok(Categories(fs::read_dir(
|
||||||
|
self.path.as_path().join("metadata/md5-cache"),
|
||||||
|
)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Category {
|
||||||
|
pub fn ebuilds(&self) -> Result<Ebuilds, Error> {
|
||||||
|
Ok(Ebuilds(fs::read_dir(self.path.as_path())?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for Categories {
|
||||||
|
type Item = Result<Category, Error>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self.0.next()? {
|
||||||
|
Ok(entry) => match read_category(entry.path()) {
|
||||||
|
Ok(category) => Some(Ok(category)),
|
||||||
|
Err(e) => Some(Err(e)),
|
||||||
|
},
|
||||||
|
Err(e) => Some(Err(Error::Io(e))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for Ebuilds {
|
||||||
|
type Item = Result<Ebuild, Error>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self.0.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())?;
|
||||||
|
|
||||||
|
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(|s| s.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_list(whitespace1(), 0..)
|
||||||
|
.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_list(whitespace1(), 0..)
|
||||||
|
.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_list(whitespace1(), 0..)
|
||||||
|
.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_list(whitespace1(), 0..)
|
||||||
|
.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(|s| s.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_list(whitespace1(), 0..)
|
||||||
|
.parse_finished(InputIter::new(line))
|
||||||
|
.map_err(|_| Error::Parser(line.to_string()))
|
||||||
|
}
|
||||||
10
tests/repo.rs
Normal file
10
tests/repo.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
use gentoo_utils::ebuild::repo::Repo;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_repo() {
|
||||||
|
let repo = Repo::new("/var/db/repos/gentoo");
|
||||||
|
|
||||||
|
for category in repo.categories().unwrap().map(|cat| cat.unwrap()) {
|
||||||
|
for _ in category.ebuilds().unwrap().map(|ebuild| ebuild.unwrap()) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user