memo-cli/src/storage.rs

159 lines
3.6 KiB
Rust
Raw Normal View History

use chrono::{DateTime, Utc};
use rusqlite::{params, Connection, Error};
use std::fmt;
pub struct Memo {
pub id: u32,
pub body: String,
pub due: Option<DateTime<Utc>>,
}
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();
}
return write!(
f,
"Memo(id={id}, body={body}, due={due})",
id = self.id,
body = self.body,
due = due_str,
);
}
}
pub trait MemoStorage {
fn all(&self, done: bool) -> Result<Vec<Memo>, Error>;
fn add(&self, body: &str, due: Option<DateTime<Utc>>) -> Result<(), Error>;
2022-03-16 02:07:02 +01:00
fn set(&self, memo: &Memo) -> Result<bool, Error>;
fn del(&self, id: u32) -> Result<bool, Error>;
fn get(&self, id: u32) -> Result<Memo, Error>;
}
// SQLiteStorage
pub struct SQLiteStorage {
conn: Connection,
}
pub fn open_sqlite_storage(path: &str) -> Result<SQLiteStorage, Error> {
let connection = Connection::open(path)?;
// TODO check if table exist and is valid
connection.execute(
"CREATE TABLE IF NOT EXISTS memo (
id INTEGER PRIMARY KEY,
body TEXT NOT NULL,
due DATETIME,
2022-03-21 00:44:55 +01:00
done DATETIME DEFAULT NULL
);",
[],
)?;
return Ok(SQLiteStorage { conn: connection });
}
impl MemoStorage for SQLiteStorage {
fn all(&self, done: bool) -> Result<Vec<Memo>, Error> {
let mut results = Vec::new();
2022-03-21 00:44:55 +01:00
let not_null = if done { "NOT" } else { "" };
2022-03-14 14:14:14 +01:00
/*
* SQLite considers NULL as smallest value, so we will always get events with no due date
* first. To circumvent this, we first query all memos with a due date, and then all
* others. This is kinda jank but will do for now.
*/
{
let mut statement = self.conn.prepare(
2022-03-21 00:44:55 +01:00
format!(// TODO eww but I can't find a way to insert "NOT" with rusqlite
"SELECT * FROM memo WHERE due IS NOT NULL AND done IS {} NULL ORDER BY due, id",
not_null
)
.as_str(),
)?;
2022-03-21 00:44:55 +01:00
let mut rows = statement.query([])?;
2022-03-14 14:14:14 +01:00
while let Some(row) = rows.next()? {
results.push(Memo {
id: row.get(0)?,
body: row.get(1)?,
due: row.get(2)?,
});
}
}
{
2022-03-21 00:44:55 +01:00
let mut statement = self.conn.prepare(
format!(// TODO eww but I can't find a way to insert "NOT" with rusqlite
"SELECT * FROM memo WHERE due IS NULL AND done IS {} NULL ORDER BY due, id",
not_null
)
.as_str(),
)?;
let mut rows = statement.query([])?;
2022-03-14 14:14:14 +01:00
while let Some(row) = rows.next()? {
results.push(Memo {
id: row.get(0)?,
body: row.get(1)?,
due: row.get(2)?,
});
}
}
return Ok(results);
}
fn add(&self, body: &str, due: Option<DateTime<Utc>>) -> Result<(), Error> {
// TODO join these 2 ifs?
if due.is_some() {
self.conn.execute(
"INSERT INTO memo (body, due) VALUES (?, ?)",
params![body, due],
)?;
} else {
self.conn
.execute("INSERT INTO memo (body) VALUES (?)", params![body])?;
}
return Ok(());
}
2022-03-16 02:07:02 +01:00
fn set(&self, memo: &Memo) -> Result<bool, Error> {
let count = self.conn.execute(
"UPDATE memo SET body = ?, due = ? WHERE id = ?",
params![memo.body, memo.due, memo.id],
)?;
if count > 0 {
return Ok(true);
} else {
return Ok(false);
}
}
fn del(&self, id: u32) -> Result<bool, Error> {
let count = self
.conn
2022-03-21 00:44:55 +01:00
.execute("UPDATE memo SET done = ? WHERE id = ?", params![Utc::now(), id])?;
if count > 0 {
return Ok(true);
} else {
return Ok(false);
}
}
fn get(&self, id: u32) -> Result<Memo, Error> {
return Ok(self.conn.query_row(
"SELECT * FROM memo WHERE id = ? AND done = 0",
params![id],
|row| {
return Ok(Memo {
id: row.get(0)?,
body: row.get(1)?,
due: row.get(2).unwrap_or(None),
});
},
)?);
}
}