Skip to main content

leptos_column_browser/topology/
node.rs

1use serde::{Deserialize, Serialize};
2
3use crate::topology::node_id::NodeId;
4
5/// Whether a node is a navigable container or a selectable leaf.
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "lowercase")]
8pub enum NodeKind {
9    /// A node that contains children (directory, schema, etc.).
10    Container,
11    /// A terminal node that can be opened for data exploration.
12    Leaf,
13}
14
15impl NodeKind {
16    /// Returns `true` if this is a leaf node.
17    pub fn is_leaf(&self) -> bool {
18        matches!(self, Self::Leaf)
19    }
20
21    /// Returns `true` if this is a container node.
22    pub fn is_container(&self) -> bool {
23        matches!(self, Self::Container)
24    }
25}
26
27/// Domain model for a node in the navigation tree.
28///
29/// `Node` is the rich domain type used server-side.
30/// Use `NodeView` for browser transfer.
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32pub struct Node {
33    /// Unique hierarchical identifier.
34    pub id: NodeId,
35    /// Human-readable display name.
36    pub label: String,
37    /// Consumer-defined type tag (e.g. `"folder"`, `"document"`, `"user"`).
38    pub node_type: String,
39    /// Whether the node is a container or leaf.
40    pub node_kind: NodeKind,
41    /// Immediate children (populated for in-memory trees).
42    pub children: Vec<Node>,
43}
44
45impl Node {
46    /// Construct a container node with no children.
47    pub fn container(id: NodeId, label: String, node_type: &str) -> Self {
48        Self {
49            id,
50            label,
51            node_type: node_type.to_owned(),
52            node_kind: NodeKind::Container,
53            children: Vec::new(),
54        }
55    }
56
57    /// Construct a leaf node.
58    pub fn leaf(id: NodeId, label: String, node_type: &str) -> Self {
59        Self {
60            id,
61            label,
62            node_type: node_type.to_owned(),
63            node_kind: NodeKind::Leaf,
64            children: Vec::new(),
65        }
66    }
67
68    /// Convenience: root-level container (wraps `container`).
69    pub fn root(id: NodeId, label: String, node_type: &str) -> Self {
70        Self::container(id, label, node_type)
71    }
72
73    /// Builder: add a child node and return `self`.
74    #[must_use]
75    pub fn with_child(mut self, child: Node) -> Self {
76        self.children.push(child);
77        self
78    }
79
80    /// Builder: add multiple children.
81    #[must_use]
82    pub fn with_children(mut self, children: Vec<Node>) -> Self {
83        self.children.extend(children);
84        self
85    }
86
87    /// Total node count in this subtree (self + all descendants).
88    pub fn count(&self) -> usize {
89        1 + self.children.iter().map(Node::count).sum::<usize>()
90    }
91
92    /// All leaf nodes in this subtree (depth-first).
93    pub fn leaf_nodes(&self) -> Vec<&Node> {
94        if self.node_kind.is_leaf() {
95            return vec![self];
96        }
97        self.children.iter().flat_map(Node::leaf_nodes).collect()
98    }
99
100    /// Find a node by `NodeId` in this subtree (depth-first).
101    pub fn find(&self, id: &NodeId) -> Option<&Node> {
102        if &self.id == id {
103            return Some(self);
104        }
105        self.children.iter().find_map(|c| c.find(id))
106    }
107
108    /// Convert to the wire-format view (non-recursive — children must be
109    /// fetched lazily via `TopologyProvider`).
110    pub fn to_view(&self) -> NodeView {
111        NodeView {
112            id: self.id.canonical(),
113            label: self.label.clone(),
114            node_type: self.node_type.clone(),
115            node_kind: match self.node_kind {
116                NodeKind::Container => "container".to_owned(),
117                NodeKind::Leaf => "leaf".to_owned(),
118            },
119            children: Vec::new(),
120        }
121    }
122}
123
124/// Flat, serialisable node representation for browser transfer.
125///
126/// Children are populated lazily by the UI — `Vec` is empty for most
127/// nodes and filled only when the UI requests children for a specific node.
128#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
129pub struct NodeView {
130    /// Canonical string form of `NodeId`.
131    pub id: String,
132    /// Human-readable display name.
133    pub label: String,
134    /// Consumer-defined type tag — matches `Node::node_type`.
135    pub node_type: String,
136    /// `"container"` or `"leaf"` — kept as string for easy JS interop.
137    pub node_kind: String,
138    /// Lazily populated child nodes.
139    pub children: Vec<NodeView>,
140}
141
142impl NodeView {
143    /// Returns `true` if this is a leaf node.
144    pub fn is_leaf(&self) -> bool {
145        self.node_kind == "leaf"
146    }
147
148    /// Returns `true` if this is a container node.
149    pub fn is_container(&self) -> bool {
150        self.node_kind == "container"
151    }
152}
153
154impl From<Node> for NodeView {
155    fn from(node: Node) -> Self {
156        node.to_view()
157    }
158}