Rustato: A powerful, thread-safe global state management library for Rust applications, offering type-safe, reactive state handling with an easy-to-use macro-based API.

Overview


Coinbase Commerce Client
Rustato State Manager

A generical thread-safe global state manager for Rust

Crates.io Version Total Downloads size License

IntroductionFeaturesInstallationUsageAdvanced UsageApi ReferenceContributing

This library is a global state management solution for Rust projects, inspired by state management concepts from various ecosystems. Our goal is to provide a robust and easy-to-use tool for Rust developers who need a centralized state management system in their applications.

Table of Contents

  1. Introduction
  2. Features
  3. Installation
  4. Usage
  5. Advanced Usage
  6. API Reference
  7. Best Practices
  8. Contributing
  9. License

Introduction

The Rustato Library is a powerful and flexible solution for managing application state in Rust projects. It provides a simple and intuitive API for creating, accessing, and modifying states, as well as registering callbacks for state changes. This library is particularly useful for applications that require a centralized state management system, such as desktop applications, web servers, or complex CLI tools.

Features

  • Global state management with a singleton pattern
  • Type-safe state creation and access
  • Automatic generation of getters and setters
  • Event system for state changes
  • Thread-safe state manipulation
  • Easy integration with existing Rust projects
  • Macro-based API for reduced boilerplate

Installation

To use the Rustato Library in your project, add the following to your Cargo.toml:

[dependencies]
rustato = "0.1.1"

Then, add the following to your main Rust file:

use rustato::*;

Usage

Creating States

To create a new state, use the create_state! macro:

use rustato::*;

create_state!(struct AppState {
    status: String,
    window_visible: bool,
    user_count: u32,
});

This macro creates a new struct AppState with the specified fields and registers it with the global state manager.

Accessing States

To access a state for read-only operations, use the get_state! macro and call the read mode:

let app_state = get_state!(AppState).read();

println!("Current status: {}", app_state.status);
println!("Window visible: {}", app_state.window_visible);
println!("User count: {}", app_state.user_count);

Modifying States

To modify a state, use the get_state! macro and call the write mode:

let mut app_state = get_state!(AppState).write();

app_state.status = "Running".to_string();
app_state.window_visible = true;
app_state.user_count = 42;

Registering Callbacks

To register a callback that will be called when a state changes, use the on_state_change! macro:

on_state_change!(CounterState, |field_changed: &str, state: &CounterState| {
    println!("Field changed: {}", field_changed);
    println!("New counter value: {}", state.counter);
});

Advanced Usage

Using with Tauri

The Rustato Library can be easily integrated with Tauri applications. Here's an example of how to use it in a Tauri app:

use rustato::*;
use tauri::Manager;

#[tauri::command]
fn increment_counter() -> i32 {
    let mut app_state = get_state!(AppState).write();
    let new_value = app_state.counter + 1;
    app_state.counter = new_value;
    new_value
}

