Some refactoring.

This commit is contained in:
shy 2021-04-14 10:39:33 +02:00
parent b746400e0a
commit d0aa9f7a4f
3 changed files with 109 additions and 100 deletions

View file

@ -8,6 +8,10 @@ use crate::layout::{Layout, Position};
use crate::utils::*; use crate::utils::*;
use crate::consts::{COLOR, LABEL_SIZE_LIMIT}; use crate::consts::{COLOR, LABEL_SIZE_LIMIT};
// Delimiter between time and label. Remember to update usage information in
// consts.rs when changing this.
const DELIMITER: char = '/';
pub struct Countdown { pub struct Countdown {
pub value: u32, pub value: u32,
@ -24,6 +28,7 @@ impl Countdown {
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.value = 0; self.value = 0;
self.position = None;
} }
// Draw countdown. // Draw countdown.
@ -88,7 +93,7 @@ impl AlarmRoster {
let mut label: String; let mut label: String;
let time_str: &str; let time_str: &str;
if let Some(i) = input.find('/') { if let Some(i) = input.find(DELIMITER) {
label = input[(i + 1)..].to_string(); label = input[(i + 1)..].to_string();
// Truncate label. // Truncate label.
unicode_truncate(&mut label, LABEL_SIZE_LIMIT); unicode_truncate(&mut label, LABEL_SIZE_LIMIT);
@ -101,16 +106,19 @@ impl AlarmRoster {
// Parse input into seconds. // Parse input into seconds.
if time_str.contains(':') { if time_str.contains(':') {
for sub in time_str.rsplit(':') { for sub in time_str.rsplit(':') {
if !sub.is_empty() {
match sub.parse::<u32>() { match sub.parse::<u32>() {
// Valid. // Valid.
Ok(d) if d < 60 && index < 3 => time += d * 60u32.pow(index), Ok(d) if d < 60 && index < 3 => time += d * 60u32.pow(index),
// Passes as u32, but does not fit into time range. // Passes as u32, but does not fit into time range.
Ok(_) => return Err("Could not parse value as time."), Ok(_) => return Err("Could not parse value as time."),
// Ignore failure caused by an empty string.
// TODO: Match error kind when stable. See documentation
// for std::num::ParseIntError and
// https://github.com/rust-lang/rust/issues/22639
Err(_) if sub.is_empty() => (),
// Could not parse to u32. // Could not parse to u32.
Err(_) => return Err("Could not parse value as integer."), Err(_) => return Err("Could not parse value as integer."),
} }
}
index += 1; index += 1;
} }
} else { } else {
@ -124,7 +132,12 @@ impl AlarmRoster {
// Skip if time is out of boundaries. // Skip if time is out of boundaries.
if time == 0 { return Err("Evaluates to zero.") }; if time == 0 { return Err("Evaluates to zero.") };
if time >= 24 * 60 * 60 { return Err("Values >24h not supported.") }; if time >= 24 * 60 * 60 { return Err("Values >24h not supported.") };
// Filter out double entries.
if self.list.iter().any(|a| a.time == time) {
return Err("Already exists.");
}
// Label will never change from now on.
label.shrink_to_fit(); label.shrink_to_fit();
let alarm = Alarm { let alarm = Alarm {
label, label,
@ -133,21 +146,11 @@ impl AlarmRoster {
exceeded: false, exceeded: false,
}; };
// Add to list, insert based on alarm time. Disallow double entries. // Add to list, insert based on alarm time.
let mut i = self.list.len(); if let Some(i) = self.list.iter().position(|a| a.time > time) {
if i == 0 {
self.list.push(alarm);
} else {
while i > 0 {
// Filter out double entries.
if self.list[i - 1].time == time {
return Err("Already exists.");
} else if self.list[i - 1].time < time {
break;
}
i -= 1;
}
self.list.insert(i, alarm); self.list.insert(i, alarm);
} else {
self.list.push(alarm);
} }
Ok(()) Ok(())
} }
@ -170,25 +173,22 @@ impl AlarmRoster {
) -> Option<(u32, &String)> ) -> Option<(u32, &String)>
{ {
let mut ret = None; let mut ret = None;
let mut index = 0;
let size = self.list.len() as u16; let size = self.list.len() as u16;
for alarm in &mut self.list { for (index, alarm) in self.list.iter_mut().enumerate()
// Ignore alarms marked exceeded. // Ignore alarms marked exceeded.
if !alarm.exceeded { .filter(|(_, a)| !a.exceeded) {
if alarm.time <= clock.elapsed { if alarm.time <= clock.elapsed {
// Found alarm to raise. // Found alarm to raise.
ret = Some((alarm.time, &alarm.label)); ret = Some((alarm.time, &alarm.label));
alarm.exceeded = true; alarm.exceeded = true;
clock.color_index = Some(alarm.color_index); clock.color_index = Some(alarm.color_index);
countdown.value = 0; countdown.reset();
countdown.position = None;
// Skip ahead to the next one. // Skip ahead to the next one.
index += 1;
continue; continue;
} }
// Reached the alarm to exceed next. Update countdown // Reached the alarm to exceed next. Update countdown accordingly.
// accordingly.
countdown.value = alarm.time - clock.elapsed; countdown.value = alarm.time - clock.elapsed;
if countdown.position.is_none() || layout.force_redraw { if countdown.position.is_none() || layout.force_redraw {
// Compute position. // Compute position.
@ -196,12 +196,12 @@ impl AlarmRoster {
layout.roster.col layout.roster.col
+ 3 + 3
+ unicode_length(&alarm.label); + unicode_length(&alarm.label);
let mut line = layout.roster.line + index; let mut line = layout.roster.line + index as u16;
// Compensate for "hidden" items in the alarm roster. // Compensate for "hidden" items in the alarm roster.
// TODO: Make this more elegant and robust. // TODO: Make this more elegant and robust.
if let Some(offset) = size.checked_sub(layout.roster_height + 1) { if let Some(offset) = size.checked_sub(layout.roster_height + 1) {
if index <= offset{ if index as u16 <= offset{
// Draw next to placeholder ("[...]"). // Draw next to placeholder ("[...]").
line = layout.roster.line; line = layout.roster.line;
col = layout.roster.col + 6; col = layout.roster.col + 6;
@ -215,8 +215,6 @@ impl AlarmRoster {
// Ignore other alarms. // Ignore other alarms.
break; break;
} }
index += 1;
}
ret // Return value. ret // Return value.
} }
@ -228,16 +226,13 @@ impl AlarmRoster {
config: &Config, config: &Config,
) -> Result<(), std::io::Error> ) -> Result<(), std::io::Error>
{ {
let mut index = 0; // Find first item to print in case we lack the space to print them
// all. Final '-1' to take account for the input buffer.
// Find first item to print in case we lack space to print them all. let mut offset = 0;
// Final '-1' to take account for the input buffer.
let mut first = 0;
if self.list.len() > layout.roster_height as usize { if self.list.len() > layout.roster_height as usize {
// Actually -1 (zero indexing) +1 (first line containing "..."). // Actually -1 (zero indexing) +1 (first line containing "[...]").
first = self.list.len() - layout.roster_height as usize; offset = self.list.len() - layout.roster_height as usize;
index += 1;
write!(stdout, write!(stdout,
"{}{}[...]{}", "{}{}[...]{}",
@ -247,12 +242,19 @@ impl AlarmRoster {
)?; )?;
} }
for alarm in &self.list[first..] { for (i, alarm) in self.list.iter().skip(offset).enumerate() {
let line = if offset > 0 {
// Add offset of one for "[...]".
layout.roster.line + i as u16 + 1
} else {
layout.roster.line + i as u16
};
match alarm.exceeded { match alarm.exceeded {
true if config.fancy => { true if config.fancy => {
write!(stdout, write!(stdout,
"{}{}{}{} {} {}🭬{}{}", "{}{}{}{} {} {}🭬{}{}",
cursor::Goto(layout.roster.col, layout.roster.line + index), cursor::Goto(layout.roster.col, line),
color::Fg(COLOR[alarm.color_index]), color::Fg(COLOR[alarm.color_index]),
style::Bold, style::Bold,
style::Invert, style::Invert,
@ -265,7 +267,7 @@ impl AlarmRoster {
false if config.fancy => { false if config.fancy => {
write!(stdout, write!(stdout,
"{}{}█🭬{}{}", "{}{}█🭬{}{}",
cursor::Goto(layout.roster.col, layout.roster.line + index), cursor::Goto(layout.roster.col, line),
color::Fg(COLOR[alarm.color_index]), color::Fg(COLOR[alarm.color_index]),
color::Fg(color::Reset), color::Fg(color::Reset),
&alarm.label, &alarm.label,
@ -274,7 +276,7 @@ impl AlarmRoster {
true => { true => {
write!(stdout, write!(stdout,
"{}{}{}{} {} {}{}", "{}{}{}{} {} {}{}",
cursor::Goto(layout.roster.col, layout.roster.line + index), cursor::Goto(layout.roster.col, line),
color::Fg(COLOR[alarm.color_index]), color::Fg(COLOR[alarm.color_index]),
style::Bold, style::Bold,
style::Invert, style::Invert,
@ -286,14 +288,13 @@ impl AlarmRoster {
false => { false => {
write!(stdout, write!(stdout,
"{}{} {} {}", "{}{} {} {}",
cursor::Goto(layout.roster.col, layout.roster.line + index), cursor::Goto(layout.roster.col, line),
color::Bg(COLOR[alarm.color_index]), color::Bg(COLOR[alarm.color_index]),
color::Bg(color::Reset), color::Bg(color::Reset),
&alarm.label, &alarm.label,
)?; )?;
}, },
} }
index += 1;
} }
Ok(()) Ok(())
} }

View file

@ -92,7 +92,7 @@ impl Clock {
// Draw clock according to layout. // Draw clock according to layout.
pub fn draw<W: Write>( pub fn draw<W: Write>(
&mut self, &self,
mut stdout: &mut RawTerminal<W>, mut stdout: &mut RawTerminal<W>,
layout: &Layout, layout: &Layout,
) -> Result<(), std::io::Error> ) -> Result<(), std::io::Error>
@ -105,18 +105,21 @@ impl Clock {
write!(stdout, "{}", color::Fg(COLOR[c]))?; write!(stdout, "{}", color::Fg(COLOR[c]))?;
} }
// Draw hours if necessary. // Run once every hour or on request.
if layout.force_redraw || self.elapsed % 3600 == 0 { if layout.force_redraw || self.elapsed % 3600 == 0 {
// Draw hours if necessary.
if self.elapsed >= 3600 { if self.elapsed >= 3600 {
self.draw_digit_pair( self.draw_digit_pair(
&mut stdout, &mut stdout,
self.elapsed / 3600, self.elapsed / 3600,
&layout.clock_hr)?; &layout.clock_hr,
)?;
// Draw colon. // Draw colon.
self.draw_colon( self.draw_colon(
&mut stdout, &mut stdout,
&layout.clock_colon1)?; &layout.clock_colon1,
)?;
} }
// Draw days. // Draw days.
@ -124,7 +127,8 @@ impl Clock {
let day_count = format!( let day_count = format!(
"+ {} {}", "+ {} {}",
self.days, self.days,
if self.days == 1 { "DAY" } else { "DAYS" }); if self.days == 1 { "DAY" } else { "DAYS" },
);
write!(stdout, write!(stdout,
"{}{:>11}", "{}{:>11}",
@ -132,61 +136,65 @@ impl Clock {
layout.clock_days.col, layout.clock_days.col,
layout.clock_days.line, layout.clock_days.line,
), ),
day_count)?; day_count,
)?;
} }
} }
// Draw minutes if necessary. // Draw minutes if necessary. Once every minute or on request.
if layout.force_redraw || self.elapsed % 60 == 0 { if layout.force_redraw || self.elapsed % 60 == 0 {
self.draw_digit_pair( self.draw_digit_pair(
&mut stdout, &mut stdout,
(self.elapsed % 3600) / 60, (self.elapsed % 3600) / 60,
&layout.clock_min)?; &layout.clock_min,
)?;
} }
// Draw colon if necessary. // Draw colon if necessary.
if layout.force_redraw { if layout.force_redraw {
self.draw_colon( self.draw_colon(
&mut stdout, &mut stdout,
&layout.clock_colon0)?; &layout.clock_colon0,
)?;
} }
// Draw seconds. // Draw seconds.
self.draw_digit_pair( self.draw_digit_pair(
&mut stdout, &mut stdout,
self.elapsed % 60, self.elapsed % 60,
&layout.clock_sec)?; &layout.clock_sec,
)?;
// Reset color and style. // Reset color and style.
if self.paused || self.color_index != None { if self.paused || self.color_index != None {
write!(stdout, write!(stdout,
"{}{}", "{}{}",
style::NoFaint, style::NoFaint,
color::Fg(color::Reset))?; color::Fg(color::Reset),
)?;
} }
Ok(()) Ok(())
} }
fn draw_digit_pair<W: Write>( fn draw_digit_pair<W: Write>(
&mut self, &self,
stdout: &mut RawTerminal<W>, stdout: &mut RawTerminal<W>,
value: u32, value: u32,
pos: &Position, pos: &Position,
) -> Result<(), std::io::Error> ) -> Result<(), std::io::Error>
{ {
let left = value / 10; let left = self.font.digits[value as usize / 10].iter();
let right = value % 10; let right = self.font.digits[value as usize % 10].iter();
for l in 0..self.font.height { for (i, (left, right)) in left.zip(right).enumerate() {
write!(stdout, write!(stdout,
"{}{} {}", "{}{} {}",
cursor::Goto(pos.col, pos.line + l), cursor::Goto(pos.col, pos.line + i as u16),
// First digit. left,
self.font.digits[left as usize][l as usize], right,
// Second digit.
self.font.digits[right as usize][l as usize]
)?; )?;
} }
Ok(()) Ok(())
} }

View file

@ -8,7 +8,7 @@ fn main() {
// Parse command line arguments into config and alarm roster. // Parse command line arguments into config and alarm roster.
let config = Config::new(args, &mut alarm_roster) let config = Config::new(args, &mut alarm_roster)
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
println!("{}", e); eprintln!("{}", e);
process::exit(1); process::exit(1);
}); });