Crete
Crete is a procedural macro that simplifies state management in Rust, in a flexible way.
It generates code for atomic access to a struct's fields using an RwLock and a static store, making it easier to work
with shared state in both synchronous and asynchronous contexts.
Because users can implement practically anything on the struct, Crete allows for a flexible store that can be tailored to a variety of needs. Perhaps you'd like to use to build a Redux-style store. Perhaps you enjoy having a billion custom setters. Perhaps you just enjoy mutating everything directly. Or maybe you even have your own homemade style of setters and getters that you love best?
No restrictions.
Features
-
Ergonomic, intuitive Design
Reduces boilerplate with a straightforward interface for managing state. -
Synchronous and Asynchronous Support
Works seamlessly in both synchronous and asynchronous environments. -
Versatile Clone Support
Adapts to your needs by supporting both cloneable and non-cloneable types. -
No shoehorning
Build the State management style you enjoy best on top... or not.
Details
-
Generates code for atomic, thread-safe access to a struct by defining a static store typically within a module that holds and manages the shared state.
-
Generates unit structs (e.g.
FooField,BarField) and implements aFieldtrait for each, enabling type-safe access and update of individual fields.
This macro produces:
-
A static store (a
LazyLockholding anRwLockprotecting anArcof the struct) whose identifier is based on the struct name (e.g.CRETE_FOOfor a struct namedFoo). -
An implementation of several associated methods on the struct:
new(): constructs a new instance using the struct'sDefaultimplementation. Called byLazyLock.read(): returns anArc-wrapped shared reference to the current state.clone(): (if the struct implementsClone) returns a cloned instance of the stored value.write(self): atomically replaces the current stored value with the provided one.select_ref<F: Field>(&self, field: F) -> &F::FieldType: returns a reference to the selected field.get<F, R>(field: F, f: impl FnOnce(&F::FieldType) -> R) -> R: applies a closure to a shared reference of the selected field.select<F: Field>(field: F) -> F::FieldType: returns a cloned value of the selected field.set<F>(field: F, value: F::FieldType): updates a specific field and writes the new state to the store.update(f: impl FnOnce(&mut Self)): applies a mutation closure to the current state and updates the store.update_async(f: impl AsyncFnOnce(&mut Self)): an asynchronous version ofupdatefor non-blocking mutations.
The generated code leverages std::sync::LazyLock, RwLock, and Arc to ensure that all operations
are safe to use concurrently from multiple threads.
How it works
Static Store
A static store is created for the struct, allowing atomic access to its state:
static #crete_store_ident: LazyLock> =
new;
With a struct like this:
use Crete;
You can now do this:
Async closure support
async
No Clone, No Problem
It works the same way, except:
select()does not exist for fields that are not clone-able.clone()does not exist for the static Struct.
Considerations
You are using RWLock behind the scenes. The usual considerations for locking in multithreaded code apply.
In essence:
- Don't let your thread panic while it holds a write lock.
- If the thread already has acquired a lock, don't try to get it again before it drops.
FAQ
What's with the name?
- It's interestingly confusing with
crate. - It's the name of the island the crate author spends lots of his time at.
License
This project is licensed under the MIT License._