feat(package): complete archiver implementation
This commit is contained in:
		
							parent
							
								
									2c955198ff
								
							
						
					
					
						commit
						64350de3a9
					
				| 
						 | 
				
			
			@ -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<ArchivedPackage> {
 | 
			
		||||
    todo!()
 | 
			
		||||
  fn get(&self, name: &str) -> Result<StoredPackage> {
 | 
			
		||||
    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<Vec<StoredPackage>> {
 | 
			
		||||
    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()
 | 
			
		||||
        })));
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,10 +17,12 @@
 | 
			
		|||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
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<ArchivedPackage>;
 | 
			
		||||
  fn save(&self, package: &dyn Package, specifier: &PackageSpecifier, save_options: &SaveOptions) -> Result<ArchivedPackage>;
 | 
			
		||||
  fn get(&self, name: &str) -> Result<StoredPackage>;
 | 
			
		||||
  fn save(
 | 
			
		||||
    &self,
 | 
			
		||||
    package: &dyn Package,
 | 
			
		||||
    specifier: &PackageSpecifier,
 | 
			
		||||
    save_options: &SaveOptions,
 | 
			
		||||
  ) -> Result<ArchivedPackage>;
 | 
			
		||||
  fn list(&self) -> Result<Vec<StoredPackage>>;
 | 
			
		||||
  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<Self> {
 | 
			
		||||
    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() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<Box<dyn PackageProvide
 | 
			
		|||
    Ok(Box::new(provider::git::GitPackageProvider::new()))
 | 
			
		||||
  } else {
 | 
			
		||||
    // TODO: use espanso-hub method
 | 
			
		||||
    todo!();
 | 
			
		||||
    bail!("espanso hub method not yet implemented")
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user