I'm starting my first Rust project, a toy implementation of a PKM system backed by a SQLite database. The basic data model is a collection of pages which hold a tree of blocks which contain the text of my notes.
In the database, I have a blocks table, where each block has its content string, the key of the page it belongs to, a foreign key of its parent block if it has one, and a foreign key to its next sibling to represent the order of child blocks under a parent. While I'm not averse to changing this schema, I designed it this way so that as changes are made to the tree within the application, they can be reflected back to the database without much complex logic for updating a lot of different references. The database also has a pages table which stores a foreign key to the root block of the page.
With this structure I'm able to get the blocks for a page out of the database with a simple query, which are then stored internally as a Vec of BlockRow structs, containing the data from each row in simple datatypes. I then construct a page struct which takes ownership of the row and page data from the database and converts the Vec into a HashMap mapping block IDs to the BlockRow structs.
Finally I need to construct a general tree structure which will allow me to represent my notes in an outline form. Each block may have sub-blocks to an arbitrary depth, and each block is in a specific order relative to its siblings. To avoid borrow-checker issues I was running into initially, the tree itself will only contain block IDs, while the blocks themselves will remain fully owned by the aforementioned HashMap. With this in mind, the tree node struct looks like this:
pub struct Tree<T> {
/// This node's value
pub value: T,
/// The children of the current node.
pub children: Vec<Tree<T>>,
}
where T is i64. I know that my blocks contain all the information needed to build them into a tree structure, but between my shaky algorithm knowledge and being new to Rust, I'm stumped and need some direction.
My current partial implementation looks like this:
pub fn build_tree(&mut self) {
// create a mirror of the block_data map with block IDs to keep track of blocks
// which haven't yet been added to the tree
let mut remaining_blocks: Vec<i64> = self.block_data.keys().copied().collect();
let mut blocks_by_parent: HashMap<i64, Vec<i64>> = HashMap::new();
for (block_id, block) in self.block_data.iter() {
let parent_id = block.parent_id.unwrap_or(0);
if let None = blocks_by_parent.get_mut(&parent_id) {
blocks_by_parent.insert(parent_id, Vec::new());
}
blocks_by_parent.get_mut(&parent_id).unwrap().push(*block_id);
}
let mut tree: Tree<i64> = Tree {
value: 0,
children: blocks_by_parent.remove(&0).unwrap().iter().map(|block_id| {
Tree {
value: *block_id,
children: Vec::new()
}
}).collect()
};
let mut subtrees: HashMap<i64, Tree<i64>> = HashMap::new();
subtrees.insert(0, tree);
for (parent_id, child_id_vec) in blocks_by_parent.iter_mut() {
subtrees.insert(*parent_id, child_id_vec.iter().map(|child| {
Tree {
value: *child,
children: Vec::new()
}
}).collect());
}
}
but this only really does a first iteration, where each block that has any child blocks is turned into a Tree containing its own block ID and a vector of its child block IDs. the next step is to connect these subtrees together and so on, but I'm getting totally lost.
Any help or hints would be really appreciated, as getting the structure out of the database and into a useful representation in memory feels like the crux of my project but I'm feeling like I can't make progress. For full context, you can take a look at the project here on my GitHub.