From ef90912bfa884d4f1a9fea0902c16797e8bd2b0c Mon Sep 17 00:00:00 2001 From: James Pace Date: Sat, 20 Jun 2026 09:05:28 -0400 Subject: [PATCH] Extract from limo mono repo commit 6d5297c814588061ba58ec4b40f5e377956b1fe5. --- .gitignore | 1 + Cargo.toml | 8 +++ README.md | 3 ++ src/lib.rs | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/node.rs | 49 +++++++++++++++++ 5 files changed, 209 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/lib.rs create mode 100644 src/node.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d88ebc0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "limbo_graph" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..08c307b --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# limbo_graph + +An implementation of a directed graph in Rust. diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4402b38 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,148 @@ +// +// Copyright 2023 James Pace +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// This Source Code Form is "Incompatible With Secondary Licenses", as +// defined by the Mozilla Public License, v. 2.0. +// +#![no_std] +extern crate alloc; +mod node; + +use alloc::string::{String, ToString}; +use alloc::vec; +use alloc::vec::Vec; +pub use node::*; + +/// A generic graph type holding values connected to other values. +/// Values can be added to the graph, but not removed. +pub struct Graph { + nodes: Vec>, +} + +impl Graph { + /// Make a new graph with a root node with value `root`. + pub fn new(root: NodeValueT) -> Self { + // Make root node from its value. + let root_node = Node::new(root, None); + // Make graph with root_node as the one value in the vec. + Graph { + nodes: vec![root_node], + } + } + + /// Add a child wth value `val` to the parent with Key `parent`. + /// If the parent key is not in the graph, returns an error. + /// Returns a result with the key of the new node or an error. + pub fn add(&mut self, val: NodeValueT, parent: Key) -> Result { + // Make sure parent is valid. + if parent >= self.nodes.len() { + return Err(GraphError::from_msg("Parent node not in graph.")); + } + // Add new node to graph, get it's key. + let new_node = Node::new(val, Some(parent)); + self.nodes.push(new_node.clone()); + let new_node_key = self.nodes.len() - 1; + // Add it's key to parent's children. + self.nodes[parent].add_child(new_node_key); + Ok(new_node_key) + } + + /// Get the value of key `key` if the key is valid. + pub fn value_of(&self, key: &Key) -> Result { + if key >= &self.nodes.len() { + return Err(GraphError::from_msg("Can't get value of invalid key.")); + } + Ok(self.nodes[*key].value()) + } + + /// Get the children (as a list of keys) of key `key` if the key is valid. + pub fn children_of(&self, key: &Key) -> Result, GraphError> { + if key >= &self.nodes.len() { + return Err(GraphError::from_msg("Can't get children of invalid key.")); + } + Ok(self.nodes[*key].children()) + } + + /// Get the parent of key `key` if the key is valid. + /// Will return None if the node at `key` as no parent (i.e. is the root node). + pub fn parent_of(&self, key: &Key) -> Result, GraphError> { + if key >= &self.nodes.len() { + return Err(GraphError::from_msg("Can't get parent of invalid key.")); + } + Ok(self.nodes[*key].parent()) + } + + /// Get the key for the root of the graph. + pub fn root_key(&self) -> Key { + // This is always 0. + 0 + } +} + +#[derive(Debug, Clone)] +pub struct GraphError { + pub msg: String, +} + +impl GraphError { + pub fn from_msg(msg: &str) -> Self { + GraphError { + msg: msg.to_string(), + } + } +} + +impl core::fmt::Display for GraphError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "Error manipulating graph: {}", self.msg) + } +} + +impl core::error::Error for GraphError {} + +#[cfg(test)] +mod tests { + use super::*; + use core::cmp::PartialEq; + + #[derive(Debug, Clone, PartialEq)] + struct NodeType { + pub x: f64, + } + impl NodeValue for NodeType {} + impl NodeType { + fn new(val: f64) -> Self { + NodeType { x: val } + } + } + + #[test] + fn node_manipulation() { + let mut node = Node::new(NodeType::new(1.0), None); + assert!(node.parent().is_none()); + assert!(node.value() == NodeType::new(1.0)); + let child_key: Key = 1; + node.add_child(child_key.clone()); + assert!(node.children().len() == 1); + assert!(node.children()[0] == child_key); + } + + #[test] + fn graph_manipulation() { + let root_val = NodeType::new(1.0); + let mut graph = Graph::new(root_val); + + let second_val = NodeType::new(2.0); + let second_add_res = graph.add(second_val, 0); + assert!(second_add_res.is_ok()); + assert!(second_add_res.unwrap() == 1); + + assert!(graph.value_of(&1).unwrap() == NodeType::new(2.0)); + assert!(graph.parent_of(&1).unwrap() == Some(0)); + assert!(graph.children_of(&1).unwrap().len() == 0); + } +} diff --git a/src/node.rs b/src/node.rs new file mode 100644 index 0000000..d5cddb6 --- /dev/null +++ b/src/node.rs @@ -0,0 +1,49 @@ +// +// Copyright 2023 James Pace +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// This Source Code Form is "Incompatible With Secondary Licenses", as +// defined by the Mozilla Public License, v. 2.0. +// +use alloc::vec; +use alloc::vec::Vec; + +pub type Key = usize; + +pub trait NodeValue: Clone {} + +#[derive(Debug, Clone)] +pub struct Node { + value: NodeValueT, + children: Vec, + parent: Option, +} + +impl Node { + pub fn new(value: NodeValueT, parent: Option) -> Self { + Node { + value: value, + children: vec![], + parent: parent, + } + } + + pub fn add_child(&mut self, child_key: Key) { + self.children.push(child_key); + } + + pub fn children(&self) -> Vec { + self.children.clone() + } + + pub fn parent(&self) -> Option { + self.parent.clone() + } + + pub fn value(&self) -> NodeValueT { + self.value.clone() + } +}