# Giving the rescue step a service of its own

## What you want to do

The bank now answers which card replaced an expired one from its card registry, a system of its own in each region, and its library hands back the registry's client already boxed. You want the rescue step to ask the registry instead of the payment system, and no other step to have it.

## The code

The shop is [chapter 34](https://itinera-dev.org/book/rust/34-saying-which-attempt-took-the-money.md)'s. The registry is a service with a trait of its own, `Cards`, and the bank's library makes its client for a region:

Rust, from the program [`ch35_giving_the_rescue_step_a_service_of_its_own`](https://github.com/itinera-dev/book/blob/main/rust/examples/ch35_giving_the_rescue_step_a_service_of_its_own.rs)

```rust
/// A card registry: it says which card replaced one, if any did.
trait Cards: Send + Sync {
    fn replacement(&self, card: &str) -> Option<String>;
}

/// The bank's own library: it connects to the bank's card registry for a region, and hands back
/// its client, boxed.
fn card_registry(region: &str) -> Box<dyn Cards> {
    Box::new(Registry {
        region: region.to_owned(),
        this_year: 2026,
    })
}
```

Rust, the same in its asynchronous form, from the program [`ch35_giving_the_rescue_step_a_service_of_its_own_async`](https://github.com/itinera-dev/book/blob/main/rust/examples/ch35_giving_the_rescue_step_a_service_of_its_own_async.rs)

```rust
/// A card registry: it says which card replaced one, if any did. Its answer comes over the network.
#[async_trait]
trait Cards: Send + Sync {
    async fn replacement(&self, card: &str) -> Option<String>;
}

/// The bank's own library: it connects to the bank's card registry for a region, and hands back
/// its client, boxed.
fn card_registry(region: &str) -> Box<dyn Cards> {
    Box::new(Registry {
        region: region.to_owned(),
        this_year: 2026,
    })
}
```

The rescue step asks for the registry under the key `cards`, though it calls it `registry`, and reads the order's card, under the key `card`, as `expired`:

Rust, from the program [`ch35_giving_the_rescue_step_a_service_of_its_own`](https://github.com/itinera-dev/book/blob/main/rust/examples/ch35_giving_the_rescue_step_a_service_of_its_own.rs)

```rust
#[derive(RescueStep)]
struct RenewCard {
    #[input("card")]
    expired: String,
    #[reason]
    reason: Reason,
}

#[rescue_step]
impl RenewCard {
    fn run(
        self,
        #[service("cards")] registry: &dyn Cards,
        #[contributor] mut contributor: Contributor<'_>,
    ) -> Result<RescueOutcome, Error> {
        if self.reason.code() != "card-expired" {
            return Self::fail("not-repairable");
        }
        let Some(card) = registry.replacement(&self.expired) else {
            return Self::fail("not-replaced");
        };
        contributor.contribute("card", card);
        Self::succeed()
    }
}
```

Rust, the same in its asynchronous form, from the program [`ch35_giving_the_rescue_step_a_service_of_its_own_async`](https://github.com/itinera-dev/book/blob/main/rust/examples/ch35_giving_the_rescue_step_a_service_of_its_own_async.rs)

```rust
#[derive(RescueStep)]
struct RenewCard {
    #[input("card")]
    expired: String,
    #[reason]
    reason: Reason,
}

#[rescue_step]
impl RenewCard {
    async fn run(
        self,
        #[service("cards")] registry: &dyn Cards,
        #[contributor] mut contributor: Contributor<'_>,
    ) -> Result<RescueOutcome, Error> {
        if self.reason.code() != "card-expired" {
            return Self::fail("not-repairable");
        }
        let Some(card) = registry.replacement(&self.expired).await else {
            return Self::fail("not-replaced");
        };
        contributor.contribute("card", card);
        Self::succeed()
    }
}
```

The workflow's provider for `cards` is attached to the rescue step alone, by its name, and hands over the library's boxed client with the region it is for:

Rust, from the program [`ch35_giving_the_rescue_step_a_service_of_its_own`](https://github.com/itinera-dev/book/blob/main/rust/examples/ch35_giving_the_rescue_step_a_service_of_its_own.rs)

```rust
/// The bank's card registry in Europe, for the charge's rescue step only.
#[provider(steps = ["charge_rescue"])]
fn cards(&self) -> Result<Provided<dyn Cards>, Error> {
    Ok(Provided::from_box(card_registry("eu")).with_data("eu".to_owned()))
}
```

The log's `detail` now hands the data a provider supplied to `supplied`, in its arm for `service_provided`:

Rust, from the program [`ch35_giving_the_rescue_step_a_service_of_its_own`](https://github.com/itinera-dev/book/blob/main/rust/examples/ch35_giving_the_rescue_step_a_service_of_its_own.rs)

```rust
EventBody::ServiceProvided {
    step,
    key,
    provider,
    implementation,
    data,
    ..
} => Some(format!(
    "{} gets {key} from {provider}, a {implementation}{}",
    step.step,
    supplied(data.as_ref())
)),
```

`supplied` writes the data when it is text, and nothing otherwise:

Rust, from the program [`ch35_giving_the_rescue_step_a_service_of_its_own`](https://github.com/itinera-dev/book/blob/main/rust/examples/ch35_giving_the_rescue_step_a_service_of_its_own.rs)

```rust
/// The data a provider supplied with a service, when it is text.
fn supplied(data: Option<&AnyValue>) -> String {
    data.and_then(|data| data.downcast_ref::<String>())
        .map(|data| format!(", with {data}"))
        .unwrap_or_default()
}
```

In the asynchronous form, `Cards` and its implementation take `#[async_trait]`, and the rescue step awaits `replacement`. `order` is [chapter 33](https://itinera-dev.org/book/rust/33-giving-every-reporter-what-it-needs.md)'s, `journey` [chapter 26](https://itinera-dev.org/book/rust/26-running-async-journeys-while-others-wait.md)'s and `Audited` [chapter 34](https://itinera-dev.org/book/rust/34-saying-which-attempt-took-the-money.md)'s. `main` runs Jo's order, whose expired card the rescue step renews, and prints:

What `ch35_giving_the_rescue_step_a_service_of_its_own` prints, recorded in [`ch35_giving_the_rescue_step_a_service_of_its_own.txt`](https://github.com/itinera-dev/book/blob/main/rust/expected/ch35_giving_the_rescue_step_a_service_of_its_own.txt)

```text
Jo's order:
log orders A-8 for Jo #1 journey_started card, customer, gift_card, order, parcel, price
log orders A-8 for Jo #2 attempt_started lookup, attempt 1
log orders A-8 for Jo #3 service_provided lookup gets order_book from ledger, a ch35_giving_the_rescue_step_a_service_of_its_own::Ledger
the order book has no order A-8 yet
log orders A-8 for Jo #4 step_succeeded lookup
log orders A-8 for Jo #5 contribution_committed completed
log orders A-8 for Jo #6 hook_called already_completed: on step success of lookup
log orders A-8 for Jo #7 attempt_started price, attempt 1
log orders A-8 for Jo #8 optional_input_absent discount
log orders A-8 for Jo #9 step_succeeded price
log orders A-8 for Jo #10 contribution_committed amount
log orders A-8 for Jo #11 attempt_started charge, attempt 1
log orders A-8 for Jo #12 service_provided charge gets payments from bank, a ch35_giving_the_rescue_step_a_service_of_its_own::Bank
the bank declines card jo-2024: it has expired
log orders A-8 for Jo #13 step_failed charge: card-expired
log orders A-8 for Jo #14 step_rescuing charge: failure, rescued by charge_rescue
log orders A-8 for Jo #15 attempt_started charge_rescue, attempt 1
log orders A-8 for Jo #16 service_provided charge_rescue gets cards from cards, a dyn ch35_giving_the_rescue_step_a_service_of_its_own::Cards, with eu
the card registry in eu says card jo-2024 was replaced by jo-2029
log orders A-8 for Jo #17 step_succeeded charge_rescue
log orders A-8 for Jo #18 contribution_committed card
log orders A-8 for Jo #19 data_overwritten card
log orders A-8 for Jo #20 step_rescued charge by charge_rescue
log orders A-8 for Jo #21 attempt_started charge, attempt 2
the bank charges 30 for order A-8
log orders A-8 for Jo, receipt R-A-8 #22 global_metadata_set receipt
log orders A-8 for Jo, receipt R-A-8 #23 step_succeeded charge
log orders A-8 for Jo, receipt R-A-8 #24 contribution_committed receipt
log orders A-8 for Jo, receipt R-A-8 #25 hook_called audit: on step success of charge
log orders A-8 for Jo, receipt R-A-8 #26 contribution_committed audit
log orders A-8 for Jo, receipt R-A-8 #27 attempt_started ship, attempt 1
log orders A-8 for Jo, receipt R-A-8 #28 input_adapter_supplied ship gets weight from parcels
log orders A-8 for Jo, receipt R-A-8 #29 service_provided ship gets carrier from carriers, a ch35_giving_the_rescue_step_a_service_of_its_own::MainCarrier
the main carrier books order A-8
log orders A-8 for Jo, receipt R-A-8 #30 step_info ship: the parcel leaves with receipt R-A-8
log orders A-8 for Jo, receipt R-A-8 #31 step_succeeded ship
log orders A-8 for Jo, receipt R-A-8 #32 contribution_committed tracking
log orders A-8 for Jo, receipt R-A-8 #33 optional_input_absent code
email to Jo: Order A-8 is on its way, tracking T-A-8.
log orders A-8 for Jo, receipt R-A-8 #34 hook_called notify: on workflow success
log orders A-8 for Jo, receipt R-A-8 #35 journey_succeeded
the journey succeeded, with receipt R-A-8 and tracking T-A-8
audit: A-8: charge took 30 on attempt 2, receipt R-A-8
```

What the asynchronous form `ch35_giving_the_rescue_step_a_service_of_its_own_async` prints, recorded in [`ch35_giving_the_rescue_step_a_service_of_its_own_async.txt`](https://github.com/itinera-dev/book/blob/main/rust/expected/ch35_giving_the_rescue_step_a_service_of_its_own_async.txt)

```text
log orders A-8 for Jo #1 journey_started card, customer, gift_card, order, parcel, price
log orders A-8 for Jo #2 attempt_started lookup, attempt 1
log orders A-8 for Jo #3 service_provided lookup gets order_book from ledger, a ch35_giving_the_rescue_step_a_service_of_its_own_async::Ledger
the order book has no order A-8 yet
log orders A-8 for Jo #4 step_succeeded lookup
log orders A-8 for Jo #5 contribution_committed completed
log orders A-8 for Jo #6 hook_called already_completed: on step success of lookup
log orders A-8 for Jo #7 attempt_started price, attempt 1
log orders A-8 for Jo #8 optional_input_absent discount
log orders A-8 for Jo #9 step_succeeded price
log orders A-8 for Jo #10 contribution_committed amount
log orders A-8 for Jo #11 attempt_started charge, attempt 1
log orders A-8 for Jo #12 service_provided charge gets payments from bank, a ch35_giving_the_rescue_step_a_service_of_its_own_async::Bank
the bank declines card jo-2024: it has expired
log orders A-8 for Jo #13 step_failed charge: card-expired
log orders A-8 for Jo #14 step_rescuing charge: failure, rescued by charge_rescue
log orders A-8 for Jo #15 attempt_started charge_rescue, attempt 1
log orders A-8 for Jo #16 service_provided charge_rescue gets cards from cards, a dyn ch35_giving_the_rescue_step_a_service_of_its_own_async::Cards, with eu
the card registry in eu says card jo-2024 was replaced by jo-2029
log orders A-8 for Jo #17 step_succeeded charge_rescue
log orders A-8 for Jo #18 contribution_committed card
log orders A-8 for Jo #19 data_overwritten card
log orders A-8 for Jo #20 step_rescued charge by charge_rescue
log orders A-8 for Jo #21 attempt_started charge, attempt 2
the bank charges 30 for order A-8
log orders A-8 for Jo, receipt R-A-8 #22 global_metadata_set receipt
log orders A-8 for Jo, receipt R-A-8 #23 step_succeeded charge
log orders A-8 for Jo, receipt R-A-8 #24 contribution_committed receipt
log orders A-8 for Jo, receipt R-A-8 #25 hook_called audit: on step success of charge
log orders A-8 for Jo, receipt R-A-8 #26 contribution_committed audit
log orders A-8 for Jo, receipt R-A-8 #27 attempt_started ship, attempt 1
log orders A-8 for Jo, receipt R-A-8 #28 input_adapter_supplied ship gets weight from parcels
log orders A-8 for Jo, receipt R-A-8 #29 service_provided ship gets carrier from carriers, a ch35_giving_the_rescue_step_a_service_of_its_own_async::MainCarrier
the main carrier books order A-8
log orders A-8 for Jo, receipt R-A-8 #30 step_info ship: the parcel leaves with receipt R-A-8
log orders A-8 for Jo, receipt R-A-8 #31 step_succeeded ship
log orders A-8 for Jo, receipt R-A-8 #32 contribution_committed tracking
log orders A-8 for Jo, receipt R-A-8 #33 optional_input_absent code
email to Jo: Order A-8 is on its way, tracking T-A-8.
log orders A-8 for Jo, receipt R-A-8 #34 hook_called notify: on workflow success
log orders A-8 for Jo, receipt R-A-8 #35 journey_succeeded
order A-8 succeeded
audit: A-8: charge took 30 on attempt 2, receipt R-A-8
```

## How it works

1. **A request can name its key.** Until now, a step's input and service were found under the name of the field or the parameter. `#[service("cards")]` asks for the service under the key `cards`, and `#[input("card")]` reads the data bag's `card` into the field `expired`. The step names what it holds for its own code, and the key says what the journey calls it.
2. **A provider can serve some steps only.** `steps = ["charge_rescue"]` attaches the registry's provider to the rescue step, named by its own name in the workflow, as [chapter 14](https://itinera-dev.org/book/rust/14-repairing-then-trying-once-more.md) showed. The bank's provider, [chapter 12](https://itinera-dev.org/book/rust/12-trying-again.md)'s, attached to no step, still serves every step that asks for `payments`, which is now the charge alone.
3. **A provider can hand over a service already boxed.** `Provided::from_box` takes the `Box<dyn Cards>` the bank's library made, as it is. The box hides the client's own type, so `service_provided`, event 16, names the implementation as the trait, `dyn …::Cards`, where `provide!` named the type inside, as [chapter 8](https://itinera-dev.org/book/rust/08-seeing-what-happened.md) showed.
4. **A provider can supply data with a service.** `with_data` gives the region along with the client, and `service_provided` carries it. The log now writes the data a provider supplied after the implementation: `with eu`. It says which registry served the rescue step, which the boxed type can't. Like everything an event carries, it is for the reporters: the rescue step never sees it.

Jo's card is now renewed by the registry, and only the rescue step can reach it. Jo's order came complete, with its number and its customer's name. But the website has started sending orders before they are finished: one arrives without an order number, another without the customer's name.

## Outcomes

We have shown how a step names the keys of what it asks for, and how a provider serves some steps only, with a service already boxed and data that says which one it is.

| The usual way                                                                                                              | With Itinera                                                                                                |
| -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| A function's parameters often have to be named as its callers name the values, or a layer between them renames everything. | A step names its inputs and services for its own code, and says under which key the journey has each.       |
| A client that a vendor's library builds often hides its type, so nothing records which one a request used.                 | The provider hands the boxed client over as it is, with data saying which one it is, on `service_provided`. |
