First steps in install package method

This commit is contained in:
Federico Terzi 2019-09-26 10:33:28 +02:00
parent 16aff94dda
commit 79d803386b
4 changed files with 184 additions and 8 deletions

View File

@ -18,13 +18,15 @@
*/ */
use std::path::{PathBuf, Path}; use std::path::{PathBuf, Path};
use crate::package::{PackageIndex, UpdateResult}; use crate::package::{PackageIndex, UpdateResult, Package, InstallResult};
use std::error::Error; use std::error::Error;
use std::fs::File; use std::fs::File;
use std::io::BufReader; use std::io::BufReader;
use chrono::{NaiveDateTime, Timelike}; use chrono::{NaiveDateTime, Timelike};
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use crate::package::UpdateResult::{NotOutdated, Updated}; use crate::package::UpdateResult::{NotOutdated, Updated};
use crate::package::InstallResult::{NotFound, AlreadyInstalled};
use std::fs;
const DEFAULT_PACKAGE_INDEX_FILE : &str = "package_index.json"; const DEFAULT_PACKAGE_INDEX_FILE : &str = "package_index.json";
@ -91,9 +93,39 @@ impl DefaultPackageManager {
return 0; return 0;
} }
fn list_local_packages(&self) -> Vec<String> {
let dir = fs::read_dir(&self.package_dir);
let mut output = Vec::new();
if let Ok(dir) = dir {
for entry in dir {
if let Ok(entry) = entry {
let path = entry.path();
if path.is_dir() {
let name = path.file_name();
if let Some(name) = name {
output.push(name.to_str().unwrap().to_owned())
}
}
}
}
}
output
}
} }
impl super::PackageManager for DefaultPackageManager { impl super::PackageManager for DefaultPackageManager {
fn is_index_outdated(&self) -> bool {
let current_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards");
let current_timestamp = current_time.as_secs();
let local_index_timestamp = self.local_index_timestamp();
// Local index is outdated if older than a day
local_index_timestamp + 60*60*24 < current_timestamp
}
fn update_index(&mut self, force: bool) -> Result<UpdateResult, Box<dyn Error>> { fn update_index(&mut self, force: bool) -> Result<UpdateResult, Box<dyn Error>> {
if force || self.is_index_outdated() { if force || self.is_index_outdated() {
let updated_index = DefaultPackageManager::request_index()?; let updated_index = DefaultPackageManager::request_index()?;
@ -105,14 +137,35 @@ impl super::PackageManager for DefaultPackageManager {
} }
} }
fn is_index_outdated(&self) -> bool { fn get_package(&self, name: &str) -> Option<Package> {
let current_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards"); if let Some(local_index) = &self.local_index {
let current_timestamp = current_time.as_secs(); let result = local_index.packages.iter().find(|package| {
package.name == name
});
if let Some(package) = result {
return Some(package.clone())
}
}
let local_index_timestamp = self.local_index_timestamp(); None
}
// Local index is outdated if older than a day fn install_package(&self, name: &str) -> Result<InstallResult, Box<dyn Error>> {
local_index_timestamp + 60*60*24 < current_timestamp let package = self.get_package(name);
match package {
Some(package) => {
// Check if package is already present
let packages = self.list_local_packages();
if packages.iter().any(|p| p == name) { // Package already installed
Ok(AlreadyInstalled)
}else{ // Package not installed
unimplemented!()
}
},
None => {
Ok(NotFound)
},
}
} }
} }
@ -122,9 +175,12 @@ mod tests {
use tempfile::TempDir; use tempfile::TempDir;
use std::path::Path; use std::path::Path;
use crate::package::PackageManager; use crate::package::PackageManager;
use std::fs::create_dir;
const OUTDATED_INDEX_CONTENT : &str = include_str!("../res/test/outdated_index.json"); const OUTDATED_INDEX_CONTENT : &str = include_str!("../res/test/outdated_index.json");
const INDEX_CONTENT_WITHOUT_UPDATE: &str = include_str!("../res/test/index_without_update.json"); const INDEX_CONTENT_WITHOUT_UPDATE: &str = include_str!("../res/test/index_without_update.json");
const GET_PACKAGE_INDEX: &str = include_str!("../res/test/get_package_index.json");
const INSTALL_PACKAGE_INDEX: &str = include_str!("../res/test/install_package_index.json");
struct TempPackageManager { struct TempPackageManager {
package_dir: TempDir, package_dir: TempDir,
@ -204,4 +260,59 @@ mod tests {
assert_eq!(temp.package_manager.update_index(false).unwrap(), UpdateResult::Updated); assert_eq!(temp.package_manager.update_index(false).unwrap(), UpdateResult::Updated);
} }
#[test]
fn test_get_package_should_be_found() {
let mut temp = create_temp_package_manager(|_, data_dir| {
let index_file = data_dir.join(DEFAULT_PACKAGE_INDEX_FILE);
std::fs::write(index_file, GET_PACKAGE_INDEX);
});
assert_eq!(temp.package_manager.get_package("italian-accents").unwrap().title, "Italian Accents");
}
#[test]
fn test_get_package_should_not_be_found() {
let mut temp = create_temp_package_manager(|_, data_dir| {
let index_file = data_dir.join(DEFAULT_PACKAGE_INDEX_FILE);
std::fs::write(index_file, GET_PACKAGE_INDEX);
});
assert!(temp.package_manager.get_package("not-existing").is_none());
}
#[test]
fn test_list_local_packages() {
let mut temp = create_temp_package_manager(|package_dir, _| {
create_dir(package_dir.join("package-1"));
create_dir(package_dir.join("package2"));
std::fs::write(package_dir.join("dummyfile.txt"), "test");
});
let packages = temp.package_manager.list_local_packages();
assert_eq!(packages.len(), 2);
assert!(packages.iter().any(|p| p == "package-1"));
assert!(packages.iter().any(|p| p == "package2"));
}
#[test]
fn test_install_package_not_found() {
let mut temp = create_temp_package_manager(|package_dir, data_dir| {
let index_file = data_dir.join(DEFAULT_PACKAGE_INDEX_FILE);
std::fs::write(index_file, INSTALL_PACKAGE_INDEX);
});
assert_eq!(temp.package_manager.install_package("not-existing").unwrap(), NotFound);
}
#[test]
fn test_install_package_already_installed() {
let mut temp = create_temp_package_manager(|package_dir, data_dir| {
create_dir(package_dir.join("italian-accents"));
let index_file = data_dir.join(DEFAULT_PACKAGE_INDEX_FILE);
std::fs::write(index_file, INSTALL_PACKAGE_INDEX);
});
assert_eq!(temp.package_manager.install_package("italian-accents").unwrap(), AlreadyInstalled);
}
} }

