u/AverageHot2647

▲ 5 r/rust

Design of middleware in flowstate

Hey, I'm looking for some guidance on how to design middleware for my crate flowstate.

Flowstate is a library for implementing self-executing workflows as a collection of self-describing states, locally encapsulating their transition logic.

I'd like to add middleware, which would at a basic level allow wrapping states and workflows.

I've implemented flowstate_middleware_tracing as a test case for this, as I don't want to "special case" the features I personally need. This crate depends on the unstable_middleware feature in flowstate. Usage looks like this:

let middleware = TracingMiddleware::default()
    .with_workflow_span(|m: &WorkflowMetadata| {
        info_span!("custom workflow span", my_workflow_name = m.name)
    })
    .with_state_span(|m: &WorkflowStateMetadata| {
        info_span!("custom state span", my_state_name = m.name)
    });

let result = workflow.run_with_middleware(middleware);

This works ok, but I'd really like to add some way to expose per-state fields which can be added to the span.

Initially, I had thought to add a WorkflowState::fields method. However, tracing doesn't allow the addition of dynamic fields, so this doesn't really work.

The only solution I can think of is to allow the WorkflowState implementation to define a make_span method, but I can't think of any way to do this that would not require a "special case" (i.e. only works for tracing) implementation in flowstate.

If anyone has any ideas, I'm open to suggestions!

The litmus test would be that the middleware implementation is flexible enough to allow flowstate_middleware_tracing to have different fields, message, etc. per state, without requiring flowstate to take a dependency on tracing.

reddit.com
u/AverageHot2647 — 16 hours ago
▲ 52 r/rust

Flowstate - A crate for modelling self-executing workflows as finite state machines

Hey Reddit,

I've been working on a new crate called `flowstate`, which helps model self-executing workflows as FSMs.

It's still very early and I wouldn't recommend it for production use as there's a high chance of breaking changes.

That said, I'd be delighted if people could have a look at the docs and provide constructive feedback.

In particular, I'd be very interested in feedback on:

  1. The documentation - e.g. Is it clear/easy to understand? Is there anything missing? Is there anything that could be improved?
  2. The API design - e.g. Can you think of any ways it could be improved or simplified? Are there any limitations?
  3. Features - e.g. If you were using this in a project, are there any features that you'd want to have but are currently missing?

If anyone has time, and is willing to review the code, that would also be appreciated!

Looking forward to your thoughts <3

---

I don't know if this is strictly necessary, but for the avoidance of any doubt, I'd like to attest that I did not use AI to generate any of the code, documentation, commit messages, etc. etc. for this project.

crates.io
u/AverageHot2647 — 4 days ago
▲ 20 r/rust

I’m currently working on some code where I found myself repeating the same pattern over and over again:

```rs

let thing = match do_something() {

Ok(thing) => thing,

Err(err) => return self.consumes_self(err),

};

```

Importantly, `self.consumes_self` takes ownership of `self`.

Ideally, I would use `map_err`:

```rs

let thing = do_something()

.map_err(|err| self.consumes_self(err))?;

```

However, because this moves `self` it results in errors if I later try to reference `self`.

Is there some way to express this without constantly repeating `Ok(thing) => thing`?

I could (and have) used a macro for this, but I’d prefer a non-macro based solution if possible.

reddit.com
u/AverageHot2647 — 9 days ago