REFACTOR: Dry'ing out the insert_new_note feature.

Since both `insert_page` and `insert_note` need to insert a note,
having that code twice in the same block was annoying, especially
since discovering that my oh-so-clever use of `include_str!`
precludes me from using the `query!` macros, which want the strings
included *before* doing analysis.

All that wrestling with the Transaction type turned out to be much
simpler when I was able to just devolve it into an Executor.
This commit is contained in:
Elf M. Sternberg 2020-09-30 08:53:45 -07:00
parent 75809d821d
commit 1565fef001
2 changed files with 65 additions and 59 deletions

View File

@ -12,9 +12,9 @@ mod tests {
async fn fresh_inmemory_database() -> NoteStore { async fn fresh_inmemory_database() -> NoteStore {
let storagepool = NoteStore::new("sqlite://:memory:").await; let storagepool = NoteStore::new("sqlite://:memory:").await;
assert!(storagepool.is_ok(), "{:?}", storagepool); assert!(storagepool.is_ok(), "{:?}", storagepool);
let storagepool = storagepool.unwrap(); let storagepool = storagepool.unwrap();
let reset = storagepool.reset_database().await; let reset = storagepool.reset_database().await;
assert!(reset.is_ok(), "{:?}", reset); assert!(reset.is_ok(), "{:?}", reset);
storagepool storagepool
} }
@ -45,29 +45,29 @@ mod tests {
#[tokio::test(threaded_scheduler)] #[tokio::test(threaded_scheduler)]
async fn can_save_a_note() { async fn can_save_a_note() {
let storagepool = fresh_inmemory_database().await; let storagepool = fresh_inmemory_database().await;
let note_id = storagepool.insert_note("noteid", "notecontent", "note").await; let note_id = storagepool.insert_note("noteid", "notecontent", "note").await;
assert!(note_id.is_ok(), "{:?}", note_id); assert!(note_id.is_ok(), "{:?}", note_id);
let note_id = note_id.unwrap(); let note_id = note_id.unwrap();
assert!(note_id > 0); assert!(note_id > 0);
let foundnote = storagepool.fetch_raw_note("noteid").await; let foundnote = storagepool.fetch_raw_note("noteid").await;
assert!(foundnote.is_ok(), "{:?}", foundnote); assert!(foundnote.is_ok(), "{:?}", foundnote);
let foundnote = foundnote.unwrap(); let foundnote = foundnote.unwrap();
assert_eq!(foundnote.content, "notecontent"); assert_eq!(foundnote.content, "notecontent");
assert_eq!(foundnote.notetype, "note"); assert_eq!(foundnote.notetype, "note");
} }
#[tokio::test(threaded_scheduler)] #[tokio::test(threaded_scheduler)]
async fn can_save_a_page() { async fn can_save_a_page() {
let storagepool = fresh_inmemory_database().await; let storagepool = fresh_inmemory_database().await;
let page_id = storagepool.insert_page("pageid", "Test page").await; let page_id = storagepool.insert_page("pageid", "Test page").await;
assert!(page_id.is_ok(), "{:?}", page_id); assert!(page_id.is_ok(), "{:?}", page_id);
let page = storagepool.fetch_raw_page("pageid").await; let page = storagepool.fetch_raw_page("pageid").await;
assert!(page.is_ok(), "{:?}", page); assert!(page.is_ok(), "{:?}", page);
let page = page.unwrap(); let page = page.unwrap();
assert_eq!(page.title, "Test page"); assert_eq!(page.title, "Test page");
assert!(page.note_id > 0); assert!(page.note_id > 0);
} }
} }

View File

