Note/Page reference relationships now built.
This commit is contained in:
parent
0f98dc4523
commit
739ff93427
|
@ -3,7 +3,10 @@ name = "nm-store-cli"
|
|||
version = "0.1.0"
|
||||
authors = ["Elf M. Sternberg <elf.sternberg@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "Command-line direct access to the notesmachine store."
|
||||
readme = "./README.org"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
|
|
|
@ -14,14 +14,10 @@ representations:
|
|||
|
||||
** Plans
|
||||
|
||||
*** TODO Make it possible to save a note
|
||||
*** TODO Make it possible to retrieve a note
|
||||
*** TODO Read how others use SQLX to initialize the database
|
||||
*** TODO Implement CLI features
|
||||
*** TODO Make it possible to connect two notes
|
||||
*** TODO Make it possible to save a page
|
||||
*** TODO Make it possible to connect a note to a page
|
||||
*** TODO Make it possible to retrieve a collection of notes
|
||||
*** TODO Make it possible to retrieve a page
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -84,6 +84,10 @@ fn build_page_titles(references: &Vec<String>) -> Vec<String> {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn build_references(content: &str) -> Vec<String> {
|
||||
build_page_titles(&find_links(content))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -122,4 +126,12 @@ Right? [[
|
|||
];
|
||||
assert!(res.iter().eq(expected.iter()), "{:?}", res);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doesnt_crash_on_empty() {
|
||||
let sample = "";
|
||||
let res = build_page_titles(&find_links(sample));
|
||||
let expected: Vec<String> = vec![];
|
||||
assert!(res.iter().eq(expected.iter()), "{:?}", res);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,6 +77,12 @@ pub(crate) struct JustId {
|
|||
pub id: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, FromRow)]
|
||||
pub(crate) struct PageTitles {
|
||||
pub id: i64,
|
||||
pub title: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, FromRow)]
|
||||
pub(crate) struct NoteRelationship {
|
||||
pub parent_id: i64,
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
use crate::errors::NoteStoreError;
|
||||
use crate::row_structs::{
|
||||
JustId, JustSlugs, NewNote, NewNoteBuilder, NewPage, NewPageBuilder, NoteRelationship, RawNote,
|
||||
PageTitles,
|
||||
RawPage,
|
||||
};
|
||||
use crate::reference_parser::build_references;
|
||||
use friendly_id;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use std::collections::HashSet;
|
||||
use shrinkwraprs::Shrinkwrap;
|
||||
use slug::slugify;
|
||||
use sqlx;
|
||||
|
@ -17,11 +20,14 @@ use std::collections::HashMap;
|
|||
use std::sync::Arc;
|
||||
|
||||
#[derive(Shrinkwrap, Copy, Clone)]
|
||||
struct ParentId(i64);
|
||||
struct PageId(i64);
|
||||
|
||||
#[derive(Shrinkwrap, Copy, Clone)]
|
||||
struct NoteId(i64);
|
||||
|
||||
#[derive(Shrinkwrap, Copy, Clone)]
|
||||
struct ParentId(i64);
|
||||
|
||||
/// A handle to our Sqlite database.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NoteStore(Arc<SqlitePool>);
|
||||
|
@ -108,11 +114,34 @@ impl NoteStore {
|
|||
) -> NoteResult<String> {
|
||||
let mut new_note = note.clone();
|
||||
new_note.uuid = friendly_id::create();
|
||||
let references = build_references(¬e.content);
|
||||
let mut tx = self.0.begin().await?;
|
||||
|
||||
// Start by building the note and putting it into its relationship.
|
||||
let parent_id = select_note_id_for_uuid(&mut tx, parent_note_uuid).await?;
|
||||
let new_note_id = insert_one_new_note(&mut tx, &new_note).await?;
|
||||
let _ = make_room_for_new_note(&mut tx, parent_id, position).await?;
|
||||
let _ = insert_note_note_relationship(&mut tx, parent_id, new_note_id, position).await?;
|
||||
|
||||
// From the references, make lists of pages that exist, and pages
|
||||
// that do not.
|
||||
let found_references = find_all_references_for(&mut tx, &references).await?;
|
||||
let new_references = diff_references(&references, &found_references);
|
||||
let mut known_reference_ids: Vec<PageId> = Vec::new();
|
||||
|
||||
// Create the pages that don't exist
|
||||
for one_reference in new_references.iter() {
|
||||
let new_root_note = create_unique_root_note();
|
||||
let new_root_note_id = insert_one_new_note(&mut tx, &new_root_note).await?;
|
||||
let new_page_slug = generate_slug(&mut tx, &one_reference).await?;
|
||||
let new_page = create_new_page_for(&one_reference, &new_page_slug, new_root_note_id);
|
||||
known_reference_ids.push(insert_one_new_page(&mut tx, &new_page).await?)
|
||||
};
|
||||
|
||||
// And associate the note with all the pages.
|
||||
known_reference_ids.append(&mut found_references.iter().map(|r| PageId(r.id)).collect());
|
||||
let _ = insert_note_page_references(&mut tx, new_note_id, &known_reference_ids).await?;
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(new_note.uuid)
|
||||
}
|
||||
|
@ -369,7 +398,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
async fn insert_one_new_page<'a, E>(executor: E, page: &NewPage) -> SqlResult<NoteId>
|
||||
async fn insert_one_new_page<'a, E>(executor: E, page: &NewPage) -> SqlResult<PageId>
|
||||
where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
|
@ -384,7 +413,7 @@ where
|
|||
"VALUES (?, ?, ?, ?, ?, ?);"
|
||||
);
|
||||
|
||||
Ok(NoteId(
|
||||
Ok(PageId(
|
||||
sqlx::query(insert_one_page_sql)
|
||||
.bind(&page.slug)
|
||||
.bind(&page.title)
|
||||
|
@ -495,6 +524,51 @@ where
|
|||
.map(|_| ())
|
||||
}
|
||||
|
||||
async fn find_all_references_for<'a, E>(
|
||||
executor: E,
|
||||
references: &Vec<String>,
|
||||
) -> SqlResult<Vec<PageTitles>>
|
||||
where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
let find_all_references_for_sql = "SELECT id, title FROM pages WHERE title IN ("
|
||||
.to_string() +
|
||||
&["?"].repeat(references.len()).join(",") +
|
||||
&");".to_string();
|
||||
|
||||
let mut request = sqlx::query_as(&find_all_references_for_sql);
|
||||
for id in references.iter() {
|
||||
request = request.bind(id);
|
||||
}
|
||||
request
|
||||
.fetch_all(executor)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn insert_note_page_references<'a, E>(
|
||||
executor: E,
|
||||
note_id: NoteId,
|
||||
references: &Vec<PageId>,
|
||||
) -> SqlResult<()>
|
||||
where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
let insert_note_page_references_sql =
|
||||
"INSERT INTO note_page_references (note_id, page_id) VALUES ".to_string() +
|
||||
&["(?, ?)"].repeat(references.len()).join(", ") +
|
||||
&";".to_string();
|
||||
|
||||
let mut request = sqlx::query(&insert_note_page_references_sql);
|
||||
for reference in references {
|
||||
request = request.bind(&*note_id).bind(**reference);
|
||||
}
|
||||
|
||||
request
|
||||
.execute(executor)
|
||||
.await
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
fn create_unique_root_note() -> NewNote {
|
||||
NewNoteBuilder::default()
|
||||
.uuid(friendly_id::create())
|
||||
|
@ -512,3 +586,13 @@ fn create_new_page_for(title: &str, slug: &str, note_id: NoteId) -> NewPage {
|
|||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
// Given the references supplied, and the references found in the datastore,
|
||||
// return a list of the references not found in the datastore.
|
||||
fn diff_references(references: &Vec<String>, found_references: &Vec<PageTitles>) -> Vec<String> {
|
||||
let all: HashSet<String> = references.iter().cloned().collect();
|
||||
let found: HashSet<String> = found_references.iter().map(|r| r.title.clone()).collect();
|
||||
all.difference(&found).cloned().collect()
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,31 +1,6 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{self, FromRow};
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, FromRow)]
|
||||
pub(crate) struct RawPage {
|
||||
pub id: i64,
|
||||
pub slug: String,
|
||||
pub title: String,
|
||||
pub note_id: i64,
|
||||
pub creation_date: DateTime<Utc>,
|
||||
pub updated_date: DateTime<Utc>,
|
||||
pub lastview_date: DateTime<Utc>,
|
||||
pub deleted_date: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, FromRow)]
|
||||
pub(crate) struct RawNote {
|
||||
pub id: i64,
|
||||
pub uuid: String,
|
||||
pub content: String,
|
||||
pub notetype: String,
|
||||
pub creation_date: DateTime<Utc>,
|
||||
pub updated_date: DateTime<Utc>,
|
||||
pub lastview_date: DateTime<Utc>,
|
||||
pub deleted_date: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
// // A Resource is either content or a URL to content that the
|
||||
// // user embeds in a note. TODO: I have no idea how to do this yet,
|
||||
// // but I'll figure it out.
|
||||
|
|
Loading…
Reference in New Issue