From 1c154e7675e92563510f2a08c7eb8e86ffdc1cf9 Mon Sep 17 00:00:00 2001 From: sisungo Date: Thu, 9 Jul 2026 01:54:25 +0800 Subject: [PATCH] feat: add global lock Signed-off-by: sisungo --- cli/src/info.rs | 64 +++++++++++++++++++++++++++++++++---- locales/libpackie/main.yml | 3 ++ locales/packie-cli/main.yml | 27 ++++++++++++++++ src/common/fs.rs | 21 ++++++++++++ src/lib.rs | 22 ++++++++++++- src/package.rs | 4 +++ 6 files changed, 134 insertions(+), 7 deletions(-) diff --git a/cli/src/info.rs b/cli/src/info.rs index 673a03f..9d50322 100644 --- a/cli/src/info.rs +++ b/cli/src/info.rs @@ -1,7 +1,11 @@ use crate::common::format_size; use anyhow::anyhow; use clap::Parser; -use packie::{PackieBuilder, package::PkgSpec}; +use packie::{ + PackieBuilder, + local::Installation, + package::{PkgManifest, PkgSpec}, +}; use rust_i18n::t; #[derive(Debug, Parser)] @@ -20,11 +24,59 @@ pub fn main(cli: Cli) -> anyhow::Result<()> { } let found = found.remove(0); - println!("Package name: {}", found.pkg_manifest.name); - println!("Package version: {}", found.pkg_manifest.version); - println!("Package architecture: {}", found.pkg_manifest.arch); - println!("Install package spec: {}", found.install_pkgspec); - println!("Installed size: {}", format_size(found.installed_size)); + print_pkgmanifest(&found.pkg_manifest); + print_install_misc(&found); Ok(()) } + +fn print_pkgmanifest(pkg_manifest: &PkgManifest) { + println!( + "{}: {}", + t!("info.print_pkgmanifest.name"), + pkg_manifest.name + ); + println!( + "{}: {}", + t!("info.print_pkgmanifest.version"), + pkg_manifest.version + ); + println!( + "{}: {}", + t!("info.print_pkgmanifest.arch"), + pkg_manifest.arch + ); + if !pkg_manifest.description.is_empty() { + println!( + "{}: {}", + t!("info.print_pkgmanifest.description"), + pkg_manifest.arch + ); + } + println!("{}:", t!("info.print_pkgmanifest.dependencies")); + for i in pkg_manifest.dependencies.iter() { + println!(" - {i}"); + } + println!("{}:", t!("info.print_pkgmanifest.recommendations")); + for i in pkg_manifest.recommendations.iter() { + println!(" - {i}"); + } +} + +fn print_install_misc(installation: &Installation) { + println!( + "{}: {}", + t!("info.print_install_misc.install_pkgspec"), + installation.install_pkgspec + ); + println!( + "{}: {}", + t!("info.print_install_misc.install_date"), + installation.install_date, + ); + println!( + "{}: {}", + t!("info.print_install_misc.installed_size"), + format_size(installation.installed_size), + ); +} diff --git a/locales/libpackie/main.yml b/locales/libpackie/main.yml index 244b43f..e37babb 100644 --- a/locales/libpackie/main.yml +++ b/locales/libpackie/main.yml @@ -41,3 +41,6 @@ repo.RepoError.AllTriesFailed: repo.download.DownloadError.Checksum: en: "checksum failed" zh-CN: "文件完整性检查失败" +BuildPackieError.Lock: + en: "failed to lock packie database: %{error}" + zh-CN: "无法锁定 Packie 数据库:%{error}" diff --git a/locales/packie-cli/main.yml b/locales/packie-cli/main.yml index 28b9080..f23fc75 100644 --- a/locales/packie-cli/main.yml +++ b/locales/packie-cli/main.yml @@ -23,6 +23,33 @@ info.no_package_found: info.pkgspec_not_unique: en: "The specified package spec was not unique." zh-CN: "指定的包规范匹配了多个包。" +info.print_pkgmanifest.name: + en: "Package name" + zh-CN: "包名" +info.print_pkgmanifest.version: + en: "Package version" + zh-CN: "版本" +info.print_pkgmanifest.arch: + en: "Package architecture" + zh-CN: "架构" +info.print_pkgmanifest.dependencies: + en: "Dependencies" + zh-CN: "依赖" +info.print_pkgmanifest.recommendations: + en: "Recommendations" + zh-CN: "建议安装" +info.print_pkgmanifest.description: + en: "Description" + zh-CN: "描述" +info.print_install_misc.install_pkgspec: + en: "Install package spec" + zh-CN: "安装请求" +info.print_install_misc.installed_size: + en: "Installed size" + zh-CN: "安装后大小" +info.print_install_misc.install_date: + en: "Install date" + zh-CN: "安装日期" remove.package_not_found: en: "The specified package was not found." zh-CN: "找不到指定的包。" diff --git a/src/common/fs.rs b/src/common/fs.rs index fa105d6..1d99be7 100644 --- a/src/common/fs.rs +++ b/src/common/fs.rs @@ -160,3 +160,24 @@ pub struct CopyError { dst: PathBuf, error: std::io::Error, } + +#[cfg(target_family = "unix")] +#[derive(Debug)] +pub struct LockGuard(Option>); +#[cfg(target_family = "unix")] +impl LockGuard { + pub fn noop() -> Self { + Self(None) + } + + pub fn open(path: impl AsRef, nonblocking: bool) -> std::io::Result { + let file = std::fs::File::options().create(true).open(path)?; + let flags = if nonblocking { + nix::fcntl::FlockArg::LockExclusiveNonblock + } else { + nix::fcntl::FlockArg::LockExclusive + }; + let lock = nix::fcntl::Flock::lock(file, flags).map_err(|x| x.1)?; + Ok(Self(Some(lock))) + } +} diff --git a/src/lib.rs b/src/lib.rs index de8a226..417755c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,9 +9,11 @@ pub mod remove; pub mod repo; pub mod version; +use common::fs::LockGuard; use config::AllConfig; use local::{Cache, DataDir, LocalDb}; use profile::Profile; +use rust_i18n::t; rust_i18n::i18n!("locales/libpackie", fallback = "en"); @@ -23,6 +25,7 @@ pub struct Packie { config: AllConfig, local_data: DataDir, cache: Cache, + _lock_guard: LockGuard, } impl Packie { /// Returns profile of the Packie instance. @@ -31,16 +34,18 @@ impl Packie { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct PackieBuilder { profile: Profile, readonly: bool, + nonblocking: bool, } impl PackieBuilder { pub fn new() -> Self { Self { profile: Profile::builtin(), readonly: false, + nonblocking: false, } } @@ -49,17 +54,27 @@ impl PackieBuilder { self } + pub fn nonblocking(mut self, val: bool) -> Self { + self.nonblocking = val; + self + } + pub fn profile(mut self, val: Profile) -> Self { self.profile = val; self } pub fn build(self) -> Result { + let mut _lock_guard = LockGuard::noop(); if !self.readonly { _ = std::fs::create_dir_all(&self.profile.packie_data_dir); _ = std::fs::create_dir_all(&self.profile.packie_cache_dir); _ = std::fs::create_dir_all(&self.profile.packie_config_dir); _ = std::fs::create_dir_all(&self.profile.pkg_dir); + + let lock_path = self.profile.packie_data_dir.join("packie.lock"); + _lock_guard = + LockGuard::open(lock_path, self.nonblocking).map_err(BuildPackieError::Lock)?; } let local_db_path = self.profile.packie_data_dir.join(LocalDb::FILENAME); let open_local_db = if self.readonly { @@ -71,12 +86,14 @@ impl PackieBuilder { let config = AllConfig::open(&self.profile.packie_config_dir); let cache = Cache(self.profile.packie_cache_dir.clone()); let local_data = DataDir(self.profile.packie_data_dir.clone()); + Ok(Packie { profile: self.profile, local_db, config, cache, local_data, + _lock_guard, }) } } @@ -85,4 +102,7 @@ impl PackieBuilder { pub enum BuildPackieError { #[error("{0}")] LocalDb(rusqlite::Error), + + #[error("{}", t!("BuildPackieError.Lock", error = .0))] + Lock(std::io::Error), } diff --git a/src/package.rs b/src/package.rs index 783638a..8809b3d 100644 --- a/src/package.rs +++ b/src/package.rs @@ -92,6 +92,10 @@ pub struct PkgManifest { #[serde(default)] pub dependencies: Vec, + /// Package recommended (soft) dependencies. + #[serde(default)] + pub recommendations: Vec, + /// Provided abstract packages. #[serde(default)] pub provides: Vec,