diff --git a/Cargo.lock b/Cargo.lock index 12af52b0c..e8eaa5fed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1757,14 +1757,6 @@ dependencies = [ "test-case", ] -[[package]] -name = "r3bl_rs_utils" -version = "0.9.16" -dependencies = [ - "pretty_assertions", - "r3bl_rs_utils_core", -] - [[package]] name = "r3bl_rs_utils_core" version = "0.9.16" diff --git a/Cargo.toml b/Cargo.toml index 57edc3de5..954fed28a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ members = [ "tuify", "macro", "tui", - "utils", "cmdr", "analytics_schema", "terminal_async", diff --git a/run b/run index 68a9c7b6a..0d8efbf61 100755 --- a/run +++ b/run @@ -29,7 +29,6 @@ let workspace_folders = [ "ansi_color", "macro", "analytics_schema", - "utils", ] # Main entry point for the script. diff --git a/utils/Cargo.toml b/utils/Cargo.toml deleted file mode 100644 index 1583a5170..000000000 --- a/utils/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "r3bl_rs_utils" -description = "Misc utility functions for r3bl-open-core repo" -# At most 5 keywords w/ no spaces, each has max length of 20 char. -keywords = ["non-binary-tree"] -# When you change this, make sure to update `README.md` as well. -version = "0.9.16" -edition = "2021" -resolver = "2" -readme = "README.md" # This is not included in cargo docs. -# Email address(es) has to be verified at https://crates.io/me/ -authors = [ - "Nazmul Idris ", - "Nadia Idris ", -] -repository = "https://github.com/r3bl-org/r3bl-rs-utils" -documentation = "https://docs.rs/r3bl_rs_utils" -homepage = "https://r3bl.com" -license = "Apache-2.0" - -[lib] -name = "r3bl_rs_utils" -path = "./src/lib.rs" - -# Documentation for crate layout. -# [General crate layout](https://stackoverflow.com/a/57767413/2085356) -# [Nesting crates for procedural macros](https://stackoverflow.com/a/64288799/2085356) - -[dependencies] -r3bl_rs_utils_core = { path = "../core", version = "0.9.16" } # version is requried to publish to crates.io - -[dev-dependencies] -# For assert_eq2! macro. -pretty_assertions = "1.4.0" diff --git a/utils/README.md b/utils/README.md deleted file mode 100644 index 954c4062d..000000000 --- a/utils/README.md +++ /dev/null @@ -1,97 +0,0 @@ -# r3bl_rs_utils - -## Why R3BL? - - - - - -R3BL -TUI -library -& -suite -of -apps -focused -on -developer -productivity - -We are working on building command line apps in Rust which have rich text user interfaces (TUI). -We want to lean into the terminal as a place of productivity, and build all kinds of awesome -apps for it. - -1. 🔮 Instead of just building one app, we are building a library to enable any kind of rich TUI - development w/ a twist: taking concepts that work really well for the frontend mobile and web - development world and re-imagining them for TUI & Rust. - - - Taking inspiration from things like [React](https://react.dev/), - [SolidJS](https://www.solidjs.com/), - [Elm](https://guide.elm-lang.org/architecture/), - [iced-rs](https://docs.rs/iced/latest/iced/), [Jetpack - Compose](https://developer.android.com/compose), - [JSX](https://ui.dev/imperative-vs-declarative-programming), - [CSS](https://www.w3.org/TR/CSS/#css), but making everything async (so they can - be run in parallel & concurrent via [Tokio](https://crates.io/crates/tokio)). - - Even the thread running the main event loop doesn't block since it is async. - - Using proc macros to create DSLs to implement something inspired by - [CSS](https://www.w3.org/TR/CSS/#css) & - [JSX](https://ui.dev/imperative-vs-declarative-programming). - -2. 🌎 We are building apps to enhance developer productivity & workflows. - - - The idea here is not to rebuild `tmux` in Rust (separate processes mux'd onto a - single terminal window). Rather it is to build a set of integrated "apps" (or - "tasks") that run in the same process that renders to one terminal window. - - Inside of this terminal window, we can implement things like "app" switching, - routing, tiling layout, stacking layout, etc. so that we can manage a lot of TUI - apps (which are tightly integrated) that are running in the same process, in the - same window. So you can imagine that all these "app"s have shared application - state. Each "app" may also have its own local application state. - - Here are some examples of the types of "app"s we plan to build (for which this - infrastructure acts as the open source engine): - 1. Multi user text editors w/ syntax highlighting. - 2. Integrations w/ github issues. - 3. Integrations w/ calendar, email, contacts APIs. - -All the crates in the `r3bl-open-core` -[repo](https://github.com/r3bl-org/r3bl-open-core/) provide lots of useful -functionality to help you build TUI (text user interface) apps, along w/ general -niceties & ergonomics that all Rustaceans 🦀 can enjoy 🎉. - -## Table of contents - - - -- [Introduction](#introduction) -- [Changelog](#changelog) -- [Learn how these crates are built, provide feedback](#learn-how-these-crates-are-built-provide-feedback) - -## Introduction - -This library contains some utilities that are useful for building TUI apps in Rust, -like memory arena for non-binary trees. - -There used to be a lot more in this library, but we have since moved them to separate -crates in the [`r3bl-open-core`](https://github.com/r3bl-org/r3bl-open-core/) repo. - -🤷‍♂️ Fun fact: before we built this crate, we built a library that is similar in -spirit for TypeScript (for TUI apps on Node.js) called -[r3bl-ts-utils](https://github.com/r3bl-org/r3bl-ts-utils/). We have long since -switched to Rust 🦀🎉. - -## Changelog - -Please check out the -[changelog](https://github.com/r3bl-org/r3bl-open-core/blob/main/CHANGELOG.md#r3bl_rs_utils) -to see how the library has evolved over time. - -## Learn how these crates are built, provide feedback - -To learn how we built this crate, please take a look at the following resources. -- If you like consuming video content, here's our [YT channel](https://www.youtube.com/@developerlifecom). Please consider [subscribing](https://www.youtube.com/channel/CHANNEL_ID?sub_confirmation=1). -- If you like consuming written content, here's our developer [site](https://developerlife.com/). Please consider subscribing to our [newsletter](https://developerlife.com/subscribe.html). -- If you have questions, please join our [discord server](https://discord.gg/8M2ePAevaM). - -License: Apache-2.0 diff --git a/utils/src/lib.rs b/utils/src/lib.rs deleted file mode 100644 index 806684f49..000000000 --- a/utils/src/lib.rs +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2022 R3BL LLC - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! # Why R3BL? -//! -//! -//! -//! -//! -//! R3BL -//! TUI -//! library -//! & -//! suite -//! of -//! apps -//! focused -//! on -//! developer -//! productivity -//! -//! We are working on building command line apps in Rust which have rich text user interfaces (TUI). -//! We want to lean into the terminal as a place of productivity, and build all kinds of awesome -//! apps for it. -//! -//! 1. 🔮 Instead of just building one app, we are building a library to enable any kind of rich TUI -//! development w/ a twist: taking concepts that work really well for the frontend mobile and web -//! development world and re-imagining them for TUI & Rust. -//! -//! - Taking inspiration from things like [React](https://react.dev/), -//! [SolidJS](https://www.solidjs.com/), -//! [Elm](https://guide.elm-lang.org/architecture/), -//! [iced-rs](https://docs.rs/iced/latest/iced/), [Jetpack -//! Compose](https://developer.android.com/compose), -//! [JSX](https://ui.dev/imperative-vs-declarative-programming), -//! [CSS](https://www.w3.org/TR/CSS/#css), but making everything async (so they can -//! be run in parallel & concurrent via [Tokio](https://crates.io/crates/tokio)). -//! - Even the thread running the main event loop doesn't block since it is async. -//! - Using proc macros to create DSLs to implement something inspired by -//! [CSS](https://www.w3.org/TR/CSS/#css) & -//! [JSX](https://ui.dev/imperative-vs-declarative-programming). -//! -//! 2. 🌎 We are building apps to enhance developer productivity & workflows. -//! -//! - The idea here is not to rebuild `tmux` in Rust (separate processes mux'd onto a -//! single terminal window). Rather it is to build a set of integrated "apps" (or -//! "tasks") that run in the same process that renders to one terminal window. -//! - Inside of this terminal window, we can implement things like "app" switching, -//! routing, tiling layout, stacking layout, etc. so that we can manage a lot of TUI -//! apps (which are tightly integrated) that are running in the same process, in the -//! same window. So you can imagine that all these "app"s have shared application -//! state. Each "app" may also have its own local application state. -//! - Here are some examples of the types of "app"s we plan to build (for which this -//! infrastructure acts as the open source engine): -//! 1. Multi user text editors w/ syntax highlighting. -//! 2. Integrations w/ github issues. -//! 3. Integrations w/ calendar, email, contacts APIs. -//! -//! All the crates in the `r3bl-open-core` -//! [repo](https://github.com/r3bl-org/r3bl-open-core/) provide lots of useful -//! functionality to help you build TUI (text user interface) apps, along w/ general -//! niceties & ergonomics that all Rustaceans 🦀 can enjoy 🎉. -//! -//! # Table of contents -//! -//! -//! -//! - [Introduction](#introduction) -//! - [Changelog](#changelog) -//! - [Learn how these crates are built, provide feedback](#learn-how-these-crates-are-built-provide-feedback) -//! -//! # Introduction -//! -//! This library contains some utilities that are useful for building TUI apps in Rust, -//! like memory arena for non-binary trees. -//! -//! There used to be a lot more in this library, but we have since moved them to separate -//! crates in the [`r3bl-open-core`](https://github.com/r3bl-org/r3bl-open-core/) repo. -//! -//! 🤷‍♂️ Fun fact: before we built this crate, we built a library that is similar in -//! spirit for TypeScript (for TUI apps on Node.js) called -//! [r3bl-ts-utils](https://github.com/r3bl-org/r3bl-ts-utils/). We have long since -//! switched to Rust 🦀🎉. -//! -//! # Changelog -//! -//! Please check out the -//! [changelog](https://github.com/r3bl-org/r3bl-open-core/blob/main/CHANGELOG.md#r3bl_rs_utils) -//! to see how the library has evolved over time. -//! -//! # Learn how these crates are built, provide feedback -//! -//! To learn how we built this crate, please take a look at the following resources. -//! - If you like consuming video content, here's our [YT channel](https://www.youtube.com/@developerlifecom). Please consider [subscribing](https://www.youtube.com/channel/CHANNEL_ID?sub_confirmation=1). -//! - If you like consuming written content, here's our developer [site](https://developerlife.com/). Please consider subscribing to our [newsletter](https://developerlife.com/subscribe.html). -//! - If you have questions, please join our [discord server](https://discord.gg/8M2ePAevaM). - -// https://github.com/rust-lang/rust-clippy -// https://rust-lang.github.io/rust-clippy/master/index.html -#![warn(clippy::all)] -#![warn(clippy::unwrap_in_result)] -#![warn(rust_2018_idioms)] - -// Attach the following files to the library module. -pub mod tree_memory_arena; -pub mod utils; - -// Re-export from core and macro (so users of public crate can use them w/out having to -// add dependency on each core and macro). -pub use tree_memory_arena::*; -pub use utils::*; diff --git a/utils/src/tree_memory_arena/arena.rs b/utils/src/tree_memory_arena/arena.rs deleted file mode 100644 index e544ccb7f..000000000 --- a/utils/src/tree_memory_arena/arena.rs +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright (c) 2022 R3BL LLC - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! [`Arena`] is defined here. - -use std::{collections::{HashMap, VecDeque}, - fmt::Debug, - sync::{atomic::AtomicUsize, Arc, RwLock}}; - -use super::{arena_types::HasId, - ArenaMap, - FilterFn, - NodeRef, - ResultUidList, - WeakNodeRef}; -use crate::utils::{call_if_some, - unwrap_arc_read_lock_and_call, - unwrap_arc_write_lock_and_call, - with_mut}; - -/// This struct represents a node in a tree. -/// -/// - It may have a parent. -/// - It can hold multiple children. -/// - And it has a payload. -/// - It also has an id that uniquely identifies it. An [`Arena`] or [`super::MTArena`] is -/// used to hold nodes. -#[derive(Debug)] -pub struct Node -where - T: Debug + Clone + Send + Sync, -{ - pub id: usize, - pub parent_id: Option, - pub children_ids: VecDeque, - pub payload: T, -} - -impl HasId for Node -where - T: Debug + Clone + Send + Sync, -{ - type IdType = usize; - - /// Delegate this to `self.id`, which is type `usize`. - fn get_id(&self) -> usize { self.id.get_id() } -} - -/// Data structure to store & manipulate a (non-binary) tree of data in memory. -/// -/// It can be used as the basis to implement a plethora of different data structures. The -/// non-binary tree is just one example of what can be built using the underlying code. -/// -/// 1. [Wikipedia definition of memory -/// arena](https://en.wikipedia.org/wiki/Region-based_memory_management) -/// 2. You can learn more about how this library was built from this [developerlife.com -/// article](https://developerlife.com/2022/02/24/rust-non-binary-tree/). -/// -/// # Basic usage -/// -/// ```rust -/// use r3bl_rs_utils::tree_memory_arena::{Arena, HasId, MTArena, ResultUidList}; -/// use r3bl_rs_utils_core::{style_primary, style_prompt}; -/// -/// let mut arena = Arena::::new(); -/// let node_1_value = 42 as usize; -/// let node_1_id = arena.add_new_node(node_1_value, None); -/// println!("{} {:#?}", style_primary("node_1_id"), node_1_id); -/// assert_eq!(node_1_id, 0); -/// ``` -/// -/// # Get weak and strong references from the arena (tree), and tree walking -/// -/// ```rust -/// use r3bl_rs_utils::tree_memory_arena::{Arena, HasId, MTArena, ResultUidList}; -/// use r3bl_rs_utils_core::{style_primary, style_prompt}; -/// -/// let mut arena = Arena::::new(); -/// let node_1_value = 42 as usize; -/// let node_1_id = arena.add_new_node(node_1_value, None); -/// -/// { -/// assert!(arena.get_node_arc(node_1_id).is_some()); -/// let node_1_ref = dbg!(arena.get_node_arc(node_1_id).unwrap()); -/// let node_1_ref_weak = arena.get_node_arc_weak(node_1_id).unwrap(); -/// assert_eq!(node_1_ref.read().unwrap().payload, node_1_value); -/// assert_eq!( -/// node_1_ref_weak.upgrade().unwrap().read().unwrap().payload, -/// 42 -/// ); -/// } -/// -/// { -/// let node_id_dne = 200 as usize; -/// assert!(arena.get_node_arc(node_id_dne).is_none()); -/// } -/// -/// { -/// let node_1_id = 0 as usize; -/// let node_list = dbg!(arena.tree_walk_dfs(node_1_id).unwrap()); -/// assert_eq!(node_list.len(), 1); -/// assert_eq!(node_list, vec![0]); -/// } -/// ``` -/// -/// 📜 There are more complex ways of using [`Arena`] and [`super::MTArena`]. Please look -/// at these extensive integration tests that put them thru their paces -/// [here](https://github.com/r3bl-org/r3bl-rs-utils/blob/main/tests/tree_memory_arena_test.rs). -#[derive(Debug)] -pub struct Arena -where - T: Debug + Clone + Send + Sync, -{ - map: RwLock>, - atomic_counter: AtomicUsize, -} - -impl Arena -where - T: Debug + Clone + Send + Sync, -{ - /// If no matching nodes can be found returns `None`. - #[allow(clippy::unwrap_in_result)] - pub fn filter_all_nodes_by(&self, filter_fn: &FilterFn) -> ResultUidList { - if let Ok(map /* ReadGuarded<'_, ArenaMap> */) = self.map.read() { - let filtered_map = map - .iter() - .filter(|(id, node_ref)| { - filter_fn(**id, node_ref.read().unwrap().payload.clone()) - }) - .map(|(id, _node_ref)| *id) - .collect::>(); - match filtered_map.len() { - 0 => None, - _ => Some(filtered_map), - } - } else { - None - } - } - - /// If `node_id` can't be found, returns `None`. - pub fn get_children_of(&self, node_id: usize) -> ResultUidList { - if !self.node_exists(node_id) { - return None; - } - - let node_to_lookup = self.get_node_arc(node_id)?; - - let result = if let Ok(node_to_lookup /* ReadGuarded<'_, Node> */) = - node_to_lookup.read() - { - if node_to_lookup.children_ids.is_empty() { - return None; - } - Some(node_to_lookup.children_ids.clone()) - } else { - None - }; - - result - } - - /// If `node_id` can't be found, returns `None`. - pub fn get_parent_of(&self, node_id: usize) -> Option { - if !self.node_exists(node_id) { - return None; - } - - let node_to_lookup = self.get_node_arc(node_id)?; - - let result = if let Ok(node_to_lookup /* ReadGuarded<'_, Node> */) = - node_to_lookup.read() - { - node_to_lookup.parent_id - } else { - None - }; - - result - } - - pub fn node_exists(&self, node_id: usize) -> bool { - self.map.read().unwrap().contains_key(&node_id.get_id()) - } - - pub fn has_parent(&self, node_id: usize) -> bool { - if self.node_exists(node_id) { - let parent_id_opt = self.get_parent_of(node_id); - if let Some(parent_id) = parent_id_opt { - return self.node_exists(parent_id); - } - } - false - } - - /// If `node_id` can't be found, returns `None`. - pub fn delete_node(&self, node_id: usize) -> ResultUidList { - if !self.node_exists(node_id) { - return None; - } - let deletion_list = self.tree_walk_dfs(node_id)?; - - // Note - this lambda expects that `parent_id` exists. - let remove_node_id_from_parent = |parent_id: usize| { - let parent_node_arc_opt = self.get_node_arc(parent_id); - if let Some(parent_node_arc) = parent_node_arc_opt { - if let Ok(mut parent_node /* WriteGuarded<'_, Node> */) = - parent_node_arc.write() - { - parent_node - .children_ids - .retain(|child_id| *child_id != node_id); - } - } - }; - - // If `node_id` has a parent, remove `node_id` its children, otherwise skip this - // step. - if self.has_parent(node_id) { - if let Some(parent_id) = self.get_parent_of(node_id) { - remove_node_id_from_parent(parent_id); - } - } - - // Actually delete the nodes in the deletion list. - if let Ok(mut map /* WriteGuarded<'_, ArenaMap> */) = self.map.write() { - for node_id in &deletion_list { - map.remove(node_id); - } - } - // Pass the deletion list back. - deletion_list.into() - } - - /// - [DFS graph walking](https://developerlife.com/2018/08/16/algorithms-in-kotlin-5/) - /// - [DFS tree walking](https://stephenweiss.dev/algorithms-depth-first-search-dfs#handling-non-binary-trees) - pub fn tree_walk_dfs(&self, node_id: usize) -> ResultUidList { - if !self.node_exists(node_id) { - return None; - } - - let mut stack: VecDeque = VecDeque::from([node_id.get_id()]); - - let mut it: VecDeque = VecDeque::new(); - - while let Some(node_id) = stack.pop_back() { - // Question mark operator works below, since it returns a `Option` to `while let ...`. - // Basically skip to the next item in the `stack` if `node_id` can't be found. - let node_ref = self.get_node_arc(node_id)?; - unwrap_arc_read_lock_and_call(&node_ref, &mut |node| { - it.push_back(node.get_id()); - // Note that the children ordering has to be flipped! You want to perform the - // traversal from RIGHT -> LEFT (not LEFT -> RIGHT). - // PTAL: - for child_id in node.children_ids.iter().rev() { - stack.push_back(*child_id); - } - }); - } - - match it.len() { - 0 => None, - _ => Some(it), - } - } - - /// - [BFS graph walking](https://developerlife.com/2018/08/16/algorithms-in-kotlin-5/) - /// - [BFS tree walking](https://stephenweiss.dev/algorithms-depth-first-search-dfs#handling-non-binary-trees) - pub fn tree_walk_bfs(&self, node_id: usize) -> ResultUidList { - if !self.node_exists(node_id) { - return None; - } - - let mut queue: VecDeque = VecDeque::from([node_id.get_id()]); - - let mut it: VecDeque = VecDeque::new(); - - while let Some(node_id) = queue.pop_front() { - // Question mark operator works below, since it returns a `Option` to `while let ...`. - // Basically skip to the next item in the `stack` if `node_id` can't be found. - let node_ref = self.get_node_arc(node_id)?; - unwrap_arc_read_lock_and_call(&node_ref, &mut |node| { - it.push_back(node.get_id()); - for child_id in node.children_ids.iter() { - queue.push_back(*child_id); - } - }); - } - - match it.len() { - 0 => None, - _ => Some(it), - } - } - - /// If `node_id` can't be found, returns `None`. More info on - /// [`Option.map()`](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d5a54a042fea085ef8c9122b7ea47c6a) - pub fn get_node_arc_weak(&self, node_id: usize) -> Option> { - if !self.node_exists(node_id) { - return None; - } - if let Ok(map) = self.map.read() { - map.get(&node_id.get_id()) // Returns `None` if `node_id` doesn't exist. - .map(Arc::downgrade) // Runs if `node_ref` is some, else returns `None`. - } else { - None - } - } - - /// If `node_id` can't be found, returns `None`. More info on - /// [`Option.map()`](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d5a54a042fea085ef8c9122b7ea47c6a) - pub fn get_node_arc(&self, node_id: usize) -> Option> { - if !self.node_exists(node_id) { - return None; - } - if let Ok(map) = self.map.read() { - map.get(&node_id.get_id()).cloned() // Runs if `node_ref` is some, else returns `None`. - } else { - None - } - } - - /// Note `data` is cloned to avoid `data` being moved. If `parent_id` can't be found, it panics. - pub fn add_new_node(&mut self, payload: T, maybe_parent_id: Option) -> usize { - let parent_id_arg_provided = maybe_parent_id.is_some(); - - // Check to see if `parent_id` exists. - if parent_id_arg_provided { - let parent_id = maybe_parent_id.unwrap(); - if !self.node_exists(parent_id) { - panic!("Parent node doesn't exist."); - } - } - - let new_node_id = self.generate_uid(); - - // Create a new node from payload & add it to the arena. - with_mut(&mut self.map.write().unwrap(), &mut |map| { - let value = Arc::new(RwLock::new(Node { - id: new_node_id, - parent_id: if parent_id_arg_provided { - let parent_id = maybe_parent_id.unwrap(); - Some(parent_id.get_id()) - } else { - None - }, - children_ids: VecDeque::new(), - payload: payload.clone(), - })); - map.insert(new_node_id, value); - }); - - // Deal w/ adding this newly created node to the parent's children list. - if let Some(parent_id) = maybe_parent_id { - let maybe_parent_node_arc = self.get_node_arc(parent_id); - call_if_some(&maybe_parent_node_arc, &|parent_node_arc| { - unwrap_arc_write_lock_and_call(parent_node_arc, &mut |parent_node| { - // Preserve the natural order of insertion. - parent_node.children_ids.push_back(new_node_id); - }); - }); - } - - // Return the node identifier. - new_node_id - } - - fn generate_uid(&self) -> usize { - self.atomic_counter - .fetch_add(1, std::sync::atomic::Ordering::SeqCst) - } - - pub fn new() -> Self { - Arena { - map: RwLock::new(HashMap::new()), - atomic_counter: AtomicUsize::new(0), - } - } -} - -impl Default for Arena -where - T: Debug + Clone + Send + Sync, -{ - fn default() -> Self { Self::new() } -} diff --git a/utils/src/tree_memory_arena/arena_types.rs b/utils/src/tree_memory_arena/arena_types.rs deleted file mode 100644 index f2bb00264..000000000 --- a/utils/src/tree_memory_arena/arena_types.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2022 R3BL LLC - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! Type aliases to improve code readability. - -use std::{collections::{HashMap, VecDeque}, - sync::{Arc, RwLock, Weak}}; - -use super::{Arena, Node}; - -pub trait HasId: Sync + Send { - type IdType; - - /// Returns a clone of the id. - fn get_id(&self) -> Self::IdType; -} - -impl HasId for usize { - type IdType = usize; - - /// Returns a clone of the id. - fn get_id(&self) -> usize { *self } -} - -// Type aliases for readability. -pub type NodeRef = Arc>>; -pub type WeakNodeRef = Weak>>; -pub type ArenaMap = HashMap>; - -pub type ResultUidList = Option>; - -// Filter lambda signature. -pub type FilterFn = dyn Fn(usize, T) -> bool + Send + Sync; - -// Parallel support. -pub type ShareableArena = Arc>>; -pub type WalkerFn = dyn Fn(usize, T) + Send + Sync; diff --git a/utils/src/tree_memory_arena/mod.rs b/utils/src/tree_memory_arena/mod.rs deleted file mode 100644 index 3e50c5a4e..000000000 --- a/utils/src/tree_memory_arena/mod.rs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2022 R3BL LLC - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! This module contains [`Arena`] a non-binary tree implementation that is -//! thread safe, and [`MTArena`] a variant of the tree that supports parallel -//! tree walking. -//! -//! 💡 You can learn more about how this library was built from this -//! [developerlife.com article](https://developerlife.com/2022/02/24/rust-non-binary-tree/). -//! -//! 📜 There are more complex ways of using [`Arena`] and [`MTArena`]. Please -//! look at these extensive integration tests that put them thru their paces -//! [here](https://github.com/r3bl-org/r3bl-rs-utils/blob/main/tests/tree_memory_arena_test.rs). - -// Attach sources. -pub mod arena; -pub mod arena_types; -pub mod mt_arena; - -// Re-export. -pub use arena::*; // Arena. -pub use arena_types::*; // Arena type aliases. -pub use mt_arena::*; // MTArena. diff --git a/utils/src/tree_memory_arena/mt_arena.rs b/utils/src/tree_memory_arena/mt_arena.rs deleted file mode 100644 index 8dc14aa28..000000000 --- a/utils/src/tree_memory_arena/mt_arena.rs +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2022 R3BL LLC - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! [`MTArena`] is defined here. - -use std::{fmt::Debug, - sync::{Arc, RwLock}, - thread::{spawn, JoinHandle}}; - -use super::{arena::Arena, Node, ResultUidList, ShareableArena, WalkerFn}; -use crate::utils::ReadGuarded; - -/// [`MTArena`] is built on top of [`Arena`] but with support for sharing the arena between threads. -/// Also supports tree walking on a separate thread w/ a lambda that's supplied. -/// -/// 1. [Wikipedia definition of memory -/// arena](https://en.wikipedia.org/wiki/Region-based_memory_management) -/// 2. You can learn more about how this library was built from this [developerlife.com -/// article](https://developerlife.com/2022/02/24/rust-non-binary-tree/). -/// -/// # Examples -/// -/// ```rust -/// use std::{sync::Arc, -/// thread::{self, JoinHandle}}; -/// -/// use r3bl_rs_utils::tree_memory_arena::{Arena, HasId, MTArena, ResultUidList, TraversalKind}; -/// use r3bl_rs_utils_core::{style_primary, style_prompt}; -/// -/// type ThreadResult = Vec; -/// type Handles = Vec>; -/// -/// let mut handles: Handles = Vec::new(); -/// let arena = MTArena::::new(); -/// -/// // Thread 1 - add root. Spawn and wait (since the 2 threads below need the root). -/// { -/// let arena_arc = arena.get_arena_arc(); -/// let thread = thread::spawn(move || { -/// let mut arena_write = arena_arc.write().unwrap(); -/// let root = arena_write.add_new_node("foo".to_string(), None); -/// vec![root] -/// }); -/// thread.join().unwrap(); -/// } -/// -/// // Perform tree walking in parallel. Note the lambda does capture many enclosing context -/// // variables. -/// { -/// let arena_arc = arena.get_arena_arc(); -/// let fn_arc = Arc::new(move |uid, payload| { -/// println!( -/// "{} {} {} Arena weak_count:{} strong_count:{}", -/// style_primary("walker_fn - closure"), -/// uid, -/// payload, -/// Arc::weak_count(&arena_arc), -/// Arc::weak_count(&arena_arc) -/// ); -/// }); -/// -/// // Walk tree w/ a new thread using arc to lambda. -/// { -/// let thread_handle: JoinHandle = arena.tree_walk_parallel( -/// 0, fn_arc.clone(), TraversalKind::BreadthFirst); -/// let result_node_list = thread_handle.join().unwrap(); -/// println!("{:#?}", result_node_list); -/// } -/// -/// // Walk tree w/ a new thread using arc to lambda. -/// { -/// let thread_handle: JoinHandle = arena.tree_walk_parallel( -/// 1, fn_arc.clone(), TraversalKind::DepthFirst); -/// let result_node_list = thread_handle.join().unwrap(); -/// println!("{:#?}", result_node_list); -/// } -/// } -/// ``` -/// 📜 There are more complex ways of using [`super::Arena`] and [`MTArena`]. Please look at these -/// extensive integration tests that put them thru their paces -/// [here](https://github.com/r3bl-org/r3bl-rs-utils/blob/main/tests/tree_memory_arena_test.rs). -#[derive(Debug)] -pub struct MTArena -where - T: Debug + Send + Sync + Clone + 'static, -{ - arena_arc: ShareableArena, -} - -impl MTArena -where - T: Debug + Send + Sync + Clone + 'static, -{ - pub fn new() -> Self { - MTArena { - arena_arc: Arc::new(RwLock::new(Arena::new())), - } - } - - pub fn get_arena_arc(&self) -> ShareableArena { self.arena_arc.clone() } - - /// `walker_fn` is a closure that captures variables. It is wrapped in an `Arc` to be able to - /// clone that and share it across threads. More info: - /// 1. SO thread: - /// 2. Scoped threads: - pub fn tree_walk_parallel( - &self, - node_id: usize, - walker_fn: Arc>, - traversal_kind: super::TraversalKind, - ) -> JoinHandle { - let arena_arc = self.get_arena_arc(); - let walker_fn_arc = walker_fn; - - spawn(move || { - let read_guard: ReadGuarded<'_, Arena> = arena_arc.read().unwrap(); - let return_value = match traversal_kind { - TraversalKind::DepthFirst => read_guard.tree_walk_dfs(node_id), - TraversalKind::BreadthFirst => read_guard.tree_walk_bfs(node_id), - }; - - // While walking the tree, in a separate thread, call the `walker_fn` for each - // node. - if let Some(result_list) = return_value.clone() { - result_list.into_iter().for_each(|uid| { - let node_arc_opt = read_guard.get_node_arc(uid); - if let Some(node_arc) = node_arc_opt { - let node_ref: ReadGuarded<'_, Node> = node_arc.read().unwrap(); - walker_fn_arc(uid, node_ref.payload.clone()); - } - }); - } - - return_value - }) - } -} - -#[derive(Debug)] -pub enum TraversalKind { - DepthFirst, - BreadthFirst, -} - -impl Default for MTArena -where - T: Debug + Send + Sync + Clone + 'static, -{ - fn default() -> Self { Self::new() } -} diff --git a/utils/src/utils/lazy_field.rs b/utils/src/utils/lazy_field.rs deleted file mode 100644 index aaab8da36..000000000 --- a/utils/src/utils/lazy_field.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2022 R3BL LLC - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -pub trait LazyExecutor -where - T: Send + Sync, -{ - fn compute(&mut self) -> T; - - /// Book: - #[allow(clippy::all)] - fn new() -> Box + Send + Sync> - where - Self: Default + Sized + Sync + Send + 'static, - { - Box::new(Self::default()) - } -} - -pub struct LazyField -where - T: Send + Sync, -{ - pub lazy_executor: Box + Send + Sync>, - pub field: T, - pub has_computed: bool, -} - -impl LazyField -where - T: Send + Sync, - T: Default + Clone, -{ - pub fn new(lazy_executor: Box + Send + Sync>) -> Self { - Self { - lazy_executor, - field: T::default(), - has_computed: false, - } - } - - pub fn compute(&mut self) -> T { - if self.has_computed { - self.field.clone() - } else { - self.field = self.lazy_executor.compute(); - self.has_computed = true; - self.field.clone() - } - } -} diff --git a/utils/src/utils/lazy_hash_map.rs b/utils/src/utils/lazy_hash_map.rs deleted file mode 100644 index bb22dc66c..000000000 --- a/utils/src/utils/lazy_hash_map.rs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2022 R3BL LLC - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! Data structures to make it easier to work w/ lazily computed values and -//! caching them. - -use std::{collections::HashMap, hash::Hash}; - -/// This struct allows users to create a lazy hash map. A function must be -/// provided that computes the values when they are first requested. These -/// values are cached for the lifetime this struct. -/// -/// # Examples -/// -/// ```rust -/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; -/// -/// use r3bl_rs_utils::utils::LazyMemoValues; -/// -/// // These are copied in the closure below. -/// let arc_atomic_count = AtomicUsize::new(0); -/// let mut a_variable = 123; -/// let mut a_flag = false; -/// -/// let mut generate_value_fn = LazyMemoValues::new(|it| { -/// arc_atomic_count.fetch_add(1, SeqCst); -/// a_variable = 12; -/// a_flag = true; -/// a_variable + it -/// }); -/// -/// assert_eq!(arc_atomic_count.load(SeqCst), 0); -/// assert_eq!(generate_value_fn.get_ref(&1), &13); -/// assert_eq!(arc_atomic_count.load(SeqCst), 1); -/// assert_eq!(generate_value_fn.get_ref(&1), &13); // Won't regenerate the value. -/// assert_eq!(arc_atomic_count.load(SeqCst), 1); // Doesn't change. -/// assert_eq!(generate_value_fn.get_ref(&2), &14); -/// assert_eq!(arc_atomic_count.load(SeqCst), 2); -/// assert_eq!(generate_value_fn.get_ref(&2), &14); -/// assert_eq!(generate_value_fn.get_copy(&2), 14); -/// assert_eq!(a_variable, 12); -/// assert_eq!(a_flag, true); -/// ``` -#[derive(Debug)] -pub struct LazyMemoValues -where - F: FnMut(&K) -> V, - K: Clone + Eq + Hash, - V: Clone, -{ - pub create_value_fn: F, - pub value_map: HashMap, -} - -impl LazyMemoValues -where - F: FnMut(&K) -> V, - K: Clone + Eq + Hash, - V: Clone, -{ - pub fn new(create_value_fn: F) -> Self { - LazyMemoValues { - create_value_fn, - value_map: HashMap::new(), - } - } - - pub fn get_ref(&mut self, arg: &K) -> &V { - if !self.value_map.contains_key(arg) { - let arg = arg.clone(); - let value = (self.create_value_fn)(&arg); - self.value_map.insert(arg, value); - } - self.value_map.get(arg).as_ref().unwrap() - } - - pub fn get_copy(&mut self, arg: &K) -> V { self.get_ref(arg).clone() } -} diff --git a/utils/src/utils/mod.rs b/utils/src/utils/mod.rs deleted file mode 100644 index d91be3b26..000000000 --- a/utils/src/utils/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2022 R3BL LLC - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! This module contains a lot of utility functions that are meant to: -//! 1. Increase the ergonomics of using wrapped values in Rust -//! 2. Colorizing console output. -//! 3. Easy to work w/ lazy hash maps. -//! 4. Easy to work w/ readline. -//! 5. Interrogation of types. - -// Attach sources. -pub mod lazy_field; -pub mod lazy_hash_map; -pub mod safe_unwrap; -pub mod type_utils; - -// Re-export. -pub use lazy_field::*; -pub use lazy_hash_map::*; -pub use safe_unwrap::*; -pub use type_utils::*; diff --git a/utils/src/utils/safe_unwrap.rs b/utils/src/utils/safe_unwrap.rs deleted file mode 100644 index 9cf4c5870..000000000 --- a/utils/src/utils/safe_unwrap.rs +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2022 R3BL LLC - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! Functions that make it easy to unwrap a value safely. -//! -//! These functions are provided to improve the ergonomics of using wrapped values in -//! Rust. Examples of wrapped values are `>`, and `