wip: ensure root always get's updated
This commit is contained in:
parent
0c369864d0
commit
780bb4e372
@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Reference {
|
pub struct Reference {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub content_address: String,
|
pub content_address: Option<String>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
pub dependents: Vec<Arc<Reference>>,
|
pub dependents: Vec<Arc<Reference>>,
|
||||||
@ -33,7 +33,7 @@ impl Reference {
|
|||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// A new Reference instance with no dependents
|
/// A new Reference instance with no dependents
|
||||||
pub fn new(content_address: String, name: String) -> Self {
|
pub fn new(content_address: Option<String>, name: String) -> Self {
|
||||||
// Calculate the reference_id from the content_address and path
|
// Calculate the reference_id from the content_address and path
|
||||||
let hasher = Self::initial_hash(&content_address, &name);
|
let hasher = Self::initial_hash(&content_address, &name);
|
||||||
let calculated_id = format!("{:x}", hasher.finalize());
|
let calculated_id = format!("{:x}", hasher.finalize());
|
||||||
@ -57,15 +57,16 @@ impl Reference {
|
|||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// The modified Reference with the new dependent added
|
/// The modified Reference with the new dependent added
|
||||||
pub fn add_dep(mut self, dep: Arc<Reference>) -> Self {
|
pub fn add_dep(&self, dep: Arc<Reference>) -> Self {
|
||||||
self.dependents.push(dep);
|
let mut cloned = self.clone();
|
||||||
|
cloned.dependents.push(dep);
|
||||||
// Recalculate the ID based on dependents, content_address, and path
|
// Recalculate the ID based on dependents, content_address, and path
|
||||||
let mut hasher = Self::initial_hash(&self.content_address, &self.name);
|
let mut hasher = Self::initial_hash(&self.content_address, &self.name);
|
||||||
for dependent in &self.dependents {
|
for dependent in &self.dependents {
|
||||||
hasher.update(&dependent.id);
|
hasher.update(&dependent.id);
|
||||||
}
|
}
|
||||||
self.id = format!("{:x}", hasher.finalize());
|
cloned.id = format!("{:x}", hasher.finalize());
|
||||||
self
|
cloned
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -91,9 +92,11 @@ impl Reference {
|
|||||||
return self.dependents.is_empty();
|
return self.dependents.is_empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initial_hash(content_address: &String, path: &String) -> Blake2b512 {
|
fn initial_hash(content_address: &Option<String>, path: &String) -> Blake2b512 {
|
||||||
let mut hasher = Blake2b512::new();
|
let mut hasher = Blake2b512::new();
|
||||||
hasher.update(content_address);
|
if let Some(content_address) = content_address {
|
||||||
|
hasher.update(content_address);
|
||||||
|
}
|
||||||
hasher.update(path);
|
hasher.update(path);
|
||||||
hasher
|
hasher
|
||||||
}
|
}
|
||||||
@ -105,18 +108,6 @@ pub struct Graph {
|
|||||||
pub objects: Arc<HashMap<String, Vec<u8>>>,
|
pub objects: Arc<HashMap<String, Vec<u8>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Graph {
|
|
||||||
/// Gets a reference by its path
|
|
||||||
pub fn get_reference(&self, path: &str) -> Option<Arc<Reference>> {
|
|
||||||
self.refs.get(path).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets an object by its content address
|
|
||||||
pub fn get_object(&self, content_address: &str) -> Option<&Vec<u8>> {
|
|
||||||
self.objects.get(content_address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_object() -> (String, Vec<u8>) {
|
pub fn random_object() -> (String, Vec<u8>) {
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
let random_size = rng.random_range(50..=4096);
|
let random_size = rng.random_range(50..=4096);
|
||||||
@ -132,12 +123,39 @@ pub fn random_object() -> (String, Vec<u8>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Graph {
|
impl Graph {
|
||||||
|
pub fn new(root: Arc<Reference>) -> Self {
|
||||||
|
let mut refs = HashMap::new();
|
||||||
|
refs.insert(root.name.clone(), root.clone());
|
||||||
|
let refs = Arc::new(refs);
|
||||||
|
let objects = Arc::new(HashMap::new());
|
||||||
|
Self {
|
||||||
|
root,
|
||||||
|
refs,
|
||||||
|
objects,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a reference by its path
|
||||||
|
pub fn get_reference(&self, path: &str) -> Option<Arc<Reference>> {
|
||||||
|
self.refs.get(path).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets an object by its content address
|
||||||
|
pub fn get_object(&self, content_address: &str) -> Option<&Vec<u8>> {
|
||||||
|
self.objects.get(content_address)
|
||||||
|
}
|
||||||
|
|
||||||
/// Updates a reference to point to a new object, recalculating content addresses and IDs
|
/// Updates a reference to point to a new object, recalculating content addresses and IDs
|
||||||
/// for all affected references in the graph.
|
/// for all affected references in the graph.
|
||||||
///
|
///
|
||||||
/// The reference ID is calculated from the content address, name, and any dependents,
|
/// The reference ID is calculated from the content address, name, and any dependents,
|
||||||
/// ensuring that it's truly content-addressable.
|
/// ensuring that it's truly content-addressable.
|
||||||
pub fn update_reference(&mut self, name: &String, new_content: Vec<u8>) -> Result<(), String> {
|
pub fn update_object_reference(&mut self, name: &String, new_content: Vec<u8>) -> Result<(), String> {
|
||||||
|
// Update the root reference if needed
|
||||||
|
if name == &self.root.name {
|
||||||
|
self.root = self.refs.get(name).unwrap().clone();
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
// Create a mutable copy of our maps
|
// Create a mutable copy of our maps
|
||||||
let mut refs = HashMap::new();
|
let mut refs = HashMap::new();
|
||||||
for (k, v) in self.refs.as_ref() {
|
for (k, v) in self.refs.as_ref() {
|
||||||
@ -160,7 +178,7 @@ impl Graph {
|
|||||||
// Create a new reference with the updated content address
|
// Create a new reference with the updated content address
|
||||||
// The ID will be calculated based on the content address, name, and dependents
|
// The ID will be calculated based on the content address, name, and dependents
|
||||||
let mut updated_ref = Reference::new(
|
let mut updated_ref = Reference::new(
|
||||||
new_address.clone(),
|
Some(new_address.clone()),
|
||||||
name.to_string()
|
name.to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -179,12 +197,7 @@ impl Graph {
|
|||||||
refs.insert(name.to_string(), updated_ref.clone());
|
refs.insert(name.to_string(), updated_ref.clone());
|
||||||
|
|
||||||
// Find and update all parent references that contain this reference
|
// Find and update all parent references that contain this reference
|
||||||
self.update_parent_references(&mut refs, name)?;
|
self.update_parent_references(&mut refs, name);
|
||||||
|
|
||||||
// Update the root reference if needed
|
|
||||||
if name == &self.root.name {
|
|
||||||
self.root = refs.get(name).unwrap().clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the Arc maps
|
// Update the Arc maps
|
||||||
self.refs = Arc::new(refs);
|
self.refs = Arc::new(refs);
|
||||||
@ -193,8 +206,10 @@ impl Graph {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO(jwall): Add new reference
|
||||||
|
|
||||||
/// Recursively updates parent references when a child reference changes
|
/// Recursively updates parent references when a child reference changes
|
||||||
fn update_parent_references(&self, refs: &mut HashMap<String, Arc<Reference>>, updated_name: &str) -> Result<(), String> {
|
fn update_parent_references(&mut self, refs: &mut HashMap<String, Arc<Reference>>, updated_name: &str) {
|
||||||
// Find all references that have the updated reference as a dependent
|
// Find all references that have the updated reference as a dependent
|
||||||
let parent_names: Vec<String> = refs
|
let parent_names: Vec<String> = refs
|
||||||
.iter()
|
.iter()
|
||||||
@ -212,6 +227,7 @@ impl Graph {
|
|||||||
|
|
||||||
// Add all dependents, replacing the updated one
|
// Add all dependents, replacing the updated one
|
||||||
for dep in &parent_ref.dependents {
|
for dep in &parent_ref.dependents {
|
||||||
|
// Update the root reference if needed
|
||||||
if dep.name == updated_name {
|
if dep.name == updated_name {
|
||||||
// Use the updated reference
|
// Use the updated reference
|
||||||
updated_parent = updated_parent.add_dep(refs.get(updated_name).unwrap().clone());
|
updated_parent = updated_parent.add_dep(refs.get(updated_name).unwrap().clone());
|
||||||
@ -223,16 +239,17 @@ impl Graph {
|
|||||||
|
|
||||||
// The ID is automatically calculated in the add_dep method
|
// The ID is automatically calculated in the add_dep method
|
||||||
let updated_parent = Arc::new(updated_parent);
|
let updated_parent = Arc::new(updated_parent);
|
||||||
|
if updated_parent.name == self.root.name {
|
||||||
|
self.root = updated_parent.clone();
|
||||||
|
}
|
||||||
|
|
||||||
// Update the references map
|
// Update the references map
|
||||||
refs.insert(parent_name.clone(), updated_parent);
|
refs.insert(parent_name.clone(), updated_parent);
|
||||||
|
|
||||||
// Recursively update parents of this parent
|
// Recursively update parents of this parent
|
||||||
self.update_parent_references(refs, &parent_name)?;
|
self.update_parent_references(refs, &parent_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn random_graph() -> Graph {
|
pub fn random_graph() -> Graph {
|
||||||
@ -242,7 +259,7 @@ impl Graph {
|
|||||||
|
|
||||||
// Create the root reference
|
// Create the root reference
|
||||||
let mut root_ref = Reference::new(
|
let mut root_ref = Reference::new(
|
||||||
String::from("root_content"),
|
Some(String::from("root_content")),
|
||||||
root_name.clone(),
|
root_name.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -250,7 +267,7 @@ impl Graph {
|
|||||||
for i in 1..=10 {
|
for i in 1..=10 {
|
||||||
let item_name = format!("/item/{}", i);
|
let item_name = format!("/item/{}", i);
|
||||||
let mut item_ref = Reference::new(
|
let mut item_ref = Reference::new(
|
||||||
format!("item_content_{}", i),
|
Some(format!("item_content_{}", i)),
|
||||||
item_name.clone(),
|
item_name.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -261,7 +278,7 @@ impl Graph {
|
|||||||
|
|
||||||
// Create a leaf reference
|
// Create a leaf reference
|
||||||
let leaf_ref = Reference::new(
|
let leaf_ref = Reference::new(
|
||||||
address.clone(),
|
Some(address.clone()),
|
||||||
subitem_name,
|
subitem_name,
|
||||||
).to_arc();
|
).to_arc();
|
||||||
|
|
||||||
|
@ -25,10 +25,10 @@ fn test_dependencies_updated_when_nodes_added() {
|
|||||||
let candidate = get_random_candidate(&graph);
|
let candidate = get_random_candidate(&graph);
|
||||||
|
|
||||||
// Update the leaf node
|
// Update the leaf node
|
||||||
graph.update_reference(&candidate.name, random_object().1).unwrap();
|
graph.update_object_reference(&candidate.name, random_object().1).unwrap();
|
||||||
|
|
||||||
// Verify that the leaf node's ID has changed
|
// Verify that the leaf node's ID has changed
|
||||||
let updated_leaf = graph.get_reference(&candidate.name).unwrap();
|
let updated_leaf = dbg!(graph.get_reference(&candidate.name).unwrap());
|
||||||
assert_ne!(updated_leaf.id, candidate.id,
|
assert_ne!(updated_leaf.id, candidate.id,
|
||||||
"Leaf node ID should change when content is updated");
|
"Leaf node ID should change when content is updated");
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ fn create_test_graph() -> Graph {
|
|||||||
|
|
||||||
// Create the root reference
|
// Create the root reference
|
||||||
let mut root_ref = Reference::new(
|
let mut root_ref = Reference::new(
|
||||||
String::from("root_content"),
|
Some(String::from("root_content")),
|
||||||
root_name.clone(),
|
root_name.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ fn create_test_graph() -> Graph {
|
|||||||
for i in 1..=3 {
|
for i in 1..=3 {
|
||||||
let item_name = format!("/item/{}", i);
|
let item_name = format!("/item/{}", i);
|
||||||
let mut item_ref = Reference::new(
|
let mut item_ref = Reference::new(
|
||||||
format!("item_content_{}", i),
|
Some(format!("item_content_{}", i)),
|
||||||
item_name.clone(),
|
item_name.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ fn create_test_graph() -> Graph {
|
|||||||
|
|
||||||
// Create a leaf reference
|
// Create a leaf reference
|
||||||
let leaf_ref = Reference::new(
|
let leaf_ref = Reference::new(
|
||||||
address.clone(),
|
Some(address.clone()),
|
||||||
subitem_name,
|
subitem_name,
|
||||||
).to_arc();
|
).to_arc();
|
||||||
|
|
||||||
@ -150,13 +150,13 @@ fn test_content_addressable_properties() {
|
|||||||
// Update a leaf node with the same content
|
// Update a leaf node with the same content
|
||||||
let leaf_path = "/item/1/subitem/1".to_string();
|
let leaf_path = "/item/1/subitem/1".to_string();
|
||||||
let initial_leaf = graph.get_reference(&leaf_path).unwrap();
|
let initial_leaf = graph.get_reference(&leaf_path).unwrap();
|
||||||
let content_address = initial_leaf.content_address.clone();
|
if let Some(content_address) = initial_leaf.content_address.clone() {
|
||||||
|
// Get the content for this address
|
||||||
|
let content = graph.get_object(&content_address).unwrap().clone();
|
||||||
|
|
||||||
// Get the content for this address
|
// Update with the same content
|
||||||
let content = graph.get_object(&content_address).unwrap().clone();
|
graph.update_object_reference(&leaf_path, content).unwrap();
|
||||||
|
}
|
||||||
// Update with the same content
|
|
||||||
graph.update_reference(&leaf_path, content).unwrap();
|
|
||||||
|
|
||||||
// Verify that nothing changed since the content is the same
|
// Verify that nothing changed since the content is the same
|
||||||
let updated_leaf = graph.get_reference(&leaf_path).unwrap();
|
let updated_leaf = graph.get_reference(&leaf_path).unwrap();
|
||||||
@ -173,7 +173,7 @@ fn test_id_calculation() {
|
|||||||
let leaf_path = "/item/1/subitem/1".to_string();
|
let leaf_path = "/item/1/subitem/1".to_string();
|
||||||
let initial_leaf = graph.get_reference(&leaf_path).unwrap();
|
let initial_leaf = graph.get_reference(&leaf_path).unwrap();
|
||||||
|
|
||||||
graph.update_reference(&leaf_path, "new content".as_bytes().to_vec()).unwrap();
|
graph.update_object_reference(&leaf_path, "new content".as_bytes().to_vec()).unwrap();
|
||||||
|
|
||||||
// Verify that the ID changed
|
// Verify that the ID changed
|
||||||
let updated_leaf = graph.get_reference(&leaf_path).unwrap();
|
let updated_leaf = graph.get_reference(&leaf_path).unwrap();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user