Saying what only a step knows
What you want to do
The events say who decided what, and when, but one thing only the shipping knows: that the parcel has left. You want the shipping to say so in the journey's own stream of events, under its own name, so that the record of who did what is complete and in order, instead of printing it on the side.
The code
The shipping asks for its step reporter, a parameter of run marked #[reporter], of type StepReporter<'_>, next to its service and its contributor. Where it used to print, it now calls info with a message.
/// Ships an order through the carrier, with its receipt in the parcel, gives back its tracking
/// number, and says when the parcel has left.
#[derive(Step)]
struct Ship {
#[input]
order: String,
#[input]
weight: i64,
#[input]
receipt: String,
}
#[step]
impl Ship {
fn run(
self,
#[service] carrier: &dyn Carrier,
#[contributor] mut contributor: Contributor<'_>,
#[reporter] mut reporter: StepReporter<'_>,
) -> Result<Outcome, Error> {
let tracking = carrier.book(&self.order);
contributor.contribute("tracking", tracking);
if !carrier.collects(self.weight) {
return Self::fail("too-heavy");
}
reporter.info(format!("the parcel leaves with receipt {}", self.receipt))?;
Self::succeed()
}
}The trait Carrier it asks for is unchanged since chapter 7 showed it.
The log is chapter 8's, with one more arm in its match, for the step's message:
EventBody::StepInfo { step, message, .. } => Some(format!("{}: {message}", step.step)),The rest of the shop is unchanged, and runs Ann's and Gil's orders again. It prints:
Ann's order:
log #1 journey_started order, price, weight
log #2 attempt_started charge
log #3 service_provided charge gets payments from bank, a ch09_saying_what_only_a_step_knows::Bank
the bank charges 40 for order A-1
log #4 step_succeeded charge
log #5 contribution_committed receipt
log #6 attempt_started ship
log #7 service_provided ship gets carrier from main_carrier, a ch09_saying_what_only_a_step_knows::MainCarrier
the carrier books order A-1
log #8 step_info ship: the parcel leaves with receipt R-A-1
log #9 step_succeeded ship
log #10 contribution_committed tracking
log #11 journey_succeeded
the journey succeeded, with receipt R-A-1 and tracking T-A-1
Gil's order:
log #1 journey_started order, price, weight
log #2 attempt_started charge
log #3 service_provided charge gets payments from bank, a ch09_saying_what_only_a_step_knows::Bank
the bank charges 45 for order A-7
log #4 step_succeeded charge
log #5 contribution_committed receipt
log #6 attempt_started ship
log #7 service_provided ship gets carrier from main_carrier, a ch09_saying_what_only_a_step_knows::MainCarrier
the carrier books order A-7
log #8 step_failed ship: too-heavy
log #9 step_given_up ship
log #10 journey_failed ship
the journey failed, with receipt R-A-7 and tracking noneHow it works
- A step can add events of its own.
infoemits astep_infoevent with the step's message, which says whatever the step chooses, here the receipt it put in the parcel. The step reporter has two more such methods,warninganderror, which emitstep_warningandstep_error, for something that went wrong without being the step's outcome. Each has a form ending in_with, such asinfo_with, which adds some data to the event: a value, captured when the event is emitted. - A step's event is part of the journey's stream. The executor stamps it with the step and the attempt it comes from, so the stream says who said it, and gives it the next number, saying when, so in Ann's journey
step_infois event 8, between the shipping'sservice_providedand itsstep_succeeded. The log receives it like any other event, and a reporter tells it apart by its kind. - It is delivered before the call returns. When
inforeturns, every reporter has received the event, so the stream keeps the order in which things happened.inforeturns aResult, since delivering an event can fail, and the shipping passes such a failure on with?. The log never fails, so hereinfoalways returnsOk. - The step reporter belongs to the attempt. Like the contributor and the services, it is lent to the step for one attempt, as a parameter of
run, and serves no other. - A step reports only what it alone knows. The shipping doesn't emit events for its start, its outcome or its contribution: the executor already records all of that, the same way for every step. A step never emits what the executor already records. Its own events are for what happens inside it, such as the parcel leaving, which nothing outside the step can see. Gil's shipping never reaches that line, so his journey has no
step_info.
The shop's log now holds everything that happened to an order, in order. The shop's customer service wants something else from the same events: the orders that failed, and nothing more. The log is not the place for that, and changing it to suit the desk would change what the log is for.
Outcomes
We have shown how a step adds what only it knows to the journey's events.
| The usual way | With Itinera |
|---|---|
| A function usually logs its own progress in words of its own, apart from any record of what was decided about it, so the two must be read together. | What a step says goes into the same stream as every decision about it, stamped with the step and attempt that said it. |