mirror of
https://git.alemi.dev/memo-cli.git
synced 2024-11-22 03:34:49 +01:00
chore: small fixes, refactored a little
This commit is contained in:
parent
03a958a23d
commit
408cb9fda1
10 changed files with 308 additions and 192 deletions
29
src/main.rs
29
src/main.rs
|
@ -1,6 +1,7 @@
|
|||
mod remote;
|
||||
mod storage;
|
||||
mod utils;
|
||||
mod model;
|
||||
|
||||
use chrono::{DateTime, Local, Utc};
|
||||
use clap::{Parser, Subcommand};
|
||||
|
@ -10,9 +11,13 @@ use git_version::git_version;
|
|||
use notify_rust::Notification;
|
||||
use regex::Regex;
|
||||
use remote::RemoteSync;
|
||||
use storage::{AuthStorage, Memo, MemoError, MemoStorage, SQLiteStorage, StateStorage, JsonStorage, SUPPORTED_FORMATS};
|
||||
use utils::{find_by_regex, find_db_file, parse_human_duration, HumanDisplay};
|
||||
use std::path::PathBuf;
|
||||
use storage::{
|
||||
AuthStorage, JsonStorage, MemoStorage, SQLiteStorage, StateStorage,
|
||||
SUPPORTED_FORMATS,
|
||||
};
|
||||
use model::memo::{Memo, MemoError};
|
||||
use utils::{find_by_regex, find_db_file, parse_human_duration, HumanDisplay};
|
||||
|
||||
const GIT_VERSION: &str = git_version!();
|
||||
const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
@ -75,11 +80,14 @@ fn main() {
|
|||
let mut db_path: PathBuf;
|
||||
let filename = args.name.unwrap_or("memostorage".to_string());
|
||||
|
||||
if let Some(db) = args.path { // if we are given a specific path, just use that
|
||||
if let Some(db) = args.path {
|
||||
// if we are given a specific path, just use that
|
||||
db_path = PathBuf::from(db);
|
||||
} else if let Some(path) = find_db_file(filename.as_str(), SUPPORTED_FORMATS) { // search up from cwd
|
||||
} else if let Some(path) = find_db_file(filename.as_str(), SUPPORTED_FORMATS) {
|
||||
// search up from cwd
|
||||
db_path = path;
|
||||
} else { // default path
|
||||
} else {
|
||||
// default path
|
||||
//TODO: a less "nix-centered" default fallback, possibly configurable
|
||||
//protip: cfg!(windows)/cfg!(unix)/cfg!(target_os = "macos") will give us the info we need
|
||||
db_path = dirs::home_dir().unwrap();
|
||||
|
@ -89,7 +97,6 @@ fn main() {
|
|||
|
||||
//TODO: permissions check, this will panic if it finds an existing db it can't write to
|
||||
|
||||
|
||||
if let Some(ext) = db_path.extension() {
|
||||
match ext.to_str().unwrap() {
|
||||
"json" => run_commands(JsonStorage::new(db_path).unwrap(), args2).unwrap(),
|
||||
|
@ -98,11 +105,13 @@ fn main() {
|
|||
}
|
||||
} else {
|
||||
println!("[!] no extension on db file");
|
||||
println!("something");
|
||||
}
|
||||
}
|
||||
|
||||
fn run_commands<T>(mut storage: T, args: Cli) -> Result<(), MemoError>
|
||||
where T : MemoStorage + AuthStorage + StateStorage + RemoteSync
|
||||
where
|
||||
T: MemoStorage + AuthStorage + StateStorage + RemoteSync,
|
||||
{
|
||||
if args.sync {
|
||||
if storage.get_key().is_err() {
|
||||
|
@ -148,6 +157,10 @@ fn run_commands<T>(mut storage: T, args: Cli) -> Result<(), MemoError>
|
|||
println!("[-] done task : {}", memo.body);
|
||||
} else if found {
|
||||
println!("[!] would remove multiple tasks");
|
||||
if let Some(other) = to_remove {
|
||||
println!(" ]> {}", other.body);
|
||||
}
|
||||
println!(" ]> {}", memo.body);
|
||||
to_remove = None;
|
||||
break;
|
||||
} else {
|
||||
|
@ -182,7 +195,7 @@ fn run_commands<T>(mut storage: T, args: Cli) -> Result<(), MemoError>
|
|||
m.due = Some(Utc::now() + parse_human_duration(d.as_str()).unwrap());
|
||||
}
|
||||
}
|
||||
storage.set(&m).unwrap();
|
||||
// storage.set(&m).unwrap(); // TODO fix editing memos
|
||||
storage.set_edit_time(Utc::now()).unwrap();
|
||||
println!(
|
||||
"{} updated memo\n{}",
|
||||
|
|
154
src/model/memo.rs
Normal file
154
src/model/memo.rs
Normal file
|
@ -0,0 +1,154 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[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>,
|
||||
}
|
||||
|
||||
impl Memo {
|
||||
pub fn new(body: String, due: Option<DateTime<Utc>>) -> Self {
|
||||
Memo { body, due, ..Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Memo {
|
||||
fn default() -> Self {
|
||||
Memo {
|
||||
id: Uuid::new_v4(),
|
||||
body: "".to_string(),
|
||||
due: None,
|
||||
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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut due_str = "null".to_string();
|
||||
if self.due.is_some() {
|
||||
due_str = self.due.unwrap().to_string();
|
||||
}
|
||||
let mut done_str = "null".to_string();
|
||||
if self.done.is_some() {
|
||||
done_str = self.done.unwrap().to_string();
|
||||
}
|
||||
return write!(
|
||||
f,
|
||||
"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,
|
||||
last_edit = self.last_edit.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===|| Memo Error
|
||||
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MemoError {
|
||||
pub cause: &'static str,
|
||||
pub sqlite: Option<rusqlite::Error>,
|
||||
pub io: Option<std::io::Error>,
|
||||
pub ureq: Option<ureq::Error>,
|
||||
}
|
||||
|
||||
impl ToString for MemoError {
|
||||
fn to_string(&self) -> String {
|
||||
format!(
|
||||
"MemoError(cause={cause}, sqlite={sqlite}, io={io}, ureq={ureq})",
|
||||
cause = self.cause,
|
||||
sqlite = if self.sqlite.is_some() {
|
||||
self.sqlite.as_ref().unwrap().to_string()
|
||||
} else {
|
||||
"null".to_string()
|
||||
},
|
||||
io = if self.io.is_some() {
|
||||
self.io.as_ref().unwrap().to_string()
|
||||
} else {
|
||||
"null".to_string()
|
||||
},
|
||||
ureq = if self.ureq.is_some() {
|
||||
self.ureq.as_ref().unwrap().to_string()
|
||||
} else {
|
||||
"null".to_string()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rusqlite::Error> for MemoError {
|
||||
fn from(e: rusqlite::Error) -> Self {
|
||||
Self {
|
||||
cause: "sqlite",
|
||||
sqlite: Some(e),
|
||||
io: None,
|
||||
ureq: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for MemoError {
|
||||
fn from(e: std::io::Error) -> Self {
|
||||
Self {
|
||||
cause: "io",
|
||||
sqlite: None,
|
||||
io: Some(e),
|
||||
ureq: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ureq::Error> for MemoError {
|
||||
fn from(e: ureq::Error) -> Self {
|
||||
Self {
|
||||
cause: "ureq",
|
||||
sqlite: None,
|
||||
io: None,
|
||||
ureq: Some(e),
|
||||
}
|
||||
}
|
||||
}
|
2
src/model/mod.rs
Normal file
2
src/model/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod memo;
|
||||
pub mod state;
|
19
src/model/state.rs
Normal file
19
src/model/state.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct State {
|
||||
pub last_edit: DateTime<Utc>,
|
||||
pub last_sync: Option<DateTime<Utc>>,
|
||||
pub last_memo: Option<Uuid>,
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> Self {
|
||||
State {
|
||||
last_edit: Utc::now(),
|
||||
last_sync: None,
|
||||
last_memo: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,13 @@
|
|||
use crate::storage::{MemoError, MemoStorage, AuthStorage};
|
||||
use crate::storage::{MemoStorage, AuthStorage};
|
||||
use crate::model::memo::MemoError;
|
||||
use std::collections::HashSet;
|
||||
use uuid::Uuid;
|
||||
use std::io::Read;
|
||||
// use openssl::aes::{AesKey, aes_ige};
|
||||
// use openssl::symm::Mode;
|
||||
|
||||
#[test]
|
||||
fn always_succeeds() { }
|
||||
|
||||
pub trait RemoteSync : AuthStorage+MemoStorage+Sized {
|
||||
fn serialize(&self) -> Result<Vec<u8>, MemoError>;
|
||||
|
@ -27,7 +33,11 @@ pub trait RemoteSync : AuthStorage+MemoStorage+Sized {
|
|||
.into_reader();
|
||||
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
// let mut encrypted : Vec<u8> = Vec::new();
|
||||
resp.read_to_end(&mut data)?;
|
||||
|
||||
// let key = AesKey::new_encrypt(self.get_key()?.as_bytes()).unwrap(); // TODO include into MemoError?
|
||||
// aes_ige(data.as_slice(), encrypted.as_slice(),
|
||||
// TODO decrypt
|
||||
|
||||
let other = Self::deserialize(data).unwrap();
|
||||
|
@ -49,7 +59,8 @@ pub trait RemoteSync : AuthStorage+MemoStorage+Sized {
|
|||
if memo_ids.contains(&memo.id) {
|
||||
let old_memo = self.get(&memo.id)?;
|
||||
if memo.last_edit > old_memo.last_edit {
|
||||
self.set(memo)?;
|
||||
// self.set(memo)?; // TODO fix merging memos!
|
||||
todo!()
|
||||
}
|
||||
} else {
|
||||
self.insert(memo.clone())?;
|
||||
|
|
160
src/storage.rs
160
src/storage.rs
|
@ -1,160 +0,0 @@
|
|||
pub mod sqlite;
|
||||
pub mod json;
|
||||
|
||||
pub use sqlite::SQLiteStorage;
|
||||
pub use json::JsonStorage;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::fmt;
|
||||
use uuid::Uuid;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
pub const SUPPORTED_FORMATS : &'static [&'static str] = &["db", "json"];
|
||||
|
||||
#[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>,
|
||||
}
|
||||
|
||||
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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut due_str = "null".to_string();
|
||||
if self.due.is_some() {
|
||||
due_str = self.due.unwrap().to_string();
|
||||
}
|
||||
let mut done_str = "null".to_string();
|
||||
if self.done.is_some() {
|
||||
done_str = self.done.unwrap().to_string();
|
||||
}
|
||||
return write!(
|
||||
f,
|
||||
"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,
|
||||
last_edit = self.last_edit.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MemoError {
|
||||
pub cause: &'static str,
|
||||
pub sqlite: Option<rusqlite::Error>,
|
||||
pub io: Option<std::io::Error>,
|
||||
pub ureq: Option<ureq::Error>,
|
||||
}
|
||||
|
||||
impl ToString for MemoError {
|
||||
fn to_string(&self) -> String {
|
||||
format!(
|
||||
"MemoError(cause={cause}, sqlite={sqlite}, io={io}, ureq={ureq})",
|
||||
cause=self.cause,
|
||||
sqlite= if self.sqlite.is_some() { self.sqlite.as_ref().unwrap().to_string() } else { "null".to_string() },
|
||||
io= if self.io.is_some() { self.io.as_ref().unwrap().to_string() } else { "null".to_string() },
|
||||
ureq= if self.ureq.is_some() { self.ureq.as_ref().unwrap().to_string() } else { "null".to_string() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rusqlite::Error> for MemoError {
|
||||
fn from(e: rusqlite::Error) -> Self { Self{cause: "sqlite", sqlite:Some(e), io:None, ureq:None} }
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for MemoError {
|
||||
fn from(e: std::io::Error) -> Self { Self{cause: "io", sqlite:None, io:Some(e), ureq:None} }
|
||||
}
|
||||
|
||||
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<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: String, due: Option<DateTime<Utc>>) -> Result<(), MemoError> {
|
||||
self.insert(Memo::new(body, due))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AuthStorage {
|
||||
fn get_hash(&self) -> Result<String, MemoError>;
|
||||
fn get_key(&self) -> Result<String, MemoError>;
|
||||
fn set_key(&mut self, key: &str) -> Result<Option<String>, MemoError>;
|
||||
}
|
||||
|
||||
pub trait StateStorage {
|
||||
fn set_state(&mut self, state: State) -> Result<Option<State>, MemoError>;
|
||||
fn get_state(&self) -> Result<State, MemoError>;
|
||||
|
||||
fn set_edit_time(&mut self, time: DateTime<Utc>) -> Result<(), MemoError> {
|
||||
let mut state = self.get_state().unwrap_or(State {
|
||||
last_edit: time,
|
||||
last_sync: None,
|
||||
}); // TODO jank way to not fail on 1st use
|
||||
state.last_edit = time;
|
||||
self.set_state(state)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_sync_time(&mut self, time: DateTime<Utc>) -> Result<(), MemoError> {
|
||||
let mut state = self.get_state()?;
|
||||
state.last_sync = Some(time);
|
||||
self.set_state(state)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -3,11 +3,12 @@ use std::io::BufReader;
|
|||
use std::path::PathBuf;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use uuid::Uuid;
|
||||
use std::io::Write;
|
||||
use sha2::{Digest, Sha512};
|
||||
use std::collections::LinkedList;
|
||||
|
||||
use crate::storage::{Memo, State, MemoError, AuthStorage, MemoStorage, StateStorage};
|
||||
use crate::storage::{Memo, State, MemoError, AuthStorage, MemoStorage, StateStorage, MemoSetField};
|
||||
use crate::remote::RemoteSync;
|
||||
|
||||
pub struct JsonStorage {
|
||||
|
@ -85,12 +86,13 @@ impl StateStorage for JsonStorage {
|
|||
State {
|
||||
last_edit: self.data.last_edit,
|
||||
last_sync: self.data.last_sync,
|
||||
last_memo: None, // TODO
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: State) -> Result<Option<State>, MemoError> {
|
||||
let old_state = Some(State{last_edit: self.data.last_edit, last_sync:self.data.last_sync});
|
||||
let old_state = Some(State{last_edit: self.data.last_edit, last_sync:self.data.last_sync, last_memo:None}); // TODO
|
||||
self.data.last_sync = state.last_sync;
|
||||
self.data.last_edit = state.last_edit;
|
||||
self.save()?;
|
||||
|
@ -128,19 +130,20 @@ impl MemoStorage for JsonStorage {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn set(&mut self, m: &Memo) -> Result<usize, MemoError> {
|
||||
let mut count = 0;
|
||||
for (_i, memo) in self.data.memo.iter().enumerate() {
|
||||
if memo.id == m.id {
|
||||
// TODO improve
|
||||
// self.data.memo[i].body = m.body;
|
||||
// self.data.memo[i].due = m.due;
|
||||
// self.data.memo[i].done = m.done;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
self.save()?;
|
||||
Ok(count)
|
||||
fn set(&mut self, id: &Uuid, field: MemoSetField) -> Result<usize, MemoError> {
|
||||
todo!()
|
||||
// let mut count = 0;
|
||||
// for (_i, memo) in self.data.memo.iter().enumerate() {
|
||||
// if memo.id == m.id {
|
||||
// // TODO improve
|
||||
// // self.data.memo[i].body = m.body;
|
||||
// // self.data.memo[i].due = m.due;
|
||||
// // self.data.memo[i].done = m.done;
|
||||
// count += 1;
|
||||
// }
|
||||
// }
|
||||
// self.save()?;
|
||||
// Ok(count)
|
||||
}
|
||||
|
||||
fn del(&mut self, id: &uuid::Uuid) -> Result<usize, MemoError> {
|
||||
|
|
55
src/storage/mod.rs
Normal file
55
src/storage/mod.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use uuid::Uuid;
|
||||
use crate::model::{memo::{Memo, MemoError}, state::State};
|
||||
|
||||
pub mod json;
|
||||
pub mod sqlite;
|
||||
|
||||
pub use json::JsonStorage;
|
||||
pub use sqlite::SQLiteStorage;
|
||||
|
||||
pub const SUPPORTED_FORMATS: &'static [&'static str] = &["db", "json"];
|
||||
|
||||
pub enum MemoSetField {
|
||||
Body(String),
|
||||
Due(Option<DateTime<Utc>>),
|
||||
Done(Option<DateTime<Utc>>),
|
||||
}
|
||||
|
||||
pub trait MemoStorage {
|
||||
fn all(&self, done: bool) -> Result<Vec<Memo>, MemoError>;
|
||||
fn get(&self, id: &Uuid) -> Result<Memo, MemoError>;
|
||||
fn del(&mut self, id: &Uuid) -> Result<usize, MemoError>;
|
||||
fn set(&mut self, id: &Uuid, field: MemoSetField) -> Result<usize, MemoError>;
|
||||
fn insert(&mut self, m: Memo) -> Result<(), MemoError>;
|
||||
|
||||
fn add(&mut self, body: String, due: Option<DateTime<Utc>>) -> Result<(), MemoError> {
|
||||
self.insert(Memo::new(body, due))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AuthStorage {
|
||||
fn get_hash(&self) -> Result<String, MemoError>;
|
||||
fn get_key(&self) -> Result<String, MemoError>;
|
||||
fn set_key(&mut self, key: &str) -> Result<Option<String>, MemoError>;
|
||||
}
|
||||
|
||||
pub trait StateStorage {
|
||||
fn set_state(&mut self, state: State) -> Result<Option<State>, MemoError>;
|
||||
fn get_state(&self) -> Result<State, MemoError>;
|
||||
|
||||
fn set_edit_time(&mut self, time: DateTime<Utc>) -> Result<(), MemoError> {
|
||||
let mut state = self.get_state()
|
||||
.unwrap_or(State { last_edit: time, ..Default::default() });
|
||||
state.last_edit = time;
|
||||
self.set_state(state)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_sync_time(&mut self, time: DateTime<Utc>) -> Result<(), MemoError> {
|
||||
let mut state = self.get_state()?;
|
||||
state.last_sync = Some(time);
|
||||
self.set_state(state)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@ use std::io::Write;
|
|||
use uuid::Uuid;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
use super::MemoSetField;
|
||||
|
||||
pub struct SQLiteStorage {
|
||||
conn: Connection,
|
||||
path: PathBuf,
|
||||
|
@ -68,7 +70,7 @@ impl AuthStorage for SQLiteStorage {
|
|||
})?);
|
||||
}
|
||||
|
||||
fn set_key(&mut self, key: &str) -> Result<Option<String>, MemoError> {
|
||||
fn set_key(&mut self, key: &str) -> Result<Option<String>, MemoError> { // TODO move the hashing itself to a default method
|
||||
let old_key = self.get_key().ok();
|
||||
let mut hasher = Sha512::new();
|
||||
hasher.update(key.as_bytes());
|
||||
|
@ -88,6 +90,7 @@ impl StateStorage for SQLiteStorage {
|
|||
return Ok(State {
|
||||
last_edit: row.get(0)?,
|
||||
last_sync: row.get(1).ok(),
|
||||
..Default::default()
|
||||
});
|
||||
})?);
|
||||
}
|
||||
|
@ -143,11 +146,27 @@ impl MemoStorage for SQLiteStorage {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn set(&mut self, m: &Memo) -> Result<usize, MemoError> {
|
||||
return Ok(self.conn.execute(
|
||||
"UPDATE memo SET body = ?, due = ?, done = ?, last_edit = ? WHERE id = ?",
|
||||
params![m.body, m.due, m.done, m.last_edit, m.id.to_string()],
|
||||
)?);
|
||||
fn set(&mut self, id: &Uuid, field: MemoSetField) -> Result<usize, MemoError> {
|
||||
match field {
|
||||
MemoSetField::Body(body) => {
|
||||
Ok(self.conn.execute(
|
||||
"UPDATE memo SET body = ?, last_edit = ? WHERE id = ?",
|
||||
params![body, Utc::now(), id.to_string()],
|
||||
)?)
|
||||
},
|
||||
MemoSetField::Due(due) => {
|
||||
Ok(self.conn.execute(
|
||||
"UPDATE memo SET due = ?, last_edit = ? WHERE id = ?",
|
||||
params![due, Utc::now(), id.to_string()]
|
||||
)?)
|
||||
},
|
||||
MemoSetField::Done(done) => {
|
||||
Ok(self.conn.execute(
|
||||
"UPDATE memo SET done = ?, last_edit = ? WHERE id = ?",
|
||||
params![done, Utc::now(), id.to_string()]
|
||||
)?)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn del(&mut self, id: &Uuid) -> Result<usize, MemoError> {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::storage::Memo;
|
||||
use crate::model::memo::Memo;
|
||||
use chrono::{Duration, Utc};
|
||||
use regex::{Error, Regex};
|
||||
use colored::Colorize;
|
||||
|
|
Loading…
Reference in a new issue