fn main() {
    create_state!(AppState {
        counter: i32,
    });

    tauri::Builder::default()
        .setup(|app| {
            let app_handle = app.handle();
            on_state_change!(AppState, |field_changed: &str, state: &CounterState| {
                if field_changed == "counter" {
                    app_handle.emit_all("counter-changed", state.counter).unwrap();
                }
            });
            Ok(())
        })
        .invoke_handler(tauri::generate_handler![increment_counter])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

In this example, we create an AppState with a counter field. We register a callback that emits a Tauri event whenever the counter changes. The increment_counter function is exposed as a Tauri command that increments the counter and returns the new value.

Using in Multithreaded Applications

The Rustato Library is designed to be thread-safe. Here's an example of using it in a multithreaded application:

use rustato::*;
use std::thread;

fn main() {
    create_state!(struct SharedState {
        value: i32,
    });

    let handles: Vec<_> = (0..10).map(|i| {
        thread::spawn(move || {
            let mut state = get_state!(SharedState).write();
            state.value += i;
        })
    }).collect();

    for handle in handles {
        handle.join().unwrap();
    }

    let final_state = get_state!(SharedState).read();
    println!("Final value: {}", final_state.value);
}

In this example, we create 10 threads that all modify the same shared state. The library ensures that these modifications are thread-safe.

Custom State Types

You can use custom types in your states as long as they implement the necessary traits. Here's an example:

use rustato::*;

#[derive(Clone, Default)]
struct User {
    id: u64,
    name: String,
    email: String,
}

create_state!(struct UserState {
    current_user: User,
    logged_in: bool,
});

fn main() {
    {
        let mut state = get_state!(UserState).write();
        state.current_user = User {
            id: 1,
            name: "John Doe".to_string(),
            email: "[email protected]".to_string(),
        };
        state.logged_in = true;
    }

    let user_state = get_state!(UserState).read();
    println!("Current user: {:?}", user_state.current_user);
    println!("Logged in: {}", user_state.logged_in);
}

API Reference

Macros

  • create_state!(struct_definition): Creates a new state with the given ID and struct definition.
  • get_state!(struct_definition).read() -> &State: Returns a reference to the state with the given struct definition.
  • get_state!(struct_definition).write() -> &mut State: Returns a mutable reference to the state with the given struct definition.
  • on_state_change!(struct_definition, callback: Fn(&str, &State)): Registers a callback to be called when the state with the given struct definition changes.

StateManager

  • StateManager::new() -> StateManager: Creates a new StateManager instance.
  • StateManager::register_state<T: 'static + Clone + Send + Sync>(&self, id: &str, state: T): Registers a new state with the given ID and initial value.
  • StateManager::get_state<T: 'static + Send + Sync>(&self, id: &str) -> Option<State<T>>: Returns the state with the given ID, if it exists.
  • StateManager::register_callback<T: 'static + Send + Sync>(&self, id: &str, callback: StateChangeCallback<T>): Registers a callback for the state with the given ID.
  • StateManager::notify_state_change<T: 'static + Send + Sync>(&self, id: &str, field: &str, state: &T): Notifies registered callbacks about a state change.

Best Practices

  1. State Granularity: Create separate states for different parts of your application to improve performance and reduce unnecessary updates.

  2. Immutable Access: Use get_state!(struct_definition).read() when you only need to read state values to prevent accidental modifications.

  3. Callback Efficiency: Keep callbacks lightweight and avoid performing heavy computations or I/O operations directly in the callback. Instead, use the callback to trigger other parts of your application.

  4. State Initialization: Initialize your states as early as possible in your application lifecycle, preferably during the setup phase.

  5. Error Handling: Always handle potential errors when accessing states, especially when using get_state!(struct_definition).read() or get_state!(struct_definition).write() with an unknown struct definition.

  6. Thread Safety: While the library is designed to be thread-safe, be cautious when sharing state across threads and consider using appropriate synchronization primitives when necessary.

  7. Testing: Create unit tests for your state management logic to ensure that state changes and callbacks work as expected.

Contributing

Contributions to the Rustato Library are welcome! Please feel free to submit issues, fork the repository and send pull requests!

When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.

Please note we have a code of conduct, please follow it in all your interactions with the project.

License

This project is licensed under the MIT License - see the LICENSE file for details.

You might also like...
Transform jsx/tsx files to reactive views in js/ts to use in Web Components, insert into DOM or integrate with other libraries/frameworks
Transform jsx/tsx files to reactive views in js/ts to use in Web Components, insert into DOM or integrate with other libraries/frameworks

viewmill Features | Installation | Getting Started | Notes | Examples viewmill is aimed to create complex UIs from a simple form of JSX. It statically

Leptos Query - a robust asynchronous state management library for Leptos,

Leptos Query is a robust asynchronous state management library for Leptos, providing simplified data fetching, integrated reactivity, server-side rendering support, and intelligent cache management.

Rust File Management CLI is a command-line tool written in Rust that provides essential file management functionalities. Whether you're working with files or directories, this tool simplifies common file operations with ease.

Rust FileOps Rust File Management CLI is a command-line tool written in Rust that provides essential file management functionalities. Whether you're w

A crate that allows you to mostly-safely cast one type into another type.

A crate that allows you to mostly-safely cast one type into another type. This is mostly useful for generic functions, e.g. pub fn fooS(s: S) {

A procedural macro that copy-pastes match arms for new type variant enums.

All the same! If you ever had code that looks like this: use std::io; use std::pin::Pin; use std::task::{Context, Poll}; use tokio::io::AsyncWrite; us

Fully-typed, async, reusable state management and synchronization for Dioxus 🧬

dioxus-query 🦀 ⚡ Fully-typed, async, reusable state management and synchronization for Dioxus 🧬. Inspired by TanStack Query. ⚠️ Work in progress ⚠️

An html macro for dioxus applications.

dioxus html macro This crate offers an html! like macro for dioxus applications. It expands to the equivalent rsx! macro call you would have made othe

An attribute macro to simplify writing simple command line applications.

fncli An attribute macro to simplify writing simple command line applications. Example #[fncli::cli] fn main(a: i32, b: i32) { println!("{}", a +

A fast, powerful, and safe interpreter written in rust!

Kraber A fast, powerful, and safe programming language written in rust! About It packs a punch like the Kraber .50-Cal. Kraber is designed to be minim

Owner
BiteCraft
Open source tools and apps
BiteCraft
Fully-typed global state management with a topics subscription system for Dioxus 🧬

dioxus-radio ???? ⚠️ Work in progress !! ⚠️ . Fully-typed global state management with a topics subscription system for Dioxus ??. Who is this for You

Dioxus Community 6 Dec 14, 2023
🚀 A blazingly fast easy to use dotfile and global theme manager written in Rust

GTHEME A blazingly fast easy to use dotfile and global theme manager for *NIX systems written in Rust ?? Demo using wip desktop. To check out more des

David Rodriguez 19 Nov 28, 2022
A thread-safe signal/slot library based on boost::signals2

About signals2 is a thread-safe signal/slot library based on the boost::signals2 C++ library. Signals are objects that contain a list of callback func

Christian Daley 15 Dec 21, 2022
Wrapper over MMTk to simplify integration with runtimes: thread management system, object header, root scanning

vmkit A library which provides bunch of building blocks to make a VM in Rust. Feautures MMTK integration out of the box Thread management provided by

null 3 Aug 31, 2024
Thread-safe cell based on atomic pointers to externally stored data

Simple thread-safe cell PtrCell is an atomic cell type that allows safe, concurrent access to shared data. No std, no data races, no nasal demons (UB)

Nikolay Levkovsky 3 Mar 23, 2024
This is a `Rust` based package to help with the management of complex medicine (pill) management cycles.

reepicheep This is a Rust based package to help with the management of complex medicine (pill) management cycles. reepicheep notifies a person(s) via

Daniel B 24 Dec 13, 2023
EmbedAnything is a powerful python library designed to streamline the creation and management of embedding pipelines

EmbedAnything is a powerful python library designed to streamline the creation and management of embedding pipelines. Built in Rust with no heavy dependencies.

Starlight 39 May 7, 2024
Ergonomic and precise error handling provided by error sets. Inspired by Zig's error set type.

Error Set Error Set simplifies error management by providing a streamlined method for defining errors and easily converting between them. Resultingly,

Henry 61 Jul 24, 2024
ergonomic and precise error handling built atop type-level set arithmetic

terrors - the Rust error handling library Handling errors means taking a set of possible error types, removing the ones that are locally addressible,

Komora 190 Jul 27, 2024
A library that creates a terminal-like window with feature-packed drawing of text and easy input handling. MIRROR.

BearLibTerminal provides a pseudoterminal window with a grid of character cells and a simple yet powerful API for flexible textual output and uncompli

Tommy Ettinger 43 Oct 31, 2022