Move notes store to rust

This commit is contained in:
Joscha 2025-02-13 22:20:56 +01:00
parent 849400cf35
commit 75f3a84e5f
12 changed files with 617 additions and 138 deletions

View file

@ -0,0 +1,238 @@
use std::collections::{HashMap, HashSet};
use crate::{ids::NoteId, types::Note};
#[derive(Debug)]
pub struct NoteInfo {
pub text: String,
pub children: Vec<NoteId>,
}
/// A note store for testing.
#[derive(Default)]
pub struct Store {
last_id: u64,
curr_id: u64,
notes: HashMap<NoteId, NoteInfo>,
parents: HashMap<NoteId, HashMap<NoteId, usize>>,
}
impl Store {
pub fn new() -> Self {
Self::default()
}
pub fn needs_update(&self) -> Option<u64> {
if self.last_id != self.curr_id {
Some(self.curr_id)
} else {
None
}
}
pub fn update(&mut self) {
self.last_id = self.curr_id;
}
pub fn get(&self, id: NoteId) -> Option<Note> {
let info = self.notes.get(&id)?;
let parents = self
.parents
.get(&id)
.map(|ps| ps.keys().copied().collect::<HashSet<_>>())
.unwrap_or_default();
Some(Note {
id,
text: info.text.clone(),
children: info.children.clone(),
parents,
})
}
fn tick(&mut self) {
self.curr_id += 1;
}
fn update_parents(&mut self) {
self.parents.clear();
for (id, info) in &self.notes {
for child in &info.children {
*self
.parents
.entry(*child)
.or_default()
.entry(*id)
.or_default() += 1;
}
}
}
pub fn create(&mut self, text: String) -> NoteId {
let id = NoteId::new();
let info = NoteInfo {
text,
children: vec![],
};
self.notes.insert(id, info);
self.update_parents();
self.tick();
id
}
pub fn delete(&mut self, id: NoteId) -> Option<NoteInfo> {
let info = self.notes.remove(&id)?;
self.update_parents();
self.tick();
Some(info)
}
pub fn set_text(&mut self, id: NoteId, text: String) -> Option<()> {
let note = self.notes.get_mut(&id)?;
if note.text == text {
return None;
}
note.text = text;
self.tick();
Some(())
}
pub fn set_children(&mut self, id: NoteId, children: Vec<NoteId>) -> Option<()> {
let note = self.notes.get_mut(&id)?;
if note.children == children {
return None;
}
note.children = children;
self.tick();
Some(())
}
/// Find the index of a child based on its id and iteration.
///
/// The index returned is in the range `[0, note.children.len())`.
///
/// Iteration 0 refers to the first occurrence of the id, iteration 1 to the
/// second, and so on. Returns [`None`] if no such iteration was found.
fn resolve_child_iteration(
children: &[NoteId],
child_id: NoteId,
child_iteration: usize,
) -> Option<usize> {
children
.iter()
.enumerate()
.filter(|(_, it)| **it == child_id)
.map(|(i, _)| i)
.nth(child_iteration)
}
/// Find the index of a child based on its position.
///
/// The index returned is in the range `[0, note.children.len()]`.
///
/// # Example
///
/// A small example for a note with children `[a, b, c]`:
///
/// ```text
/// Child [ a, b, c] _
/// Position 0 1 2 3
/// Position -4 -3 -2 -1
/// ```
fn resolve_child_position(children: &[NoteId], child_position: isize) -> usize {
if child_position > 0 {
let child_position = child_position as usize;
child_position.min(children.len())
} else {
let child_position = (-child_position - 1) as usize;
children.len().saturating_sub(child_position)
}
}
/// Add a child at the specified position.
///
/// Returns `Some(())` if the operation was successful.
pub fn add_child_at_position(
&mut self,
id: NoteId,
child_id: NoteId,
child_position: isize,
) -> Option<()> {
let note = self.notes.get_mut(&id)?;
let index = Self::resolve_child_position(&note.children, child_position);
note.children.insert(index, child_id);
self.update_parents();
self.tick();
Some(())
}
/// Remove the specified iteration of a child.
///
/// Returns `Some(())` if the operation was successful.
pub fn remove_child_by_id(
&mut self,
id: NoteId,
child_id: NoteId,
child_iteration: usize,
) -> Option<()> {
let note = self.notes.get_mut(&id)?;
let index = Self::resolve_child_iteration(&note.children, child_id, child_iteration)?;
note.children.remove(index);
self.update_parents();
self.tick();
Some(())
}
/// A combination of [`Self::add_child_at_position`] and
/// [`Self::remove_child_by_id`].
///
/// Returns `Some(())` if the operation was successful.
pub fn move_child_by_id_to_position(
&mut self,
child_id: NoteId,
from_id: NoteId,
from_iteration: usize,
to_id: NoteId,
to_position: isize,
) -> Option<()> {
let from = self.get(from_id)?;
let to = self.get(to_id)?;
let from_idx = Self::resolve_child_iteration(&from.children, child_id, from_iteration)?;
let mut to_idx = Self::resolve_child_position(&to.children, to_position);
if from_id == to_id && from_idx < to_idx {
to_idx += 1;
}
let removed_id = self
.notes
.get_mut(&from_id)
.unwrap()
.children
.remove(from_idx);
assert!(removed_id == child_id);
self.notes
.get_mut(&to_id)
.unwrap()
.children
.insert(to_idx, child_id);
self.update_parents();
self.tick();
Some(())
}
pub fn clear(&mut self) {
self.notes.clear();
self.update_parents();
self.tick();
}
}