From 64350de3a982ea073dffa5ff2122588f26c80b8c Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Fri, 3 Sep 2021 23:13:37 +0200 Subject: [PATCH] feat(package): complete archiver implementation --- espanso-package/src/archive/default.rs | 123 +++++++++++++++++++++++-- espanso-package/src/archive/mod.rs | 41 +++++++-- espanso-package/src/lib.rs | 4 +- 3 files changed, 153 insertions(+), 15 deletions(-) diff --git a/espanso-package/src/archive/default.rs b/espanso-package/src/archive/default.rs index a00be55..56ccfdf 100644 --- a/espanso-package/src/archive/default.rs +++ b/espanso-package/src/archive/default.rs @@ -20,9 +20,11 @@ use anyhow::{bail, Context, Result}; use std::path::{Path, PathBuf}; -use crate::{ArchivedPackage, Archiver, Package, PackageSpecifier, SaveOptions}; +use crate::{ + manifest::Manifest, ArchivedPackage, Archiver, Package, PackageSpecifier, SaveOptions, +}; -use super::StoredPackage; +use super::{LegacyPackage, PackageSource, StoredPackage, PACKAGE_SOURCE_FILE}; pub struct DefaultArchiver { package_dir: PathBuf, @@ -92,16 +94,58 @@ impl Archiver for DefaultArchiver { Ok(archived_package) } - fn get(&self, name: &str) -> Result { - todo!() + fn get(&self, name: &str) -> Result { + let target_dir = self.package_dir.join(name); + + if !target_dir.is_dir() { + bail!("package '{}' not found", name); + } + + let manifest_path = target_dir.join("_manifest.yml"); + if !manifest_path.is_file() { + return Ok(StoredPackage::Legacy(LegacyPackage { + name: name.to_string(), + })); + } + + let manifest = Manifest::parse(&manifest_path).context("unable to parse package manifest")?; + + let source_path = target_dir.join(PACKAGE_SOURCE_FILE); + let source = + PackageSource::parse(&source_path).context("unable to parse package source file")?; + + Ok(StoredPackage::Modern(ArchivedPackage { manifest, source })) } fn list(&self) -> Result> { - todo!() + let mut output = Vec::new(); + + for path in std::fs::read_dir(&self.package_dir)? { + let path = path?.path(); + if !path.is_dir() { + continue; + } + + if let Some(package_name) = path.file_name() { + let package_name = package_name.to_string_lossy().to_string(); + + output.push(self.get(&package_name)?); + } + } + + Ok(output) } fn delete(&self, name: &str) -> Result<()> { - todo!() + let target_dir = self.package_dir.join(name); + + if !target_dir.is_dir() { + bail!("package {} not found", name); + } + + std::fs::remove_dir_all(&target_dir).context("unable to remove package directory")?; + + Ok(()) } } @@ -232,4 +276,71 @@ matches: assert!(result.is_ok()); }); } + + #[test] + fn test_delete_package() { + run_with_two_temp_dirs(|package_dir, dest_dir| { + let package = create_fake_package(package_dir); + + let archiver = DefaultArchiver::new(dest_dir); + let result = archiver.save( + &*package, + &PackageSpecifier { + name: "package1".to_string(), + git_repo_url: Some("https://github.com/espanso/dummy-package".to_string()), + git_branch: Some("main".to_string()), + ..Default::default() + }, + &SaveOptions::default(), + ); + + assert!(result.is_ok()); + + let package_out_dir = dest_dir.join("package1"); + assert!(package_out_dir.is_dir()); + + archiver.delete("package1").unwrap(); + + assert!(!package_out_dir.is_dir()); + }); + } + + #[test] + fn test_list_packages() { + run_with_two_temp_dirs(|package_dir, dest_dir| { + let package = create_fake_package(package_dir); + + let archiver = DefaultArchiver::new(dest_dir); + let result = archiver.save( + &*package, + &PackageSpecifier { + name: "package1".to_string(), + git_repo_url: Some("https://github.com/espanso/dummy-package".to_string()), + git_branch: Some("main".to_string()), + ..Default::default() + }, + &SaveOptions::default(), + ); + + assert!(result.is_ok()); + + let package_out_dir = dest_dir.join("package1"); + assert!(package_out_dir.is_dir()); + + let legacy_package = dest_dir.join("z_legacypackage1"); + create_dir_all(&legacy_package).unwrap(); + + let package_list = archiver.list().unwrap(); + + assert!(package_list.iter().any(|package| *package + == StoredPackage::Modern(ArchivedPackage { + manifest: Manifest::parse(&package_out_dir.join("_manifest.yml")).unwrap(), + source: PackageSource::parse(&package_out_dir.join(PACKAGE_SOURCE_FILE)).unwrap(), + }))); + assert!(package_list.iter().any(|package| *package + == StoredPackage::Legacy(LegacyPackage { + name: "z_legacypackage1".to_string() + }))); + }); + } } diff --git a/espanso-package/src/archive/mod.rs b/espanso-package/src/archive/mod.rs index 8be0557..62ae7df 100644 --- a/espanso-package/src/archive/mod.rs +++ b/espanso-package/src/archive/mod.rs @@ -17,10 +17,12 @@ * along with espanso. If not, see . */ +use std::path::Path; + use anyhow::Result; use serde::{Deserialize, Serialize}; -use crate::{Package, PackageSpecifier, manifest::Manifest}; +use crate::{manifest::Manifest, Package, PackageSpecifier}; pub mod default; mod read; @@ -28,7 +30,7 @@ mod util; pub const PACKAGE_SOURCE_FILE: &str = "_pkgsource.yml"; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct ArchivedPackage { // Metadata pub manifest: Manifest, @@ -37,20 +39,25 @@ pub struct ArchivedPackage { pub source: PackageSource, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct LegacyPackage { pub name: String, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum StoredPackage { Legacy(LegacyPackage), Modern(ArchivedPackage), } pub trait Archiver { - fn get(&self, name: &str) -> Result; - fn save(&self, package: &dyn Package, specifier: &PackageSpecifier, save_options: &SaveOptions) -> Result; + fn get(&self, name: &str) -> Result; + fn save( + &self, + package: &dyn Package, + specifier: &PackageSpecifier, + save_options: &SaveOptions, + ) -> Result; fn list(&self) -> Result>; fn delete(&self, name: &str) -> Result<()>; } @@ -60,7 +67,7 @@ pub struct SaveOptions { pub overwrite_existing: bool, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "snake_case")] pub enum PackageSource { Hub, @@ -71,6 +78,26 @@ pub enum PackageSource { }, } +impl PackageSource { + pub fn parse(source_path: &Path) -> Result { + let source_str = std::fs::read_to_string(source_path)?; + Ok(serde_yaml::from_str(&source_str)?) + } +} + +impl std::fmt::Display for PackageSource { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PackageSource::Hub => write!(f, "espanso-hub"), + PackageSource::Git { + repo_url, + repo_branch: _, + use_native_git: _, + } => write!(f, "git: {}", repo_url), + } + } +} + impl From<&PackageSpecifier> for PackageSource { fn from(package: &PackageSpecifier) -> Self { if let Some(git_repo_url) = package.git_repo_url.as_deref() { diff --git a/espanso-package/src/lib.rs b/espanso-package/src/lib.rs index 4f084a5..4183d9e 100644 --- a/espanso-package/src/lib.rs +++ b/espanso-package/src/lib.rs @@ -28,7 +28,7 @@ mod provider; mod resolver; mod util; -pub use archive::{ArchivedPackage, Archiver, SaveOptions}; +pub use archive::{ArchivedPackage, Archiver, SaveOptions, StoredPackage}; pub use provider::{PackageSpecifier, PackageProvider}; pub use package::Package; @@ -74,7 +74,7 @@ pub fn get_provider(package: &PackageSpecifier) -> Result