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

Add datetime value parser #1723

Merged
merged 3 commits into from
Feb 16, 2023
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
66 changes: 18 additions & 48 deletions src/blocks/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,62 +4,51 @@
//!
//! Key | Values | Default
//! ----|--------|--------
//! `format` | Format string. See [chrono docs](https://docs.rs/chrono/0.3.0/chrono/format/strftime/index.html#specifiers) for all options. | `" $icon %a %d/%m %R "`
//! `format` | Format string. See [chrono docs](https://docs.rs/chrono/0.3.0/chrono/format/strftime/index.html#specifiers) for all options. | `" $icon $timestamp.datetime() "`
//! `interval` | Update interval in seconds | `10`
//! `timezone` | A timezone specifier (e.g. "Europe/Lisbon") | Local timezone
//! `locale` | Locale to apply when formatting the time | System locale
//!
//! Placeholder | Value | Type | Unit
//! --------------|---------------------------------------------|--------|-----
//! `icon` | A static icon | Icon | -
//! Placeholder | Value | Type | Unit
//! --------------|---------------------------------------------|----------|-----
//! `icon` | A static icon | Icon | -
//! `timestamp` | The current time | Datetime | -
//!
//! # Example
//!
//! ```toml
//! [[block]]
//! block = "time"
//! interval = 60
//! locale = "fr_BE"
//! [block.format]
//! full = " $icon %d/%m %R "
//! short = " $icon %R "
//! full = " $icon $timestamp.datetime(f:'%a %Y-%m-%d %R %Z', l:fr_BE) "
//! short = " $icon $timestamp.datetime(f:%R) "
//! ```
//!
//! # Icons Used
//! - `time`

use chrono::offset::{Local, Utc};
use chrono::Locale;
use chrono::Utc;
use chrono_tz::Tz;

use super::prelude::*;
use crate::formatting::config::DummyConfig;

#[derive(Deserialize, Debug, SmartDefault)]
#[serde(default)]
pub struct Config {
format: DummyConfig,
format: FormatConfig,
#[default(1.into())]
bim9262 marked this conversation as resolved.
Show resolved Hide resolved
interval: Seconds,
timezone: Option<Tz>,
locale: Option<String>,
}

pub async fn run(config: Config, mut api: CommonApi) -> Result<()> {
let mut widget = Widget::new();

let format = config
.format
.full
.as_deref()
.unwrap_or(" $icon %a %d/%m %R ");
let format_short = config.format.short.as_deref();
let mut widget = Widget::new().with_format(
config
.format
.with_default(" $icon $timestamp.datetime() ")?,
);

let timezone = config.timezone;
let locale = match config.locale.as_deref() {
Some(locale) => Some(locale.try_into().ok().error("invalid locale")?),
None => None,
};

let mut timer = config.interval.timer();

Expand All @@ -70,13 +59,10 @@ pub async fn run(config: Config, mut api: CommonApi) -> Result<()> {
unsafe { tzset() };
}

let full_time = get_time(format, timezone, locale);
let short_time = format_short
.map(|f| get_time(f, timezone, locale))
.unwrap_or_else(|| "".into());

widget.set_format(FormatConfig::default().with_defaults(&full_time, &short_time)?);
widget.set_values(map!("icon" => Value::icon(api.get_icon("time")?)));
widget.set_values(map!(
"icon" => Value::icon(api.get_icon("time")?),
"timestamp" => Value::datetime(Utc::now(), timezone)
));

api.set_widget(&widget).await?;

Expand All @@ -87,22 +73,6 @@ pub async fn run(config: Config, mut api: CommonApi) -> Result<()> {
}
}

fn get_time(format: &str, timezone: Option<Tz>, locale: Option<Locale>) -> String {
match locale {
Some(locale) => match timezone {
Some(tz) => Utc::now()
.with_timezone(&tz)
.format_localized(format, locale)
.to_string(),
None => Local::now().format_localized(format, locale).to_string(),
},
None => match timezone {
Some(tz) => Utc::now().with_timezone(&tz).format(format).to_string(),
None => Local::now().format(format).to_string(),
},
}
}

extern "C" {
/// The tzset function initializes the tzname variable from the value of the TZ environment
/// variable. It is not usually necessary for your program to call this function, because it is
Expand Down
7 changes: 7 additions & 0 deletions src/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@
//!
//! No arguments.
//!
//! ## `datetime` - Display datetime
//!
//! Argument | Description |Default value
//! -----------------------|-----------------------------------------------------------------------------------------------------------|-------------
//! `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
//!
//! # Handling missing placeholders and incorrect types
//!
//! Some blocks allow missing placeholders, for example [bluetooth](crate::blocks::bluetooth)'s
Expand Down
67 changes: 0 additions & 67 deletions src/formatting/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ pub struct Config {
pub short: Option<Arc<FormatTemplate>>,
}

#[derive(Debug, Default)]
pub struct DummyConfig {
pub full: Option<String>,
pub short: Option<String>,
}

impl Config {
pub fn with_default(self, default_full: &str) -> Result<Format> {
self.with_defaults(default_full, "")
Expand Down Expand Up @@ -149,64 +143,3 @@ impl<'de> Deserialize<'de> for Config {
deserializer.deserialize_any(FormatTemplateVisitor)
}
}

impl<'de> Deserialize<'de> for DummyConfig {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
enum Field {
Full,
Short,
}

struct FormatTemplateVisitor;

impl<'de> Visitor<'de> for FormatTemplateVisitor {
type Value = DummyConfig;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("format structure")
}

fn visit_str<E>(self, full: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(DummyConfig {
full: Some(full.into()),
short: None,
})
}

fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: MapAccess<'de>,
{
let mut full: Option<String> = None;
let mut short: Option<String> = None;
while let Some(key) = map.next_key()? {
match key {
Field::Full => {
if full.is_some() {
return Err(de::Error::duplicate_field("full"));
}
full = Some(map.next_value::<String>()?);
}
Field::Short => {
if short.is_some() {
return Err(de::Error::duplicate_field("short"));
}
short = Some(map.next_value::<String>()?);
}
}
}
Ok(DummyConfig { full, short })
}
}

deserializer.deserialize_any(FormatTemplateVisitor)
}
}
Loading