summaryrefslogtreecommitdiffstats
path: root/src/repository/repository.rs
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2020-10-04 14:58:06 +0200
committerMatthias Beyer <mail@beyermatthias.de>2020-10-12 19:45:21 +0200
commit86b85ca4f5a535e3470dc3ae8e3aa1c2a75bb6b3 (patch)
treea6a05621eb121c95ca6818aa58acef519436e070 /src/repository/repository.rs
parentc40f0ae3e868279e0df32e7403da505e4415cbed (diff)
Implement Repository
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
Diffstat (limited to 'src/repository/repository.rs')
-rw-r--r--src/repository/repository.rs100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/repository/repository.rs b/src/repository/repository.rs
new file mode 100644
index 0000000..8997e6a
--- /dev/null
+++ b/src/repository/repository.rs
@@ -0,0 +1,100 @@
+use std::collections::BTreeMap;
+use std::path::PathBuf;
+use std::path::Path;
+use anyhow::Result;
+use anyhow::Context;
+use resiter::Map;
+
+use crate::package::Package;
+use crate::package::PackageName;
+use crate::package::PackageVersion;
+use crate::package::PackageVersionConstraint;
+
+/// A repository represents a collection of packages
+pub struct Repository {
+ inner: BTreeMap<(PackageName, PackageVersion), Package>,
+}
+
+impl Repository {
+
+ pub fn load(path: &Path) -> Result<Self> {
+ fn all_subdirs(p: &Path) -> Result<Vec<PathBuf>> {
+ let mut v = Vec::new();
+ for de in p.read_dir()? {
+ let de = de?;
+ if de.file_type()?.is_dir() {
+ v.push(de.path());
+ }
+ }
+
+ return Ok(v)
+ }
+
+ fn load_recursive(path: &Path, mut config: config::Config) -> Result<Vec<Result<Package>>> {
+ let pkg_file = path.join("pkg.toml");
+
+ if pkg_file.is_file() {
+ let buf = std::fs::read_to_string(&pkg_file)
+ .with_context(|| format!("Reading {}", pkg_file.display()))?;
+
+ config.merge(config::File::from_str(&buf, config::FileFormat::Toml))
+ .with_context(|| format!("Loading contents of {}", pkg_file.display()))?;
+ }
+
+ let subdirs = all_subdirs(path).with_context(|| format!("Finding subdirs for {}", pkg_file.display()))?;
+
+ if subdirs.is_empty() {
+ let package = config
+ .deserialize()
+ .with_context(|| format!("Failed to parse {} into package", path.display()));
+
+ Ok(vec![package])
+ } else {
+ subdirs
+ .into_iter()
+ .fold(Ok(Vec::new()), |vec, dir| {
+ vec.and_then(|mut v| {
+ let mut loaded = load_recursive(&dir, config.clone())
+ .with_context(|| format!("Recursing for {}", pkg_file.display()))?;
+
+ v.append(&mut loaded);
+ Ok(v)
+ })
+ })
+ }
+ }
+
+ let inner = load_recursive(path, config::Config::default())
+ .with_context(|| format!("Recursing for {}", path.display()))?
+ .into_iter()
+ .map_ok(|p| ((p.name().clone(), p.version().clone()), p))
+ .collect::<Result<_>>()?;
+
+ Ok(Repository { inner })
+ }
+
+ pub fn find_by_name<'a>(&'a self, name: &PackageName) -> Vec<&'a Package> {
+ self.inner
+ .iter()
+ .filter(|((n, _), _)| name == n)
+ .map(|(_, pack)| pack)
+ .collect()
+ }
+
+ pub fn find<'a>(&'a self, name: &PackageName, version: &PackageVersion) -> Option<&'a Package> {
+ self.inner
+ .iter()
+ .find(|((n, v), _)| n == name && v == version)
+ .map(|(_, p)| p)
+ }
+
+ pub fn find_with_version_contraint<'a>(&'a self, name: &PackageName, vc: &PackageVersionConstraint) -> Vec<&'a Package> {
+ self.inner
+ .iter()
+ .filter(|((n, v), _)| {
+ n == name && vc.matches(v).map(|mtch| !mtch.is_false()).unwrap_or(false)
+ })
+ .map(|(_, p)| p)
+ .collect()
+ }
+}