/// Instruction for creating contacts. See each variant's steps. pubenum CreateContactsInstruction { /// Run [`BeginTransactionInstruction`], wrapping any response into a /// [`CreateContactsResponse::BeginTransactionResponse`].
BeginTransaction(BeginTransactionInstruction),
/// Run [`CommitTransactionInstruction`], wrapping any response into a /// [`CreateContactsResponse::CommitTransactionResponse`].
ReflectAndCommitTransaction(CommitTransactionInstruction),
}
/// Possible response for an [`CreateContactsInstruction`]. pubenum CreateContactsResponse { /// Possible response for an inner [`BeginTransactionInstruction`].
BeginTransactionResponse(BeginTransactionResponse),
/// Possible response for an inner [`CommitTransactionInstruction`].
CommitTransactionResponse(CommitTransactionResponse),
}
/// Result of creating contacts. pubstruct CreateContactsResult { /// A list of identities that have been created. pub added: Vec<ThreemaId>,
}
/// Result of polling a [`CreateContactsTask`]. pubtype CreateContactsLoop = TaskLoop<CreateContactsInstruction, CreateContactsResult>;
// Add tracing span // // TODO(LIB-16): This should be applied to the whole task somehow let _span = tracing::info_span! { "contacts", contacts = ?identities }.entered();
// Non-MD: Add contacts and done if context.d2x.is_none() { let added = Self::add_contacts(&mut *context.contacts.borrow_mut(), state.contacts)?; return Ok(( Self::Done,
CreateContactsLoop::Done(CreateContactsResult { added }),
));
}
// MD: We need to create a transaction.
// Precondition: At least one of the contacts has not yet been added. let contact_provider = Rc::clone(&context.contacts); let precondition = Box::new(move || {
Ok( if contact_provider.borrow().has_many(&identities)? < identities.len() {
PreconditionVerdict::Continue
} else {
PreconditionVerdict::Abort
},
)
});
// Begin the transaction in the next state Self::poll_begin_transaction(
context,
BeginTransactionState {
contacts: state.contacts,
transaction_task: BeginTransactionSubtask::new(
precondition,
protobuf::d2d::transaction_scope::Scope::ContactSync,
None,
),
},
)
}
// Poll until the transaction is in progress match state.transaction_task.poll(d2x_context)? {
BeginTransactionLoop::Instruction(instruction) => { return Ok(( Self::BeginTransaction(state),
CreateContactsLoop::Instruction(CreateContactsInstruction::BeginTransaction(instruction)),
));
},
BeginTransactionLoop::Done(result) => match result {
BeginTransactionResult::TransactionInProgress => {},
BeginTransactionResult::TransactionAborted => {
info!("Contacts already added"); return Ok(( Self::Done,
CreateContactsLoop::Done(CreateContactsResult { added: vec![] }),
));
},
},
}
// Contacts may have been added in between states, so we need to filter affected letmut contacts = state
.contacts
.into_iter()
.filter_map(|contact| match context.contacts.borrow().has(contact.identity) {
Ok(true) => Some(Ok(contact)),
Ok(false) => None,
Err(error) => Some(Err(error)),
})
.collect::<Result<Vec<Contact>, ProviderError>>()?;
// Encode and encrypt reflection messages containing the contacts to be added let (reflect_messages, nonces) = contacts
.iter_mut()
.map(|contact| { // Bump _created-at_
contact.created_at = utc_now_ms();
// Add contacts let expected: Vec<ThreemaId> = state.contacts.iter().map(|contact| contact.identity).collect(); let added = Self::add_contacts(&mut *context.contacts.borrow_mut(), state.contacts)?; if expected.len() != added.len() { let message = "One or more contact were added unexpectedly during a transaction";
error!(?expected, ?added, message); return Err(CspE2eProtocolError::DesyncError(message.to_owned()));
}
// TODO(SE-510): Schedule fetching gateway-defined profile picture here, if contact was // added and if necessary.
// Added
Ok(identities)
}
}
/// Task for creating contacts. #[derive(Debug, Name)] pubstruct CreateContactsTask {
state: State,
} impl CreateContactsTask { /// Create a new task for creating contacts. /// /// Note: The contacts need to have already been looked up at this point. #[must_use] pubfn new(contacts: Vec<ContactInit>) -> Self { Self {
state: State::Init(InitState { contacts }),
}
}
/// Poll to advance the state. /// /// # Errors /// /// Returns [`CspE2eProtocolError`] for all possible reasons. #[tracing::instrument(skip_all, fields(?self))] pubfn poll(
&mutself,
context: &mut CspE2eProtocolContext,
) -> Result<CreateContactsLoop, CspE2eProtocolError> { let result = match mem::replace(
&mutself.state,
State::Error(CspE2eProtocolError::InvalidState(formatcp!( "{} in a transitional state",
CreateContactsTask::NAME
))),
) {
State::Error(error) => Err(error),
State::Init(state) => State::poll_init(context, state),
State::BeginTransaction(state) => State::poll_begin_transaction(context, state),
State::ReflectAndCommitTransaction(state) => {
State::poll_reflect_and_commit_transaction(context, state)
},
State::Done => Err(CspE2eProtocolError::InvalidState(formatcp!( "{} already done",
CreateContactsTask::NAME
))),
}; match result {
Ok((state, instruction)) => { self.state = state;
debug!(state = ?self.state, "Changed state");
Ok(instruction)
},
Err(error) => { self.state = State::Error(error.clone());
debug!(state = ?self.state, "Changed state to error");
Err(error)
},
}
}
/// Possible results after handling a [`CreateContactsInstruction`]. /// /// # Errors /// /// Returns [`CspE2eProtocolError`] for all possible reasons. #[tracing::instrument(skip_all, fields(?self))] pubfn response(&mutself, response: CreateContactsResponse) -> Result<(), CspE2eProtocolError> { match response {
CreateContactsResponse::BeginTransactionResponse(response) => { let State::BeginTransaction(state) = &mutself.state else { return Err(CspE2eProtocolError::InvalidState(formatcp!( "Must be in '{}' state",
State::BEGIN_TRANSACTION
)));
};
state.transaction_task.response(response)
},
CreateContactsResponse::CommitTransactionResponse(response) => { let State::ReflectAndCommitTransaction(state) = &mutself.state else { return Err(CspE2eProtocolError::InvalidState(formatcp!( "Must be in '{}' state",
State::REFLECT_AND_COMMIT_TRANSACTION
)));
};
state.transaction_task.response(response);
Ok(())
},
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.