feat(core): implement list, uninstall and update package commands

This commit is contained in:
Federico Terzi 2021-09-03 23:23:35 +02:00
parent 64350de3a9
commit 393f431bc3
7 changed files with 261 additions and 12 deletions

View File

@ -27,7 +27,7 @@ use crate::info_println;
pub fn install_package(paths: &Paths, matches: &ArgMatches) -> Result<()> {
let package_name = matches
.value_of("package_name")
.ok_or(anyhow!("missing package name"))?;
.ok_or_else(|| anyhow!("missing package name"))?;
let version = matches.value_of("version");
let force = matches.is_present("force");

View File

@ -0,0 +1,53 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use anyhow::{Context, Result};
use clap::ArgMatches;
use espanso_package::StoredPackage;
use espanso_path::Paths;
use crate::info_println;
pub fn list_packages(paths: &Paths, _: &ArgMatches) -> Result<()> {
let archiver =
espanso_package::get_archiver(&paths.packages).context("unable to get package archiver")?;
let packages = archiver.list().context("unable to list packages")?;
if packages.is_empty() {
info_println!("No packages found!");
return Ok(());
}
info_println!("Installed packages:");
info_println!("");
for package in packages {
match package {
StoredPackage::Legacy(legacy) => {
info_println!("- {} (legacy)", legacy.name);
},
StoredPackage::Modern(package) => {
info_println!("- {} - version: {} ({})", package.manifest.name, package.manifest.version, package.source);
},
}
}
Ok(())
}

View File

@ -19,12 +19,18 @@
use crate::{
error_eprintln,
exit_code::{configure_custom_panic_hook, PACKAGE_INSTALL_FAILED, PACKAGE_SUCCESS},
exit_code::{
configure_custom_panic_hook, PACKAGE_INSTALL_FAILED, PACKAGE_LIST_FAILED, PACKAGE_SUCCESS,
PACKAGE_UNINSTALL_FAILED, PACKAGE_UPDATE_FAILED, PACKAGE_UPDATE_PARTIAL_FAILURE,
},
};
use super::{CliModule, CliModuleArgs};
mod install;
mod list;
mod uninstall;
mod update;
pub fn new() -> CliModule {
CliModule {
@ -49,9 +55,29 @@ fn package_main(args: CliModuleArgs) -> i32 {
error_eprintln!("unable to install package: {:?}", err);
return PACKAGE_INSTALL_FAILED;
}
} else if let Some(sub_matches) = cli_args.subcommand_matches("uninstall") {
if let Err(err) = uninstall::uninstall_package(&paths, sub_matches) {
error_eprintln!("unable to uninstall package: {:?}", err);
return PACKAGE_UNINSTALL_FAILED;
}
} else if let Some(sub_matches) = cli_args.subcommand_matches("list") {
if let Err(err) = list::list_packages(&paths, sub_matches) {
error_eprintln!("unable to list packages: {:?}", err);
return PACKAGE_LIST_FAILED;
}
} else if let Some(sub_matches) = cli_args.subcommand_matches("update") {
match update::update_package(&paths, sub_matches) {
Ok(update::UpdateResults::PartialFailure) => {
error_eprintln!("some packages were updated, but not all of them. Check the previous log for more information");
return PACKAGE_UPDATE_PARTIAL_FAILURE;
}
Err(err) => {
error_eprintln!("unable to update package: {:?}", err);
return PACKAGE_UPDATE_FAILED;
}
_ => {}
}
}
// TODO: uninstall, list, update
PACKAGE_SUCCESS
}

View File

@ -0,0 +1,39 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use anyhow::{anyhow, Context, Result};
use clap::ArgMatches;
use espanso_path::Paths;
use crate::info_println;
pub fn uninstall_package(paths: &Paths, matches: &ArgMatches) -> Result<()> {
let package_name = matches
.value_of("package_name")
.ok_or_else(|| anyhow!("missing package name"))?;
let archiver =
espanso_package::get_archiver(&paths.packages).context("unable to get package archiver")?;
archiver.delete(package_name).context("unable to delete package")?;
info_println!("package '{}' uninstalled!", package_name);
Ok(())
}

View File

@ -0,0 +1,124 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use anyhow::{anyhow, Context, Result};
use clap::ArgMatches;
use espanso_package::{Archiver, PackageSpecifier, SaveOptions, StoredPackage};
use espanso_path::Paths;
use crate::{error_eprintln, info_println, warn_eprintln};
pub enum UpdateResults {
Success,
PartialFailure,
}
pub fn update_package(paths: &Paths, matches: &ArgMatches) -> Result<UpdateResults> {
let package_name = matches
.value_of("package_name")
.ok_or_else(|| anyhow!("missing package name"))?;
let archiver =
espanso_package::get_archiver(&paths.packages).context("unable to get package archiver")?;
let packages_to_update = if package_name == "all" {
let packages = archiver.list()?;
info_println!("updating {} packages", packages.len());
packages
.into_iter()
.map(|package| match package {
StoredPackage::Legacy(legacy) => legacy.name,
StoredPackage::Modern(modern) => modern.manifest.name,
})
.collect()
} else {
vec![package_name.to_owned()]
};
let mut update_errors = Vec::new();
for package_name in &packages_to_update {
if let Err(err) = perform_package_update(&*archiver, &package_name) {
error_eprintln!("error updating package '{}': {:?}", package_name, err);
update_errors.push(err);
}
}
if update_errors.is_empty() {
Ok(UpdateResults::Success)
} else if packages_to_update.len() == update_errors.len() {
Err(update_errors.pop().expect("unable to extract error"))
} else {
Ok(UpdateResults::PartialFailure)
}
}
fn perform_package_update(archiver: &dyn Archiver, package_name: &str) -> Result<()> {
info_println!("updating package: {}", package_name);
let package = archiver.get(package_name)?;
let (package_specifier, old_version) = match package {
StoredPackage::Legacy(legacy) => {
warn_eprintln!(
"detected legacy package '{}' without source information, pulling from espanso hub.",
legacy.name
);
(
PackageSpecifier {
name: legacy.name,
..Default::default()
},
None,
)
}
StoredPackage::Modern(modern) => ((&modern).into(), Some(modern.manifest.version)),
};
let package_provider = espanso_package::get_provider(&package_specifier)
.context("unable to obtain compatible package provider")?;
info_println!("using package provider: {}", package_provider.name());
let new_package = package_provider.download(&package_specifier)?;
if new_package.version() == old_version.unwrap_or_default() {
info_println!("already up to date!");
return Ok(());
}
archiver
.save(
&*new_package,
&package_specifier,
&SaveOptions {
overwrite_existing: true,
},
)
.context("unable to save package")?;
info_println!(
"updated package '{}' to version: {}",
new_package.name(),
new_package.version()
);
Ok(())
}

View File

@ -58,6 +58,10 @@ pub const WORKAROUND_NOT_AVAILABLE: i32 = 2;
pub const PACKAGE_SUCCESS: i32 = 0;
pub const PACKAGE_UNEXPECTED_FAILURE: i32 = 1;
pub const PACKAGE_INSTALL_FAILED: i32 = 2;
pub const PACKAGE_UNINSTALL_FAILED: i32 = 3;
pub const PACKAGE_LIST_FAILED: i32 = 4;
pub const PACKAGE_UPDATE_FAILED: i32 = 5;
pub const PACKAGE_UPDATE_PARTIAL_FAILURE: i32 = 6;
use std::sync::Mutex;

View File

@ -368,18 +368,21 @@ fn main() {
.about("package-management commands")
.subcommand(install_subcommand.clone())
.subcommand(uninstall_subcommand.clone())
.subcommand(SubCommand::with_name("update").about(
"Update a package. If 'all' is passed as package name, attempts to update all packages.",
).arg(Arg::with_name("package_name").help("Package name")))
.subcommand(
SubCommand::with_name("list").about("List all installed packages"), // TODO: update <Package> and update all
)
.subcommand(
SubCommand::with_name("workaround")
.subcommand(
SubCommand::with_name("secure-input")
.about("Attempt to disable secure input by automating the common steps."),
)
.about("A collection of workarounds to solve some common problems."),
),
)
.subcommand(
SubCommand::with_name("workaround")
.subcommand(
SubCommand::with_name("secure-input")
.about("Attempt to disable secure input by automating the common steps."),
)
.about("A collection of workarounds to solve some common problems."),
)
.subcommand(
SubCommand::with_name("worker")
.setting(AppSettings::Hidden)