View File

@ -22,8 +22,12 @@ use serde::{Serialize, Deserialize};
use std::error::Error; use std::error::Error;
pub trait PackageManager { pub trait PackageManager {
fn update_index(&mut self, force: bool) -> Result<UpdateResult, Box<dyn Error>>;
fn is_index_outdated(&self) -> bool; fn is_index_outdated(&self) -> bool;
fn update_index(&mut self, force: bool) -> Result<UpdateResult, Box<dyn Error>>;
fn get_package(&self, name: &str) -> Option<Package>;
fn install_package(&self, name: &str) -> Result<InstallResult, Box<dyn Error>>;
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -50,3 +54,10 @@ pub enum UpdateResult {
NotOutdated, NotOutdated,
Updated, Updated,
} }
#[derive(Clone, Debug, PartialEq)]
pub enum InstallResult {
NotFound,
AlreadyInstalled,
Installed
}

View File

@ -0,0 +1,27 @@
{
"lastUpdate": 1565437389,
"packages": [
{
"name": "basic-emojis",
"title": "Basic Emojis",
"version": "0.1.0",
"repo": "https://github.com/federico-terzi/espanso-hub-core",
"desc": "A package to include some basic emojis in espanso.",
"author": "Federico Terzi"
},
{
"name": "italian-accents",
"title": "Italian Accents",
"version": "0.1.0",
"repo": "https://github.com/federico-terzi/espanso-hub-core",
"desc": "Include Italian accents substitutions to espanso.",
"author": "Federico Terzi"
}
]}

View File

@ -0,0 +1,27 @@
{
"lastUpdate": 1565437389,
"packages": [
{
"name": "basic-emojis",
"title": "Basic Emojis",
"version": "0.1.0",
"repo": "https://github.com/federico-terzi/espanso-hub-core",
"desc": "A package to include some basic emojis in espanso.",
"author": "Federico Terzi"
},
{
"name": "italian-accents",
"title": "Italian Accents",
"version": "0.1.0",
"repo": "https://github.com/federico-terzi/espanso-hub-core",
"desc": "Include Italian accents substitutions to espanso.",
"author": "Federico Terzi"
}
]}