use std::path::PathBuf;
use bytes::Bytes;
use miette::{Context, IntoDiagnostic, miette};
use tokio::fs;
use crate::{
manifest::{Dependency, DependencyManifest},
package::Package,
};
#[derive(Debug, Clone)]
pub struct LocalRegistry {
base_dir: PathBuf,
}
impl LocalRegistry {
#[allow(dead_code)]
pub fn new(base_dir: PathBuf) -> Self {
LocalRegistry { base_dir }
}
pub async fn download(&self, dependency: Dependency) -> miette::Result<Package> {
let DependencyManifest::Remote(ref manifest) = dependency.manifest else {
return Err(miette!(
"unable to serialize version of local dependency ({})",
dependency.package
));
};
let version = super::dependency_version_string(&dependency)?;
let path = self.base_dir.join(PathBuf::from(format!(
"{}/{}/{}-{}.tgz",
manifest.repository, dependency.package, dependency.package, version
)));
tracing::debug!("downloaded dependency {dependency} from {:?}", path);
let bytes = Bytes::from(
fs::read(path)
.await
.into_diagnostic()
.wrap_err(miette!("could not read file"))?,
);
Package::try_from(bytes).wrap_err(miette!(
"failed to download dependency {}",
dependency.package
))
}
pub async fn publish(&self, package: Package, repository: String) -> miette::Result<()> {
let path = self.base_dir.join(PathBuf::from(format!(
"{}/{}/{}-{}.tgz",
repository,
package.name(),
package.name(),
package.version(),
)));
fs::create_dir_all(path.parent().unwrap())
.await
.into_diagnostic()?;
fs::write(&path, &package.tgz)
.await
.into_diagnostic()
.wrap_err(miette!("could not write to file: {}", path.display()))?;
tracing::info!(
"published {}/{}@{} to {:?}",
repository,
package.name(),
package.version(),
path
);
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::{
manifest::{Dependency, PackageManifest, PackagesManifest},
package::{Package, PackageType},
registry::cache::LocalRegistry,
};
use bytes::Bytes;
use std::{env, path::PathBuf};
use tokio::fs;
#[tokio::test]
#[ignore = "gzid header issues"]
async fn can_publish_and_fetch() {
let dir = env::temp_dir();
let registry = LocalRegistry::new(dir.clone());
let manifest = PackagesManifest::builder()
.package(PackageManifest {
kind: PackageType::Api,
name: "test-api".parse().unwrap(),
version: "0.1.0".parse().unwrap(),
description: None,
})
.dependencies(Default::default())
.build();
let package_bytes =
Bytes::from(include_bytes!("../../tests/data/packages/test-api-0.1.0.tgz").to_vec());
registry
.publish(
Package {
manifest: manifest.clone(),
tgz: package_bytes.clone(),
},
"test-repo".into(),
)
.await
.unwrap();
assert_eq!(
Bytes::from(
fs::read(dir.join(PathBuf::from("test-repo/test-api/test-api-0.1.0.tgz")))
.await
.unwrap()
),
package_bytes
);
let registry_uri = "http://some-registry/artifactory"
.parse()
.expect("Failed to parse registry URL");
let fetched = registry
.download(Dependency::new(
registry_uri,
"test-repo".into(),
"test-api".parse().unwrap(),
"=0.1.0".parse().unwrap(),
))
.await
.unwrap();
assert_eq!(fetched.manifest, manifest);
assert_eq!(fetched.tgz, package_bytes);
}
}