diff --git a/Cargo.toml b/Cargo.toml index 0cd2f42..f58c745 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ tar = "0.4" tempfile = "3" thiserror = "2" toml = "1" +itertools = "0.15" ureq = { version = "3", features = ["socks-proxy"] } url = { version = "2", features = ["serde"] } nix = { version = "0.31", features = ["fs"] } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 996e5e9..2dd2f64 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -11,6 +11,8 @@ path = "src/main.rs" anyhow = "1" clap = { version = "4", features = ["derive"] } console = "0.16" +nix = { version = "0.31", features = ["sched"] } +itertools = "0.15" packie = { path = "../" } rust-i18n = "4" indicatif = "0.18" diff --git a/cli/src/common.rs b/cli/src/common.rs index 3c6cb3a..0592d43 100644 --- a/cli/src/common.rs +++ b/cli/src/common.rs @@ -14,11 +14,11 @@ pub fn yesno(prompt: &str) -> bool { } pub fn print_list(indent: u32, list: impl Iterator>) { - let (_, column) = Term::stdout().size(); + let (row, _) = Term::stdout().size(); let mut line_used: usize = 0; for i in list { let mut i = i.as_ref().to_string(); - if line_used >= column as _ { + if line_used >= row as _ { line_used = 0; println!(); } diff --git a/cli/src/info.rs b/cli/src/info.rs index e75f7aa..673a03f 100644 --- a/cli/src/info.rs +++ b/cli/src/info.rs @@ -1,8 +1,8 @@ +use crate::common::format_size; use anyhow::anyhow; use clap::Parser; use packie::{PackieBuilder, package::PkgSpec}; - -use crate::common::format_size; +use rust_i18n::t; #[derive(Debug, Parser)] pub struct Cli { @@ -13,10 +13,10 @@ pub fn main(cli: Cli) -> anyhow::Result<()> { let mut packie = PackieBuilder::new().readonly(true).build()?; let mut found = packie.search_installation(&cli.pkgspec)?; if found.len() == 0 { - return Err(anyhow!("no package found")); + return Err(anyhow!("{}", t!("info.no_package_found"))); } if found.len() != 1 { - return Err(anyhow!("not unique package spec")); + return Err(anyhow!("{}", t!("info.pkgspec_not_unique"))); } let found = found.remove(0); diff --git a/cli/src/install.rs b/cli/src/install.rs index 37af533..77cde88 100644 --- a/cli/src/install.rs +++ b/cli/src/install.rs @@ -1,6 +1,7 @@ use crate::common::{format_size, print_list, yesno}; use clap::Parser; use indicatif::ProgressBar; +use itertools::Itertools; use packie::{ PackieBuilder, install::InstallOptions, package::PkgSpec, repo::DownloadPackageEvent, }; @@ -30,7 +31,10 @@ pub fn main(cli: Cli) -> anyhow::Result<()> { for i in cli.items { packages.append(&mut packie.calculate_depgraph(i.parse::()?)?); } - packages.dedup_by_key(|x| x.repo_pkg.pkg_manifest.pkg_ident()); + let packages: Vec<_> = packages + .into_iter() + .unique_by(|x| x.repo_pkg.pkg_manifest.pkg_ident()) + .collect(); if packages.is_empty() { eprintln!("No packages to install."); diff --git a/cli/src/main.rs b/cli/src/main.rs index 95781cf..39c216c 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -74,7 +74,16 @@ fn main() { } } -#[cfg(target_family = "unix")] +#[cfg(target_os = "linux")] +fn chroot(new_root: &Path) -> std::io::Result<()> { + nix::sched::unshare(nix::sched::CloneFlags::CLONE_NEWUSER)?; + nix::sched::unshare(nix::sched::CloneFlags::CLONE_NEWNS)?; + std::os::unix::fs::chroot(new_root)?; + std::env::set_current_dir("/")?; + Ok(()) +} + +#[cfg(all(target_family = "unix", not(target_os = "linux")))] fn chroot(new_root: &Path) -> std::io::Result<()> { std::os::unix::fs::chroot(new_root)?; std::env::set_current_dir("/")?; diff --git a/cli/src/remove.rs b/cli/src/remove.rs index e493af9..ccc47ec 100644 --- a/cli/src/remove.rs +++ b/cli/src/remove.rs @@ -1,18 +1,45 @@ +use crate::common::{print_list, yesno}; use anyhow::anyhow; use clap::Parser; use packie::{PackieBuilder, package::PkgSpec}; +use rust_i18n::t; #[derive(Debug, Parser)] pub struct Cli { - pkgspecs: Vec, + #[arg(short, long)] + yes: bool, + + items: Vec, } pub fn main(cli: Cli) -> anyhow::Result<()> { let mut packie = PackieBuilder::new().build()?; - for pkgspec in cli.pkgspecs { + let mut packages = Vec::with_capacity(cli.items.len()); + for pkgspec in cli.items { + let mut found = packie.search_installation(&pkgspec)?; + if found.is_empty() { + return Err(anyhow!("{}", t!("remove.package_not_found"))); + } + if found.len() != 1 { + return Err(anyhow!("{}", t!("remove.pkgspec_not_unique"))); + } + packages.push(found.remove(0)); + } + println!("{}", t!("remove.prompt_to_remove")); + print_list(4, packages.iter().map(|x| x.install_pkgspec.to_string())); + if cli.yes { + println!("y"); + } else if !yesno(&t!("remove.confirm_removal")) { + println!("{}", t!("remove.aborting")); + } + for i in packages { + println!( + "{}", + t!("remove.action_hint", package = i.pkg_manifest.pkg_ident()) + ); packie - .remove(&pkgspec) - .map_err(|err| anyhow!("failed to remove {pkgspec}: {err}"))?; + .remove(&i.pkg_manifest.pkg_ident().into()) + .map_err(|error| anyhow!("{}", t!("remove.action_failure", error = error)))?; } Ok(()) } diff --git a/locales/packie-cli/main.yml b/locales/packie-cli/main.yml index 1280a91..28b9080 100644 --- a/locales/packie-cli/main.yml +++ b/locales/packie-cli/main.yml @@ -17,3 +17,30 @@ install.confirm_installation: install.aborting: en: "Aborting." zh-CN: "中止。" +info.no_package_found: + en: "The specified package was not found." + zh-CN: "找不到指定的包。" +info.pkgspec_not_unique: + en: "The specified package spec was not unique." + zh-CN: "指定的包规范匹配了多个包。" +remove.package_not_found: + en: "The specified package was not found." + zh-CN: "找不到指定的包。" +remove.pkgspec_not_unique: + en: "The specified package spec was not unique." + zh-CN: "指定的包规范匹配了多个包。" +remove.action_failure: + en: "Failed to remove the specified package: %{error}" + zh-CN: "无法移除指定的包:%{error}" +remove.prompt_to_remove: + en: "The following packages are to be removed:" + zh-CN: "将要移除下面的包:" +remove.confirm_removal: + en: "Confirm to remove?" + zh-CN: "确认移除?" +remove.aborting: + en: "Aborting." + zh-CN: "中止。" +remove.action_hint: + en: "Removing package %{package} ..." + zh-CN: "正在移除包 %{package} ..." diff --git a/src/install.rs b/src/install.rs index 9759a90..06cab91 100644 --- a/src/install.rs +++ b/src/install.rs @@ -8,6 +8,7 @@ use std::path::Path; #[derive(Debug, Default, Clone)] pub struct InstallOptions { + /// Package spec to record for this installation. pub install_pkgspec: Option, } impl InstallOptions { diff --git a/src/local.rs b/src/local.rs index 8ba2152..346cd53 100644 --- a/src/local.rs +++ b/src/local.rs @@ -2,6 +2,7 @@ use crate::{ package::{AbsPkgIdent, PkgIdent, PkgManifest, PkgSpec}, version::VersionFilter, }; +use itertools::Itertools; use rusqlite::{OpenFlags, Row, params}; use std::path::{Path, PathBuf}; @@ -27,8 +28,10 @@ impl super::Packie { for (_, provider) in abs { real.push(self.local_db.select_installation_by_ident(&provider)?); } - real.dedup_by_key(|x| x.pkg_manifest.pkg_ident()); - Ok(real) + Ok(real + .into_iter() + .unique_by(|x| x.pkg_manifest.pkg_ident()) + .collect()) } } diff --git a/src/repo/mod.rs b/src/repo/mod.rs index d088bd5..7b4f00a 100644 --- a/src/repo/mod.rs +++ b/src/repo/mod.rs @@ -4,6 +4,7 @@ mod serve; #[doc(inline)] pub use download::DownloadError; +use itertools::Itertools; use rustc_hash::FxHashMap; #[doc(inline)] pub use serve::RepoServeDir; @@ -389,8 +390,9 @@ impl QuerySession { all.push((name.into(), real)); } } - all.dedup_by_key(|(repo, pkg)| (repo.to_string(), pkg.pkg_manifest.pkg_ident())); - all + all.into_iter() + .unique_by(|(repo, pkg)| (repo.to_string(), pkg.pkg_manifest.pkg_ident())) + .collect() } }