diff --git a/rust/agama-software/src/callbacks/commit_download.rs b/rust/agama-software/src/callbacks/commit_download.rs index 5297c512a7..1d87a99f96 100644 --- a/rust/agama-software/src/callbacks/commit_download.rs +++ b/rust/agama-software/src/callbacks/commit_download.rs @@ -1,6 +1,8 @@ +use std::{ffi::OsStr, path::Path}; + use agama_utils::{ actor::Handler, - api::question::QuestionSpec, + api::{question::QuestionSpec, Scope}, progress, question::{self}, }; @@ -29,7 +31,6 @@ impl CommitDownload { impl Callback for CommitDownload { fn start_preload(&self) { - // TODO: report progress that we start preloading packages tracing::info!("Start preload"); } @@ -97,4 +98,38 @@ impl Callback for CommitDownload { None } + + fn finish_preload( + &self, + _url: String, + local_path: String, + error: zypp_agama::callbacks::pkg_download::PreloadError, + error_details: String, + ) { + let file = Path::new(&local_path); + let file_str = file + .file_name() + .and_then(OsStr::to_str) + .unwrap_or("package"); + + if error == zypp_agama::callbacks::pkg_download::PreloadError::NoError { + let msg = format!("Finished downloading {}", file_str); + // just ignore issues with reporting progress + let _ = self + .progress + .cast(progress::message::NextWithStep::new(Scope::Software, &msg)); + } else { + let labels = [gettext("Ok")]; + let actions = [("Ok", labels[0].as_str())]; + let question = + QuestionSpec::new(&error_details, "software.package_error.preload_error") + .with_actions(&actions) + .with_data(&[ + ("package", file_str), + ("error_code", error.to_string().as_str()), + ]); + // answer can be only OK so ignore it + let _ = ask_software_question(&self.questions, question); + } + } } diff --git a/rust/agama-software/src/callbacks/install.rs b/rust/agama-software/src/callbacks/install.rs index 0186170169..e7d6e0c4b8 100644 --- a/rust/agama-software/src/callbacks/install.rs +++ b/rust/agama-software/src/callbacks/install.rs @@ -1,6 +1,6 @@ use agama_utils::{ actor::Handler, - api::question::QuestionSpec, + api::{question::QuestionSpec, Scope}, progress, question::{self}, }; @@ -30,7 +30,11 @@ impl Install { impl install::Callback for Install { fn package_start(&self, package_name: String) { tracing::info!("Installing package {}", package_name); - // TODO: report progress + let msg = format!("Installing {}", package_name); + // just ignore issues with reporting progress + let _ = self + .progress + .cast(progress::message::NextWithStep::new(Scope::Software, &msg)); } fn package_problem( @@ -104,6 +108,5 @@ impl install::Callback for Install { fn package_finish(&self, package_name: String) { tracing::info!("Finished installing package {}", package_name); - // TODO: report progress } } diff --git a/rust/agama-software/src/zypp_server.rs b/rust/agama-software/src/zypp_server.rs index aab214c658..accacb7581 100644 --- a/rust/agama-software/src/zypp_server.rs +++ b/rust/agama-software/src/zypp_server.rs @@ -41,7 +41,7 @@ use crate::{ callbacks, model::state::{self, SoftwareState}, state::ResolvableSelection, - Resolvable, ResolvableType, + ResolvableType, }; const GPG_KEYS: &str = "/usr/lib/rpm/gnupg/keys/gpg-*"; @@ -179,18 +179,8 @@ impl ZyppServer { self.system_info(product_spec, tx, zypp)?; } SoftwareAction::Install(tx, progress, question) => { - let mut download_callback = - callbacks::CommitDownload::new(progress.clone(), question.clone()); - let mut install_callback = - callbacks::Install::new(progress.clone(), question.clone()); - let mut security_callback = callbacks::Security::new(question); - tx.send(self.install( - zypp, - &mut download_callback, - &mut install_callback, - &mut security_callback, - )) - .map_err(|_| ZyppDispatchError::ResponseChannelClosed)?; + tx.send(self.install(zypp, progress, question)) + .map_err(|_| ZyppDispatchError::ResponseChannelClosed)?; } SoftwareAction::Finish(tx) => { self.finish(zypp, tx)?; @@ -206,14 +196,32 @@ impl ZyppServer { fn install( &self, zypp: &zypp_agama::Zypp, - download_callback: &mut callbacks::CommitDownload, - install_callback: &mut callbacks::Install, - security_callback: &mut callbacks::Security, + progress: Handler, + question: Handler, ) -> ZyppServerResult { + let mut download_callback = + callbacks::CommitDownload::new(progress.clone(), question.clone()); + let mut install_callback = callbacks::Install::new(progress.clone(), question.clone()); + let mut security_callback = callbacks::Security::new(question); + + let packages_count = zypp.packages_count(); + // use packages count *2 as we need to download package and also install it + let steps = (packages_count * 2) as usize; + let _ = progress.cast(progress::message::Start::new( + Scope::Software, + steps, + "Starting packages installation", + )); + let target = "/mnt"; zypp.switch_target(target)?; - let result = zypp.commit(download_callback, install_callback, security_callback)?; + let result = zypp.commit( + &mut download_callback, + &mut install_callback, + &mut security_callback, + )?; tracing::info!("libzypp commit ends with {}", result); + let _ = progress.cast(progress::message::Finish::new(Scope::Software)); Ok(result) } diff --git a/rust/zypp-agama/src/callbacks/pkg_download.rs b/rust/zypp-agama/src/callbacks/pkg_download.rs index 3eab33bd63..d16fa3ff73 100644 --- a/rust/zypp-agama/src/callbacks/pkg_download.rs +++ b/rust/zypp-agama/src/callbacks/pkg_download.rs @@ -71,6 +71,7 @@ impl From for GPGCheckResult { } } +#[derive(Debug, PartialEq, Eq)] pub enum PreloadError { NoError, NotFound, // the requested Url was not found @@ -97,6 +98,19 @@ impl From for PreloadError { } } +impl Display for PreloadError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match self { + PreloadError::NoError => "NoError", + PreloadError::NotFound => "NotFound", + PreloadError::IO => "IO", + PreloadError::AccessDenied => "AccessDenied", + PreloadError::Error => "Error", + }; + write!(f, "{}", str) + } +} + /// Callbacks for the download phase of a zypp commit. /// /// This trait provides hooks into the package download process, which consists of two main phases: diff --git a/rust/zypp-agama/src/lib.rs b/rust/zypp-agama/src/lib.rs index 510049550a..23a64d1efe 100644 --- a/rust/zypp-agama/src/lib.rs +++ b/rust/zypp-agama/src/lib.rs @@ -226,6 +226,10 @@ impl Zypp { } } + pub fn packages_count(&self) -> u32 { + unsafe { zypp_agama_sys::packages_to_install(self.ptr) } + } + pub fn list_repositories(&self) -> ZyppResult> { let mut repos_v = vec![]; diff --git a/rust/zypp-agama/zypp-agama-sys/c-layer/include/lib.h b/rust/zypp-agama/zypp-agama-sys/c-layer/include/lib.h index 940a08115e..7e86e62a02 100644 --- a/rust/zypp-agama/zypp-agama-sys/c-layer/include/lib.h +++ b/rust/zypp-agama/zypp-agama-sys/c-layer/include/lib.h @@ -145,6 +145,11 @@ void resolvable_unselect(struct Zypp *zypp, const char *name, /// Note: this also resets the user locks ("taboo" or "keep installed") void resolvable_reset_all(struct Zypp *_zypp) noexcept; +/// @brief Amount of packages selected for installation +/// @param _zypp see \ref init_target +/// @return count of packages +unsigned packages_to_install(struct Zypp *_zypp) noexcept; + struct PatternNames { /// names of patterns const char *const *const names; diff --git a/rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx b/rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx index 2e177fa724..4583789b94 100644 --- a/rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx +++ b/rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx @@ -150,7 +150,8 @@ void switch_target(struct Zypp *zypp, const char *root, false /* rebuild rpmdb: no */); // switch cache for repositories, otherwise we run out of space in tmpfs - // see https://github.com/yast/yast-pkg-bindings/blob/853496f527543e6d51730fd7e3126ad94b13c303/src/PkgFunctions.cc#L496 + // see + // https://github.com/yast/yast-pkg-bindings/blob/853496f527543e6d51730fd7e3126ad94b13c303/src/PkgFunctions.cc#L496 zypp::RepoManagerOptions repo_options(root); zypp::Pathname packages_prefix = repo_options.repoPackagesCachePath; @@ -467,6 +468,12 @@ bool is_local_url(const char *url, struct Status *status) noexcept { } } +unsigned packages_to_install(struct Zypp *zypp) noexcept { + return zypp::ResPool::instance() + .byStatus(&zypp::ResStatus::isToBeInstalled) + .size(); +} + static bool package_check(Zypp *zypp, const char *tag, bool selected, Status *status) noexcept { try { diff --git a/rust/zypp-agama/zypp-agama-sys/src/bindings.rs b/rust/zypp-agama/zypp-agama-sys/src/bindings.rs index 7dd95d4855..1bcf55ce1d 100644 --- a/rust/zypp-agama/zypp-agama-sys/src/bindings.rs +++ b/rust/zypp-agama/zypp-agama-sys/src/bindings.rs @@ -620,6 +620,8 @@ unsafe extern "C" { ); #[doc = " Reset status of all resolvables, unselects selected packages, patterns...\n Note: this also resets the user locks (\"taboo\" or \"keep installed\")"] pub fn resolvable_reset_all(_zypp: *mut Zypp); + #[doc = " @brief Amount of packages selected for installation\n @param _zypp see \\ref init_target\n @return count of packages"] + pub fn packages_to_install(_zypp: *mut Zypp) -> ::std::os::raw::c_uint; #[doc = " Get Pattern details.\n Unknown patterns are simply omitted from the result. Match by\n PatternInfo.name, not by index."] pub fn get_patterns_info( _zypp: *mut Zypp,