Refinements to the tree structure.
This commit is contained in:
parent
4776541df4
commit
83d5858a45
|
@ -0,0 +1,2 @@
|
|||
[ ] Add RelationshipKind to Notes passed out
|
||||
[ ] Add KastenKind to Backreferences passed out
|
|
@ -6,7 +6,8 @@ mod structs;
|
|||
|
||||
pub use crate::errors::NoteStoreError;
|
||||
pub use crate::store::NoteStore;
|
||||
pub use crate::structs::{Note, NoteKind};
|
||||
pub use crate::structs::{Note, NoteKind, NoteRelationship, KastenRelationship};
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -81,22 +82,22 @@ mod tests {
|
|||
// <- 2 <- 4
|
||||
|
||||
let note1 = make_new_note("1");
|
||||
let note1_id = storagepool.add_note(¬e1, &root.id, 0).await;
|
||||
let note1_id = storagepool.add_note(¬e1, &root.id, Some(0)).await;
|
||||
assert!(note1_id.is_ok(), "{:?}", note1_id);
|
||||
let note1_id = note1_id.unwrap();
|
||||
|
||||
let note2 = make_new_note("2");
|
||||
let note2_id = storagepool.add_note(¬e2, &root.id, 0).await;
|
||||
let note2_id = storagepool.add_note(¬e2, &root.id, Some(0)).await;
|
||||
assert!(note2_id.is_ok(), "{:?}", note2_id);
|
||||
let note2_id = note2_id.unwrap();
|
||||
|
||||
let note3 = make_new_note("3");
|
||||
let note3_id = storagepool.add_note(¬e3, ¬e1_id, 0).await;
|
||||
let note3_id = storagepool.add_note(¬e3, ¬e1_id, Some(0)).await;
|
||||
assert!(note3_id.is_ok(), "{:?}", note3_id);
|
||||
let _note3_id = note3_id.unwrap();
|
||||
|
||||
let note4 = make_new_note("4");
|
||||
let note4_id = storagepool.add_note(¬e4, ¬e2_id, 0).await;
|
||||
let note4_id = storagepool.add_note(¬e4, ¬e2_id, Some(0)).await;
|
||||
assert!(note4_id.is_ok(), "{:?}", note4_id);
|
||||
let _note4_id = note4_id.unwrap();
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ CREATE TABLE notes (
|
|||
CREATE INDEX note_ids ON notes (id);
|
||||
|
||||
CREATE TABLE favorites (
|
||||
id TEXT NOT NULL,
|
||||
id TEXT NOT NULL UNIQUE,
|
||||
location INTEGER NOT NULL,
|
||||
FOREIGN KEY (id) REFERENCES notes (id) ON DELETE CASCADE
|
||||
);
|
||||
|
@ -33,6 +33,7 @@ CREATE TABLE note_relationships (
|
|||
-- If either note disappears, we want all the edges to disappear as well.
|
||||
FOREIGN KEY (note_id) REFERENCES notes (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (parent_id) REFERENCES notes (id) ON DELETE CASCADE,
|
||||
UNIQUE (note_id, parent_id),
|
||||
CHECK (note_id <> parent_id)
|
||||
);
|
||||
|
||||
|
@ -45,6 +46,7 @@ CREATE TABLE note_kasten_relationships (
|
|||
-- If either note disappears, we want all the edges to disappear as well.
|
||||
FOREIGN KEY (note_id) REFERENCES notes (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (kasten_id) REFERENCES notes (id) ON DELETE CASCADE,
|
||||
UNIQUE (note_id, kasten_id),
|
||||
CHECK (note_id <> kasten_id)
|
||||
);
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ impl NoteStore {
|
|||
Ok((vec![Note::from(zettlekasten)], vec![]))
|
||||
}
|
||||
|
||||
pub async fn add_note(&self, note: &NewNote, parent_id: &str, location: i64) -> NoteResult<String> {
|
||||
pub async fn add_note(&self, note: &NewNote, parent_id: &str, location: Option<i64>) -> NoteResult<String> {
|
||||
self.insert_note(
|
||||
note,
|
||||
&ParentId(parent_id.to_string()),
|
||||
|
@ -229,16 +229,18 @@ impl NoteStore {
|
|||
&self,
|
||||
note: &NewNote,
|
||||
parent_id: &ParentId,
|
||||
location: i64,
|
||||
location: Option<i64>,
|
||||
kind: RelationshipKind,
|
||||
) -> NoteResult<String> {
|
||||
if location < 0 {
|
||||
return Err(NoteStoreError::InvalidNoteStructure(
|
||||
"Add note: A negative position is not valid.".to_string(),
|
||||
));
|
||||
}
|
||||
if let Some(location) = location {
|
||||
if location < 0 {
|
||||
return Err(NoteStoreError::InvalidNoteStructure(
|
||||
"Add note: A negative location is not valid.".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if parent_id.is_empty() {
|
||||
if parent_id.is_empty() {
|
||||
return Err(NoteStoreError::InvalidNoteStructure(
|
||||
"Add note: A parent note ID is required.".to_string(),
|
||||
));
|
||||
|
@ -259,10 +261,14 @@ impl NoteStore {
|
|||
let references = build_references(¬e.content);
|
||||
|
||||
let mut tx = self.0.begin().await?;
|
||||
let location = cmp::min(
|
||||
assert_max_child_location_for_note(&mut tx, parent_id).await? + 1,
|
||||
location,
|
||||
);
|
||||
let location = {
|
||||
let max_child = assert_max_child_location_for_note(&mut tx, parent_id).await? + 1;
|
||||
if let Some(location) = location {
|
||||
cmp::min(max_child, location)
|
||||
} else {
|
||||
max_child
|
||||
}
|
||||
};
|
||||
|
||||
let note_id = NoteId(note.id.clone());
|
||||
insert_note(&mut tx, ¬e).await?;
|
||||
|
|
|
@ -198,7 +198,7 @@ pub(crate) struct NoteRelationshipRow {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct NoteRelationship {
|
||||
pub struct NoteRelationship {
|
||||
pub parent_id: String,
|
||||
pub note_id: String,
|
||||
pub location: i64,
|
||||
|
@ -224,7 +224,7 @@ pub(crate) struct KastenRelationshipRow {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct KastenRelationship {
|
||||
pub struct KastenRelationship {
|
||||
pub note_id: String,
|
||||
pub kasten_id: String,
|
||||
pub kind: KastenRelationshipKind,
|
||||
|
|
|
@ -13,28 +13,41 @@ mod make_tree;
|
|||
mod structs;
|
||||
|
||||
use nm_store::{NoteStore, NoteStoreError, NewNote};
|
||||
use crate::structs::Page;
|
||||
use crate::make_tree::make_tree;
|
||||
use crate::structs::{Page, Note};
|
||||
use crate::make_tree::{make_note_tree, make_backreferences};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Notesmachine(pub(crate) NoteStore);
|
||||
|
||||
type Result<T> = core::result::Result<T, NoteStoreError>;
|
||||
|
||||
pub fn make_page(foundtree: &Note, backreferences: Vec<Vec<Note>>) -> Page {
|
||||
Page {
|
||||
slug: foundtree.id,
|
||||
title: foundtree.content,
|
||||
creation_date: foundtree.creation_date,
|
||||
updated_date: foundtree.updated_date,
|
||||
lastview_date: foundtree.lastview_date,
|
||||
deleted_date: foundtree.deleted_date,
|
||||
notes: foundtree.children,
|
||||
backreferences: backreferences
|
||||
}
|
||||
}
|
||||
|
||||
impl Notesmachine {
|
||||
pub async fn new(url: &str) -> Result<Self> {
|
||||
let notestore = NoteStore::new(url).await?;
|
||||
Ok(Notesmachine(notestore))
|
||||
}
|
||||
|
||||
pub async fn get_box_via_slug(&self, slug: &str) -> Result<Page> {
|
||||
let (rawpage, rawnotes) = self.0.get_page_by_slug(slug).await?;
|
||||
Ok(make_tree(&rawpage, &rawnotes))
|
||||
pub async fn get_page_via_slug(&self, slug: &str) -> Result<Page> {
|
||||
let (rawtree, rawbackreferences) = self.0.get_kasten_by_slug(slug).await?;
|
||||
Ok(make_page(&make_note_tree(&rawtree), make_backreferences(&rawbackreferences)))
|
||||
}
|
||||
|
||||
pub async fn get_box(&self, title: &str) -> Result<Page> {
|
||||
let (rawpage, rawnotes) = self.0.get_page_by_title(title).await?;
|
||||
Ok(make_tree(&rawpage, &rawnotes))
|
||||
pub async fn get_page(&self, title: &str) -> Result<Page> {
|
||||
let (rawtree, rawbackreferences) = self.0.get_kasten_by_title(title).await?;
|
||||
Ok(make_page(&make_note_tree(&rawtree), make_backreferences(&rawbackreferences)))
|
||||
}
|
||||
|
||||
// TODO:
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
use crate::structs::{Note, Page};
|
||||
use nm_store::{RawNote, RawPage};
|
||||
use nm_store::NoteKind;
|
||||
|
||||
fn make_note_tree(rawnotes: &[RawNote], root: i64) -> Note {
|
||||
let the_note = rawnotes.iter().find(|note| note.id == root).unwrap().clone();
|
||||
fn make_note_tree_from(rawnotes: &[nm_store::Note], root_id: &str) -> Note {
|
||||
let the_note = {
|
||||
let foundroots: Vec<&nm_store::Note> = rawnotes.iter().filter(|note| note.id == root_id).collect();
|
||||
debug_assert!(foundroots.len() == 1);
|
||||
foundroots.iter().next().unwrap().clone()
|
||||
};
|
||||
|
||||
// The special case of the root node must be filtered out here to
|
||||
// prevent the first pass from smashing the stack in an infinite
|
||||
|
@ -11,35 +15,61 @@ fn make_note_tree(rawnotes: &[RawNote], root: i64) -> Note {
|
|||
// are faster.
|
||||
let mut children = rawnotes
|
||||
.iter()
|
||||
.filter(|note| note.parent_id == root && note.id != root)
|
||||
.map(|note| make_note_tree(rawnotes, note.id))
|
||||
.filter(|note| note.parent_id.is_some() && note.parent_id.unwrap() == root_id && note.id != the_note.id)
|
||||
.map(|note| make_note_tree_from(rawnotes, ¬e.id))
|
||||
.collect::<Vec<Note>>();
|
||||
children.sort_unstable_by(|a, b| a.position.cmp(&b.position));
|
||||
children.sort_unstable_by(|a, b| a.location.cmp(&b.location));
|
||||
|
||||
Note {
|
||||
uuid: the_note.uuid,
|
||||
parent_uuid: the_note.parent_uuid,
|
||||
id: the_note.id,
|
||||
parent_id: the_note.parent_id,
|
||||
content: the_note.content,
|
||||
notetype: the_note.notetype,
|
||||
position: the_note.position,
|
||||
kind: the_note.kind.to_string(),
|
||||
location: the_note.location,
|
||||
creation_date: the_note.creation_date,
|
||||
updated_date: the_note.updated_date,
|
||||
lastview_date: the_note.updated_date,
|
||||
deleted_date: the_note.deleted_date,
|
||||
children: vec![],
|
||||
children: children,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn make_tree(rawpage: &RawPage, rawnotes: &[RawNote]) -> Page {
|
||||
let the_page = rawpage.clone();
|
||||
pub(crate) fn make_note_tree(rawnotes: &[nm_store::Note]) -> Note {
|
||||
let the_root = {
|
||||
let foundroots: Vec<&nm_store::Note> = rawnotes.iter().filter(|note| note.kind == NoteKind::Kasten).collect();
|
||||
debug_assert!(foundroots.len() == 1);
|
||||
foundroots.iter().next().unwrap().clone()
|
||||
};
|
||||
make_note_tree_from(&rawnotes, &the_root.id)
|
||||
}
|
||||
|
||||
Page {
|
||||
slug: the_page.slug,
|
||||
title: the_page.title,
|
||||
creation_date: the_page.creation_date,
|
||||
updated_date: the_page.updated_date,
|
||||
lastview_date: the_page.updated_date,
|
||||
deleted_date: the_page.deleted_date,
|
||||
root_note: make_note_tree(rawnotes, rawpage.note_id),
|
||||
fn add_child(rawnotes: &[nm_store::Note], acc: &mut Vec<Note>, note_id: &str) -> Vec<Note> {
|
||||
let child = rawnotes
|
||||
.iter()
|
||||
.find(|note| note.parent_id.is_some() && note.parent_id.unwrap() == note_id);
|
||||
if let Some(c) = child {
|
||||
acc.push(Note {
|
||||
id: c.id,
|
||||
parent_id: Some(note_id.to_string()),
|
||||
content: c.content,
|
||||
kind: c.kind.to_string(),
|
||||
location: c.location,
|
||||
creation_date: c.creation_date,
|
||||
updated_date: c.updated_date,
|
||||
lastview_date: c.updated_date,
|
||||
deleted_date: c.deleted_date,
|
||||
children: vec![],
|
||||
});
|
||||
add_child(rawnotes, acc, &c.id)
|
||||
} else {
|
||||
acc.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn make_backreferences(rawnotes: &[nm_store::Note]) -> Vec<Vec<Note>> {
|
||||
rawnotes
|
||||
.iter()
|
||||
.filter(|note| note.parent_id.is_none() && note.kind == NoteKind::Kasten)
|
||||
.map(|root| add_child(rawnotes, &mut Vec::<Note>::new(), &root.id))
|
||||
.collect()
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct Note {
|
||||
pub uuid: String,
|
||||
pub parent_uuid: String,
|
||||
pub id: String,
|
||||
pub parent_id: Option<String>,
|
||||
pub content: String,
|
||||
pub position: i64,
|
||||
pub notetype: String,
|
||||
pub location: i64,
|
||||
pub kind: String,
|
||||
pub creation_date: DateTime<Utc>,
|
||||
pub updated_date: DateTime<Utc>,
|
||||
pub lastview_date: DateTime<Utc>,
|
||||
|
@ -15,7 +15,6 @@ pub struct Note {
|
|||
pub children: Vec<Note>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct Page {
|
||||
pub slug: String,
|
||||
pub title: String,
|
||||
|
@ -23,5 +22,6 @@ pub struct Page {
|
|||
pub updated_date: DateTime<Utc>,
|
||||
pub lastview_date: DateTime<Utc>,
|
||||
pub deleted_date: Option<DateTime<Utc>>,
|
||||
pub root_note: Note,
|
||||
pub notes: Vec<Note>,
|
||||
pub backreferences: Vec<Vec<Note>>,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue