Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combine apt, pacman & dnf block to packages block #1988

Merged
merged 33 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8eb4a47
refactor: Add `packages` in block and remove apt,pacman
IshanGrover2004 Jan 19, 2024
36025ed
refactor: Remove `pacman.rs` file to add in `packages` block
IshanGrover2004 Jan 19, 2024
9a6bb88
refactor: Remove `apt.rs` file to add in `packages` block
IshanGrover2004 Jan 19, 2024
ba1dc95
feat: Add `packages` block to check updates for package managers
IshanGrover2004 Jan 19, 2024
76c829e
feat: Add `apt` package manager to `packages` block
IshanGrover2004 Jan 19, 2024
8d2b5f4
feat: Add `pacman` & `aur` package manager to `packages` block
IshanGrover2004 Jan 19, 2024
bd5b9d6
docs: Add examples to documentation for packages block
IshanGrover2004 Jan 19, 2024
d2f3037
Merge branch 'greshake:master' into 1964-list-update-packages
IshanGrover2004 Jan 19, 2024
b15ffe4
fix: Spelling check
IshanGrover2004 Jan 19, 2024
85eff45
feat: Add deleted old blocks
IshanGrover2004 Jan 19, 2024
0c4dee8
refactor: Re-arrange block list in alphabetical order
IshanGrover2004 Jan 19, 2024
3dcc9ef
refactor: Add function to `impl Apt` block
IshanGrover2004 Jan 19, 2024
9897b3a
docs: Remove documentation from submodules and add to `packages` block
IshanGrover2004 Jan 20, 2024
4c115cc
refactor: Use `Vec<Box<dyn Backend>>` for later use also and clean code
IshanGrover2004 Jan 20, 2024
762c3e6
refactor: Remove `setup` function and add in constructor in backends
IshanGrover2004 Jan 20, 2024
3ff1397
chore: clippy fixes
IshanGrover2004 Jan 20, 2024
b4f229b
feat: Add depreciation patches for apt & pacman blocks
IshanGrover2004 Jan 20, 2024
ad035f0
refactor: Remove vec of critical & warning updates regex and Add opti…
IshanGrover2004 Jan 21, 2024
7d61b37
refactor: Add parameters to Apt::new()
IshanGrover2004 Jan 21, 2024
12fc736
refactor: Add HashMap for getting updates in generic way
IshanGrover2004 Jan 21, 2024
b76fed9
feat: Re-use the code in `blocks/apt.rs` & `blocks/pacman.rs`
IshanGrover2004 Jan 21, 2024
2d6c702
feat: Add `dnf` to packages block and re-use code in `dnf.rs`
IshanGrover2004 Jan 21, 2024
94b6bea
chore: Clippy fixes
IshanGrover2004 Jan 21, 2024
1da106f
refactor: Update `get_update_list` to return `Vec<String>` and remove…
IshanGrover2004 Jan 21, 2024
d48d0f7
refactor: Move `has_matching_updates` function out of Backend trait
IshanGrover2004 Jan 21, 2024
142e701
fix: Remove tokio runtime usage
IshanGrover2004 Jan 21, 2024
61902f4
refactor: Inline backend match block
IshanGrover2004 Jan 21, 2024
bff8686
refactor: Use same hashmap while setting the values
IshanGrover2004 Jan 21, 2024
203de17
feat: Restore `tokio::try_join!` for parallel execution
IshanGrover2004 Jan 22, 2024
cd6abc2
feat: Add depreciation patches for `dnf` blocks
IshanGrover2004 Jan 23, 2024
8806b57
docs: Add Default value for `package_manager`
IshanGrover2004 Jan 24, 2024
94aad81
docs: Make less verbose by deleting repetitive info
IshanGrover2004 Jan 24, 2024
4ce8aba
fix: Swap `$both` with `$total` in docs
IshanGrover2004 Jan 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ macro_rules! define_blocks {
define_blocks!(
amd_gpu,
apt,
pacman,
packages,
backlight,
battery,
bluetooth,
Expand All @@ -162,7 +164,6 @@ define_blocks!(
#[cfg(feature = "notmuch")]
notmuch,
nvidia_gpu,
pacman,
pomodoro,
rofication,
service_status,
Expand Down
250 changes: 250 additions & 0 deletions src/blocks/packages.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
//! Shows pending updates for different package manager like apt, pacman, etc.
//!
//! Currently 2 package manager are available:
//! - `apt` for Debian/Ubuntu based system
//! - `pacman` for Arch based system
//! - `aur` for Arch based system
//!
//! # Configuration
//!
//! Key | Values | Default
//! ----|--------|--------
//! `interval` | Update interval in seconds. | `600`
//! `package_manager` | Package manager to check for updates | -
//! `format` | A string to customise the output of this block. See below for available placeholders. | `" $icon $count.eng(w:1) "`
//! `format_singular` | Same as `format`, but for when exactly one update is available. | `" $icon $count.eng(w:1) "`
//! `format_up_to_date` | Same as `format`, but for when no updates are available. | `" $icon $count.eng(w:1) "`
//! `warning_updates_regex` | Display block as warning if updates matching regex are available. | `None`
//! `critical_updates_regex` | Display block as critical if updates matching regex are available. | `None`
//! `ignore_updates_regex` | Doesn't include updates matching regex in the count. | `None`
//! `ignore_phased_updates` | Doesn't include potentially held back phased updates in the count. (For Debian/Ubuntu based system) | `false`
//! `aur_command` | AUR command to check available updates, which outputs in the same format as pacman. e.g. `yay -Qua` (For Arch based system) | Required if `$both` or `$aur` are used
//!
//! Placeholder | Value | Type | Unit
//! -------------|----------------------------------------------------------------------------------|--------|-----
//! `icon` | A static icon | Icon | -
//! `apt` | Number of updates available in Debian/Ubuntu based system | Number | -
//! `pacman` | Number of updates available in Arch based system | Number | -
//! `aur` | Number of updates available in Arch based system | Number | -
//! `total` | Number of updates available in all package manager listed | Number | -
//!
//! # Example
//!
//! Update the list of pending updates every thirty minutes (1800 seconds):
//!
//! ```toml
//! [[block]]
//! block = "packages"
//! package_manager = ["apt", "pacman", "aur"]
//! interval = 1800
//! format = " $icon $apt + $pacman + $aur = $total updates available "
//! format_singular = " $icon One update available "
//! format_up_to_date = " $icon system up to date "
//! ```
//!
//! # Icons Used
//!
//! - `update`

pub mod apt;
use apt::Apt;

pub mod pacman;
use pacman::{Aur, Pacman};

use regex::Regex;

use super::prelude::*;

#[derive(Deserialize, Debug, SmartDefault, Clone)]
#[serde(deny_unknown_fields, default)]
pub struct Config {
#[default(600.into())]
pub interval: Seconds,
pub package_manager: Vec<PackageManager>,
pub format: FormatConfig,
pub format_singular: FormatConfig,
pub format_up_to_date: FormatConfig,
pub warning_updates_regex: Option<String>,
pub critical_updates_regex: Option<String>,
pub ignore_updates_regex: Option<String>,
pub ignore_phased_updates: bool,
pub aur_command: Option<String>,
}

#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum PackageManager {
Apt,
Pacman,
Aur,
}

pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
let mut config: Config = config.clone();

let format = config.format.with_default(" $icon $total.eng(w:1) ")?;
let format_singular = config
.format_singular
.with_default(" $icon $total.eng(w:1) ")?;
let format_up_to_date = config
.format_up_to_date
.with_default(" $icon $total.eng(w:1) ")?;

// If user provide package manager in any of the formats then consider that also
macro_rules! any_format_contains {
($name:expr) => {
format.contains_key($name)
|| format_singular.contains_key($name)
|| format_up_to_date.contains_key($name)
};
}

let apt = any_format_contains!("apt");
let aur = any_format_contains!("aur");
let pacman = any_format_contains!("pacman");

if !config.package_manager.contains(&PackageManager::Apt) && apt {
config.package_manager.push(PackageManager::Apt);
}
if !config.package_manager.contains(&PackageManager::Pacman) && pacman {
config.package_manager.push(PackageManager::Pacman);
}
if !config.package_manager.contains(&PackageManager::Aur) && aur {
config.package_manager.push(PackageManager::Aur);
}

let warning_updates_regex = config
.warning_updates_regex
.as_deref()
.map(Regex::new)
.transpose()
.error("invalid warning updates regex")?;
let critical_updates_regex = config
.critical_updates_regex
.as_deref()
.map(Regex::new)
.transpose()
.error("invalid critical updates regex")?;
let ignore_updates_regex = config
.ignore_updates_regex
.as_deref()
.map(Regex::new)
.transpose()
.error("invalid ignore updates regex")?;

// Setup once everything it takes to check updates for every package manager
for package_manager in &config.package_manager {
let mut backend: Box<dyn Backend> = match package_manager {
PackageManager::Apt => Box::new(Apt::new()),
PackageManager::Pacman => Box::new(Pacman::new()),
PackageManager::Aur => Box::new(Aur::new()),
};

backend.setup().await?;
}

loop {
let (mut apt_count, mut pacman_count, mut aur_count) = (0, 0, 0);
let mut critical_vec = vec![false];
let mut warning_vec = vec![false];

// Iterate over the all package manager listed in Config
for package_manager in config.package_manager.clone() {
match package_manager {
PackageManager::Apt => {
let mut apt = Apt::new();
apt.ignore_updates_regex = ignore_updates_regex.clone();
apt.ignore_phased_updates = config.ignore_phased_updates;
let updates = apt.get_updates_list().await?;
apt_count = apt.get_update_count(&updates).await?;
let warning = warning_updates_regex
.as_ref()
.is_some_and(|regex| apt.has_matching_update(&updates, regex));
let critical = critical_updates_regex
.as_ref()
.is_some_and(|regex| apt.has_matching_update(&updates, regex));
warning_vec.push(warning);
critical_vec.push(critical);
}
PackageManager::Pacman => {
let pacman = Pacman::new();
let updates = pacman.get_updates_list().await?;
pacman_count = pacman.get_update_count(&updates).await?;
let warning = warning_updates_regex
.as_ref()
.is_some_and(|regex| pacman.has_matching_update(&updates, regex));
let critical = critical_updates_regex
.as_ref()
.is_some_and(|regex| pacman.has_matching_update(&updates, regex));
warning_vec.push(warning);
critical_vec.push(critical);
}
PackageManager::Aur => {
let aur = Aur::new();
let updates = aur.get_updates_list().await?;
aur_count = aur.get_update_count(&updates).await?;
let warning = warning_updates_regex
.as_ref()
.is_some_and(|regex| aur.has_matching_update(&updates, regex));
let critical = critical_updates_regex
.as_ref()
.is_some_and(|regex| aur.has_matching_update(&updates, regex));
warning_vec.push(warning);
critical_vec.push(critical);
}
}
}

let mut widget = Widget::new();

let total_count = apt_count + pacman_count + aur_count;
widget.set_format(match total_count {
0 => format_up_to_date.clone(),
1 => format_singular.clone(),
_ => format.clone(),
});
widget.set_values(map!(
"icon" => Value::icon("update"),
"apt" => Value::number(apt_count),
"pacman" => Value::number(pacman_count),
"aur" => Value::number(aur_count),
"total" => Value::number(total_count),
));

let warning = warning_vec.iter().any(|&x| x);
let critical = critical_vec.iter().any(|&x| x);

widget.state = match total_count {
0 => State::Idle,
_ => {
if critical {
State::Critical
} else if warning {
State::Warning
} else {
State::Info
}
}
};
api.set_widget(widget)?;

select! {
_ = sleep(config.interval.0) => (),
_ = api.wait_for_update_request() => (),
}
}
}

#[async_trait]
trait Backend {
async fn setup(&mut self) -> Result<()>;

async fn get_updates_list(&self) -> Result<String>;

async fn get_update_count(&self, updates: &str) -> Result<usize>;

fn has_matching_update(&self, updates: &str, regex: &Regex) -> bool {
updates.lines().any(|line| regex.is_match(line))
}
}
Loading