@ -1,9 +1,10 @@
use crate::errors::NoteStoreError; use crate::errors::NoteStoreError;
use crate::structs::{RawNote, RawPage}; use crate::structs::{RawNote, RawPage};
use friendly_id;
use chrono; use chrono;
use friendly_id;
use sqlx; use sqlx;
use sqlx::sqlite::SqlitePool; use sqlx::sqlite::SqlitePool;
use sqlx::{sqlite::Sqlite, Executor};
use std::sync::Arc; use std::sync::Arc;
/// A handle to our Sqlite database. /// A handle to our Sqlite database.
@ -13,6 +14,24 @@ pub struct NoteStore(Arc<SqlitePool>);
type NoteResult<T> = core::result::Result<T, NoteStoreError>; type NoteResult<T> = core::result::Result<T, NoteStoreError>;
type SqlResult<T> = sqlx::Result<T>; type SqlResult<T> = sqlx::Result<T>;
async fn insert_note<'e, E>(executor: E, id: &str, content: &str, notetype: &str) -> SqlResult<i64>
where
E: 'e + Executor<'e, Database = Sqlite>,
{
let insert_one_note_sql = include_str!("sql/insert_one_note.sql");
let now = chrono::Utc::now();
Ok(sqlx::query(insert_one_note_sql)
.bind(&id)
.bind(&content)
.bind(&notetype)
.bind(&now)
.bind(&now)
.bind(&now)
.execute(executor)
.await?
.last_insert_rowid())
}
impl NoteStore { impl NoteStore {
pub async fn new(url: &str) -> NoteResult<Self> { pub async fn new(url: &str) -> NoteResult<Self> {
let pool = SqlitePool::connect(url).await?; let pool = SqlitePool::connect(url).await?;
@ -37,47 +56,34 @@ impl NoteStore {
sqlx::query_as(select_one_note_sql).bind(&id).fetch_one(&*self.0).await sqlx::query_as(select_one_note_sql).bind(&id).fetch_one(&*self.0).await
} }
pub async fn insert_note(&self, id: &str, content: &str, notetype: &str) -> SqlResult<i64> { pub async fn insert_note(&self, id: &str, content: &str, notetype: &str) -> SqlResult<i64> {
let insert_one_note_sql = include_str!("sql/insert_one_note.sql"); insert_note(&*self.0, id, content, notetype).await
let now = chrono::Utc::now(); }
Ok(sqlx::query(insert_one_note_sql)
.bind(&id)
.bind(&content)
.bind(&notetype)
.bind(&now).bind(&now).bind(&now)
.execute(&*self.0).await?
.last_insert_rowid())
}
// TODO: We're returning the raw page with the raw note id, note // TODO: We're returning the raw page with the raw note id, note
// the friendly ID. Is there a disconnect there? It's making me // the friendly ID. Is there a disconnect there? It's making me
// furiously to think. // furiously to think.
pub async fn insert_page(&self, id: &str, title: &str) -> SqlResult<i64> { pub async fn insert_page(&self, id: &str, title: &str) -> SqlResult<i64> {
let insert_one_note_sql = include_str!("sql/insert_one_note.sql"); let insert_one_page_sql = include_str!("sql/insert_one_page.sql");
let insert_one_page_sql = include_str!("sql/insert_one_page.sql");
let new_note_id = friendly_id::create(); let new_note_id = friendly_id::create();
let now = chrono::Utc::now(); let now = chrono::Utc::now();
let mut tx = self.0.begin().await?; let mut tx = self.0.begin().await?;
let note_id = sqlx::query(insert_one_note_sql) let note_id = insert_note(&mut tx, &new_note_id, &"", &"page").await?;
.bind(&new_note_id)
.bind(&"")
.bind(&"page")
.bind(&now).bind(&now).bind(&now)
.execute(&mut tx).await?
.last_insert_rowid();
let page_id = sqlx::query(insert_one_page_sql) let page_id = sqlx::query(insert_one_page_sql)
.bind(&id) .bind(&id)
.bind(&title) .bind(&title)
.bind(&note_id) .bind(&note_id)
.bind(&now).bind(&now).bind(&now) .bind(&now)
.execute(&mut tx).await? .bind(&now)
.last_insert_rowid(); .bind(&now)
.execute(&mut tx)
tx.commit().await?; .await?
Ok(page_id) .last_insert_rowid();
}
tx.commit().await?;
Ok(page_id)
}
} }