Track last_edit, make Memo sortable, fixes storages

This commit is contained in:
əlemi 2022-03-29 00:40:32 +02:00
parent dd503d5702
commit 5ab9f2c5af
No known key found for this signature in database
GPG key ID: BBCBFE5D7244634E
6 changed files with 123 additions and 80 deletions

View file

@ -2,86 +2,91 @@
"key": "",
"hash": "",
"last_sync": null,
"last_run": "2022-03-28T00:13:34.265758517Z",
"last_edit": "2022-03-28T22:38:22.622270502Z",
"memo": [
{
"id": "dba39f62-919e-4dcf-8c2b-65db2e11e6d5",
"body": "store last_edit and not last_run",
"due": "2022-04-04T00:03:02.096181142Z",
"done": null
},
{
"id": "41a975f7-3f2d-4f72-bf0e-21de91631fbc",
"body": "store last_edit in memos",
"due": "2022-04-04T00:03:02.096181142Z",
"done": null
},
{
"id": "eaac08b0-c367-4901-a980-0cda66efbb54",
"body": "fix merge policy (always uses remote data)",
"due": "2022-04-04T00:03:02.096181142Z",
"done": null
"done": null,
"last_edit": "2022-03-28T23:03:02.096181142Z"
},
{
"id": "171b69de-677c-4a82-a57b-7f00af4ae7e3",
"body": "data encryption",
"due": "2022-04-04T00:03:02.096181142Z",
"done": null
"done": null,
"last_edit": "2022-03-28T23:03:02.096181142Z"
},
{
"id": "d3cdd44f-96c1-4063-855a-8d55283be768",
"body": "recurring memos",
"due": "2022-04-27T00:03:41.531165213Z",
"done": null
"done": null,
"last_edit": "2022-03-28T23:03:02.096181142Z"
},
{
"id": "78622159-8bdc-4c1f-bf04-147e165e0f6e",
"body": "yml and toml formats?",
"due": "2022-04-27T00:03:41.531165213Z",
"done": null
"done": null,
"last_edit": "2022-03-28T23:03:02.096181142Z"
},
{
"id": "0f0df75d-6a0c-4e4e-9a3c-2075a045963c",
"body": "improve server...",
"due": "2022-07-06T00:04:05.889521359Z",
"done": null
"done": null,
"last_edit": "2022-03-28T23:03:02.096181142Z"
},
{
"id": "69b7d74c-3bcb-4cf6-bfbe-4e703c9957a2",
"body": "add title to each storage",
"due": "2022-04-04T00:03:02.096181142Z",
"done": null
"done": null,
"last_edit": "2022-03-28T23:03:02.096181142Z"
},
{
"id": "f81ffe23-ac41-4939-82e1-3a73066d1680",
"body": "maybe implement memo dependancies?",
"due": "2022-04-27T00:03:41.531165213Z",
"done": null
"done": null,
"last_edit": "2022-03-28T23:03:02.096181142Z"
},
{
"id": "f665d76b-79ed-45de-9f1e-73863b02f08a",
"body": "make android app synched with server",
"due": "2022-07-06T00:04:05.889521359Z",
"done": null
"done": null,
"last_edit": "2022-03-28T23:03:02.096181142Z"
},
{
"id": "a5a44ada-54fe-49e6-aebd-5353dc30ced4",
"body": "add description to each storage",
"due": "2022-04-04T00:06:05.528741299Z",
"done": null
"done": null,
"last_edit": "2022-03-28T23:03:02.096181142Z"
},
{
"id": "bf6e3b8e-e941-43f0-a2a4-51f8cf08a65c",
"body": "impl eq and ord for memo",
"due": "2022-04-04T00:13:10.508332057Z",
"done": null
"done": null,
"last_edit": "2022-03-28T23:03:02.096181142Z"
},
{
"id": "c03f6c49-ed72-4f52-8076-639278783598",
"body": "fix: json storage all() is not sorted",
"due": "2022-04-04T00:13:31.734454630Z",
"done": null
"id": "dba39f62-919e-4dcf-8c2b-65db2e11e6d5",
"body": "store last_edit and not last_run",
"due": "2022-04-04T00:03:02.096181142Z",
"done": "2022-03-28T22:38:20.101121011Z",
"last_edit": "2022-03-28T23:03:02.096181142Z"
},
{
"id": "41a975f7-3f2d-4f72-bf0e-21de91631fbc",
"body": "store last_edit in memos",
"due": "2022-04-04T00:03:02.096181142Z",
"done": "2022-03-28T22:38:22.621730633Z",
"last_edit": "2022-03-28T23:03:02.096181142Z"
}
],
"archive": []
]
}

