Some refactoring.
This commit is contained in:
parent
b746400e0a
commit
d0aa9f7a4f
3 changed files with 109 additions and 100 deletions
159
src/alarm.rs
159
src/alarm.rs
|
@ -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,15 +106,18 @@ 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.
|
||||||
// Could not parse to u32.
|
// TODO: Match error kind when stable. See documentation
|
||||||
Err(_) => return Err("Could not parse value as integer."),
|
// for std::num::ParseIntError and
|
||||||
}
|
// https://github.com/rust-lang/rust/issues/22639
|
||||||
|
Err(_) if sub.is_empty() => (),
|
||||||
|
// Could not parse to u32.
|
||||||
|
Err(_) => return Err("Could not parse value as integer."),
|
||||||
}
|
}
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
|
@ -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,52 +173,47 @@ 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 {
|
|
||||||
// Found alarm to raise.
|
|
||||||
ret = Some((alarm.time, &alarm.label));
|
|
||||||
alarm.exceeded = true;
|
|
||||||
clock.color_index = Some(alarm.color_index);
|
|
||||||
countdown.value = 0;
|
|
||||||
countdown.position = None;
|
|
||||||
// Skip ahead to the next one.
|
|
||||||
index += 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Reached the alarm to exceed next. Update countdown
|
|
||||||
// accordingly.
|
|
||||||
countdown.value = alarm.time - clock.elapsed;
|
|
||||||
if countdown.position.is_none() || layout.force_redraw {
|
|
||||||
// Compute position.
|
|
||||||
let mut col =
|
|
||||||
layout.roster.col
|
|
||||||
+ 3
|
|
||||||
+ unicode_length(&alarm.label);
|
|
||||||
let mut line = layout.roster.line + index;
|
|
||||||
|
|
||||||
// Compensate for "hidden" items in the alarm roster.
|
if alarm.time <= clock.elapsed {
|
||||||
// TODO: Make this more elegant and robust.
|
// Found alarm to raise.
|
||||||
if let Some(offset) = size.checked_sub(layout.roster_height + 1) {
|
ret = Some((alarm.time, &alarm.label));
|
||||||
if index <= offset{
|
alarm.exceeded = true;
|
||||||
// Draw next to placeholder ("[...]").
|
clock.color_index = Some(alarm.color_index);
|
||||||
line = layout.roster.line;
|
countdown.reset();
|
||||||
col = layout.roster.col + 6;
|
// Skip ahead to the next one.
|
||||||
} else {
|
continue;
|
||||||
line = line.checked_sub(offset)
|
|
||||||
.unwrap_or(layout.roster.line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
countdown.position = Some(Position { col, line, });
|
|
||||||
}
|
|
||||||
// Ignore other alarms.
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
index += 1;
|
// Reached the alarm to exceed next. Update countdown accordingly.
|
||||||
|
countdown.value = alarm.time - clock.elapsed;
|
||||||
|
if countdown.position.is_none() || layout.force_redraw {
|
||||||
|
// Compute position.
|
||||||
|
let mut col =
|
||||||
|
layout.roster.col
|
||||||
|
+ 3
|
||||||
|
+ unicode_length(&alarm.label);
|
||||||
|
let mut line = layout.roster.line + index as u16;
|
||||||
|
|
||||||
|
// Compensate for "hidden" items in the alarm roster.
|
||||||
|
// TODO: Make this more elegant and robust.
|
||||||
|
if let Some(offset) = size.checked_sub(layout.roster_height + 1) {
|
||||||
|
if index as u16 <= offset{
|
||||||
|
// Draw next to placeholder ("[...]").
|
||||||
|
line = layout.roster.line;
|
||||||
|
col = layout.roster.col + 6;
|
||||||
|
} else {
|
||||||
|
line = line.checked_sub(offset)
|
||||||
|
.unwrap_or(layout.roster.line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
countdown.position = Some(Position { col, line, });
|
||||||
|
}
|
||||||
|
// Ignore other alarms.
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
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(())
|
||||||
}
|
}
|
||||||
|
|
48
src/clock.rs
48
src/clock.rs
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue