summaryrefslogtreecommitdiff
path: root/src/package_installer.rs
diff options
context:
space:
mode:
authorHermes Agent <hermes@localhost>2026-08-12 01:06:37 +0000
committerHermes Agent <hermes@localhost>2026-08-12 01:06:37 +0000
commit915fcc5efbf45d8adee870cfbe2a25e466454a2f (patch)
treed5fdf2a745f905bd5fcce2ddac14d3e1c7a852e9 /src/package_installer.rs
parentfacbf9629f45b95d34a4d0844e617f87a35be5ff (diff)
Isolate Hasher behind typed installer adapter
Diffstat (limited to 'src/package_installer.rs')
-rw-r--r--src/package_installer.rs91
1 files changed, 91 insertions, 0 deletions
diff --git a/src/package_installer.rs b/src/package_installer.rs
new file mode 100644
index 0000000..b08c469
--- /dev/null
+++ b/src/package_installer.rs
@@ -0,0 +1,91 @@
+use std::path::{Path, PathBuf};
+
+use anyhow::{bail, Result};
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct AptConfig(PathBuf);
+
+impl AptConfig {
+ pub fn new(path: impl AsRef<Path>) -> Result<Self> {
+ let path = path.as_ref();
+ if path.as_os_str().is_empty() || path.is_dir() {
+ bail!("APT configuration must be a file path: {}", path.display());
+ }
+ Ok(Self(path.to_path_buf()))
+ }
+
+ pub fn as_path(&self) -> &Path {
+ &self.0
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PackageRequest {
+ workdir: PathBuf,
+ apt_config: AptConfig,
+ packages: Vec<String>,
+ selectors: Vec<String>,
+}
+
+impl PackageRequest {
+ pub fn new(
+ workdir: impl AsRef<Path>,
+ apt_config: AptConfig,
+ packages: impl IntoIterator<Item = impl Into<String>>,
+ selectors: impl IntoIterator<Item = impl Into<String>>,
+ ) -> Result<Self> {
+ let workdir = workdir.as_ref();
+ if workdir.as_os_str().is_empty() {
+ bail!("Hasher workdir cannot be empty");
+ }
+
+ let mut packages = normalize("package", packages)?;
+ let mut selectors = normalize("package selector", selectors)?;
+ packages.sort();
+ packages.dedup();
+ selectors.sort();
+ selectors.dedup();
+ Ok(Self {
+ workdir: workdir.to_path_buf(),
+ apt_config,
+ packages,
+ selectors,
+ })
+ }
+
+ pub fn workdir(&self) -> &Path {
+ &self.workdir
+ }
+
+ pub fn apt_config(&self) -> &AptConfig {
+ &self.apt_config
+ }
+
+ pub fn packages(&self) -> &[String] {
+ &self.packages
+ }
+
+ pub fn selectors(&self) -> &[String] {
+ &self.selectors
+ }
+}
+
+fn normalize(
+ kind: &str,
+ values: impl IntoIterator<Item = impl Into<String>>,
+) -> Result<Vec<String>> {
+ values
+ .into_iter()
+ .map(Into::into)
+ .map(|value: String| {
+ if value.trim().is_empty() {
+ bail!("{kind} cannot be empty");
+ }
+ Ok(value)
+ })
+ .collect()
+}
+
+pub trait PackageInstaller {
+ fn install(&self, request: &PackageRequest) -> Result<()>;
+}