diff options
Diffstat (limited to 'crates/repo/src/lib.rs')
| -rw-r--r-- | crates/repo/src/lib.rs | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/crates/repo/src/lib.rs b/crates/repo/src/lib.rs new file mode 100644 index 0000000..df4412d --- /dev/null +++ b/crates/repo/src/lib.rs @@ -0,0 +1,335 @@ +#![deny(clippy::pedantic, unused_imports)] +#![allow( + dead_code, + unstable_name_collisions, + clippy::missing_errors_doc, + clippy::missing_panics_doc +)] +#![feature(impl_trait_in_assoc_type)] + +use std::{ + fs, io, + os::unix::ffi::OsStrExt, + path::{Path, PathBuf}, +}; + +use crate::ebuild::{Depend, Eapi, Ebuild, Eclass, License, SrcUri}; + +use get::Get; + +use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter, tag}; + +use parseable::Parseable; + +use atom::{self, Atom}; + +use 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> { + loop { + match self.1.next()? { + Ok(entry) if entry.path().file_name().unwrap().as_bytes() == b"Manifest.gz" => (), + Ok(entry) => match read_category(entry.path()) { + Ok(category) => break Some(Ok(category)), + Err(e) => break Some(Err(e)), + }, + Err(e) => break Some(Err(Error::ReadDir(self.0.clone(), e))), + } + } + } +} + +impl Iterator for Ebuilds { + type Item = Result<Ebuild, Error>; + + fn next(&mut self) -> Option<Self::Item> { + loop { + match self.1.next()? { + Ok(entry) if entry.path().file_name().unwrap().as_bytes() == b"Manifest.gz" => (), + Ok(entry) => match read_ebuild(entry.path()) { + Ok(ebuild) => break Some(Ok(ebuild)), + Err(e) => break Some(Err(e)), + }, + Err(e) => break Some(Err(Error::ReadDir(self.0.clone(), e))), + } + } + } +} + +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())) +} |