View file

@ -132,8 +132,9 @@ fn run_commands<T>(mut storage: T, args: Cli) -> Result<(), MemoError>
}
}
let txt = body.join(" ");
storage.add(txt.as_str(), due_date).unwrap();
println!("{} new memo: {}", "[+]".bold(), txt);
println!("{} new memo: {}", "[+]".bold(), &txt);
storage.add(txt, due_date).unwrap();
storage.set_edit_time(Utc::now()).unwrap();
}
Some(Commands::Done { search, many }) => {
let rex = Regex::new(search.as_str());
@ -157,6 +158,7 @@ fn run_commands<T>(mut storage: T, args: Cli) -> Result<(), MemoError>
}
if let Some(rm) = to_remove {
storage.del(&rm.id).unwrap();
storage.set_edit_time(Utc::now()).unwrap();
println!("{} done memo: {}", "[-]".bold(), rm.body);
}
} else {
@ -181,6 +183,7 @@ fn run_commands<T>(mut storage: T, args: Cli) -> Result<(), MemoError>
}
}
storage.set(&m).unwrap();
storage.set_edit_time(Utc::now()).unwrap();
println!(
"{} updated memo\n{}",
format!("[{}]", ">".green()).bold().to_string(),
@ -193,9 +196,9 @@ fn run_commands<T>(mut storage: T, args: Cli) -> Result<(), MemoError>
let timing = if let Some(state) = storage.get_state().ok() {
let now = Local::now();
format!(
"last run: {}",
"last edit: {}",
state
.last_run
.last_edit
.with_timezone(&now.timezone())
.format("%a %d/%m %H:%M")
)
@ -224,11 +227,6 @@ fn run_commands<T>(mut storage: T, args: Cli) -> Result<(), MemoError>
println!("{} | {}", "memo-cli".bold(), timing);
print!("{}", builder);
}
if !args.notify {
// shitty assumption: notification is run by a daemon TODO
storage.set_run_time(Utc::now()).unwrap();
}
}
}

View file

