i3status_rs/formatting.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
//! # Formatting system
//! Many blocks have a `format` configuration option, which allows to heavily customize the block's
//! appearance. In short, each block with `format` option provides a set of values, which are
//! displayed according to `format`. `format`'s value is just a text with embedded variables.
//! Similarly to PHP and shell, variable name must start with a `$`:
//! `this is a variable: -> $var <-`.
//!
//! Also, format strings can embed icons. For example, `^icon_ping` in `" ^icon_ping $ping "` gets
//! substituted with a "ping" icon from your icon set. For a complete list of icons, see
//! [this](https://github.com/greshake/i3status-rust/blob/master/doc/themes.md#available-icon-overrides).
//!
//! # Types
//!
//! The allowed types of variables are:
//!
//! Type | Default formatter
//! --------------------------|------------------
//! Text | `str`
//! Number | `eng`
//! Datetime | `datetime`
//! Duration | `duration`
//! [Flag](#how-to-use-flags) | N/A
//!
//! # Formatters
//!
//! A formatter is something that converts a value into a text. Because there are many ways to do
//! this, a number of formatters is available. Formatter can be specified using the syntax similar
//! to method calls in many programming languages: `<variable>.<formatter>(<args>)`. For example:
//! `$title.str(min_w:10, max_w:20)`.
//!
//! ## `str` - Format text
//!
//! Argument | Description |Default value
//! -----------------------|---------------------------------------------------|-------------
//! `min_width` or `min_w` | if text is shorter it will be padded using spaces | `0`
//! `max_width` or `max_w` | if text is longer it will be truncated | Infinity
//! `width` or `w` | Text will be exactly this length by padding or truncating as needed | N/A
//! `rot_interval` | if text is longer than `max_width` it will be rotated every `rot_interval` seconds, if set | None
//! `rot_separator` | if text is longer than `max_width` it will be rotated with this seporator | <code>\"\|\"</code>
//!
//! Note: width just changes the values of both min_width and max_width to be the same. Use width
//! if you want the values to be the same, or the other two otherwise. Don't mix width with
//! min_width or max_width.
//!
//! ## `eng` - Format numbers using engineering notation
//!
//! Argument | Description |Default value
//! ----------------|--------------------------------------------------------------------------------------------------|-------------
//! `width` or `w` | the resulting text will be at least `width` characters long | `2`
//! `unit` or `u` | some values have a [unit](unit::Unit), and it is possible to convert them by setting this option | N/A
//! `hide_unit` | hide the unit symbol | `false`
//! `unit_space` | have a whitespace before unit symbol | `false`
//! `prefix` or `p` | specify this argument if you want to set the minimal [SI prefix](prefix::Prefix) | N/A
//! `hide_prefix` | hide the prefix symbol | `false`
//! `prefix_space` | have a whitespace before prefix symbol | `false`
//! `force_prefix` | force the prefix value instead of setting a "minimal prefix" | `false`
//! `pad_with` | the character that is used to pad the number to be `width` long | ` ` (a space)
//! `range` | a range of allowed values, in the format `<start>..<end>`, inclusive. Both start and end are optional. Can be used to, for example, hide the block when the value is not in a given range. | `..`
//!
//! ## `bar` - Display numbers as progress bars
//!
//! Argument | Description |Default value
//! -----------------------|---------------------------------------------------------------------------------|-------------------------
//! `width` or `w` | the width of the bar (in characters) | `5` (`1` for `vertical`)
//! `max_value` | which value is treated as "full". For example, for battery level `100` is full. | `100`
//! `vertical` or `v` | whether to render the bar vertically or not | `false`
//!
//! ## `tally` - Display numbers as tally marks
//!
//! Argument | Description |Default value
//! ---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------
//! `style` or `s` | One of [`chinese_counting_rods`/`ccr`](https://en.wikipedia.org/wiki/Counting_rods), [`chinese_tally`/`ct`, `western_tally`/`wt`, `western_tally_ungrouped`/`wtu`](https://en.wikipedia.org/wiki/Tally_marks) | western_tally
//!
//! ## `pango-str` - Just display the text without pango markup escaping
//!
//! 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
//!
//!
//! ## `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
//! "percentage" may be absent if the device is not supported. To handle such cases it is possible
//! to queue multiple formats together by using `|` symbol: `<something that can fail>|<otherwise
//! try this>|<or this>`.
//!
//! In addition, formats can be recursive. To set a format inside of another format, place it
//! inside of `{}`. For example, in `Percentage: {$percentage|N/A}` the text "Percentage: " will be
//! always displayed, followed by the actual percentage or "N/A" in case percentage is not
//! available. This example does exactly the same thing as `Percentage: $percentage|Percentage: N/A`
//!
//! # How to use flags
//!
//! Some blocks provide flags, which can be used to change the format based on some criteria. For
//! example, [taskwarrior](crate::blocks::taskwarrior) defines `done` if the count is zero. In
//! general, flags are used in this way:
//!
//! ```text
//! $a{a is set}|$b$c{b and c are set}|${b|c}{b or c is set}|neither flag is set
//! ```
pub mod config;
pub mod formatter;
pub mod parse;
pub mod prefix;
pub mod scheduling;
pub mod template;
pub mod unit;
pub mod value;
use std::borrow::Cow;
use std::collections::HashMap;
use crate::config::SharedConfig;
use crate::errors::*;
use template::FormatTemplate;
use value::Value;
pub type Values = HashMap<Cow<'static, str>, Value>;
#[derive(Debug, thiserror::Error)]
pub enum FormatError {
#[error("Placeholder '{0}' not found")]
PlaceholderNotFound(String),
#[error("{} cannot be formatted with '{}' formatter", .ty, .fmt)]
IncompatibleFormatter { ty: &'static str, fmt: &'static str },
#[error("Number {0} is out of range")]
NumberOutOfRange(f64),
#[error(transparent)]
Other(#[from] Error),
}
#[derive(Debug, Clone)]
pub struct Format {
full: FormatTemplate,
short: FormatTemplate,
intervals: Vec<u64>,
}
impl Format {
pub fn contains_key(&self, key: &str) -> bool {
self.full.contains_key(key) || self.short.contains_key(key)
}
pub fn intervals(&self) -> Vec<u64> {
self.intervals.clone()
}
pub fn render(
&self,
values: &Values,
config: &SharedConfig,
) -> Result<(Vec<Fragment>, Vec<Fragment>)> {
let full = self
.full
.render(values, config)
.error("Failed to render full text")?;
let short = self
.short
.render(values, config)
.error("Failed to render short text")?;
Ok((full, short))
}
}
#[derive(Debug, Default, Clone)]
pub struct Fragment {
pub text: String,
pub metadata: Metadata,
}
impl From<String> for Fragment {
fn from(text: String) -> Self {
Self {
text,
metadata: Default::default(),
}
}
}
impl Fragment {
pub fn formatted_text(&self) -> String {
match (self.metadata.italic, self.metadata.underline) {
(true, true) => format!("<i><u>{}</u></i>", self.text),
(false, true) => format!("<u>{}</u>", self.text),
(true, false) => format!("<i>{}</i>", self.text),
(false, false) => self.text.clone(),
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct Metadata {
pub instance: Option<&'static str>,
pub underline: bool,
pub italic: bool,
}
impl Metadata {
pub fn is_default(&self) -> bool {
*self == Default::default()
}
}