feat(package): implement espanso hub package index caching
This commit is contained in:
		
							parent
							
								
									23a73f7ea2
								
							
						
					
					
						commit
						bfb5a8ac4c
					
				| 
						 | 
				
			
			@ -32,11 +32,9 @@ mod util;
 | 
			
		|||
 | 
			
		||||
pub use archive::{ArchivedPackage, Archiver, SaveOptions, StoredPackage};
 | 
			
		||||
pub use package::Package;
 | 
			
		||||
pub use provider::{PackageProvider, PackageSpecifier};
 | 
			
		||||
pub use provider::{PackageProvider, PackageSpecifier, ProviderOptions};
 | 
			
		||||
 | 
			
		||||
// TODO: once the download is completed, avoid copying files beginning with "."
 | 
			
		||||
 | 
			
		||||
pub fn get_provider(package: &PackageSpecifier,) -> Result<Box<dyn PackageProvider>> {
 | 
			
		||||
pub fn get_provider(package: &PackageSpecifier, runtime_dir: &Path, options: &ProviderOptions) -> Result<Box<dyn PackageProvider>> {
 | 
			
		||||
  if let Some(git_repo_url) = package.git_repo_url.as_deref() {
 | 
			
		||||
    if !package.use_native_git {
 | 
			
		||||
      let matches_known_hosts =
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +74,7 @@ pub fn get_provider(package: &PackageSpecifier,) -> Result<Box<dyn PackageProvid
 | 
			
		|||
    Ok(Box::new(provider::git::GitPackageProvider::new()))
 | 
			
		||||
  } else {
 | 
			
		||||
    // Download from the official espanso hub
 | 
			
		||||
    Ok(Box::new(provider::hub::EspansoHubPackageProvider::new()))
 | 
			
		||||
    Ok(Box::new(provider::hub::EspansoHubPackageProvider::new(runtime_dir, options.force_index_update)))
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,11 @@
 | 
			
		|||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::{
 | 
			
		||||
  path::{Path, PathBuf},
 | 
			
		||||
  time::UNIX_EPOCH,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::PackageProvider;
 | 
			
		||||
use crate::{
 | 
			
		||||
  package::DefaultPackage, resolver::resolve_package, util::download::read_string_from_url,
 | 
			
		||||
| 
						 | 
				
			
			@ -28,11 +33,20 @@ use serde::{Deserialize, Serialize};
 | 
			
		|||
pub const ESPANSO_HUB_PACKAGE_INDEX_URL: &str =
 | 
			
		||||
  "https://github.com/espanso/hub/releases/latest/download/package_index.json";
 | 
			
		||||
 | 
			
		||||
pub struct EspansoHubPackageProvider {}
 | 
			
		||||
const PACKAGE_INDEX_CACHE_FILE: &str = "package_index_cache.json";
 | 
			
		||||
const PACKAGE_INDEX_CACHE_INVALIDATION_SECONDS: u64 = 60 * 60;
 | 
			
		||||
 | 
			
		||||
pub struct EspansoHubPackageProvider {
 | 
			
		||||
  runtime_dir: PathBuf,
 | 
			
		||||
  force_index_update: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl EspansoHubPackageProvider {
 | 
			
		||||
  pub fn new() -> Self {
 | 
			
		||||
    Self {}
 | 
			
		||||
  pub fn new(runtime_dir: &Path, force_index_update: bool) -> Self {
 | 
			
		||||
    Self {
 | 
			
		||||
      runtime_dir: runtime_dir.to_path_buf(),
 | 
			
		||||
      force_index_update,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,9 +56,8 @@ impl PackageProvider for EspansoHubPackageProvider {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  fn download(&self, package: &PackageSpecifier) -> Result<Box<dyn Package>> {
 | 
			
		||||
    // TODO: pass index update flag
 | 
			
		||||
    let index = self
 | 
			
		||||
      .get_index(true)
 | 
			
		||||
      .get_index(self.force_index_update)
 | 
			
		||||
      .context("unable to get package index from espanso hub")?;
 | 
			
		||||
 | 
			
		||||
    let package_info = index
 | 
			
		||||
| 
						 | 
				
			
			@ -82,9 +95,23 @@ impl PackageProvider for EspansoHubPackageProvider {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
impl EspansoHubPackageProvider {
 | 
			
		||||
  fn get_index(&self, _force_update: bool) -> Result<PackageIndex> {
 | 
			
		||||
    // TODO: if force_update is false, we should try to use a locally-cached version of the package index
 | 
			
		||||
    self.download_index()
 | 
			
		||||
  fn get_index(&self, force_update: bool) -> Result<PackageIndex> {
 | 
			
		||||
    let old_index = self.get_index_from_cache()?;
 | 
			
		||||
 | 
			
		||||
    if let Some(old_index) = old_index {
 | 
			
		||||
      if !force_update {
 | 
			
		||||
        let current_time = std::time::SystemTime::now().duration_since(UNIX_EPOCH)?;
 | 
			
		||||
        let current_unix = current_time.as_secs();
 | 
			
		||||
        if old_index.cached_at >= (current_unix - PACKAGE_INDEX_CACHE_INVALIDATION_SECONDS) {
 | 
			
		||||
          info_println!("using cached package index");
 | 
			
		||||
          return Ok(old_index.index);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let new_index = self.download_index()?;
 | 
			
		||||
    self.save_index_to_cache(new_index.clone())?;
 | 
			
		||||
    Ok(new_index)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn download_index(&self) -> Result<PackageIndex> {
 | 
			
		||||
| 
						 | 
				
			
			@ -95,9 +122,40 @@ impl EspansoHubPackageProvider {
 | 
			
		|||
 | 
			
		||||
    Ok(index)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn get_index_from_cache(&self) -> Result<Option<CachedPackageIndex>> {
 | 
			
		||||
    let target_file = self.runtime_dir.join(PACKAGE_INDEX_CACHE_FILE);
 | 
			
		||||
    if !target_file.is_file() {
 | 
			
		||||
      return Ok(None);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let content =
 | 
			
		||||
      std::fs::read_to_string(&target_file).context("unable to read package index cache")?;
 | 
			
		||||
    let index: CachedPackageIndex = serde_json::from_str(&content)?;
 | 
			
		||||
    Ok(Some(index))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn save_index_to_cache(&self, index: PackageIndex) -> Result<()> {
 | 
			
		||||
    let target_file = self.runtime_dir.join(PACKAGE_INDEX_CACHE_FILE);
 | 
			
		||||
    let current_time = std::time::SystemTime::now().duration_since(UNIX_EPOCH)?;
 | 
			
		||||
    let current_unix = current_time.as_secs();
 | 
			
		||||
    let cached_index = CachedPackageIndex {
 | 
			
		||||
      cached_at: current_unix,
 | 
			
		||||
      index,
 | 
			
		||||
    };
 | 
			
		||||
    let serialized = serde_json::to_string(&cached_index)?;
 | 
			
		||||
    std::fs::write(&target_file, serialized)?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize)]
 | 
			
		||||
struct CachedPackageIndex {
 | 
			
		||||
  cached_at: u64,
 | 
			
		||||
  index: PackageIndex,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
struct PackageIndex {
 | 
			
		||||
  last_update: u64,
 | 
			
		||||
  packages: Vec<PackageInfo>,
 | 
			
		||||
| 
						 | 
				
			
			@ -134,4 +192,4 @@ impl PackageIndex {
 | 
			
		|||
      matching_packages.into_iter().last()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,3 +44,7 @@ pub trait PackageProvider {
 | 
			
		|||
  // TODO: fn check update available? (probably should be only available in the hub)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct ProviderOptions {
 | 
			
		||||
  pub force_index_update: bool,
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user