use ansi_term::{Colour, Style};
use indicatif::{ProgressBar, ProgressStyle};
use notify_rust::Notification;
use pager::Pager;
use serde::{Deserialize, Serialize};
use simpler_timer::Timer;
use std::fs::File;
use std::io::ErrorKind;
use std::io::{BufReader, Read};
use std::io::{BufWriter, Write};
use std::time::Duration;
use time::OffsetDateTime;
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Session {
title: String,
labels: Vec<String>,
date: String,
time: String,
}
pub fn timer(title: &str, labels: &str) -> std::io::Result<()> {
let bar = ProgressBar::new(1500);
bar.set_style(
ProgressStyle::default_bar()
.template(" {bar:40.cyan/blue} {elapsed_precise}")
.progress_chars("##-"),
);
let periodic = Timer::with_duration(Duration::from_secs(1));
let timeout = Timer::with_duration(Duration::from_secs(1500));
println!(" Focusing on: {}", Colour::Cyan.paint(title));
loop {
if periodic.expired() {
bar.inc(1);
periodic.reset();
}
if timeout.expired() {
bar.finish();
println!("Session ended, take a break.");
break;
}
}
let session = generate_session(title, labels)?;
init_history()?;
let mut history = read_history()?;
history.insert(0, session);
write_history(&history)?;
notify(title)?;
Ok(())
}
fn notify(title: &str) -> std::io::Result<()> {
let body = format!("Session Ended: `{}`, take a break.", title);
Notification::new()
.summary("Session")
.body(&body)
.appname("session")
.timeout(0)
.show()
.unwrap();
Ok(())
}
fn generate_session(title: &str, labels: &str) -> std::io::Result<Session> {
let date = format!("{}", OffsetDateTime::now_local().date());
let time = OffsetDateTime::now_local().time().format("%R");
let lbls: Vec<String> = labels.split(',').map(|s| s.trim().to_owned()).collect();
Ok(Session {
title: String::from(title),
labels: lbls,
date,
time,
})
}
fn init_history() -> std::io::Result<()> {
File::open("./.session.json").unwrap_or_else(|error| {
if error.kind() == ErrorKind::NotFound {
let file = File::create("./.session.json").unwrap_or_else(|error| {
panic!("Problem creating the file: {:?}", error);
});
let initial_json = "[]";
let f = File::create("./.session.json").expect("Unable to create file");
let mut f = BufWriter::new(f);
f.write_all(initial_json.as_bytes())
.expect("Unable to write data");
file
} else {
panic!("Problem opening the file: {:?}", error);
}
});
Ok(())
}
fn read_history() -> std::io::Result<Vec<Session>> {
let mut data = String::new();
let f = File::open("./.session.json").expect("Unable to open file");
let mut br = BufReader::new(f);
br.read_to_string(&mut data).expect("Unable to read string");
let v: Vec<Session> = serde_json::from_str(&data)?;
Ok(v)
}
fn write_history(history: &[Session]) -> std::io::Result<()> {
let output = serde_json::to_string(&history).unwrap();
let f = File::create("./.session.json").expect("Unable to create file");
let mut f = BufWriter::new(f);
f.write_all(output.as_bytes())
.expect("Unable to write data");
Ok(())
}
fn filter_sessions(labels: &str) -> Vec<Session> {
let history = read_history().unwrap();
let mut filtered: Vec<Session> = vec![];
for label in labels.split(',') {
for session in history.iter() {
if session.labels.iter().any(|p| p == label.trim()) {
filtered.push(session.clone());
}
}
}
if labels == "" {
history
} else {
filtered
}
}
pub fn status(labels: &str) -> std::io::Result<()> {
let to_show = filter_sessions(labels);
let total_minutes = to_show.len() * 25;
let hours = total_minutes / 60;
let minutes = total_minutes % 60;
let date = format!("{}", OffsetDateTime::now_local().date());
let today = to_show.iter().filter(|p| p.date == date).count();
let today_total_minutes = today * 25;
let today_hours = today_total_minutes / 60;
let today_minutes = today_total_minutes % 60;
let today_str = format!(
"TODAY: {} sessions {}h:{}m ",
today, today_hours, today_minutes
);
let today = to_show.iter().filter(|p| p.date == date).count();
match today {
0..=2 => {
print!("{}", Colour::Red.paint(today_str));
}
3..=7 => {
print!("{}", Colour::Yellow.paint(today_str));
}
_ => {
print!("{}", Colour::Green.paint(today_str));
}
}
println!(
"{}",
format!(
"<{} sessions {}h:{}m (all time)>",
to_show.len(),
hours,
minutes
)
);
Ok(())
}
pub fn log(labels: &str) -> std::io::Result<()> {
let to_show = filter_sessions(labels);
let label_style = Style::new().bold().on(Colour::Cyan).fg(Colour::Black);
Pager::with_pager("less -r").setup();
status(labels)?;
for session in to_show {
println!(
"\n{}",
Colour::Cyan.paint(format!("Title: {}", session.title))
);
println!("Labels: {}", label_style.paint(session.labels.join(",")));
println!("Date: {} {}", session.date, session.time);
}
Ok(())
}