//! Показывает локальные дату, время, день недели и часть суток с обновлением раз в секунду. mod common; use anyhow::Result; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use m::vfd::fit_to_width; use m::worker::VfdWorker; fn run_date(args: &[&str]) -> Option { let out = std::process::Command::new("date") .args(args) .output() .ok()?; if !out.status.success() { return None; } Some(String::from_utf8_lossy(&out.stdout).trim().to_string()) } fn parse_datetime_and_weekday(value: &str) -> Option<(String, u8)> { let (datetime, weekday) = value.split_once('|')?; let weekday = weekday.parse::().ok()?; (1..=7) .contains(&weekday) .then(|| (datetime.to_string(), weekday)) } fn local_datetime_and_weekday() -> (String, u8) { run_date(&["+%d.%m.%Y %H:%M:%S|%u"]) .as_deref() .and_then(parse_datetime_and_weekday) .unwrap_or_else(|| ("??.??.???? ??:??:??".to_string(), 1)) } fn weekday_ru_full(n: u8) -> &'static str { match n { 1 => "Понедельник", 2 => "Вторник", 3 => "Среда", 4 => "Четверг", 5 => "Пятница", 6 => "Суббота", 7 => "Воскресенье", _ => "Понедельник", } } fn weekday_ru_short(n: u8) -> &'static str { match n { 1 => "Пн", 2 => "Вт", 3 => "Ср", 4 => "Чт", 5 => "Пт", 6 => "Сб", 7 => "Вс", _ => "Пн", } } fn time_of_day_ru(hour: u8) -> &'static str { match hour { 5..=10 => "утро", 11..=16 => "день", 17..=22 => "вечер", _ => "ночь", } } fn extract_hour(datetime: &str) -> u8 { // ожидаем "DD.MM.YYYY HH:MM:SS" // берём HH как 2 символа после пробела datetime .split_whitespace() .nth(1) .and_then(|t| t.get(0..2)) .and_then(|hh| hh.parse::().ok()) .unwrap_or(12) } fn main() -> Result<()> { // args: // 1) port (default: /dev/cu.usbmodem101) // 2) width (default: 20) // 3) brightness 1..4 (default: 2) let mut args = common::ExampleArgs::from_env(); let brightness: u8 = args.parse_or(2); let width = args.width; let worker = VfdWorker::start(args.config)?; let vfd = worker.handle(); vfd.clear(); vfd.set_brightness(brightness); loop { let (dt, wd) = local_datetime_and_weekday(); let hour = extract_hour(&dt); let tod = time_of_day_ru(hour); // 1 строка: "28.01.2006 12:03:34" let line1 = fit_to_width(&dt, width); // 2 строка: "Понедельник сейчас день" // но если не влазит — "Пн сейчас день" let full = format!("{} сейчас {}", weekday_ru_full(wd), tod); let mut line2 = full; if line2.chars().count() > width { line2 = format!("{} сейчас {}", weekday_ru_short(wd), tod); } line2 = fit_to_width(&line2, width); vfd.print_line_diff(1, line1)?; vfd.print_line_diff(2, line2)?; // чтобы обновлялось ровно раз в секунду (плюс/минус), можно “привязать” к UNIX time let now = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_millis(); let ms_to_next = 1000 - (now % 1000) as u64; std::thread::sleep(Duration::from_millis(ms_to_next)); } } #[cfg(test)] mod tests { use super::*; #[test] fn parses_date_and_weekday_from_one_process_result() { assert_eq!( parse_datetime_and_weekday("15.08.2026 23:59:58|6"), Some(("15.08.2026 23:59:58".to_string(), 6)) ); assert_eq!(parse_datetime_and_weekday("invalid"), None); assert_eq!(parse_datetime_and_weekday("15.08.2026 23:59:58|8"), None); } #[test] fn classifies_hours_and_extracts_them_safely() { assert_eq!(extract_hour("15.08.2026 05:00:00"), 5); assert_eq!(extract_hour("invalid"), 12); assert_eq!(time_of_day_ru(5), "утро"); assert_eq!(time_of_day_ru(23), "ночь"); } }