Skip to content

Add duration formatter #2008

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

Merged
merged 6 commits into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 18 additions & 11 deletions src/blocks/battery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@
//! -------------|-------------------------------------------------------------------------|-------------------|-----
//! `icon` | Icon based on battery's state | Icon | -
//! `percentage` | Battery level, in percent | Number | Percents
//! `time` | Time remaining until (dis)charge is complete. Presented only if battery's status is (dis)charging. | String | -
//! `time_remaining` | Time remaining until (dis)charge is complete. Presented only if battery's status is (dis)charging. | Duration | -
//! `time` | Time remaining until (dis)charge is complete. Presented only if battery's status is (dis)charging. | String *DEPRECATED* | -
//! `power` | Power consumption by the battery or from the power supply when charging | String or Float | Watts
//!
//! `time` has been deprecated in favor of `time_remaining`.
//!
//! # Examples
//!
//! Basic usage:
Expand All @@ -44,7 +47,7 @@
//! ```toml
//! [[block]]
//! block = "battery"
//! format = " $percentage {$time |}"
//! format = " $percentage {$time_remaining.dur(hms:true, min_unit:m) |}"
//! device = "DisplayDevice"
//! driver = "upower"
//! ```
Expand Down Expand Up @@ -161,15 +164,19 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {

info.power
.map(|p| values.insert("power".into(), Value::watts(p)));
info.time_remaining.map(|t| {
values.insert(
"time".into(),
Value::text(format!(
"{}:{:02}",
(t / 3600.) as i32,
(t % 3600. / 60.) as i32
)),
)
info.time_remaining.inspect(|&t| {
map! { @extend values
"time" => Value::text(
format!(
"{}:{:02}",
(t / 3600.) as i32,
(t % 3600. / 60.) as i32
),
),
"time_remaining" => Value::duration(
Duration::from_secs(t as u64),
),
}
});

let (icon_name, icon_value, state) = match (info.status, info.capacity) {
Expand Down
79 changes: 46 additions & 33 deletions src/blocks/tea_timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@
//!
//! Key | Values | Default
//! ----|--------|--------
//! `format` | A string to customise the output of this block. See below for available placeholders. | <code>\" $icon {$minutes:$seconds \|}\"</code>
//! `format` | A string to customise the output of this block. See below for available placeholders. | <code>\" $icon {$time.duration(hms:true) \|}\"</code>
//! `increment` | The numbers of seconds to add each time the block is clicked. | 30
//! `done_cmd` | A command to run in `sh` when timer finishes. | None
//!
//! Placeholder | Value | Type | Unit
//! -----------------|----------------------------------------------------------------|--------|---------------
//! `icon` | A static icon | Icon | -
//! `hours` | The hours remaining on the timer | Text | h
//! `minutes` | The minutes remaining on the timer | Text | mn
//! `seconds` | The seconds remaining on the timer | Text | s
//! Placeholder | Value | Type | Unit
//! -----------------------|----------------------------------------------------------------|----------|---------------
//! `icon` | A static icon | Icon | -
//! `time` | The time remaining on the timer | Duration | -
//! `hours` *DEPRECATED* | The hours remaining on the timer | Text | h
//! `minutes` *DEPRECATED* | The minutes remaining on the timer | Text | mn
//! `seconds` *DEPRECATED* | The seconds remaining on the timer | Text | s
//!
//! `hours`, `minutes`, and `seconds` are unset when the timer is inactive.
//! `time`, `hours`, `minutes`, and `seconds` are unset when the timer is inactive.
//!
//! `hours`, `minutes`, and `seconds` have been deprecated in favor of `time`.
//!
//! Action | Default button
//! ------------|---------------
Expand All @@ -37,13 +40,14 @@

use super::prelude::*;
use crate::subprocess::spawn_shell;
use chrono::{Duration, Utc};

use std::time::{Duration, Instant};

#[derive(Deserialize, Debug, SmartDefault)]
#[serde(deny_unknown_fields, default)]
pub struct Config {
pub format: FormatConfig,
pub increment: Option<i64>,
pub increment: Option<u64>,
pub done_cmd: Option<String>,
}

Expand All @@ -59,17 +63,20 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
let interval: Seconds = 1.into();
let mut timer = interval.timer();

let format = config.format.with_default(" $icon {$minutes:$seconds |}")?;
let format = config
.format
.with_default(" $icon {$time.duration(hms:true) |}")?;

let increment =
Duration::try_seconds(config.increment.unwrap_or(30)).error("invalid increment value")?;
let mut timer_end = Utc::now();
let increment = Duration::from_secs(config.increment.unwrap_or(30));
let mut timer_end = Instant::now();

let mut timer_was_active = false;

loop {
let remaining_time = timer_end - Utc::now();
let is_timer_active = remaining_time > Duration::zero();
let mut widget = Widget::new().with_format(format.clone());

let remaining_time = timer_end - Instant::now();
let is_timer_active = !remaining_time.is_zero();

if !is_timer_active && timer_was_active {
if let Some(cmd) = &config.done_cmd {
Expand All @@ -78,32 +85,38 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
}
timer_was_active = is_timer_active;

let (hours, minutes, seconds) = if is_timer_active {
(
remaining_time.num_hours(),
remaining_time.num_minutes() % 60,
remaining_time.num_seconds() % 60,
)
} else {
(0, 0, 0)
};
let mut values = map!(
"icon" => Value::icon("tea"),
);

let mut widget = Widget::new().with_format(format.clone());
if is_timer_active {
values.insert("time".into(), Value::duration(remaining_time));
let mut seconds = remaining_time.as_secs();

widget.set_values(map!(
"icon" => Value::icon("tea"),
[if is_timer_active] "hours" => Value::text(format!("{hours:02}")),
[if is_timer_active] "minutes" => Value::text(format!("{minutes:02}")),
[if is_timer_active] "seconds" => Value::text(format!("{seconds:02}")),
));
if format.contains_key("hours") {
let hours = seconds / 3_600;
values.insert("hours".into(), Value::text(format!("{hours:02}")));
seconds %= 3_600;
}

if format.contains_key("minutes") {
let minutes = seconds / 60;
values.insert("minutes".into(), Value::text(format!("{minutes:02}")));
seconds %= 60;
}

values.insert("seconds".into(), Value::text(format!("{seconds:02}")));
}

widget.set_values(values);

api.set_widget(widget)?;

select! {
_ = timer.tick(), if is_timer_active => (),
_ = api.wait_for_update_request() => (),
Some(action) = actions.recv() => {
let now = Utc::now();
let now = Instant::now();
match action.as_ref() {
"increment" if is_timer_active => timer_end += increment,
"increment" => timer_end = now + increment,
Expand Down
23 changes: 13 additions & 10 deletions src/blocks/uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
//!
//! Key | Values | Default
//! -----------|----------------------------|--------
//! `format` | A string to customise the output of this block. See below for available placeholders | `" $icon $text "`
//! `format` | A string to customise the output of this block. See below for available placeholders | `" $icon $uptime "`
//! `interval` | Update interval in seconds | `60`
//!
//! Placeholder | Value | Type | Unit
//! --------------|-------------------------|--------|-----
//! `icon` | A static icon | Icon | -
//! `text` | Current uptime | Text | -
//! Placeholder | Value | Type | Unit
//! --------------------|-------------------------|----------|-----
//! `icon` | A static icon | Icon | -
//! `text` *DEPRECATED* | Current uptime | Text | -
//! `uptime` | Current uptime | Duration | -
//!
//! `text` has been deprecated in favor of `uptime`.
//!
//! # Example
//!
Expand All @@ -25,9 +28,6 @@
//!
//! # Used Icons
//! - `uptime`
//!
//! # TODO:
//! - Add `time` or `dur` formatter to `src/formatting/formatter.rs`

use super::prelude::*;
use tokio::fs::read_to_string;
Expand All @@ -41,7 +41,7 @@ pub struct Config {
}

pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
let format = config.format.with_default(" $icon $text ")?;
let format = config.format.with_default(" $icon $uptime ")?;

loop {
let uptime = read_to_string("/proc/uptime")
Expand All @@ -53,6 +53,8 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
.and_then(|u| u.parse().ok())
.error("/proc/uptime has invalid content")?;

let uptime = Duration::from_secs(seconds);

let weeks = seconds / 604_800;
seconds %= 604_800;
let days = seconds / 86_400;
Expand All @@ -75,7 +77,8 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
let mut widget = Widget::new().with_format(format.clone());
widget.set_values(map! {
"icon" => Value::icon("uptime"),
"text" => Value::text(text)
"text" => Value::text(text),
"uptime" => Value::duration(uptime)
});
api.set_widget(widget)?;

Expand Down
26 changes: 26 additions & 0 deletions src/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
//! --------------------------|------------------
//! Text | `str`
//! Number | `eng`
//! Datetime | `datetime`
//! Duration | `duration`
//! [Flag](#how-to-use-flags) | N/A
//!
//! # Formatters
Expand Down Expand Up @@ -74,6 +76,30 @@
//! `format` or `f` | [chrono docs](https://docs.rs/chrono/0.3.0/chrono/format/strftime/index.html#specifiers) for all options. | `'%a %d/%m %R'`
//! `locale` or `l` | Locale to apply when formatting the time | System locale
//!
//!
//! ## `duration`/`dur` - Format durations
//!
//! Argument | Description |Default value
//! -----------------|--------------------------------------------------------------------------------------------------|------------------------------------------------------
//! `hms` | Should the format be hours:minutes:seconds.milliseconds | `false`
//! `max_unit` | The largest unit to display the duration with (see below for the list of all possible units) | hms ? `h` : `y`
//! `min_unit` | The smallest unit to display the duration with (see below for the list of all possible units) | `s`
//! `units` | The number of units to display | min(# of units between `max_unit` and `min_unit``, 2)
//! `round_up` | Round up to the nearest minimum displayed unit | `true`
//! `unit_space` | Should there be a space between the value and unit symbol (not allowed when `hms:true`) | `false`
//! `pad_with` | The character that is used to pad the numbers | hms ? `0` : ` ` (a space)
//! `leading_zeroes` | If fewer than `units` are non-zero should leading numbers that have a value of zero be shown | `true`
//!
//! Unit | Description
//! -----|------------
//! y | years
//! w | weeks
//! d | days
//! h | hours
//! m | minutes
//! s | seconds
//! ms | milliseconds
//!
//! # Handling missing placeholders and incorrect types
//!
//! Some blocks allow missing placeholders, for example [bluetooth](crate::blocks::bluetooth)'s
Expand Down
11 changes: 9 additions & 2 deletions src/formatting/formatter.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use unicode_segmentation::UnicodeSegmentation;

use std::fmt::Debug;
use std::time::Duration;
use std::{borrow::Cow, fmt::Debug};

use super::parse::Arg;
use super::value::ValueInner as Value;
Expand All @@ -14,7 +14,7 @@ use crate::errors::*;
#[macro_export]
macro_rules! new_fmt {
($name:ident) => {{
fmt!($name,)
new_fmt!($name,)
}};
($name:ident, $($key:ident : $value:tt),* $(,)?) => {
new_formatter(stringify!($name), &[
Expand All @@ -27,6 +27,8 @@ mod bar;
pub use bar::BarFormatter;
mod datetime;
pub use datetime::{DatetimeFormatter, DEFAULT_DATETIME_FORMATTER};
mod duration;
pub use duration::{DurationFormatter, DEFAULT_DURATION_FORMATTER};
mod eng;
pub use eng::{EngFormatter, DEFAULT_NUMBER_FORMATTER};
mod flag;
Expand All @@ -36,6 +38,10 @@ pub use pango::PangoStrFormatter;
mod str;
pub use str::{StrFormatter, DEFAULT_STRING_FORMATTER};

type PadWith = Cow<'static, str>;

const DEFAULT_NUMBER_PAD_WITH: PadWith = Cow::Borrowed(" ");

pub trait Formatter: Debug + Send + Sync {
fn format(&self, val: &Value, config: &SharedConfig) -> Result<String, FormatError>;

Expand All @@ -48,6 +54,7 @@ pub fn new_formatter(name: &str, args: &[Arg]) -> Result<Box<dyn Formatter>> {
match name {
"bar" => Ok(Box::new(BarFormatter::from_args(args)?)),
"datetime" => Ok(Box::new(DatetimeFormatter::from_args(args)?)),
"dur" | "duration" => Ok(Box::new(DurationFormatter::from_args(args)?)),
"eng" => Ok(Box::new(EngFormatter::from_args(args)?)),
"pango-str" => Ok(Box::new(PangoStrFormatter::from_args(args)?)),
"str" => Ok(Box::new(StrFormatter::from_args(args)?)),
Expand Down
Loading