191 lines
5.6 KiB
Rust
191 lines
5.6 KiB
Rust
|
/async fn insert_note<'e, E>(executor: E, id: &str, content: &str, notetype: &str) -> SqlResult<i64>
|
||
|
where
|
||
|
E: 'e + Executor<'e, Database = Sqlite>,
|
||
|
{
|
||
|
lazy_static! {
|
||
|
static ref INSERT_ONE_NOTE_SQL: String = include_str!("sql/insert_one_note.sql");
|
||
|
}
|
||
|
let now = chrono::Utc::now();
|
||
|
Ok(sqlx::query(INSERT_ONE_NOTE_SQL)
|
||
|
.bind(&id)
|
||
|
.bind(&content)
|
||
|
.bind(¬etype)
|
||
|
.bind(&now)
|
||
|
.bind(&now)
|
||
|
.bind(&now)
|
||
|
.execute(executor)
|
||
|
.await?
|
||
|
.last_insert_rowid())
|
||
|
}
|
||
|
|
||
|
|
||
|
#[derive(Clone, FromRow)]
|
||
|
struct JustSlugs {
|
||
|
slug: String
|
||
|
}
|
||
|
|
||
|
|
||
|
// Given an initial string and an existing collection of slugs,
|
||
|
// generate a new slug that does not conflict with the current
|
||
|
// collection.
|
||
|
async fn generate_slug<'e, E>(executor: E, title: &str) -> SqlResult<String>
|
||
|
where
|
||
|
E: 'e + Executor<'e, Database = Sqlite>,
|
||
|
{
|
||
|
lazy_static! {
|
||
|
static ref RE_JUSTNUM: Regex = Regex::new(r"-\d+$").unwrap();
|
||
|
}
|
||
|
lazy_static! {
|
||
|
static ref RE_CAPNUM: Regex = Regex::new(r"-(\d+)$").unwrap();
|
||
|
}
|
||
|
|
||
|
let initial_slug = slugify::slugify(title);
|
||
|
let sample_slug = RE_JUSTNUM.replace_all(slug, "");
|
||
|
let similar_slugs: Vec<JustSlugs> = sqlx::query("SELECT slug FROM pages WHERE slug LIKE '?%';")
|
||
|
.bind(&sample_slug)
|
||
|
.execute(executor)
|
||
|
.await?;
|
||
|
let slug_counters = similar_slugs
|
||
|
.iter()
|
||
|
.map(|slug| RE_CAPNUM.captures(slug.slug))
|
||
|
.filter_map(|cap| cap.get(1).unwrap().parse::<u32>().unwrap())
|
||
|
.collect();
|
||
|
match slug_counters.len() {
|
||
|
0 => Ok(initial_slug),
|
||
|
_ => {
|
||
|
slug_counters.sort_unstable();
|
||
|
return Ok(format!("{}-{}", initial_slug, slug_counters.pop() + 1))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async fn insert_page<'e, E>(executor: E, page: &RawPage) -> SqlResult<i64>
|
||
|
where
|
||
|
E: 'e + Executor<'e, Database = Sqlite>,
|
||
|
{
|
||
|
let insert_one_page_sql = include_str!("sql/insert_one_page.sql");
|
||
|
Ok(sqlx::query(insert_one_page_sql)
|
||
|
.bind(&page.id)
|
||
|
.bind(&page.title)
|
||
|
.bind(&page.note_id)
|
||
|
.bind(&page.creation_date)
|
||
|
.bind(&page.updated_date)
|
||
|
.bind(&page.lastview_date)
|
||
|
.execute(&mut tx)
|
||
|
.await?
|
||
|
.last_insert_rowid())
|
||
|
}
|
||
|
|
||
|
/// Given a title, insert a new page. All dates are today, and the slug is
|
||
|
/// generated as above:
|
||
|
async fn insert_new_page_for_title<'e, E>(executor: E, title: &str) -> SqlResult<Page> {
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// /// Fetch page by title
|
||
|
// ///
|
||
|
// /// This is the most common use case, in which a specific title
|
||
|
// /// has been requested of the server via POST. The page always
|
||
|
// /// exists; if it doesn't, it will be automatically generated.
|
||
|
// pub async fn get_page_by_title(&slug, slug: &title) -> NoteResult<(Page, Notes)> {
|
||
|
// let mut tx = self.0.begin().await?;
|
||
|
// let maybe_page = sqlx::query_as(select_one_page_by_title)
|
||
|
// .bind(&title)
|
||
|
// .fetch_one(&tx)
|
||
|
// .await;
|
||
|
// let page = match maybe_page {
|
||
|
// Ok(page) => page,
|
||
|
// Err(sqlx::Error::NotFound) => insert_new_page_for_title(tx, title),
|
||
|
// Err(a) => return Err(a)
|
||
|
// };
|
||
|
// let notes = sqlx::query_as(select_note_collection_for_root)
|
||
|
// .bind(page.note_id)
|
||
|
// .fetch(&tx)
|
||
|
// .await?;
|
||
|
// tx.commit().await?;
|
||
|
// Ok((page, notes))
|
||
|
// }
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
// /// This will erase all the data in the database. Only use this
|
||
|
// /// if you're sure that's what you want.
|
||
|
// pub async fn reset_database(&self) -> NoteResult<()> {
|
||
|
// let initialize_sql = include_str!("sql/initialize_database.sql");
|
||
|
// sqlx::query(initialize_sql).execute(&*self.0).await?;
|
||
|
// Ok(())
|
||
|
// }
|
||
|
//
|
||
|
// async fn create_new_page(&self, title: &str) -> SqlResult<Page, Vec<Notes>> {
|
||
|
// let now = chrono::Utc::now();
|
||
|
// let new_note_id = friendly_id::create();
|
||
|
//
|
||
|
// let mut tx = self.0.begin().await?;
|
||
|
// let new_slug = generate_slug(&mut tx, title);
|
||
|
// let note_id = insert_note(&mut tx, &new_note_id, &"", &"page").await?;
|
||
|
// insert_page(&mut tx, NewPage {
|
||
|
// slug,
|
||
|
// title,
|
||
|
// note_id,
|
||
|
// creation_date: now,
|
||
|
// updated_date: now,
|
||
|
// lastview_date: now
|
||
|
// }).await;
|
||
|
// tx.commit();
|
||
|
// self.fetch_one_page(title)
|
||
|
// }
|
||
|
//
|
||
|
// async fn fetch_one_page(&self, title: &str) ->
|
||
|
//
|
||
|
// pub async fn fetch_page(&self, title: &str) -> SqlResult<(Page, Vec<Notes>)> {
|
||
|
// match self.fetch_one_page(title) {
|
||
|
// Ok((page, notes)) => Ok((page, notes)),
|
||
|
// Err(NotFound) => self.create_new_page(title),
|
||
|
// Err(e) => Err(e)
|
||
|
// }
|
||
|
// }
|
||
|
//
|
||
|
// pub async fn fetch_raw_page(&self, id: &str) -> SqlResult<RawPage> {
|
||
|
// let select_one_page_sql = include_str!("sql/select_one_page.sql");
|
||
|
// sqlx::query_as(select_one_page_sql).bind(&id).fetch_one(&*self.0).await
|
||
|
// }
|
||
|
//
|
||
|
// pub async fn fetch_raw_note(&self, id: &str) -> SqlResult<RawNote> {
|
||
|
// let select_one_note_sql = include_str!("sql/select_one_note.sql");
|
||
|
// 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> {
|
||
|
// insert_note(&*self.0, id, content, notetype).await
|
||
|
// }
|
||
|
//
|
||
|
// pub async fn update_raw_note(&self, id: &str, content: &str) -> NoteResult<()> {
|
||
|
// let update_one_note_sql = include_str!("sql/update_one_note.sql");
|
||
|
// let now = chrono::Utc::now();
|
||
|
// let rows_updated = sqlx::query(update_one_note_sql)
|
||
|
// .bind(&content)
|
||
|
// .bind(&now)
|
||
|
// .bind(&now)
|
||
|
// .bind(&id)
|
||
|
// .execute(&*self.0).await?
|
||
|
// .rows_affected();
|
||
|
// match rows_updated {
|
||
|
// 1 => Ok(()),
|
||
|
// _ => Err(NoteStoreError::NotFound)
|
||
|
// }
|
||
|
// }
|
||
|
//
|
||
|
// // 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
|
||
|
// // furiously to think.
|
||
|
//
|
||
|
|