diff --git a/macros/src/emit.rs b/macros/src/emit.rs index 60884ca..91f8b4f 100644 --- a/macros/src/emit.rs +++ b/macros/src/emit.rs @@ -1,11 +1,17 @@ +//! # RSX Emission +//! +//! Converts parsed RSX AST into Rust code that constructs RSX objects. + use crate::parse::*; use proc_macro2::TokenStream; use quote::quote; +/// Emits code for the root RSX pub fn emit_rsx(root: RsxRoot) -> TokenStream { emit_rsx_vec(&root.nodes) } +/// Emits code for a vector of RSX nodes pub fn emit_rsx_vec(nodes: &Vec) -> TokenStream { let nodes = nodes.iter().map(emit_node_scope); @@ -16,6 +22,7 @@ pub fn emit_rsx_vec(nodes: &Vec) -> TokenStream { }} } +/// Emits variable bindings for dependencies fn emit_deps(deps: &[Dep]) -> TokenStream { deps.iter() .map(|d| { @@ -29,6 +36,7 @@ fn emit_deps(deps: &[Dep]) -> TokenStream { .collect() } +/// Emits a Vec of dependencies as HookDependency trait objects fn emit_deps_vec(deps: &[Dep]) -> TokenStream { let deps = deps.iter().map(|d| { let ident = &d.ident; @@ -43,6 +51,7 @@ fn emit_deps_vec(deps: &[Dep]) -> TokenStream { } } +/// Emits code for a single node within a scope fn emit_node_scope(node: &RsxNode) -> TokenStream { match node { RsxNode::Text(_) => { diff --git a/macros/src/lib.rs b/macros/src/lib.rs index f13a496..78c3dd7 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,3 +1,12 @@ +//! # OSUI Macros +//! +//! Procedural macros for OSUI that provide ergonomic syntax for defining components. +//! +//! ## Features +//! +//! - `#[component]` - Transforms a function into a reusable component with props +//! - `rsx!` - Creates RSX (React-like Syntax) for component hierarchies + use proc_macro::TokenStream; use quote::quote; use syn::{FnArg, ItemFn, Pat, ReturnType, Type, parse_macro_input}; @@ -5,12 +14,41 @@ use syn::{FnArg, ItemFn, Pat, ReturnType, Type, parse_macro_input}; mod emit; mod parse; +/// RSX (React-like Syntax) macro for building component hierarchies +/// +/// # Example +/// +/// ```rust,ignore +/// rsx! { +/// Component { +/// prop: value, +/// } +/// } +/// ``` #[proc_macro] pub fn rsx(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as parse::RsxRoot); emit::emit_rsx(ast).into() } +/// Component attribute macro for defining reusable components +/// +/// Transforms a function into a component with automatic prop handling. +/// The first parameter must be `cx: &Arc`. +/// Remaining parameters become component props. +/// +/// # Example +/// +/// ```rust,ignore +/// #[component] +/// pub fn Counter(cx: &Arc, initial: &i32) -> View { +/// let count = use_state(*initial); +/// +/// Arc::new(move |ctx| { +/// ctx.draw_text(Point { x: 0, y: 0 }, &format!("Count: {}", count.get_dl())); +/// }) +/// } +/// ``` #[proc_macro_attribute] pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream { let input = parse_macro_input!(item as ItemFn); diff --git a/macros/src/parse.rs b/macros/src/parse.rs index 7252ec3..0d474e2 100644 --- a/macros/src/parse.rs +++ b/macros/src/parse.rs @@ -1,3 +1,7 @@ +//! # RSX Parser +//! +//! Parses RSX syntax into an AST that can be emitted as Rust code. + use syn::braced; use syn::parse::discouraged::Speculative; use syn::{ @@ -6,7 +10,9 @@ use syn::{ token::Brace, }; +/// Root of an RSX expression pub struct RsxRoot { + /// Top-level nodes in the RSX pub nodes: Vec, } @@ -20,30 +26,49 @@ impl Parse for RsxRoot { } } -/// A single prop: `ident: expr`. +/// A single component prop: `name: value` pub struct RsxProp { + /// Property name pub name: Ident, + /// Property value expression pub value: Expr, } +/// AST node representing different RSX constructs pub enum RsxNode { + /// String literal: `"text"` Text(LitStr), + /// Expression node: `{expr}` Expr(Expr), + /// Component instantiation: `Component { prop: value, ... }` Component { + /// Component path (e.g., `my_module::MyComponent`) path: Path, + /// Component properties props: Vec, + /// Child nodes children: Vec, }, + /// Mount lifecycle: `@mount` Mount(Ident), + /// Conditional rendering: `@if condition { ... }` If { + /// Dependencies to track for reactivity deps: Vec, + /// Condition expression cond: Expr, + /// Child nodes to render if true children: Vec, }, + /// Loop rendering: `@for pattern in expr { ... }` For { + /// Dependencies to track for reactivity deps: Vec, + /// Loop pattern (e.g., `(key, value)`) pat: Pat, + /// Iterable expression expr: Expr, + /// Child nodes to render for each iteration children: Vec, }, } diff --git a/src/component/context.rs b/src/component/context.rs index 636cf5f..7402f3d 100644 --- a/src/component/context.rs +++ b/src/component/context.rs @@ -1,3 +1,9 @@ +//! # Context Module +//! +//! Provides the Context type which is central to component state management. +//! Context holds component state, manages event handlers, and coordinates +//! rendering and updates across the component tree. + use std::{ any::{Any, TypeId}, collections::HashMap, @@ -16,15 +22,28 @@ use crate::{ use super::{scope::Scope, Component, ComponentImpl}; +/// Context represents the runtime state and behavior of a component +/// +/// Each component instance has a Context that holds: +/// - The component implementation +/// - The current view (render result) +/// - Event handlers for responding to events +/// - Child scopes for managing child components pub struct Context { + /// The component implementation component: AccessCell, + /// The current rendered view view: AccessCell, + /// Event handlers grouped by event type event_handlers: AccessCell>>, + /// Child scopes (component hierarchies) pub(crate) scopes: Mutex>>, + /// Command executor for this context's command handling executor: Arc, } impl Context { + /// Creates a new context for the given component pub fn new( component: F, executor: Arc, @@ -38,6 +57,9 @@ impl Context { }) } + /// Refreshes the component by re-rendering it + /// + /// Clears event handlers and calls the component to produce a new view. pub fn refresh(self: &Arc) { self.event_handlers .access(|event_handlers| event_handlers.clear()); @@ -53,6 +75,9 @@ impl Context { }); } + /// Synchronously refreshes the component + /// + /// Blocks until the component has finished rendering. pub fn refresh_sync(self: &Arc) { let (tx, rx) = std::sync::mpsc::channel::<()>(); @@ -79,10 +104,15 @@ impl Context { let _ = rx.recv(); } + /// Gets the current view pub fn get_view(self: &Arc) -> View { self.view.access_ref().clone() } + /// Registers an event handler for events of type T + /// + /// When an event of type T is emitted, the handler is called with + /// the context and a reference to the event. pub fn on_event, &T) + Send + Sync + 'static>( self: &Arc, handler: F, @@ -101,6 +131,10 @@ impl Context { }); } + /// Emits an event to this component and all descendants + /// + /// Calls all registered handlers for this event type, + /// then propagates the event to child components. pub fn emit_event(self: &Arc, event: E) { let event = Arc::new(event); let handlers_to_call: Vec = { @@ -118,6 +152,10 @@ impl Context { } } + /// Emits an event to this component in a spawned thread + /// + /// Similar to emit_event but handlers are called in spawned threads + /// for concurrent execution. pub fn emit_event_threaded( self: &Arc, event: &E, @@ -141,6 +179,7 @@ impl Context { } } + /// Creates a new child scope pub fn scope(self: &Arc) -> Arc { let scope = Scope::new(self.executor.clone()); self.scopes.lock().unwrap().push(scope.clone()); @@ -148,6 +187,7 @@ impl Context { scope } + /// Creates a dynamic child scope that re-renders when dependencies change pub fn dyn_scope) + Send + Sync + 'static>( self: &Arc, drawer: F, @@ -171,10 +211,12 @@ impl Context { scope } + /// Adds a pre-constructed scope as a child pub fn add_scope(self: &Arc, scope: Arc) { self.scopes.lock().unwrap().push(scope); } + /// Renders all child components to the draw context pub fn draw_children(self: &Arc, ctx: &mut DrawContext) { for scope in self.scopes.lock().unwrap().iter() { for (child, view_wrapper) in scope.children.lock().unwrap().iter() { @@ -189,15 +231,18 @@ impl Context { } } + /// Gets the command executor for this context pub fn get_executor(self: &Arc) -> Arc { self.executor.clone() } + /// Executes a command pub fn execute(self: &Arc, command: T) -> crate::Result<()> { self.executor .execute_command(&(Arc::new(command) as Arc)) } + /// Stops the application pub fn stop(self: &Arc) -> crate::Result<()> { self.execute(crate::engine::commands::Stop) } diff --git a/src/component/mod.rs b/src/component/mod.rs index f2f83a9..e48b642 100644 --- a/src/component/mod.rs +++ b/src/component/mod.rs @@ -1,3 +1,9 @@ +//! # Component Module +//! +//! Provides the component system that forms the foundation of OSUI. +//! Components are reusable units of UI that can manage their own state +//! and respond to events. + pub mod context; pub mod scope; @@ -10,10 +16,15 @@ use crate::View; use context::Context; +/// A Component is an implementor of the ComponentImpl trait, wrapped in Arc pub type Component = Arc; + +/// An event handler function stored in a mutex for thread-safe mutation pub type EventHandler = Arc, &dyn Any) + Send + Sync>>; +/// Trait implemented by components to render themselves pub trait ComponentImpl: Send + Sync { + /// Renders the component within the given context, returning a View fn call(&self, cx: &Arc) -> View; } diff --git a/src/component/scope.rs b/src/component/scope.rs index 6cfe6e5..6ac0926 100644 --- a/src/component/scope.rs +++ b/src/component/scope.rs @@ -1,15 +1,28 @@ +//! # Scope Module +//! +//! Provides the Scope type for managing component hierarchies. +//! Scopes group child components and manage their lifecycle. + use std::sync::{Arc, Mutex}; use crate::{engine::CommandExecutor, View, ViewWrapper}; use super::{context::Context, ComponentImpl}; +/// A scope groups child components and manages their rendering +/// +/// Scopes form the hierarchical structure of a component tree. +/// Each scope contains references to its child components and their +/// optional view wrappers (for layout/styling). pub struct Scope { + /// Child components with optional view wrappers pub children: Mutex, Option)>>, + /// Command executor for this scope's children executor: Arc, } impl Scope { + /// Creates a new scope with the given command executor pub fn new(executor: Arc) -> Arc { Arc::new(Self { children: Mutex::new(Vec::new()), @@ -17,6 +30,9 @@ impl Scope { }) } + /// Adds a child component to this scope + /// + /// The view_wrapper is optional and can be used for layout or styling. pub fn child( self: &Arc, child: F, @@ -29,6 +45,7 @@ impl Scope { self.children.lock().unwrap().push((ctx, view_wrapper)); } + /// Adds a view directly to this scope pub fn view(self: &Arc, view: View) { let ctx = Context::new(view, self.executor.clone()); diff --git a/src/engine/benchmark.rs b/src/engine/benchmark.rs index 1843050..a844cad 100644 --- a/src/engine/benchmark.rs +++ b/src/engine/benchmark.rs @@ -1,3 +1,7 @@ +//! # Benchmark Module +//! +//! Provides performance benchmarking capabilities for rendering engines. + use std::{io::stdout, sync::Arc, time::Instant}; use crossterm::{cursor::MoveTo, execute, terminal::Clear}; @@ -7,18 +11,26 @@ use crate::{render::Area, DrawContext, View}; use super::Engine; +/// Results of a benchmark run #[derive(Debug, Clone)] pub struct BenchmarkResult { + /// Average render time in microseconds pub average: u128, + /// Minimum render time in microseconds pub min: u128, + /// Maximum render time in microseconds pub max: u128, + /// Total time spent rendering in microseconds pub total_render: u128, + /// Total benchmark time including setup in microseconds pub total: u128, } +/// Wraps an engine to benchmark its rendering performance pub struct Benchmark(T); impl Benchmark { + /// Creates a new benchmark wrapper around the given engine pub fn new(engine: T) -> Self { Self(engine) } @@ -31,6 +43,7 @@ impl Engine for Benchmark { let start = Instant::now(); + // Run 40 render cycles and measure each for _ in 0..40 { let start = Instant::now(); self.render(&cx); diff --git a/src/engine/commands.rs b/src/engine/commands.rs index 8cda616..e3baecd 100644 --- a/src/engine/commands.rs +++ b/src/engine/commands.rs @@ -1,5 +1,10 @@ +//! # Commands Module +//! +//! Defines built-in commands for controlling the engine. + use crate::engine::Command; +/// Command to stop the engine and terminate the application pub struct Stop; impl Command for Stop { diff --git a/src/engine/console.rs b/src/engine/console.rs index afa8cb2..236ee41 100644 --- a/src/engine/console.rs +++ b/src/engine/console.rs @@ -1,3 +1,8 @@ +//! # Console Engine Implementation +//! +//! Provides a Console implementation of the Engine trait for rendering +//! to the terminal using crossterm. + use std::{ io::{stdout, Write}, sync::{Arc, Mutex}, @@ -14,16 +19,24 @@ use crate::{ use super::Engine; +/// Executes commands for the console engine pub struct ConsoleExecutor { + /// Flag indicating whether the application is running running: Mutex, } +/// Console-based rendering engine +/// +/// Renders components to the terminal using crossterm for cross-platform support. pub struct Console { + /// Thread functions to execute threads: Mutex) + Send + Sync>>>, + /// The executor for this console executor: Arc, } impl Console { + /// Creates a new console engine pub fn new() -> Self { Self { threads: Mutex::new(Vec::new()), @@ -33,6 +46,7 @@ impl Console { } } + /// Registers a thread function to run alongside the engine pub fn thread) + Send + Sync + 'static>(&self, run: F) { self.threads.lock().unwrap().push(Arc::new(run)); } @@ -114,10 +128,12 @@ impl Engine for Console { } impl ConsoleExecutor { + /// Checks if the engine is still running pub fn is_running(self: &Arc) -> bool { *self.running.lock().unwrap() } + /// Stops the engine pub fn stop(&self) -> crate::Result<()> { *self.running.lock()? = false; Ok(()) diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 2ee53e5..af31049 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -1,3 +1,9 @@ +//! # Engine Module +//! +//! Provides the rendering engine and command execution system. +//! The engine is responsible for initializing components, rendering frames, +//! and handling user commands. + pub mod benchmark; pub mod commands; pub mod console; @@ -10,23 +16,40 @@ use std::{any::Any, sync::Arc}; use crate::component::{context::Context, ComponentImpl}; use crate::{render::Area, DrawContext, View}; +/// Main engine trait for rendering and running components pub trait Engine { + /// Runs a component to completion fn run(&self, component: C) -> crate::Result; + + /// Initializes a component and returns its context fn init(&self, component: C) -> Arc; + + /// Renders the current state of a component fn render(&self, cx: &Arc); + + /// Sleeps between render frames (default 16ms for ~60fps) fn render_delay(&self) { crate::sleep(16); } + /// Renders a view within an area and returns the draw context fn render_view(&self, area: &Area, view: &View) -> DrawContext; + + /// Executes the drawing instructions in a draw context fn draw_context(&self, ctx: &DrawContext); + + /// Returns the command executor for this engine fn executor(&self) -> Arc; } +/// Trait for commands that can be executed by the engine pub trait Command { + /// Returns the command as Any for downcasting fn as_any(&self) -> &dyn Any; } +/// Executes commands during the application lifecycle pub trait CommandExecutor: Send + Sync { + /// Executes the given command fn execute_command(&self, command: &Arc) -> crate::Result<()>; } diff --git a/src/frontend.rs b/src/frontend.rs index df66c54..c094956 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -1,34 +1,59 @@ +//! # Frontend Module +//! +//! Provides the RSX (React-like Syntax) system for composing components. +//! This module defines the structure for building component hierarchies +//! with static and dynamic scopes, similar to React's JSX. + use std::sync::Arc; use crate::component::{context::Context, scope::Scope}; use crate::{render::Point, state::HookDependency, View}; +/// Trait for converting values to RSX pub trait ToRsx { + /// Convert to RSX representation fn to_rsx(&self) -> Rsx; } +/// Scope types for RSX components #[derive(Clone)] pub enum RsxScope { + /// Static scope - executed once and never updated Static(Arc) + Send + Sync>), + /// Dynamic scope - re-executed when dependencies change Dynamic( Arc) + Send + Sync>, Vec>, ), + /// Child RSX scope for composition Child(Rsx), } +/// RSX (React-like Syntax) builder for component hierarchies +/// +/// Represents a collection of scopes that define component structure. +/// Scopes can be static (execute once) or dynamic (reactive to changes). #[derive(Clone)] pub struct Rsx(Vec); impl Rsx { + /// Creates a new empty RSX pub fn new() -> Self { Self(Vec::new()) } + /// Adds a static scope to this RSX + /// + /// The provided function is executed once during rendering + /// and will not be re-executed on dependency changes. pub fn static_scope) + Send + Sync + 'static>(&mut self, scope: F) { self.0.push(RsxScope::Static(Arc::new(scope))); } + /// Adds a dynamic scope to this RSX + /// + /// The provided function is executed when dependencies change, + /// allowing for reactive updates similar to React hooks. pub fn dynamic_scope) + Send + Sync + 'static>( &mut self, drawer: F, @@ -38,10 +63,12 @@ impl Rsx { .push(RsxScope::Dynamic(Arc::new(drawer), dependencies)); } + /// Adds a child RSX pub fn child(&mut self, child: R) { self.0.push(RsxScope::Child(child.to_rsx())); } + /// Generates child components within the given context pub fn generate_children(&self, context: &Arc) { let executor = context.get_executor(); @@ -64,6 +91,7 @@ impl Rsx { } } + /// Converts this RSX to a View pub fn view(&self, context: &Arc) -> View { let context = context.clone(); diff --git a/src/lib.rs b/src/lib.rs index 9d65be7..2f434e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,41 @@ +//! # OSUI - A TUI Library for Advanced UIs +//! +//! OSUI is a Rust library for building sophisticated Terminal User Interfaces (TUIs). +//! It provides a component-based architecture with state management, event handling, +//! and rendering capabilities for creating interactive console applications. +//! +//! ## Key Features +//! +//! - **Component System**: Build UIs using composable components +//! - **State Management**: React-like hooks for managing component state +//! - **Event Handling**: Type-safe event system with reactive updates +//! - **RSX Syntax**: Macro-based DSL for defining component hierarchies +//! - **Console Engine**: Terminal rendering with crossterm support +//! +//! ## Architecture +//! +//! - [`component`] - Component system and context management +//! - [`state`] - State management with hooks (useState, useEffect, etc.) +//! - [`engine`] - Rendering engine and command execution +//! - [`frontend`] - RSX (React-like Syntax) for component definitions +//! - [`render`] - Low-level rendering primitives +//! +//! ## Example +//! +//! ```rust,no_run +//! use osui::prelude::*; +//! use std::sync::Arc; +//! +//! #[component] +//! pub fn Counter(cx: &Arc) -> View { +//! let count = use_state(0); +//! +//! Arc::new(move |ctx| { +//! ctx.draw_text(Point { x: 0, y: 0 }, &format!("Count: {}", count.get_dl())); +//! }) +//! } +//! ``` + use std::sync::Arc; use crate::render::DrawContext; @@ -9,6 +47,7 @@ pub mod render; pub mod state; pub mod prelude { + //! Prelude module - Re-exports commonly used items for convenience pub use crate::component::{context::*, scope::*, *}; pub use crate::engine::*; pub use crate::frontend::*; @@ -20,12 +59,21 @@ pub mod prelude { pub use std::sync::{Arc, Mutex}; } +/// A View is an async function that renders content to a DrawContext. +/// It takes a mutable DrawContext and produces drawing instructions. pub type View = Arc; + +/// A ViewWrapper is a higher-order function that wraps views. +/// It can modify or enhance how a view is rendered. pub type ViewWrapper = Arc; + +/// Result type for OSUI operations pub type Result = std::result::Result; +/// Error type for OSUI operations #[derive(Debug, Clone)] pub enum Error { + /// Error that occurs when a mutex is poisoned PoisonError, } @@ -35,6 +83,8 @@ impl From>> for Error { } } +/// Sleep for the specified duration in milliseconds. +/// Useful for controlling render frame rate or delays. pub fn sleep(delay_ms: u64) { std::thread::sleep(std::time::Duration::from_millis(delay_ms)); } diff --git a/src/render.rs b/src/render.rs index fadbe84..0c75837 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,40 +1,69 @@ +//! # Rendering Module +//! +//! This module provides low-level rendering primitives and data structures +//! for drawing content to the terminal. It includes geometric primitives +//! (Point, Area, Size) and drawing instructions. + use crate::View; +/// Represents a drawing instruction that can be executed by the rendering engine #[derive(Clone)] pub enum DrawInstruction { + /// Draw text at a specific point Text(Point, String), + /// Render a view within a specified area View(Area, View), + /// Render a child drawing context at an offset Child(Point, DrawContext), } +/// Represents the dimensions of a drawable area #[derive(Clone)] pub struct Size { + /// Width in terminal columns pub width: u16, + /// Height in terminal rows pub height: u16, } +/// Represents a position in 2D space #[derive(Clone)] pub struct Point { + /// X coordinate (column) pub x: u16, + /// Y coordinate (row) pub y: u16, } +/// Represents a rectangular area with position and dimensions #[derive(Clone)] pub struct Area { + /// X coordinate (column) of the top-left corner pub x: u16, + /// Y coordinate (row) of the top-left corner pub y: u16, + /// Width in terminal columns pub width: u16, + /// Height in terminal rows pub height: u16, } +/// Context for drawing operations +/// +/// Accumulates drawing instructions that are executed by the rendering engine. +/// Tracks allocated space within the drawable area. #[derive(Clone)] pub struct DrawContext { + /// The total area available for drawing pub area: Area, + /// The area that has been allocated for drawing (union of all allocations) pub allocated: Area, + /// List of drawing instructions to execute pub drawing: Vec, } impl DrawContext { + /// Creates a new DrawContext with the specified area pub fn new(area: Area) -> Self { Self { area, @@ -48,6 +77,8 @@ impl DrawContext { } } + /// Allocates space within the drawable area and returns the allocated area + /// Updates the allocated bounds to include this allocation pub fn allocate(&mut self, x: u16, y: u16, width: u16, height: u16) -> Area { self.allocated.x = self.allocated.x.min(x); self.allocated.y = self.allocated.y.min(y); @@ -62,15 +93,18 @@ impl DrawContext { } } + /// Adds a drawing instruction to be executed pub fn draw(&mut self, inst: DrawInstruction) { self.drawing.push(inst); } + /// Draws text at the specified point pub fn draw_text(&mut self, point: Point, text: &str) { self.drawing .push(DrawInstruction::Text(point, text.to_string())); } + /// Draws a view within the specified area pub fn draw_view(&mut self, area: Area, view: View) { self.drawing.push(DrawInstruction::View(area, view)); } diff --git a/src/state.rs b/src/state.rs index 15f7631..8e4c7a7 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,3 +1,8 @@ +//! # State Management Module +//! +//! Provides React-like hooks for managing component state and side effects. +//! This module includes useState, useEffect, useMount, and state synchronization hooks. + use std::{ any::Any, fmt::{Debug, Display, Formatter, Result as FmtResult}, @@ -7,24 +12,42 @@ use std::{ use crate::component::context::Context; +/// Effect callback that can be triggered by state changes #[derive(Clone)] pub struct HookEffect(Arc>); +/// State holder for reactive values +/// +/// Similar to React's useState hook. Holds a value and tracks dependents +/// that need to be notified when the value changes. #[derive(Debug)] pub struct State { + /// The actual state value value: Arc>, + /// Functions to call when state is updated dependents: Arc>>, } +/// Guard for accessing and potentially modifying state +/// +/// Dereferences to the state value. When dropped after modification, +/// automatically triggers all dependent effects. pub struct Inner<'a, T> { value: MutexGuard<'a, T>, dependents: Arc>>, updated: bool, } +/// Mount lifecycle hook +/// +/// Tracks whether a component has been mounted and executes +/// any pending mount effects. #[derive(Debug, Clone)] pub struct Mount(Arc>, Arc>>); +/// Creates a new state value +/// +/// Returns a State that can be read and written from multiple threads. pub fn use_state(v: T) -> State { State { value: Arc::new(Mutex::new(v)), @@ -33,14 +56,20 @@ pub fn use_state(v: T) -> State { } impl State { - /// Gets the cloned value, recommended for preventing deadlocks + /// Gets a cloned copy of the state value + /// + /// Recommended over `get()` to prevent deadlocks when cloning is acceptable. + /// "dl" stands for "deadlock-less". pub fn get_dl(&self) -> T { self.value.lock().unwrap().clone() } } impl State { - /// Gets a lock on the state for read/write access. + /// Acquires a lock on the state for read/write access + /// + /// Returns an Inner guard that implements Deref and DerefMut. + /// When dropped after modification, triggers dependent effects. pub fn get(&self) -> Inner<'_, T> { Inner { value: self.value.lock().unwrap(), @@ -49,18 +78,20 @@ impl State { } } - /// Sets the value and marks it as changed. + /// Sets the state value and triggers dependents pub fn set(&self, v: T) { *self.value.lock().unwrap() = v; self.update(); } + /// Notifies all dependents of an update pub fn update(&self) { for d in self.dependents.lock().unwrap().iter() { d.call(); } } + /// Clones the State handle (not the value) pub fn clone(&self) -> Self { Self { dependents: self.dependents.clone(), @@ -82,15 +113,19 @@ impl Debug for HookEffect { } impl HookEffect { + /// Creates a new effect from a function pub fn new(f: F) -> Self { Self(Arc::new(Mutex::new(f))) } + /// Executes the effect function pub fn call(&self) { (self.0.lock().unwrap())() } } +/// Inner implements Deref for read access and DerefMut for write access +/// On drop after mutation, automatically triggers dependent effects impl Drop for Inner<'_, T> { fn drop(&mut self) { if self.updated { @@ -115,7 +150,9 @@ impl DerefMut for Inner<'_, T> { } } +/// Trait for values that can be tracked as dependencies in hooks pub trait HookDependency: Send + Sync { + /// Register an effect to be triggered on updates fn on_update(&self, hook: HookEffect); } @@ -136,6 +173,7 @@ impl HookDependency for Mount { } impl Mount { + /// Mark the component as mounted and execute pending effects pub fn mount(&self) { *self.0.lock().unwrap() = true; for hook_effect in self.1.lock().unwrap().iter() { @@ -145,6 +183,10 @@ impl Mount { } } +/// Executes a function when dependencies change +/// +/// Similar to React's useEffect. The provided function is executed +/// when any of the dependencies change. pub fn use_effect(f: F, dependencies: &[&dyn HookDependency]) { let f = Arc::new(Mutex::new(f)); let hook = HookEffect(Arc::new(Mutex::new({ @@ -160,10 +202,18 @@ pub fn use_effect(f: F, dependencies: &[&dyn } } +/// Creates a mount lifecycle hook +/// +/// Returns a Mount that tracks component lifecycle and executes +/// effects after mounting. pub fn use_mount() -> Mount { Mount(Arc::new(Mutex::new(true)), Arc::new(Mutex::new(Vec::new()))) } +/// Creates a manual mount lifecycle hook +/// +/// Similar to use_mount but the component starts as unmounted. +/// Must call .mount() to trigger mounted effects. pub fn use_mount_manual() -> Mount { Mount( Arc::new(Mutex::new(false)), @@ -171,6 +221,10 @@ pub fn use_mount_manual() -> Mount { ) } +/// Synchronizes state with events from the context +/// +/// Creates state that is automatically updated when events are emitted +/// to the context. The decoder function converts events to state values. pub fn use_sync_state< T: Send + Sync + 'static, E: Any + 'static, @@ -190,6 +244,10 @@ pub fn use_sync_state< state } +/// Synchronizes state changes back to the context as events +/// +/// Creates an effect that emits an event whenever the state changes. +/// The encoder function converts state values to events. pub fn use_sync_effect< T: Send + Sync + 'static, Ev: Send + Sync + 'static,