memo-cli/src/main.rs

130 lines
3.3 KiB
Rust
Raw Normal View History

2022-03-13 20:13:13 +01:00
extern crate argparse;
use argparse::{ArgumentParser, Store, StoreTrue};
use chrono::{DateTime, Duration, Utc};
use rusqlite::{params, Connection, Error};
struct Memo {
id: u32,
body: String,
due: DateTime<Utc>,
}
fn init_db(path: &str) -> Result<Connection, Error> {
let connection = Connection::open(path)?;
connection.execute(
"CREATE TABLE IF NOT EXISTS memo (
id INTEGER PRIMARY KEY,
body TEXT NOT NULL,
due DATETIME
);",
[],
)?;
return Ok(connection);
}
fn all(conn: Connection) -> Result<Vec<Memo>, rusqlite::Error> {
let mut statement = conn.prepare("SELECT * FROM memo ORDER BY due, id")?;
let mut rows = statement.query([])?;
let mut results = Vec::new();
while let Some(row) = rows.next()? {
results.push(Memo {
id: row.get(0)?,
body: row.get(1)?,
due: row.get(2)?,
});
}
return Ok(results);
}
pub trait PrettyPrint {
fn pretty(&self) -> String;
}
impl PrettyPrint for Duration {
fn pretty(&self) -> String {
if self.num_days().abs() > 400 {
return format!("{}y {}m", self.num_days().abs() / 365, (self.num_days() % (30*12)) / 30);
} else if self.num_days().abs() >= 365 {
return format!("{}y", self.num_days().abs() / 365);
} else if self.num_days().abs() >= 90 {
return format!("{}m", self.num_days().abs() / 30); // sort of
} else if self.num_days().abs() >= 1 {
return format!("{}d", self.num_days().abs());
} else if self.num_hours().abs() > 0 {
let h = self.num_minutes().abs() / 60;
let m = self.num_minutes().abs() % 60;
return format!("{}h {}min", h, m);
} else if self.num_minutes().abs() > 0 {
return format!("{}min", self.num_minutes().abs());
} else if self.num_seconds().abs() > 0 {
return format!("{}s", self.num_seconds().abs());
}
return self.to_string();
}
}
fn main() {
let home_path = std::env!("HOME").to_string();
if home_path.len() < 1 {
panic!("Cannot work without a home folder");
}
let mut version = false;
let mut memo: String = "".to_string();
let mut db_path: String = home_path + "/.local/share/memo-cli.db";
{
// this block limits scope of borrows by ap.refer() method
let mut ap = ArgumentParser::new();
ap.set_description("A simple tool to remember things");
ap.refer(&mut version).add_option(
&["-V", "--version"],
StoreTrue,
"Show current version and die",
);
ap.refer(&mut memo)
.add_option(&["-n", "--new"], Store, "Add a new memo");
ap.refer(&mut db_path)
.add_option(&["--path"], Store, "Specify db path");
ap.parse_args_or_exit();
}
if version {
const VERSION: &str = env!("CARGO_PKG_VERSION");
println!("memo-cli v{}", VERSION);
return;
}
let connection =
init_db(&db_path).unwrap_or_else(|err| panic!("Could not open db : {}", err.to_string()));
if memo.len() > 0 {
connection
.execute(
"INSERT INTO memo (body, due) VALUES (?, ?)",
params![memo, Utc::now()],
)
.unwrap_or_else(|err| panic!("Could not insert into db : {}", err.to_string()));
println!("New memo : '{}'", memo);
} else {
let zero_duration = Duration::seconds(0);
let memos = all(connection)
.unwrap_or_else(|err| panic!("Could not read all memos : {}", err.to_string()));
for m in memos {
let delta = m.due - Utc::now();
if delta.le(&zero_duration) {
println!("[*] {} : +{} \t[{}]", m.body, delta.pretty(), m.id);
} else {
println!(" * {} : -{} \t[{}]", m.body, delta.pretty(), m.id);
}
}
}
}