/async fn insert_note<'e, E>(executor: E, id: &str, content: &str, notetype: &str) -> SqlResult 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 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 = 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::().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 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 { // /// 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> { // 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)> { // 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 { // 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 { // 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 { // 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. //