pub(crate) mod api;
pub(crate) mod checker;
pub(crate) mod config;
pub(crate) mod error;
pub(crate) mod installer;
pub(crate) mod paths;
pub(crate) mod registry;
pub(crate) mod types;
pub(crate) mod utils;
pub(crate) mod version;
#[cfg(feature = "cli")]
pub mod cli;
use api::ApiClient;
use serde::Serialize;
use types::UpdateCheckResult;
pub use config::{Config, RestartBehavior};
pub use error::Error;
pub use types::{AvailableUpdate, ComponentType, Diagnostic, InstalledComponent};
pub type Result<T> = std::result::Result<T, Error>;
pub fn check(config: &Config) -> Result<CheckResult> {
crate::utils::validate_environment(config.skip_plasma_detection)?;
let api_client = ApiClient::new();
let result = crate::utils::fetch_updates(&api_client, config)?;
#[cfg(feature = "cli")]
crate::utils::display_check_results(&result);
Ok(CheckResult::from_internal(result))
}
#[derive(Debug, Clone, Serialize)]
pub struct CheckResult {
pub available_updates: Vec<AvailableUpdate>,
pub diagnostics: Vec<Diagnostic>,
}
impl CheckResult {
pub(crate) fn from_internal(result: UpdateCheckResult) -> Self {
let diagnostics = result
.unresolved
.into_iter()
.chain(result.check_failures)
.collect();
Self {
available_updates: result.updates,
diagnostics,
}
}
pub fn has_updates(&self) -> bool {
!self.available_updates.is_empty()
}
pub fn update_count(&self) -> usize {
self.available_updates.len()
}
pub fn is_empty(&self) -> bool {
self.available_updates.is_empty() && self.diagnostics.is_empty()
}
}
pub fn update(config: &Config) -> Result<UpdateResult> {
let _lock = installer::UpdateLock::acquire()?;
crate::utils::validate_environment(config.skip_plasma_detection)?;
let api_client = ApiClient::new();
let check_result = crate::utils::fetch_updates(&api_client, config)?;
if check_result.updates.is_empty() {
#[cfg(feature = "cli")]
println!("no updates available");
return Ok(UpdateResult::default());
}
let selected = crate::utils::select_updates(&check_result.updates, config)?;
if selected.is_empty() {
#[cfg(feature = "cli")]
println!("nothing to update");
return Ok(UpdateResult::default());
}
let result = crate::utils::install_selected_updates(&selected, &api_client, config)?;
#[cfg(feature = "debug")]
{
let n = api_client.request_count();
let plural = if n == 1 { "" } else { "s" };
println!("{n} web request{plural}");
}
crate::utils::handle_restart(config, &check_result.updates, &result);
Ok(result)
}
#[derive(Debug, Clone, Serialize)]
pub struct FailedUpdate {
pub name: String,
pub error: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct UnverifiedUpdate {
pub name: String,
pub expected_version: String,
pub actual_version: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct UpdateResult {
pub succeeded: Vec<String>,
pub failed: Vec<FailedUpdate>,
pub skipped: Vec<String>,
pub unverified: Vec<UnverifiedUpdate>,
}
impl UpdateResult {
pub fn has_failures(&self) -> bool {
!self.failed.is_empty()
}
pub fn is_empty(&self) -> bool {
self.succeeded.is_empty()
&& self.failed.is_empty()
&& self.skipped.is_empty()
&& self.unverified.is_empty()
}
pub fn success_count(&self) -> usize {
self.succeeded.len()
}
pub fn failure_count(&self) -> usize {
self.failed.len()
}
#[cfg(feature = "cli")]
pub fn print_error_table(&self) {
crate::cli::output::print_error_table(self);
}
#[cfg(feature = "cli")]
pub fn print_summary(&self) {
crate::cli::output::print_summary(self);
}
}
pub fn get_installed(config: &Config) -> Result<Vec<InstalledComponent>> {
checker::find_installed(config.system)
}
pub fn install_update(update: &AvailableUpdate, config: &Config) -> Result<()> {
let _lock = installer::UpdateLock::acquire()?;
let _inhibit = if config.inhibit_idle {
installer::InhibitGuard::acquire()
} else {
installer::InhibitGuard::None
};
let api_client = ApiClient::new();
let counter = api_client.request_counter();
installer::update_component(update, api_client.http_client(), |_| {}, &counter).map(|_| ())
}
#[cfg(feature = "cli")]
#[doc(hidden)]
pub fn show_installed(config: &Config) -> Result<()> {
let components = checker::find_installed(config.system)?;
if components.is_empty() {
println!("no components installed");
return Ok(());
}
cli::output::print_count_message(components.len(), "installed component");
cli::output::print_components_table(&components);
Ok(())
}