[GH-ISSUE #1488] 🍝 Cleaning up ironbar's structure #4667

Open
opened 2026-05-23 00:54:57 +01:00 by JakeStanger · 1 comment
Owner

Originally created by @esther-ff on GitHub (May 11, 2026).
Original GitHub issue: https://github.com/JakeStanger/ironbar/issues/1488

Originally assigned to: @esther-ff on GitHub.

Basically, the idea is to decouple ironbar's very tangled code and split it into several crates to improve compile times and clarity because it is currently somewhat of a mess.

Me and @JakeStanger have talked a bit about it.

Currently we're at separating the client traits (and a client manager trait) into an ironbar_traits crate, that would allow us to put the implementations of modules into ironbar_modules.

This allows for splitting the "core" code into ironbar_core and the implementation of a registry in the singleton Ironbar struct to avoid any circular dependencies. (the registry will rely on Arc'ing the individual client implementations and downcasting of the references by the module which requires them).

The base client trait would roughly look like this:

trait Client: Debug + Any + ... {
	// error type not yet decided, but it's probably going
	// to be a simple enum.
	fn start(&self) -> Result<T, Err>;
	fn state(&self) -> ClientState;
}

The use of Any allows each module to acquire a dyn Client and downcast it once to hold a concrete Arc pointer to it's desired client while also allowing us to store it in a simple map as described below.

The registry itself would be a HashMap<ClientKind, Arc<dyn Client> or something similar.

Here ClientKind would represent many types of clients used throughout modules.

enum ClientKind {
     Tray,
     Music,
     ...
}

Example to better show off the idea: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=f9ef1a66e92a66bf7b3048cb5dc4f65b

There is a possibility to also use a concurrent lock-free map like this one to allow converting the Rc'd Ironbar singleton into a (lazy) static with a few Mutexes where they would be needed.

I think also the separation of client code if successful would be a great model to handle modules similarly as a similar issue arises with them.

Input on this would be great as this would be somewhat a big overhaul, though it'd be best if the structure of module/client code itself wouldn't be affected much as it could break many PRs.

Originally created by @esther-ff on GitHub (May 11, 2026). Original GitHub issue: https://github.com/JakeStanger/ironbar/issues/1488 Originally assigned to: @esther-ff on GitHub. Basically, the idea is to decouple ironbar's *very* tangled code and split it into several crates to improve compile times and clarity because it is currently somewhat of a mess. Me and @JakeStanger have talked a bit about it. Currently we're at separating the client traits (and a client manager trait) into an `ironbar_traits` crate, that would allow us to put the implementations of modules into `ironbar_modules`. This allows for splitting the "core" code into `ironbar_core` and the implementation of a registry in the singleton `Ironbar` struct to avoid any circular dependencies. (the registry will rely on `Arc`'ing the individual client implementations and downcasting of the references by the module which requires them). The base client trait would roughly look like this: ```rs trait Client: Debug + Any + ... { // error type not yet decided, but it's probably going // to be a simple enum. fn start(&self) -> Result<T, Err>; fn state(&self) -> ClientState; } ``` The use of `Any` allows each module to acquire a `dyn Client` and downcast it once to hold a concrete `Arc` pointer to it's desired client while also allowing us to store it in a simple map as described below. The registry itself would be a `HashMap<ClientKind, Arc<dyn Client>` or something similar. Here `ClientKind` would represent many types of clients used throughout modules. ```rs enum ClientKind { Tray, Music, ... } ``` Example to better show off the idea: <https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=f9ef1a66e92a66bf7b3048cb5dc4f65b> There is a possibility to also use a concurrent lock-free map like [this one](<https://github.com/robclu/leapfrog>) to allow converting the `Rc`'d `Ironbar` singleton into a (lazy) `static` with a few `Mutex`es where they would be needed. I think also the separation of client code if successful would be a great model to handle modules similarly as a similar issue arises with them. Input on this would be great as this would be somewhat a big overhaul, though it'd be best if the structure of module/client code itself wouldn't be affected much as it could break many PRs.
Author
Owner

@JakeStanger commented on GitHub (May 12, 2026):

The current thinking is to split this into lots of small chunks to keep things easy to review and minimise the impact on outstanding PRs. This would roughly be:

  1. Add Client trait and implement for all existing clients. This can be done bit by bit as the trait won't be used by anything at this stage. A suppression can be put on the unused warning if necessary.
  2. Replace the current Clients struct with the new design, based on the playground link above. The expectation is this should not have much of a wider impact, and should play nicely with the existing module-facing API.
  3. Refactor the central Ironbar struct and several related structs to break their logic down into a series of traits. Some of the static code such as the runtime or variable manager will additionally be detached.
  4. Move the trait code into a new ironbar_traits crate, which the main crate depends on.
  5. Move the core code into a new ironbar_core crate, which the main crate depends on. This will depend on the traits crate.
  6. Move the client code into a new ironbar_clients crate, which the main crate depends on. This will depend on the traits crate, and probably the core crate.

These steps will probably take a long time to be fully implemented and may stretch over several releases to soften the blow.

The eventual goal would be a structure that looks like this:

%%{ init: { 'flowchart': { 'curve': 'linear' } } }%%

flowchart TD
    ironbar_traits
    ironbar_core
    ironbar_clients
    ironbar

    ironbar_core --> ironbar_traits
    ironbar --> ironbar_core
    ironbar --> ironbar_clients
    ironbar_clients --> ironbar_traits
    ironbar --> ironbar_traits

This should have quite a few benefits as mentioned above:

  • Decoupled code which is a lot easier to manage
  • Less Rc<RefCell<T>> and Arc<Mutex<T>> spam everywhere (maybe, hopefully)
  • Faster incremental compilation times
  • The clients code can be published separately, allowing other projects to make use of it.
<!-- gh-comment-id:4428904422 --> @JakeStanger commented on GitHub (May 12, 2026): The current thinking is to split this into lots of small chunks to keep things easy to review and minimise the impact on outstanding PRs. This would roughly be: 1. Add `Client` trait and implement for all existing clients. This can be done bit by bit as the trait won't be used by anything at this stage. A suppression can be put on the unused warning if necessary. 2. Replace the current `Clients` struct with the new design, based on the playground link above. The expectation is this should not have much of a wider impact, and should play nicely with the existing module-facing API. 3. Refactor the central Ironbar struct and several related structs to break their logic down into a series of traits. Some of the static code such as the runtime or variable manager will additionally be detached. 4. Move the trait code into a new `ironbar_traits` crate, which the main crate depends on. 5. Move the core code into a new `ironbar_core` crate, which the main crate depends on. This will depend on the traits crate. 6. Move the client code into a new `ironbar_clients` crate, which the main crate depends on. This will depend on the traits crate, and probably the core crate. These steps will probably take a long time to be fully implemented and may stretch over several releases to soften the blow. The eventual goal would be a structure that looks like this: ```mermaid %%{ init: { 'flowchart': { 'curve': 'linear' } } }%% flowchart TD ironbar_traits ironbar_core ironbar_clients ironbar ironbar_core --> ironbar_traits ironbar --> ironbar_core ironbar --> ironbar_clients ironbar_clients --> ironbar_traits ironbar --> ironbar_traits ``` This should have quite a few benefits as mentioned above: - Decoupled code which is a lot easier to manage - Less `Rc<RefCell<T>>` and `Arc<Mutex<T>>` spam everywhere (maybe, hopefully) - Faster incremental compilation times - The clients code can be published separately, allowing other projects to make use of it.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
JakeStanger/ironbar#4667
No description provided.