#observer #resources #registry

adar-registry

Advanced Architecture (ADAR) is a collection of architectural tools that help you write more readable and performant code

1 unstable release

0.1.0 Nov 12, 2025

#921 in Data structures

MIT license

40KB
865 lines

Advanced Architecture (ADAR)

Crates.io Downloads Docs

Adar is a collection of architectural tools that help you write more readable and performant code.

Disclaimer: This crate uses some unsafe code. Please refer to the comments in the source code for details. (PRs are welcome to convert it to safe code)

Registry

Registry is a container that lets you control the lifetime of elements through an Entry struct returned after calling Registry::register(). Entry cannot be cloned, but it can be cast to a generic type using Entry::as_generic(), which makes it possible to store entries from multiple registries in a single container. Registry can be cloned and behaves like an Arc. Whenever the data is mutated, an internal RwLock is locked. You can also run code when an element is removed by using the set_remove_callback() callback.

Example

use adar_registry::prelude::*;

struct MenuItem(pub &'static str);
struct StyleSheet(pub &'static str);

fn main() {
    // Original website setup
    let menu = Registry::<MenuItem>::new();
    let styles = Registry::<StyleSheet>::new();
    let mut website_store = vec![];

    website_store.push(menu.register(MenuItem("Home")).as_generic());
    website_store.push(menu.register(MenuItem("About")).as_generic());
    website_store.push(styles.register(StyleSheet("website.css")).as_generic());

    print_state("Original website", &menu, &styles);

    // Loading an extension which registers new resources
    let mut extension_store = vec![];
    extension_store.push(menu.register(MenuItem("Weather")).as_generic());
    extension_store.push(menu.register(MenuItem("News")).as_generic());
    extension_store.push(styles.register(StyleSheet("extension.css")).as_generic());

    print_state("After extension is loaded", &menu, &styles);

    // Unloading the extension
    drop(extension_store);

    print_state("After extension is unloaded", &menu, &styles);
}

fn print_state(step: &'static str, menu: &Registry<MenuItem>, styles: &Registry<StyleSheet>) {
    println!("{}", step);
    println!("\tMenu:");
    for (e, item) in menu.read().iter() {
        println!("\t\t{}: {}", e, item.0);
    }
    println!("\tStyleSheets:");
    for (e, item) in styles.read().iter() {
        println!("\t\t{}: {}", e, item.0);
    }
}// All of the different kind of resources registered by the extension are unloaded here
Click to see the output >> cargo run --example registry_extension
Original website
        Menu:
                0: Home
                1: About
        StyleSheets:
                0: website.css
After extension is loaded
        Menu:
                0: Home
                1: About
                2: Weather
                3: News
        StyleSheets:
                0: website.css
                1: extension.css
After extension is unloaded
        Menu:
                0: Home
                1: About
        StyleSheets:
                0: website.css

RegistryMap

RegistryMap is similar to Registry. But you need to identify each element in the registry with a key. The key need to be provided during register() and you can later get the elements using get() or get(). RegistryMap uses a BTreeMap internally.

Example

use adar_registry::prelude::*;

trait EndPoint {
    fn execute(&self);
}

struct GetUser;

impl EndPoint for GetUser {
    fn execute(&self) {
        println!("Getting user");
    }
}

fn main() {
    let registry = RegistryMap::<&'static str, Box<dyn EndPoint + Send + Sync + 'static>>::new();
    let _entry = registry.register("get_user", Box::new(GetUser));

    registry.read().get(&"get_user").unwrap().execute();
}
Click to see the output >> cargo run --example registry_map
Getting user

Event

Event is a lightweight wrapper around Registry. It provides an implementation of an event/observer architecture.
Please note that during event dispatch the Registry remains locked. This means that you cannot add elements to the registry from the callbacks. Also keep your observers lightweight!

Example

use adar_registry::prelude::*;

fn main() {
    // Create an event and register two observers.
    let event: Event<(u32, String)> = Event::new();
    let _entry1 = event.register_observer(|data: &(u32, String)| {
        println!("Observer #1 called: {:?}", data);
    });
    let entry2 = event.register_observer(|data: &(u32, String)| {
        println!("Observer #2 called: {:?}", data);
    });

    // Since all entries are still in scope all the observers will be called.
    event.dispatch((1, "First event".into()));

    // After dropping entry2. The associated observer will be dropped.
    drop(entry2);
    event.dispatch((2, "Second event".into()));
}
Click to see the output >> cargo run --example registry_extension
Observer #1 called: (1, "First event")
Observer #2 called: (1, "First event")
Observer #1 called: (2, "Second event")

TracedRegistry

TracedRegistry is an extension of Registry. It enables you to register multiple observers that handle registering or unregistering elements.

Example

use adar_registry::prelude::*;

fn main() {
    let registry = TracedRegistry::<&'static str>::new();
    let _observer = registry.register_observer(|(event, entry, value): &_| {
        println!("{:?}, {:?}, {}", event, entry, value)
    });

    let foo = registry.register("foo");
    let bar = registry.register("bar");
    drop(foo);
    let baz = registry.register("baz");
    drop(bar);
    drop(baz);
}
Click to see the output >> cargo run --example registry_extension
Register, 0, foo
Register, 1, bar
UnRegister, 0, foo
Register, 2, baz
UnRegister, 1, bar
UnRegister, 2, baz

No runtime deps