Skip to main content

InteractionWriter

Trait InteractionWriter 

Source
pub trait InteractionWriter: Send + Sync {
    // Required methods
    fn insert<'life0, 'async_trait>(
        &'life0 self,
        interaction: Interaction,
    ) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait;
    fn insert_replacing_blocking<'life0, 'async_trait>(
        &'life0 self,
        interaction: Interaction,
    ) -> Pin<Box<dyn Future<Output = Result<Vec<InteractionId>, StoreError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait;
    fn begin_resolution<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        account: &'life1 AccountId,
        id: &'life2 InteractionId,
        expected_status: InteractionStatus,
        option_id: OptionId,
        resolved_by: TurnId,
    ) -> Pin<Box<dyn Future<Output = Result<InteractionRecord, StoreError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait;
    fn finish_resolution<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        account: &'life1 AccountId,
        id: &'life2 InteractionId,
        outcome: ResolutionOutcome,
    ) -> Pin<Box<dyn Future<Output = Result<InteractionRecord, StoreError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait;
    fn invalidate_for_case<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        account: &'life1 AccountId,
        case_key: &'life2 CaseKey,
        new_revision: CaseRevision,
        reason: InvalidationReason,
    ) -> Pin<Box<dyn Future<Output = Result<Vec<InteractionId>, StoreError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait;
    fn invalidate_case_cards<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        account: &'life1 AccountId,
        case_key: &'life2 CaseKey,
        reason: InvalidationReason,
    ) -> Pin<Box<dyn Future<Output = Result<Vec<InteractionId>, StoreError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait;
    fn expire_due<'life0, 'async_trait>(
        &'life0 self,
        now: DateTime<Utc>,
    ) -> Pin<Box<dyn Future<Output = Result<Vec<InteractionId>, StoreError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait;
}
Expand description

The write half of the interaction contract.

Required Methods§

Source

fn insert<'life0, 'async_trait>( &'life0 self, interaction: Interaction, ) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Inserts a new Active interaction.

§Errors
  • Other(INVALID_RECORD) when interaction.status is not Active.
  • Conflict when the identifier exists for the account, or when the interaction is blocking and its case already has an open blocking interaction (I5).
Source

fn insert_replacing_blocking<'life0, 'async_trait>( &'life0 self, interaction: Interaction, ) -> Pin<Box<dyn Future<Output = Result<Vec<InteractionId>, StoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Inserts a new Active interaction, invalidating the Active blocking occupant of the same case when there is one. Returns the identifiers it invalidated (empty when the slot was free or the new card is not blocking).

§Errors
  • Other(INVALID_RECORD) when interaction.status is not Active.
  • Conflict when the identifier exists, or when the occupant is Resolving (its commands are executing; it cannot be replaced).
Source

fn begin_resolution<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, account: &'life1 AccountId, id: &'life2 InteractionId, expected_status: InteractionStatus, option_id: OptionId, resolved_by: TurnId, ) -> Pin<Box<dyn Future<Output = Result<InteractionRecord, StoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Compare-and-swap expected_status → Resolving, recording the chosen option and the resolving turn. The only legal source status is Active; the explicit parameter makes a lost race visible to the caller instead of hiding it behind a re-read.

§Errors
  • NotFound when the interaction does not exist for account.
  • Conflict when the current status differs from expected_status, or when expected_status → Resolving is not a legal transition.
Source

fn finish_resolution<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, account: &'life1 AccountId, id: &'life2 InteractionId, outcome: ResolutionOutcome, ) -> Pin<Box<dyn Future<Output = Result<InteractionRecord, StoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Settles a Resolving interaction.

Repeating the call with an outcome whose target status is already the current status and whose data (event ids, failure code) matches is accepted without change. RestoreActive clears the resolving turn, the chosen option and resolved_at so the card is answerable again.

Resolved and Failed leave resolved_at as InteractionWriter::begin_resolution stamped it: it means “when the answer was accepted”. When the commands actually committed is recorded by the command journal and by the events, and is not duplicated here.

§Errors
  • NotFound when the interaction does not exist for account.
  • Conflict when the status is neither Resolving nor the outcome’s target, or when the target status matches but the data differs.
Source

fn invalidate_for_case<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, account: &'life1 AccountId, case_key: &'life2 CaseKey, new_revision: CaseRevision, reason: InvalidationReason, ) -> Pin<Box<dyn Future<Output = Result<Vec<InteractionId>, StoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Invalidates every Active interaction of the case that is bound to a revision other than new_revision and does not declare revision independence. Returns the invalidated identifiers in list order.

§Errors
Source

fn invalidate_case_cards<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, account: &'life1 AccountId, case_key: &'life2 CaseKey, reason: InvalidationReason, ) -> Pin<Box<dyn Future<Output = Result<Vec<InteractionId>, StoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Invalidates every Active interaction of the case, whatever revision it is bound to, and whether or not it declares revision independence. Returns the invalidated identifiers in list order.

This is the operator’s path, not the runtime’s, and it exists because invalidate_for_case cannot express it: a card is invalidated there for having been bound to a revision the case has left, so a decision that has nothing to do with the revision — a workflow rolled back to a version that cannot compile the option the card offers, an account suspended, a card withdrawn — has no way to run. Without it the honest rollback step is to leave the user holding an option nothing will honour.

A Resolving interaction is deliberately left alone: a command it authorized is in flight, and taking the card away underneath it would settle nothing while making the outcome unattributable.

§Errors
Source

fn expire_due<'life0, 'async_trait>( &'life0 self, now: DateTime<Utc>, ) -> Pin<Box<dyn Future<Output = Result<Vec<InteractionId>, StoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Moves every Active interaction whose expires_at <= now to Expired, across all accounts. Returns the expired identifiers in list order.

§Errors

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§