wayver's git archive


an obsidian renderer
git clone https://git.wayver.dev/sable

sable-canvas/src/canvas.rs@337ba67f65eaa17b44e371af7c0f0c761d6aa914

raw
Date Commit Message Author Files + -
2026-02-23 01:55 initial mvp wayverd 139 17808 0
...

1use std::{
2    collections::HashMap,
3    fmt::{Display, Formatter},
4    str::FromStr,
5};
6
7use serde::{Deserialize as _, Deserializer, Serialize as _, Serializer};
8
9use crate::{
10    EdgeId, NodeId,
11    edge::Edge,
12    id::EmptyId,
13    node::{GenericNodeInfo, Node},
14};
15
16/// Errors that can occur when loading/saving or modifing a [`Canvas`].
17#[derive(Debug, thiserror::Error)]
18pub enum CanvasError {
19    /// A [`Node`] with that ID already exists in the [`Canvas`].
20    #[error("Node {0} already exists")]
21    NodeExists(NodeId),
22    /// A [`Edge`] with that ID already exists in the [`Canvas`].
23    #[error("Edge {0} already exists")]
24    EdgeExists(EdgeId),
25    /// A [`Node`] does not exists with that ID and the [`Edge`] is unable to connect to it as a result.
26    #[error("Node {0} does not exist")]
27    NodeNotExists(NodeId),
28    /// Failed to parse the raw JSON to the [`Canvas`] struct.
29    #[error(transparent)]
30    ParseError(#[from] serde_json::Error),
31    /// The [`Node`] or [`Edge`] ID supplied is empty.
32    ///
33    /// This isn't *not* allowed by the spec, but there should never be a reason where this is the case.
34    #[error(transparent)]
35    EmptyId(#[from] EmptyId),
36}
37
38/// Main struct for the canvas
39#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
40pub struct Canvas {
41    #[serde(
42        serialize_with = "serialize_as_vec_node",
43        deserialize_with = "deserialize_as_map_node"
44    )]
45    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
46    nodes: HashMap<NodeId, Node>,
47    #[serde(
48        serialize_with = "serialize_as_vec_edge",
49        deserialize_with = "deserialize_as_map_edge"
50    )]
51    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
52    edges: HashMap<EdgeId, Edge>,
53}
54fn serialize_as_vec_node<S>(data: &HashMap<NodeId, Node>, serializer: S) -> Result<S::Ok, S::Error>
55where
56    S: Serializer,
57{
58    let vec: Vec<&Node> = data.values().collect();
59    vec.serialize(serializer)
60}
61
62fn deserialize_as_map_node<'de, D>(deserializer: D) -> Result<HashMap<NodeId, Node>, D::Error>
63where
64    D: Deserializer<'de>,
65{
66    let vec: Vec<Node> = Vec::deserialize(deserializer)?;
67    let map: HashMap<_, _> = vec
68        .into_iter()
69        .map(|node| (node.id().clone(), node))
70        .collect();
71    Ok(map)
72}
73
74fn serialize_as_vec_edge<S>(data: &HashMap<EdgeId, Edge>, serializer: S) -> Result<S::Ok, S::Error>
75where
76    S: Serializer,
77{
78    let vec: Vec<&Edge> = data.values().collect();
79    vec.serialize(serializer)
80}
81
82fn deserialize_as_map_edge<'de, D>(deserializer: D) -> Result<HashMap<EdgeId, Edge>, D::Error>
83where
84    D: Deserializer<'de>,
85{
86    let vec: Vec<Edge> = Vec::deserialize(deserializer)?;
87    let map: HashMap<_, _> = vec
88        .into_iter()
89        .map(|node| (node.id().clone(), node))
90        .collect();
91    Ok(map)
92}
93
94impl Canvas {
95    /// Add a node to the canvas.
96    ///
97    /// # Errors
98    ///
99    /// This is fail if the node already exists in the canvas.
100    pub fn add_node(&mut self, node: Node) -> Result<(), CanvasError> {
101        if self.nodes.contains_key(node.id()) {
102            return Err(CanvasError::NodeExists(node.id().clone()));
103        }
104        self.nodes.insert(node.id().clone(), node);
105        Ok(())
106    }
107
108    /// Add a edge to the canvas.
109    ///
110    /// # Errors
111    ///
112    /// This is fail if the edge already exists in the canvas,
113    /// or the start and end nodes don't exist.
114    pub fn add_edge(&mut self, edge: Edge) -> Result<(), CanvasError> {
115        if self.edges.contains_key(edge.id()) {
116            return Err(CanvasError::EdgeExists(edge.id().clone()));
117        }
118
119        if !self.nodes.contains_key(edge.from_node()) {
120            return Err(CanvasError::NodeNotExists(edge.from_node().clone()));
121        }
122
123        if !self.nodes.contains_key(edge.to_node()) {
124            return Err(CanvasError::NodeNotExists(edge.to_node().clone()));
125        }
126
127        self.edges.insert(edge.id().clone(), edge);
128        Ok(())
129    }
130
131    /// Returns a mutable reference of a specific [`Node`] from this canvas.
132    pub fn get_node(&mut self, id: &NodeId) -> Option<&mut Node> {
133        self.nodes.get_mut(id)
134    }
135
136    /// Returns a mutable reference of a specific [`Edge`] from this canvas.
137    pub fn get_edge(&mut self, id: &EdgeId) -> Option<&mut Edge> {
138        self.edges.get_mut(id)
139    }
140
141    /// Get a reference to all the [`Node`]s of this canvas.
142    #[must_use]
143    pub const fn get_nodes(&self) -> &HashMap<NodeId, Node> {
144        &self.nodes
145    }
146
147    /// Get a mutable reference to all the [`Node`]s of this canvas.
148    pub const fn get_mut_nodes(&mut self) -> &mut HashMap<NodeId, Node> {
149        &mut self.nodes
150    }
151
152    /// Get a reference to all the [`Edge`]s of this canvas.
153    #[must_use]
154    pub const fn get_edges(&self) -> &HashMap<EdgeId, Edge> {
155        &self.edges
156    }
157
158    /// Get a mutable reference to all the [`Edge`]s of this canvas.
159    pub const fn get_mut_edges(&mut self) -> &mut HashMap<EdgeId, Edge> {
160        &mut self.edges
161    }
162}
163
164impl FromStr for Canvas {
165    type Err = CanvasError;
166
167    fn from_str(s: &str) -> Result<Self, Self::Err> {
168        Ok(serde_json::from_str(s)?)
169    }
170}
171
172impl Display for Canvas {
173    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
174        write!(f, "{}", serde_json::to_string(self).unwrap())
175    }
176}
177