#async #state #handler #business #actix-web #complex #frame

intrepid

Manage complex async business logic with ease

8 releases

new 0.3.0 Dec 9, 2024
0.2.0 Nov 13, 2024
0.1.6 Nov 7, 2024
0.1.4 Oct 30, 2024
0.1.0 Oct 11, 2023

#440 in Game dev

Download history 104/week @ 2024-09-09 169/week @ 2024-09-16 12/week @ 2024-09-23 9/week @ 2024-09-30 31/week @ 2024-10-07 13/week @ 2024-10-14 40/week @ 2024-10-21 153/week @ 2024-10-28 293/week @ 2024-11-04 145/week @ 2024-11-11 19/week @ 2024-11-18

463 downloads per month

MIT license

12KB

Intrepid

Intrepid is a magic handler kit where most of the boilerplate work is already done. For cases where you'd like to get the ergonomics of Bevy or Actix-Web, but for your own stuff.

TODO

  • Demote pattern as a central concern, put it into routing and extract packages. It's mostly gone now anyway, because of path_tree. It's mostly just a matter of clean-up.
  • Closer integration between actions and tower services, so that more Layers can be put into place on actions and systems. Right now, the impediment is that actions and systems have points where they're "not quite ready" to be used as a service, and unfortunately we want them to be configured as a service at that point. In other words, there probably needs to be some kind of concept introduced that lets unrealized actions/systems store layers for later. Maybe by giving them an idea of the kind of service they'll "eventually" be, along with a place to store a Layer impl that will work when that day comes?

About the current state of handlers

Handler might be "SimpleHandler", or in other words, it results in an ecosystem with a Frame, State dependency. The "canon handler" could be better designed as a wrapper trait that adds two specific trait dependencies.

  • The ExtractFrame trait, which describes a future that resolves to an implementing type. The extract frame approach lets us add asynchrony to this portion, and it lets us change (Frame, State) into a form where state could be hypothetically extracted from a frame context, for example FrameContext<State>.
  • The IntoFrame trait, which describes a response type that is guaranteed to always resolve to something that fits the expectations of the surrounding ecosystem. Right now that guarantee isn't there.

Together both of these traits could also improve documentation / error messaging.

Finally, if we have an async fn(impl ExtractFrame) -> impl IntoFrame type of any sort, we know we can do the Stream / Sink guarantee in outer levels with no additional work, because we'll know if the State of our FrameContext<State> contains anything that implements our repo traits.

More on a second trait layer

This might be the key to getting our composition boundaries in shape. Right now we need to know that something can turn into a Frame in the innermost layer, and then as we progress to more concrete layers, we have to just say "ok give me a Frame" in our generic notation or else shit gets cray.

The key to getting these layers into a more generic umbrella might be to create a secondary layer of traits, as outlined above with ExtractFrame and IntoFrame.

Even more about it

There's undoubtedly an opportunity to create a different function signature for handlers. Right now we have the ability to model out logic like this, and it's very powerful:

async fn(data, state) -> data

But, this might be more powerful:

async fn(data, state) -> (data, effects)

In other words, "things for the program to do", right now, can happen in the function, and "things to delegate to the program for whenever", that can go into effects.

This isn't a pressing need! Because right now, state can represent the application machinery, and it's highly customizable in that regard. So, for example, application-modeled persistence and that kind of side-effect are already something we can register there.

Dependencies

~59MB
~1M SLoC