summaryrefslogtreecommitdiff
path: root/src/ebuild/repo/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ebuild/repo/mod.rs')
-rw-r--r--src/ebuild/repo/mod.rs303
1 files changed, 303 insertions, 0 deletions
diff --git a/src/ebuild/repo/mod.rs b/src/ebuild/repo/mod.rs
new file mode 100644
index 0000000..0b1f23c
--- /dev/null
+++ b/src/ebuild/repo/mod.rs
@@ -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()))
+}