Working on the API that translates the simple vector into a
tree-like structure suitable to RESTification.
This commit is contained in:
parent
8c8352259a
commit
dd61f8c0c2
|
@ -6,6 +6,7 @@ mod structs;
|
|||
|
||||
pub use crate::errors::NoteStoreError;
|
||||
pub use crate::store::NoteStore;
|
||||
pub use crate::structs::{RawPage, RawNote, NewPage, NewNote};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
Cargo.lock
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "nm-trees"
|
||||
version = "0.1.0"
|
||||
authors = ["Elf M. Sternberg <elf.sternberg@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nm-store = { path = "../nm-store" }
|
||||
thiserror = "1.0.20"
|
||||
tokio = { version = "0.2.22", features = ["rt-threaded", "blocking"] }
|
||||
serde = { version = "1.0.116", features = ["derive"] }
|
||||
serde_json = "1.0.56"
|
||||
chrono = { version = "0.4.18", features = ["serde"] }
|
|
@ -0,0 +1,115 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
//! # Tree Layer
|
||||
//!
|
||||
//! This layer provides an interface between the storage layer and
|
||||
//! the outside world. It provides all of the basic logic, including
|
||||
//! the premise that a note without a parent is automatically
|
||||
//! made a child of the day's notepad.
|
||||
|
||||
mod make_tree;
|
||||
mod structs;
|
||||
|
||||
use nm_store::{NoteStore, NoteStoreError, NewNote};
|
||||
use crate::structs::Page;
|
||||
use crate::make_tree::make_tree;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Notesmachine(pub(crate) NoteStore);
|
||||
|
||||
type Result<T> = core::result::Result<T, NoteStoreError>;
|
||||
|
||||
impl Notesmachine {
|
||||
pub async fn new(url: &str) -> Result<Self> {
|
||||
let notestore = NoteStore::new(url).await?;
|
||||
Ok(Notesmachine(notestore))
|
||||
}
|
||||
|
||||
pub async fn navigate_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_box(&self, title: &str) -> Result<Page> {
|
||||
let (rawpage, rawnotes) = self.0.get_page_by_title(title).await?;
|
||||
Ok(make_tree(&rawpage, &rawnotes))
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// You should be able to:
|
||||
// Add a note that has no parent (gets added to "today")
|
||||
// Add a note that specifies only the page (gets added to page/root)
|
||||
// Add a note that has no position (gets tacked onto the end of the above)
|
||||
// Add a note that specifies the date of creation.
|
||||
pub async fn add_note(&self, note: &NewNote) -> Result<()> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub async fn add_note_to_page(&self, note: &NewNote) -> Result<()> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub async fn add_note_to_today(&self, note: &NewNote) -> Result<()> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub async fn reference_note(&self, note_id: &str, new_parent_id: &str, new_position: i64) -> Result<()> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub async fn embed_note(&self, note_id: &str, new_parent_id: &str, new_position: i64) -> Result<()> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub async fn move_note(&self, note_id: &str, old_parent_id: &str, new_parent_id: &str, position: i64) -> Result<()> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub async fn update_note(&self, note_id: &str, content: &str) -> Result<()> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub async fn delete_note(&self, note_id: &str) -> Result<()> {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tokio;
|
||||
|
||||
async fn fresh_inmemory_database() -> Notesmachine {
|
||||
let notesmachine = Notesmachine::new("sqlite://:memory:").await;
|
||||
assert!(notesmachine.is_ok(), "{:?}", notesmachine);
|
||||
let notesmachine = notesmachine.unwrap();
|
||||
let reset = notesmachine.0.reset_database().await;
|
||||
assert!(reset.is_ok(), "{:?}", reset);
|
||||
notesmachine
|
||||
}
|
||||
|
||||
#[tokio::test(threaded_scheduler)]
|
||||
async fn fetching_unfound_page_by_slug_works() {
|
||||
let notesmachine = fresh_inmemory_database().await;
|
||||
let unfoundpage = notesmachine.navigate_via_slug("nonexistent-slug").await;
|
||||
assert!(unfoundpage.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test(threaded_scheduler)]
|
||||
async fn fetching_unfound_page_by_title_works() {
|
||||
let title = "Nonexistent Page";
|
||||
let notesmachine = fresh_inmemory_database().await;
|
||||
let newpageresult = notesmachine.get_box(&title).await;
|
||||
assert!(newpageresult.is_ok(), "{:?}", newpageresult);
|
||||
|
||||
let newpage = newpageresult.unwrap();
|
||||
assert_eq!(newpage.title, title, "{:?}", newpage.title);
|
||||
assert_eq!(newpage.slug, "nonexistent-page", "{:?}", newpage.slug);
|
||||
assert_eq!(newpage.root_note.content, "", "{:?}", newpage.root_note.content);
|
||||
assert_eq!(newpage.root_note.notetype, "root", "{:?}", newpage.root_note.notetype);
|
||||
assert_eq!(newpage.root_note.children.len(), 0, "{:?}", newpage.root_note.children);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
use crate::structs::{Note, Page};
|
||||
use nm_store::{RawNote, RawPage};
|
||||
|
||||
fn make_note_tree(rawnotes: &[RawNote], root: i64) -> Note {
|
||||
let the_note = rawnotes.iter().find(|note| note.id == root).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
|
||||
// loop. The root node is identified by the type 'root' and
|
||||
// having its `id` and `parent_id` be equal. Numeric comparisons
|
||||
// are faster.
|
||||
let mut children = rawnotes
|
||||
.iter()
|
||||
.filter(|note| note.parent_id == root && note.id != root)
|
||||
.map(|note| make_note_tree(rawnotes, note.id))
|
||||
.collect::<Vec<Note>>();
|
||||
children.sort_unstable_by(|a, b| a.position.cmp(&b.position));
|
||||
|
||||
Note {
|
||||
uuid: the_note.uuid,
|
||||
parent_uuid: the_note.parent_uuid,
|
||||
content: the_note.content,
|
||||
notetype: the_note.notetype,
|
||||
position: the_note.position,
|
||||
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![],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn make_tree(rawpage: &RawPage, rawnotes: &[RawNote]) -> Page {
|
||||
let the_page = rawpage.clone();
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct Note {
|
||||
pub uuid: String,
|
||||
pub parent_uuid: String,
|
||||
pub content: String,
|
||||
pub position: i64,
|
||||
pub notetype: String,
|
||||
pub creation_date: DateTime<Utc>,
|
||||
pub updated_date: DateTime<Utc>,
|
||||
pub lastview_date: DateTime<Utc>,
|
||||
pub deleted_date: Option<DateTime<Utc>>,
|
||||
pub children: Vec<Note>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct Page {
|
||||
pub slug: String,
|
||||
pub title: String,
|
||||
pub creation_date: DateTime<Utc>,
|
||||
pub updated_date: DateTime<Utc>,
|
||||
pub lastview_date: DateTime<Utc>,
|
||||
pub deleted_date: Option<DateTime<Utc>>,
|
||||
pub root_note: Note,
|
||||
}
|
Loading…
Reference in New Issue