mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
* Add graph-storage crate * Split graph-storage lib.rs into smaller modules * Make graph_craft/core_types dependency optional * Fix CRDT correctness issues flagged in graph-storage PR review * Treat trailing empty export slots as value-equal in Network::value_equal * Address graph-storage review: parameterize CrdtError, reject hot-log corruption, drop LamportClock Default * Address graph-storage review: fix root-delta history walk, validate cross-network refs, make compute_deltas deterministic, harden Priority, index nodes by network * Address graph-storage review: sort sources on deserialize, treat inputs_attributes length as structural, dedupe demo-artwork loader * Add opt-in Delta rev validation, dedup resource sources on deserialize, fix doc grammar * Persist scope injections through storage; harden gesture-end and embedded-source checks * Review * Review * Review * Use new types for NodeId and NetworkId * Adress minor review comments --------- Co-authored-by: Timon <me@timon.zip>
72 lines
2.5 KiB
Rust
72 lines
2.5 KiB
Rust
use crate::TimeStamp;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::BTreeMap;
|
|
|
|
/// Attribute keys. Glob-import (`use crate::attr::*`) at conversion sites.
|
|
///
|
|
/// `ui::*` keys are namespaced per CRDT design so each value gets its own LWW timestamp. Per-input
|
|
/// keys live on `Node.inputs_attributes[i]`; per-network keys live on `Network.attributes`.
|
|
pub mod attr;
|
|
|
|
/// A type-erased attribute value paired with the timestamp at which it was last set.
|
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
pub struct Value {
|
|
pub value: serde_json::Value,
|
|
pub timestamp: TimeStamp,
|
|
}
|
|
|
|
impl Value {
|
|
pub fn new(value: serde_json::Value, timestamp: TimeStamp) -> Self {
|
|
Self { value, timestamp }
|
|
}
|
|
}
|
|
|
|
pub type Attributes = BTreeMap<String, Value>;
|
|
|
|
/// Write helpers for `Attributes`.
|
|
pub trait AttributesWrite {
|
|
/// Inserts a JSON value under `key`.
|
|
fn set(&mut self, key: &str, value: serde_json::Value, timestamp: TimeStamp);
|
|
|
|
/// Serializes `value` and inserts it under `key`.
|
|
fn set_serialized<T: serde::Serialize>(&mut self, key: &str, value: &T, timestamp: TimeStamp) -> Result<(), serde_json::Error> {
|
|
self.set(key, serde_json::to_value(value)?, timestamp);
|
|
Ok(())
|
|
}
|
|
/// Inserts only when `value != default`, so the read side falls back to the same default.
|
|
fn set_if_not_default<T: serde::Serialize + PartialEq>(&mut self, key: &str, value: &T, default: &T, timestamp: TimeStamp) -> Result<(), serde_json::Error> {
|
|
if value != default {
|
|
self.set_serialized(key, value, timestamp)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl AttributesWrite for Attributes {
|
|
fn set(&mut self, key: &str, value: serde_json::Value, timestamp: TimeStamp) {
|
|
self.insert(key.to_string(), Value { value, timestamp });
|
|
}
|
|
}
|
|
|
|
/// Typed read helpers for `Attributes`.
|
|
pub trait AttributesRead {
|
|
/// Deserializes the value under `key`, or `None` if missing or undecodable.
|
|
fn get_typed<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T>;
|
|
|
|
/// Same as `get_typed`, falling back to `default`.
|
|
fn get_or<T: serde::de::DeserializeOwned>(&self, key: &str, default: T) -> T {
|
|
self.get_typed(key).unwrap_or(default)
|
|
}
|
|
|
|
/// Same as `get_typed`, falling back to `T::default()`.
|
|
fn get_or_default<T: serde::de::DeserializeOwned + Default>(&self, key: &str) -> T {
|
|
self.get_typed(key).unwrap_or_default()
|
|
}
|
|
}
|
|
|
|
impl AttributesRead for Attributes {
|
|
fn get_typed<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T> {
|
|
self.get(key).and_then(|v| serde_json::from_value(v.value.clone()).ok())
|
|
}
|
|
}
|