use std::collections::HashMap;
use std::time::Duration;
use chrono::{DateTime, Local, Utc};
pub fn local_time_hm(when: DateTime<Utc>) -> String {
when.with_timezone(&Local).format("%H:%M").to_string()
}
pub fn local_time_hms(when: DateTime<Utc>) -> String {
when.with_timezone(&Local).format("%H:%M:%S").to_string()
}
pub fn updated_at_hm(now: DateTime<Utc>, cache_age: Option<Duration>) -> String {
match cache_age {
Some(age) => local_time_hm(now - chrono::Duration::from_std(age).unwrap_or_default()),
None => "—".to_string(),
}
}
pub fn updated_at_hms(now: DateTime<Utc>, cache_age: Option<Duration>) -> String {
match cache_age {
Some(age) => local_time_hms(now - chrono::Duration::from_std(age).unwrap_or_default()),
None => "—".to_string(),
}
}
pub fn substitute(template: &str, values: &HashMap<&str, String>) -> String {
let mut out = String::with_capacity(template.len());
let mut rest = template;
while !rest.is_empty() {
match rest.find('{') {
None => {
out.push_str(rest);
break;
}
Some(open) => {
out.push_str(&rest[..open]);
let after_open = &rest[open + 1..];
if let Some(close) = after_open.find('}') {
let key = &after_open[..close];
if let Some(val) = values.get(key) {
out.push_str(val);
rest = &after_open[close + 1..];
continue;
}
}
out.push('{');
rest = after_open;
}
}
}
out
}
pub fn placeholders<I, V>(pairs: I) -> HashMap<&'static str, String>
where
I: IntoIterator<Item = (&'static str, V)>,
V: Into<String>,
{
pairs.into_iter().map(|(k, v)| (k, v.into())).collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn pm(pairs: &[(&'static str, &str)]) -> HashMap<&'static str, String> {
placeholders(pairs.iter().map(|(k, v)| (*k, v.to_string())))
}
#[test]
fn single_substitution() {
let v = pm(&[("session_pct", "42")]);
assert_eq!(substitute("{session_pct}%", &v), "42%");
}
#[test]
fn multiple_substitutions() {
let v = pm(&[("a", "1"), ("b", "2")]);
assert_eq!(substitute("{a}-{b}-{a}", &v), "1-2-1");
}
#[test]
fn unknown_placeholder_passes_through() {
let v = pm(&[("a", "1")]);
assert_eq!(substitute("{a} {unknown}", &v), "1 {unknown}");
}
#[test]
fn no_re_substitution_in_replacement_text() {
let v = pm(&[("a", "{a}"), ("b", "X")]);
assert_eq!(substitute("{b}{a}{b}", &v), "X{a}X");
}
#[test]
fn empty_template() {
let v = pm(&[("a", "1")]);
assert_eq!(substitute("", &v), "");
}
#[test]
fn template_without_braces() {
let v = pm(&[("a", "1")]);
assert_eq!(substitute("hello world", &v), "hello world");
}
#[test]
fn unmatched_open_brace_is_literal() {
let v = pm(&[("a", "1")]);
assert_eq!(substitute("{a {x", &v), "{a {x");
}
#[test]
fn placeholders_with_underscores_and_digits() {
let v = pm(&[("session_pct_2", "x")]);
assert_eq!(substitute("{session_pct_2}", &v), "x");
}
#[test]
fn utf8_around_braces() {
let v = pm(&[("x", "→")]);
assert_eq!(substitute("α{x}β", &v), "α→β");
}
}