use csv::Reader;
use libc::{signal, alarm, pause};
use serde::Deserialize;
use std::env::args;
use std::sync::Mutex;
#[derive(PartialEq,Debug)]
enum lastDrawn {
time,
between,
none,
}
#[derive(Debug, Deserialize)]
struct csvTime {
name: String,
begin: String,
end: String,
}
#[derive(Debug)]
struct Time {
hour: String,
minute: String,
}
struct PrintableTime {
hours: i64,
minutes: i64,
seconds: i64,
}
impl PrintableTime {
fn from_duration(d:Duration) -> Self {
let h =d.num_hours();
let m = d.num_minutes()-(60*h);
let s = d.num_seconds()-(60*m);
return Self { hours: h, minutes: m, seconds: s };
}
}
fn add_zero(n:i64) -> String {
if n<10 {
let mut s = n.to_string();
s.insert(0, '0');
return s;
} else {
return n.to_string();
}
}
use std::{fmt, cmp::Ordering, io::Write};
impl fmt::Display for PrintableTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
{
if self.seconds==0 {
write!(f, "{}:{}",self.hours,add_zero(self.minutes))
} else {
write!(f, "{}:{}:{}",self.hours,add_zero(self.minutes),add_zero(self.seconds))
}
}
}
}
use chrono::{prelude::Local, Duration};
#[derive(PartialEq)]
enum RelPos {
Before,
In,
After,
}
static csvtimes:Mutex<Vec<csvTime>> = Mutex::new(Vec::new());
static drawn_global:Mutex<lastDrawn> = Mutex::new(lastDrawn::none);
fn move_relative(x:i32,y:i32) { if x.is_positive() {
print!("\x1b[{}C",x);
} else if x.is_negative() {
print!("\x1b[{}D",x.abs());
}
if y.is_positive() {
print!("\x1b[{}A",y);
} else if y.is_negative() {
print!("\x1b[{}B",y.abs());
}
}
#[inline]
fn move_to(x:u32) {
print!("\x1b[{}G",x);
}
#[inline]
fn save_pos() { print!("\x1b7");}
#[inline]
fn restore_pos() { print!("\x1b8");}
fn handler (_:i32) {
let current_time = Local::now().time();
let mut time_class_pos: Vec<RelPos> = Vec::new(); let mut found_class = false;
let csvtimes_local = csvtimes.lock().unwrap();
let mut drawn = drawn_global.lock().unwrap();
for i in csvtimes_local.as_slice() {
let mut begin: Time = Time { hour: String::new(), minute: String::new() };
let mut end: Time = Time { hour: String::new(), minute: String::new() };
let mut t = i.begin.split(':').into_iter();
begin.hour=t.next().expect("Couldn't get hour.").to_string();
begin.minute=t.next().expect("Couldn't get minute.").to_string();
t = i.end.split(':').into_iter();
end.hour=t.next().expect("Couldn't get hour.").to_string();
end.minute=t.next().expect("Couldn't get minute.").to_string();
let begin_time = chrono::NaiveTime::parse_from_str(&i.begin,"%H:%M").expect("can't get time:(");
let end_time = chrono::NaiveTime::parse_from_str(&i.end,"%H:%M").expect("can't get time:(");
if (begin_time..=end_time).contains(¤t_time) {
time_class_pos.push(RelPos::In);
let d = end_time.signed_duration_since(current_time);
let p = PrintableTime::from_duration(d);
let hr = format!("{}hr:",p.hours.to_string());
let m = format!("{}m:", p.minutes.to_string());
let TimeLeft = format!("{}{}{}s",if hr!="0hr:" {hr} else {"".to_string()},if m!="0m:" {m} else {"".to_string()},p.seconds);
let CurrentTime = current_time.format("%I:%M:%S %P");
let mut stdout = std::io::stdout();
if *drawn!=lastDrawn::time {
print!("Class:");move_to(17);println!("{}",i.name);
print!("TimeLeft:");
move_to(17);
save_pos();
println!("{}",TimeLeft);
print!("Current Time:");move_to(17);println!("{}",CurrentTime);
} else {
restore_pos();
move_relative(0, 2);
print!("{}",TimeLeft);
restore_pos();
move_relative(0, 1);
println!("{}",CurrentTime);
}
*drawn=lastDrawn::time;
found_class=true;
} else {
time_class_pos.push(
match begin_time.cmp(¤t_time) {
Ordering::Greater => RelPos::Before,
Ordering::Equal => RelPos::In,
Ordering::Less =>
match end_time.cmp(¤t_time) {
Ordering::Less => RelPos::In,
Ordering::Equal => RelPos::In,
Ordering::Greater => RelPos::After,
},
}
);
}
unsafe{libc::alarm(1);}
} if found_class==false {
let first = &time_class_pos[0];
let last = time_class_pos.last().unwrap();
if *drawn!=lastDrawn::between {
if ((first == &RelPos::After) || (first == &RelPos::In)) && ((last == &RelPos::In) || (last == &RelPos::Before)) {
println!("In between classes");
*drawn=lastDrawn::between;
} else {
println!("Outta school B)");
}
}
}
}
fn main() {
let current_time = Local::now().time();
const TIME_FN: &str = "times.csv";
let home = std::env::var("HOME").expect("Couldn't get $HOME var");
let csvpath:String = format!("{home}/{fn}",home=home,fn=TIME_FN);
let f_result = Reader::from_path(csvpath.clone());
let mut f;
if f_result.is_err() {
println!("HOME FOLDER:{}",home);
panic!("\nPlease put times.csv in your home folder (/Users/[your username]/times.csv)\nTo get to there:\nPress ⌘+Shift+G in finder. Type ~ and press enter.\n")
} else {
f=f_result.unwrap();
}
for i in f.deserialize() {
csvtimes.lock().unwrap().push(i.unwrap());
}
if csvtimes.lock().unwrap().is_empty() {
panic!("{} is empty.\nexample:\n\nname,begin,end\n1st Math,12:05,13:00\n",csvpath);
}
let args = args();
if args.count()>1 {
handler(0);
} else {
unsafe {
signal(libc::SIGALRM, handler as usize);
loop {
alarm(1);
pause();
}
}
}
}