@ -5,24 +5,59 @@ pub use sqlite::SQLiteStorage;
pub use json::JsonStorage;
use chrono::{DateTime, Utc};
use std::collections::LinkedList;
use std::fmt;
use uuid::Uuid;
use serde::{Serialize, Deserialize};
pub const SUPPORTED_FORMATS : &'static [&'static str] = &["db", "json"];
#[derive(Clone, Serialize, Deserialize)]
#[derive(Clone, Eq, Serialize, Deserialize)]
pub struct Memo {
pub id: Uuid,
pub body: String,
pub due: Option<DateTime<Utc>>,
pub done: Option<DateTime<Utc>>,
pub last_edit: DateTime<Utc>,
}
pub struct State {
pub last_run: DateTime<Utc>,
pub last_sync: Option<DateTime<Utc>>,
impl Memo {
pub fn new(body: String, due: Option<DateTime<Utc>>) -> Self {
return Memo{
id: Uuid::new_v4(),
body, due,
done: None,
last_edit: Utc::now(),
};
}
}
impl PartialEq for Memo {
fn eq(&self, other: &Memo) -> bool {
self.id == other.id
}
}
impl Ord for Memo {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
if self.due.is_some() {
if other.due.is_some() {
return self.due.unwrap().cmp(&other.due.unwrap());
} else { return std::cmp::Ordering::Less; }
} else {
if other.due.is_some() {
return std::cmp::Ordering::Greater;
}
else {
return self.last_edit.cmp(&other.last_edit);
}
}
}
}
impl PartialOrd for Memo {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl fmt::Display for Memo {
@ -37,11 +72,12 @@ impl fmt::Display for Memo {
}
return write!(
f,
"Memo(id={id}, body={body}, due={due}, done={done})",
"Memo(id={id}, body={body}, due={due}, done={done}, last_edit={last_edit})",
id = self.id,
body = self.body,
due = due_str,
done = done_str
done = done_str,
last_edit = self.last_edit.to_string(),
);
}
}
@ -78,20 +114,20 @@ impl From<ureq::Error> for MemoError {
fn from(e: ureq::Error) -> Self { Self{cause: "ureq", sqlite:None, io:None, ureq:Some(e)} }
}
pub struct State {
pub last_edit: DateTime<Utc>,
pub last_sync: Option<DateTime<Utc>>,
}
pub trait MemoStorage {
fn all(&self, done: bool) -> Result<LinkedList<Memo>, MemoError>;
fn all(&self, done: bool) -> Result<Vec<Memo>, MemoError>;
fn get(&self, id: &Uuid) -> Result<Memo, MemoError>;
fn set(&mut self, memo: &Memo) -> Result<usize, MemoError>;
fn del(&mut self, id: &Uuid) -> Result<usize, MemoError>;
fn insert(&mut self, m: Memo) -> Result<(), MemoError>;
fn add(&mut self, body: &str, due: Option<DateTime<Utc>>) -> Result<(), MemoError> {
self.insert(Memo {
id: Uuid::new_v4(),
body: body.to_string(),
due: due,
done: None,
})
fn add(&mut self, body: String, due: Option<DateTime<Utc>>) -> Result<(), MemoError> {
self.insert(Memo::new(body, due))
}
}
@ -105,12 +141,12 @@ pub trait StateStorage {
fn set_state(&mut self, state: State) -> Result<Option<State>, MemoError>;
fn get_state(&self) -> Result<State, MemoError>;
fn set_run_time(&mut self, time: DateTime<Utc>) -> Result<(), MemoError> {
fn set_edit_time(&mut self, time: DateTime<Utc>) -> Result<(), MemoError> {
let mut state = self.get_state().unwrap_or(State {
last_run: time,
last_edit: time,
last_sync: None,
}); // TODO jank way to not fail on 1st use
state.last_run = time;
state.last_edit = time;
self.set_state(state)?;
Ok(())
}

View file

@ -20,9 +20,8 @@ struct Store {
key: String,
hash: String,
last_sync: Option<DateTime<Utc>>,
last_run: DateTime<Utc>,
last_edit: DateTime<Utc>,
memo: Vec<Memo>,
archive: Vec<Memo>,
}
impl Default for Store {
@ -31,9 +30,8 @@ impl Default for Store {
key: "".to_string(),
hash: "".to_string(),
last_sync: None,
last_run: Utc::now(),
last_edit: Utc::now(),
memo: Vec::new(),
archive: Vec::new(),
};
}
}
@ -85,29 +83,29 @@ impl StateStorage for JsonStorage {
fn get_state(&self) -> Result<State, MemoError> {
return Ok(
State {
last_run: self.data.last_run,
last_edit: self.data.last_edit,
last_sync: self.data.last_sync,
}
);
}
fn set_state(&mut self, state: State) -> Result<Option<State>, MemoError> {
let old_state = Some(State{last_run: self.data.last_run, last_sync:self.data.last_sync});
let old_state = Some(State{last_edit: self.data.last_edit, last_sync:self.data.last_sync});
self.data.last_sync = state.last_sync;
self.data.last_run = state.last_run;
self.data.last_edit = state.last_edit;
self.save()?;
return Ok(old_state);
}
}
impl MemoStorage for JsonStorage {
fn all(&self, done: bool) -> Result<LinkedList<Memo>, MemoError> {
fn all(&self, done: bool) -> Result<Vec<Memo>, MemoError> {
let mut results_due : LinkedList<Memo> = LinkedList::new();
let mut results_not_due : LinkedList<Memo> = LinkedList::new();
for memo in &self.data.memo {
if done ^ memo.done.is_none() { // if done is true, has to not be none, if done is false, it has to be none
if memo.due.is_some() {
if memo.due.is_some() { // TODO I sort it afterwards this part is now useless
results_due.push_back((*memo).clone());
} else {
results_not_due.push_back((*memo).clone());
@ -115,9 +113,13 @@ impl MemoStorage for JsonStorage {
}
}
results_due.append(&mut results_not_due);
return Ok(results_due);
let mut out : Vec<Memo> = results_due.into_iter().collect();
out.sort();
return Ok(out);
}
fn insert(&mut self, m: Memo) -> Result<(), MemoError> {
@ -151,7 +153,7 @@ impl MemoStorage for JsonStorage {
}
}
if index >= 0 {
self.data.memo.remove(index as usize);
self.data.memo[index as usize].done = Some(Utc::now());
self.save()?;
}
return Ok(count);

View file

@ -30,13 +30,14 @@ impl SQLiteStorage {
id TEXT PRIMARY KEY,
body TEXT NOT NULL,
due DATETIME,
done DATETIME DEFAULT NULL
done DATETIME DEFAULT NULL,
last_edit DATETIME NOT NULL
);",
[],
)?;
connection.execute(
"CREATE TABLE IF NOT EXISTS state (
last_run DATETIME DEFAULT NULL,
last_edit DATETIME DEFAULT NULL,
last_sync DATETIME DEFAULT NULL
);",
[],
@ -85,7 +86,7 @@ impl StateStorage for SQLiteStorage {
fn get_state(&self) -> Result<State, MemoError> {
return Ok(self.conn.query_row("SELECT * FROM state", [], |row| {
return Ok(State {
last_run: row.get(0)?,
last_edit: row.get(0)?,
last_sync: row.get(1).ok(),
});
})?);
@ -95,15 +96,15 @@ impl StateStorage for SQLiteStorage {
let old_state = self.get_state().ok();
self.conn.execute("DELETE FROM state;", [])?;
self.conn.execute(
"INSERT INTO state (last_run, last_sync) VALUES (?, ?);",
params![state.last_run, state.last_sync],
"INSERT INTO state (last_edit, last_sync) VALUES (?, ?);",
params![state.last_edit, state.last_sync],
)?;
return Ok(old_state);
}
}
impl MemoStorage for SQLiteStorage {
fn all(&self, done: bool) -> Result<LinkedList<Memo>, MemoError> {
fn all(&self, done: bool) -> Result<Vec<Memo>, MemoError> {
let mut results_due : LinkedList<Memo> = LinkedList::new();
let mut results_not_due : LinkedList<Memo> = LinkedList::new();
let not_null = if done { "NOT" } else { "" };
@ -125,26 +126,27 @@ impl MemoStorage for SQLiteStorage {
body: row.get(1)?,
due: row.get(2).ok(),
done: row.get(3).ok(),
last_edit: row.get(4)?,
});
}
results_due.append(&mut results_not_due);
return Ok(results_due);
return Ok(results_due.into_iter().collect());
}
fn insert(&mut self, m: Memo) -> Result<(), MemoError> {
self.conn.execute(
"INSERT INTO memo (id, body, due, done) VALUES (?, ?, ?, ?)",
params![m.id.to_string(), m.body, m.due, m.done],
"INSERT INTO memo (id, body, due, done, last_edit) VALUES (?, ?, ?, ?, ?)",
params![m.id.to_string(), m.body, m.due, m.done, m.last_edit],
)?;
Ok(())
}
fn set(&mut self, m: &Memo) -> Result<usize, MemoError> {
return Ok(self.conn.execute(
"UPDATE memo SET body = ?, due = ?, done = ? WHERE id = ?",
params![m.body, m.due, m.done, m.id.to_string()],
"UPDATE memo SET body = ?, due = ?, done = ?, last_edit = ? WHERE id = ?",
params![m.body, m.due, m.done, m.last_edit, m.id.to_string()],
)?);
}
@ -165,6 +167,7 @@ impl MemoStorage for SQLiteStorage {
body: row.get(1)?,
due: row.get(2).ok(),
done: row.get(3).ok(),
last_edit: row.get(4)?,
});
},
)?);

View file

@ -2,7 +2,6 @@ use crate::storage::Memo;
use chrono::{Duration, Utc};
use regex::{Error, Regex};
use colored::Colorize;
use std::collections::LinkedList;
use std::path::PathBuf;
pub trait HumanDisplay {
@ -93,7 +92,7 @@ pub fn parse_human_duration(input: &str) -> Result<Duration, Error> {
return Ok(Duration::seconds(secs));
}
pub fn find_by_regex(re: Regex, memos: LinkedList<Memo>) -> Option<Memo> {
pub fn find_by_regex(re: Regex, memos: Vec<Memo>) -> Option<Memo> {
let mut found = false;
let mut out: Option<Memo> = None;
for memo in memos {