use polyc_proto::proto::polychrome::state::v1 as pb;
use polyc_state::{
command::{CommandEnvelope, CommandMetadata, ResourceBounds},
digest::ContentDigest,
error::StateError,
id::{Audience, CommandId, NamespaceId, Purpose},
persona::{
AdministrationDependency, ExpectedEntry, IdentityLink, LinkAttemptKey, LinkAttemptKind,
LinkAttemptOwner, LinkAttempts, LinkChallenge, LinkCodeDigest, LinkProvenance,
Participation, ParticipationRole, PersonaMutation, PersonaProfile, PersonaStatus,
PersonaTransaction, ProfileDocument, SearchVisibility, SplitProvenance, directory_scope,
},
revision::Revision,
versioned::{MAX_MUTATIONS_PER_TRANSACTION, MAX_TRANSACTION_PAYLOAD_BYTES, authority},
};
use crate::wire::{Kernel, fixed_bytes, known, malformed, required};
pub(crate) fn identity_to_wire(value: &authority::ExternalIdentity) -> pb::PersonaExternalIdentity {
pb::PersonaExternalIdentity {
surface: value.surface().as_str().to_owned(),
provider_scope: value.scope().as_str().to_owned(),
subject: value.subject().as_str().to_owned(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn identity_from_wire(
value: pb::PersonaExternalIdentity,
) -> authority::ExternalIdentity {
authority::ExternalIdentity::scoped(
authority::SurfaceId::new(value.surface),
authority::IdentityScope::new(value.provider_scope),
authority::SubjectId::new(value.subject),
)
}
fn provenance_to_wire(value: &LinkProvenance) -> pb::PersonaLinkProvenance {
pb::PersonaLinkProvenance {
method: value.method().to_owned(),
linked_by: value.linked_by().to_owned(),
evidence: value.evidence().to_owned(),
verified_at_ms: value.verified_at_ms(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
fn provenance_from_wire(value: pb::PersonaLinkProvenance) -> LinkProvenance {
LinkProvenance::new(
value.method,
value.linked_by,
value.evidence,
value.verified_at_ms,
)
}
pub(crate) fn link_to_wire(value: &IdentityLink) -> pb::PersonaIdentityLink {
pb::PersonaIdentityLink {
identity: buffa::MessageField::some(identity_to_wire(value.identity())),
persona: value.persona().as_str().to_owned(),
display_name: value.display_name().to_owned(),
time_zone: value.time_zone().to_owned(),
first_seen_ms: value.first_seen_ms(),
last_seen_ms: value.last_seen_ms(),
provenance: buffa::MessageField::some(provenance_to_wire(value.provenance())),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn link_from_wire(value: pb::PersonaIdentityLink) -> Result<IdentityLink, StateError> {
Ok(IdentityLink::new(
identity_from_wire(required(
"identity",
"an identity link names its external identity",
value.identity,
)?),
authority::PersonaId::new(value.persona),
value.display_name,
value.time_zone,
value.first_seen_ms,
value.last_seen_ms,
provenance_from_wire(required(
"provenance",
"an identity link records its provenance",
value.provenance,
)?),
))
}
fn document_to_wire(value: &ProfileDocument) -> pb::PersonaProfileDocument {
pb::PersonaProfileDocument {
language: value.language.clone(),
tone: value.tone.clone(),
preferences: value
.preferences
.iter()
.map(|(key, value)| pb::PersonaPreference {
key: key.clone(),
value: value.clone(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
.collect(),
tool_grants: value.tool_grants.clone(),
approval_policy: value.approval_policy.clone(),
turn_quota: value.turn_quota,
auto_review: value.auto_review,
updated_at_ms: value.updated_at_ms,
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
fn document_from_wire(value: pb::PersonaProfileDocument) -> ProfileDocument {
ProfileDocument {
language: value.language,
tone: value.tone,
preferences: value
.preferences
.into_iter()
.map(|entry| (entry.key, entry.value))
.collect(),
tool_grants: value.tool_grants,
approval_policy: value.approval_policy,
turn_quota: value.turn_quota,
auto_review: value.auto_review,
updated_at_ms: value.updated_at_ms,
}
}
pub(crate) fn profile_to_wire(value: &PersonaProfile) -> pb::PersonaProfile {
let (status, target, actor, evidence, at_ms) = match &value.status {
PersonaStatus::Provisional => (
pb::PersonaProfileStatus::PERSONA_PROFILE_STATUS_PROVISIONAL,
String::new(),
String::new(),
String::new(),
0,
),
PersonaStatus::Linked => (
pb::PersonaProfileStatus::PERSONA_PROFILE_STATUS_LINKED,
String::new(),
String::new(),
String::new(),
0,
),
PersonaStatus::Merged {
into,
by,
evidence,
at_ms,
} => (
pb::PersonaProfileStatus::PERSONA_PROFILE_STATUS_MERGED,
into.as_str().to_owned(),
by.clone(),
evidence.clone(),
*at_ms,
),
PersonaStatus::Removed { by, at_ms } => (
pb::PersonaProfileStatus::PERSONA_PROFILE_STATUS_REMOVED,
String::new(),
by.clone(),
String::new(),
*at_ms,
),
};
pb::PersonaProfile {
persona: value.persona.as_str().to_owned(),
display_name: value.display_name.clone(),
created_at_ms: value.created_at_ms,
identities: value.identities.iter().map(identity_to_wire).collect(),
status: status.into(),
lifecycle_target: target,
lifecycle_actor: actor,
lifecycle_evidence: evidence,
lifecycle_at_ms: at_ms,
document: buffa::MessageField::some(document_to_wire(&value.document)),
split: value
.split
.as_ref()
.map_or_else(buffa::MessageField::none, |split| {
buffa::MessageField::some(pb::PersonaSplitProvenance {
from: split.from.as_str().to_owned(),
by: split.by.clone(),
reason: split.reason.clone(),
at_ms: split.at_ms,
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
}),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn profile_from_wire(value: pb::PersonaProfile) -> Result<PersonaProfile, StateError> {
let status = match known("profile.status", value.status)? {
pb::PersonaProfileStatus::PERSONA_PROFILE_STATUS_PROVISIONAL => PersonaStatus::Provisional,
pb::PersonaProfileStatus::PERSONA_PROFILE_STATUS_LINKED => PersonaStatus::Linked,
pb::PersonaProfileStatus::PERSONA_PROFILE_STATUS_MERGED => PersonaStatus::Merged {
into: authority::PersonaId::new(value.lifecycle_target),
by: value.lifecycle_actor,
evidence: value.lifecycle_evidence,
at_ms: value.lifecycle_at_ms,
},
pb::PersonaProfileStatus::PERSONA_PROFILE_STATUS_REMOVED => PersonaStatus::Removed {
by: value.lifecycle_actor,
at_ms: value.lifecycle_at_ms,
},
pb::PersonaProfileStatus::PERSONA_PROFILE_STATUS_UNSPECIFIED => {
return Err(malformed(
"profile.status",
"a profile declares its lifecycle status",
));
}
};
Ok(PersonaProfile {
persona: authority::PersonaId::new(value.persona),
display_name: value.display_name,
created_at_ms: value.created_at_ms,
identities: value
.identities
.into_iter()
.map(identity_from_wire)
.collect(),
status,
document: document_from_wire(required(
"profile.document",
"a profile carries its non-privileged document",
value.document,
)?),
split: value.split.into_option().map(|split| SplitProvenance {
from: authority::PersonaId::new(split.from),
by: split.by,
reason: split.reason,
at_ms: split.at_ms,
}),
})
}
pub(crate) fn participation_to_wire(value: &Participation) -> pb::PersonaParticipation {
pb::PersonaParticipation {
scope: value.scope.as_str().to_owned(),
role: match value.role {
ParticipationRole::Participant => {
pb::PersonaParticipationRole::PERSONA_PARTICIPATION_ROLE_PARTICIPANT
}
ParticipationRole::Initiator => {
pb::PersonaParticipationRole::PERSONA_PARTICIPATION_ROLE_INITIATOR
}
}
.into(),
first_at_ms: value.first_at_ms,
via_persona: value.via_persona.as_ref().map(|id| id.as_str().to_owned()),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn participation_from_wire(
value: pb::PersonaParticipation,
) -> Result<Participation, StateError> {
let role = match known("participation.role", value.role)? {
pb::PersonaParticipationRole::PERSONA_PARTICIPATION_ROLE_PARTICIPANT => {
ParticipationRole::Participant
}
pb::PersonaParticipationRole::PERSONA_PARTICIPATION_ROLE_INITIATOR => {
ParticipationRole::Initiator
}
pb::PersonaParticipationRole::PERSONA_PARTICIPATION_ROLE_UNSPECIFIED => {
return Err(malformed(
"participation.role",
"a participation declares its role",
));
}
};
Ok(Participation {
scope: authority::ScopeId::new(value.scope),
role,
first_at_ms: value.first_at_ms,
via_persona: value.via_persona.map(authority::PersonaId::new),
})
}
pub(crate) fn visibility_to_wire(value: &SearchVisibility) -> pb::PersonaSearchVisibility {
pb::PersonaSearchVisibility {
scope: value.scope.as_str().to_owned(),
hidden: value.hidden,
set_at_ms: value.set_at_ms,
set_by: value.set_by.as_str().to_owned(),
via_persona: value.via_persona.as_ref().map(|id| id.as_str().to_owned()),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn visibility_from_wire(value: pb::PersonaSearchVisibility) -> SearchVisibility {
SearchVisibility {
scope: authority::ScopeId::new(value.scope),
hidden: value.hidden,
set_at_ms: value.set_at_ms,
set_by: authority::PersonaId::new(value.set_by),
via_persona: value.via_persona.map(authority::PersonaId::new),
}
}
pub(crate) fn challenge_to_wire(value: &LinkChallenge) -> pb::PersonaLinkChallenge {
pb::PersonaLinkChallenge {
digest: value.digest.as_str().to_owned(),
persona: value.persona.as_str().to_owned(),
bound_identity: value
.bound_identity
.as_ref()
.map_or_else(buffa::MessageField::none, |identity| {
buffa::MessageField::some(identity_to_wire(identity))
}),
invited_by: value
.invited_by
.as_ref()
.map(|persona| persona.as_str().to_owned()),
created_at_ms: value.created_at_ms,
expires_at_ms: value.expires_at_ms,
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn challenge_from_wire(value: pb::PersonaLinkChallenge) -> LinkChallenge {
LinkChallenge {
digest: LinkCodeDigest::new(value.digest),
persona: authority::PersonaId::new(value.persona),
bound_identity: value.bound_identity.into_option().map(identity_from_wire),
invited_by: value.invited_by.map(authority::PersonaId::new),
created_at_ms: value.created_at_ms,
expires_at_ms: value.expires_at_ms,
}
}
pub(crate) fn attempt_key_to_wire(value: &LinkAttemptKey) -> pb::PersonaLinkAttemptKey {
use pb::__buffa::oneof::persona_link_attempt_key::Owner;
let owner = match &value.owner {
LinkAttemptOwner::Global => Owner::from(pb::PersonaLinkGlobalOwner {
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
LinkAttemptOwner::Identity(identity) => Owner::from(identity_to_wire(identity)),
};
pb::PersonaLinkAttemptKey {
kind: match value.kind {
LinkAttemptKind::Mint => pb::PersonaLinkAttemptKind::PERSONA_LINK_ATTEMPT_KIND_MINT,
LinkAttemptKind::RedemptionFailure => {
pb::PersonaLinkAttemptKind::PERSONA_LINK_ATTEMPT_KIND_REDEMPTION_FAILURE
}
LinkAttemptKind::AutoLink => {
pb::PersonaLinkAttemptKind::PERSONA_LINK_ATTEMPT_KIND_AUTO_LINK
}
}
.into(),
owner: Some(owner),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn attempt_key_from_wire(
value: pb::PersonaLinkAttemptKey,
) -> Result<LinkAttemptKey, StateError> {
use pb::__buffa::oneof::persona_link_attempt_key::Owner;
let kind = match known("link_attempts.kind", value.kind)? {
pb::PersonaLinkAttemptKind::PERSONA_LINK_ATTEMPT_KIND_MINT => LinkAttemptKind::Mint,
pb::PersonaLinkAttemptKind::PERSONA_LINK_ATTEMPT_KIND_REDEMPTION_FAILURE => {
LinkAttemptKind::RedemptionFailure
}
pb::PersonaLinkAttemptKind::PERSONA_LINK_ATTEMPT_KIND_AUTO_LINK => {
LinkAttemptKind::AutoLink
}
pb::PersonaLinkAttemptKind::PERSONA_LINK_ATTEMPT_KIND_UNSPECIFIED => {
return Err(malformed(
"link_attempts.kind",
"a link counter names its class",
));
}
};
let owner = match value.owner {
Some(Owner::Global(_)) => LinkAttemptOwner::Global,
Some(Owner::Identity(identity)) => {
LinkAttemptOwner::Identity(identity_from_wire(*identity))
}
None => {
return Err(malformed(
"link_attempts.owner",
"a link counter names its owner",
));
}
};
Ok(LinkAttemptKey { kind, owner })
}
pub(crate) fn attempts_to_wire(value: &LinkAttempts) -> pb::PersonaLinkAttempts {
pb::PersonaLinkAttempts {
count: value.count,
window_started_at_ms: value.window_started_at_ms,
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) const fn attempts_from_wire(value: &pb::PersonaLinkAttempts) -> LinkAttempts {
LinkAttempts {
count: value.count,
window_started_at_ms: value.window_started_at_ms,
}
}
pub(crate) fn expected_to_wire(value: ExpectedEntry) -> pb::PersonaExpectedEntry {
use pb::__buffa::oneof::persona_expected_entry::Expected;
let expected = match value {
ExpectedEntry::Absent => Expected::from(pb::PersonaExpectedAbsent {
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
ExpectedEntry::Revision(revision) => Expected::from(pb::PersonaExpectedRevision {
revision: revision.get(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
};
pb::PersonaExpectedEntry {
expected: Some(expected),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn expected_from_wire(
value: pb::PersonaExpectedEntry,
) -> Result<ExpectedEntry, StateError> {
use pb::__buffa::oneof::persona_expected_entry::Expected;
match value.expected {
Some(Expected::Absent(_)) => Ok(ExpectedEntry::Absent),
Some(Expected::Revision(value)) => {
Ok(ExpectedEntry::Revision(Revision::new(value.revision)))
}
None => Err(malformed(
"expected",
"a persona mutation declares its exact row premise",
)),
}
}
#[allow(
clippy::too_many_lines,
reason = "one exhaustive encoder keeps every PersonaMutation wire variant visibly covered"
)]
pub(crate) fn mutation_to_wire(value: &PersonaMutation) -> pb::PersonaMutation {
use pb::__buffa::oneof::persona_mutation::Mutation;
let mutation = match value {
PersonaMutation::PutProfile { profile, expected } => {
Mutation::from(pb::PutPersonaProfile {
profile: buffa::MessageField::some(profile_to_wire(profile)),
expected: buffa::MessageField::some(expected_to_wire(*expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
}
PersonaMutation::PutIdentity { link, expected } => Mutation::from(pb::PutPersonaIdentity {
link: buffa::MessageField::some(link_to_wire(link)),
expected: buffa::MessageField::some(expected_to_wire(*expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
PersonaMutation::DeleteIdentity {
identity,
persona,
expected,
} => Mutation::from(pb::DeletePersonaIdentity {
identity: buffa::MessageField::some(identity_to_wire(identity)),
expected_revision: expected.get(),
persona: persona.as_str().to_owned(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
PersonaMutation::PutParticipation {
persona,
participation,
expected,
} => Mutation::from(pb::PutPersonaParticipation {
persona: persona.as_str().to_owned(),
participation: buffa::MessageField::some(participation_to_wire(participation)),
expected: buffa::MessageField::some(expected_to_wire(*expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
PersonaMutation::PutParticipationIndex {
persona,
scopes,
expected,
} => Mutation::from(pb::PutPersonaParticipationIndex {
persona: persona.as_str().to_owned(),
scopes: scopes
.iter()
.map(|scope| scope.as_str().to_owned())
.collect(),
expected: buffa::MessageField::some(expected_to_wire(*expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
PersonaMutation::PutSearchVisibility {
persona,
visibility,
expected,
} => Mutation::from(pb::PutPersonaSearchVisibility {
persona: persona.as_str().to_owned(),
visibility: buffa::MessageField::some(visibility_to_wire(visibility)),
expected: buffa::MessageField::some(expected_to_wire(*expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
PersonaMutation::PutPersonaIndex { personas, expected } => {
Mutation::from(pb::PutPersonaIndex {
personas: personas
.iter()
.map(|persona| persona.as_str().to_owned())
.collect(),
expected: buffa::MessageField::some(expected_to_wire(*expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
}
PersonaMutation::PutLinkChallenge {
challenge,
expected,
} => Mutation::from(pb::PutPersonaLinkChallenge {
challenge: buffa::MessageField::some(challenge_to_wire(challenge)),
expected: buffa::MessageField::some(expected_to_wire(*expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
PersonaMutation::DeleteLinkChallenge { digest, expected } => {
Mutation::from(pb::DeletePersonaLinkChallenge {
digest: digest.as_str().to_owned(),
expected_revision: expected.get(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
}
PersonaMutation::PutLinkAttempts {
key,
attempts,
expected,
} => Mutation::from(pb::PutPersonaLinkAttempts {
key: buffa::MessageField::some(attempt_key_to_wire(key)),
attempts: buffa::MessageField::some(attempts_to_wire(attempts)),
expected: buffa::MessageField::some(expected_to_wire(*expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
PersonaMutation::DeleteLinkAttempts { key, expected } => {
Mutation::from(pb::DeletePersonaLinkAttempts {
key: buffa::MessageField::some(attempt_key_to_wire(key)),
expected_revision: expected.get(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
}
PersonaMutation::PutPendingInvite {
identity,
digest,
expected,
} => Mutation::from(pb::PutPersonaPendingInvite {
identity: buffa::MessageField::some(identity_to_wire(identity)),
digest: digest.as_str().to_owned(),
expected: buffa::MessageField::some(expected_to_wire(*expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
PersonaMutation::DeletePendingInvite { identity, expected } => {
Mutation::from(pb::DeletePersonaPendingInvite {
identity: buffa::MessageField::some(identity_to_wire(identity)),
expected_revision: expected.get(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
}
PersonaMutation::DeleteParticipation {
persona,
scope,
expected,
} => Mutation::from(pb::DeletePersonaParticipation {
persona: persona.as_str().to_owned(),
scope: scope.as_str().to_owned(),
expected_revision: expected.get(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
PersonaMutation::DeleteParticipationIndex { persona, expected } => {
Mutation::from(pb::DeletePersonaParticipationIndex {
persona: persona.as_str().to_owned(),
expected_revision: expected.get(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
}
PersonaMutation::DeleteSearchVisibility {
persona,
scope,
expected,
} => Mutation::from(pb::DeletePersonaSearchVisibility {
persona: persona.as_str().to_owned(),
scope: scope.as_str().to_owned(),
expected_revision: expected.get(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
};
pb::PersonaMutation {
mutation: Some(mutation),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
#[allow(
clippy::too_many_lines,
reason = "one exhaustive decoder keeps every PersonaMutation wire variant visibly covered"
)]
pub(crate) fn mutation_from_wire(
value: pb::PersonaMutation,
) -> Result<PersonaMutation, StateError> {
use pb::__buffa::oneof::persona_mutation::Mutation;
Ok(match value.mutation {
Some(Mutation::Profile(value)) => PersonaMutation::PutProfile {
profile: profile_from_wire(required(
"profile",
"a profile mutation carries its value",
value.profile,
)?)?,
expected: expected_from_wire(required(
"expected",
"a profile mutation carries its premise",
value.expected,
)?)?,
},
Some(Mutation::Identity(value)) => PersonaMutation::PutIdentity {
link: link_from_wire(required(
"link",
"an identity mutation carries its value",
value.link,
)?)?,
expected: expected_from_wire(required(
"expected",
"an identity mutation carries its premise",
value.expected,
)?)?,
},
Some(Mutation::DeleteIdentity(value)) => PersonaMutation::DeleteIdentity {
identity: identity_from_wire(required(
"identity",
"an identity deletion names its row",
value.identity,
)?),
persona: authority::PersonaId::new(value.persona),
expected: Revision::new(value.expected_revision),
},
Some(Mutation::Participation(value)) => PersonaMutation::PutParticipation {
persona: authority::PersonaId::new(value.persona),
participation: participation_from_wire(required(
"participation",
"a participation mutation carries its value",
value.participation,
)?)?,
expected: expected_from_wire(required(
"expected",
"a participation mutation carries its premise",
value.expected,
)?)?,
},
Some(Mutation::ParticipationIndex(value)) => PersonaMutation::PutParticipationIndex {
persona: authority::PersonaId::new(value.persona),
scopes: value
.scopes
.into_iter()
.map(authority::ScopeId::new)
.collect(),
expected: expected_from_wire(required(
"expected",
"an index mutation carries its premise",
value.expected,
)?)?,
},
Some(Mutation::SearchVisibility(value)) => PersonaMutation::PutSearchVisibility {
persona: authority::PersonaId::new(value.persona),
visibility: visibility_from_wire(required(
"visibility",
"a visibility mutation carries its value",
value.visibility,
)?),
expected: expected_from_wire(required(
"expected",
"a visibility mutation carries its premise",
value.expected,
)?)?,
},
Some(Mutation::PersonaIndex(value)) => PersonaMutation::PutPersonaIndex {
personas: value
.personas
.into_iter()
.map(authority::PersonaId::new)
.collect(),
expected: expected_from_wire(required(
"expected",
"a persona-index mutation carries its premise",
value.expected,
)?)?,
},
Some(Mutation::LinkChallenge(value)) => PersonaMutation::PutLinkChallenge {
challenge: challenge_from_wire(required(
"challenge",
"a link-challenge mutation carries its value",
value.challenge,
)?),
expected: expected_from_wire(required(
"expected",
"a link-challenge mutation carries its premise",
value.expected,
)?)?,
},
Some(Mutation::DeleteLinkChallenge(value)) => PersonaMutation::DeleteLinkChallenge {
digest: LinkCodeDigest::new(value.digest),
expected: Revision::new(value.expected_revision),
},
Some(Mutation::LinkAttempts(value)) => PersonaMutation::PutLinkAttempts {
key: attempt_key_from_wire(required(
"key",
"a link-attempt mutation names its counter",
value.key,
)?)?,
attempts: attempts_from_wire(&required(
"attempts",
"a link-attempt mutation carries its value",
value.attempts,
)?),
expected: expected_from_wire(required(
"expected",
"a link-attempt mutation carries its premise",
value.expected,
)?)?,
},
Some(Mutation::DeleteLinkAttempts(value)) => PersonaMutation::DeleteLinkAttempts {
key: attempt_key_from_wire(required(
"key",
"a link-attempt deletion names its counter",
value.key,
)?)?,
expected: Revision::new(value.expected_revision),
},
Some(Mutation::PendingInvite(value)) => PersonaMutation::PutPendingInvite {
identity: identity_from_wire(required(
"identity",
"a pending invite names its target identity",
value.identity,
)?),
digest: LinkCodeDigest::new(value.digest),
expected: expected_from_wire(required(
"expected",
"a pending invite carries its premise",
value.expected,
)?)?,
},
Some(Mutation::DeletePendingInvite(value)) => PersonaMutation::DeletePendingInvite {
identity: identity_from_wire(required(
"identity",
"a pending-invite deletion names its target identity",
value.identity,
)?),
expected: Revision::new(value.expected_revision),
},
Some(Mutation::DeleteParticipation(value)) => PersonaMutation::DeleteParticipation {
persona: authority::PersonaId::new(value.persona),
scope: authority::ScopeId::new(value.scope),
expected: Revision::new(value.expected_revision),
},
Some(Mutation::DeleteParticipationIndex(value)) => {
PersonaMutation::DeleteParticipationIndex {
persona: authority::PersonaId::new(value.persona),
expected: Revision::new(value.expected_revision),
}
}
Some(Mutation::DeleteSearchVisibility(value)) => PersonaMutation::DeleteSearchVisibility {
persona: authority::PersonaId::new(value.persona),
scope: authority::ScopeId::new(value.scope),
expected: Revision::new(value.expected_revision),
},
None => {
return Err(malformed(
"mutation",
"a persona mutation names one typed operation",
));
}
})
}
pub(crate) fn command_to_wire(command: &PersonaTransaction) -> pb::PersonaCommandMetadata {
let metadata = command.metadata();
pb::PersonaCommandMetadata {
command_id: metadata.command_id().as_str().to_owned(),
namespace: metadata.scope().namespace().as_str().to_owned(),
purpose: metadata.envelope().purpose().as_str().to_owned(),
command_audience: metadata.envelope().audience().as_str().to_owned(),
digest: metadata.digest().as_bytes().to_vec(),
administration_dependencies: command
.administration_dependencies()
.iter()
.map(|dependency| pb::PersonaAdministrationDependency {
persona: dependency.persona.as_str().to_owned(),
expected: buffa::MessageField::some(expected_to_wire(dependency.expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
.collect(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn command_from_wire(
metadata: pb::PersonaCommandMetadata,
mutations: Vec<pb::PersonaMutation>,
) -> Result<PersonaTransaction, StateError> {
let namespace = NamespaceId::new(metadata.namespace);
let digest = ContentDigest::from_bytes(fixed_bytes::<{ ContentDigest::LEN }>(
"digest",
&metadata.digest,
)?);
let kernel = CommandMetadata::new(
CommandId::new(metadata.command_id),
polyc_state::versioned::family(),
digest,
directory_scope(&namespace),
CommandEnvelope::new(
Purpose::new(metadata.purpose),
Audience::new(metadata.command_audience),
ResourceBounds::new(MAX_TRANSACTION_PAYLOAD_BYTES, MAX_MUTATIONS_PER_TRANSACTION),
),
);
let dependencies = metadata
.administration_dependencies
.into_iter()
.map(|dependency| {
Ok(AdministrationDependency {
persona: authority::PersonaId::new(dependency.persona),
expected: expected_from_wire(required(
"administration_dependency.expected",
"a persona administration premise carries an expected role row",
dependency.expected,
)?)?,
})
})
.collect::<Result<_, StateError>>()?;
Ok(PersonaTransaction::new(
kernel,
mutations
.into_iter()
.map(mutation_from_wire)
.collect::<Result<_, _>>()?,
)
.depending_on_administration(dependencies))
}
pub(crate) fn receipt(
field: impl Into<Option<pb::Receipt>>,
) -> Result<polyc_state::receipt::Receipt, StateError> {
let field = field.into().ok_or_else(|| {
malformed(
"receipt",
"a successful persona mutation returns its receipt",
)
})?;
Kernel::<polyc_state::receipt::Receipt>::try_from(field).map(Kernel::into_inner)
}
#[cfg(test)]
mod tests {
use super::*;
use pb::__buffa::oneof::persona_mutation::Mutation;
fn identity() -> authority::ExternalIdentity {
authority::ExternalIdentity::scoped(
authority::SurfaceId::new("chat"),
authority::IdentityScope::new("workspace"),
authority::SubjectId::new("u-1"),
)
}
fn profile() -> PersonaProfile {
PersonaProfile {
persona: authority::PersonaId::new("p-1"),
display_name: "Alice".into(),
created_at_ms: 1,
identities: vec![identity()],
status: PersonaStatus::Linked,
document: ProfileDocument::default(),
split: None,
}
}
fn link() -> IdentityLink {
IdentityLink::new(
identity(),
authority::PersonaId::new("p-1"),
"Alice",
"UTC",
1,
2,
LinkProvenance::new("verified", "p-admin", "ceremony-1", 1),
)
}
#[test]
#[allow(
clippy::too_many_lines,
reason = "one table-driven case enumerates every semantic mutation variant"
)]
fn every_semantic_mutation_round_trips() {
let persona = authority::PersonaId::new("p-1");
let scope = authority::ScopeId::new("conversation-1");
let values = vec![
PersonaMutation::PutProfile {
profile: profile(),
expected: ExpectedEntry::Absent,
},
PersonaMutation::PutIdentity {
link: link(),
expected: ExpectedEntry::Revision(Revision::new(1)),
},
PersonaMutation::DeleteIdentity {
identity: identity(),
persona: persona.clone(),
expected: Revision::new(2),
},
PersonaMutation::PutParticipation {
persona: persona.clone(),
participation: Participation {
scope: scope.clone(),
role: ParticipationRole::Initiator,
first_at_ms: 3,
via_persona: None,
},
expected: ExpectedEntry::Absent,
},
PersonaMutation::PutParticipationIndex {
persona: persona.clone(),
scopes: vec![scope.clone()],
expected: ExpectedEntry::Revision(Revision::new(4)),
},
PersonaMutation::PutSearchVisibility {
persona: persona.clone(),
visibility: SearchVisibility {
scope,
hidden: true,
set_at_ms: 5,
set_by: persona.clone(),
via_persona: None,
},
expected: ExpectedEntry::Absent,
},
PersonaMutation::PutPersonaIndex {
personas: vec![persona.clone()],
expected: ExpectedEntry::Absent,
},
PersonaMutation::PutLinkChallenge {
challenge: LinkChallenge {
digest: LinkCodeDigest::new("digest-1"),
persona: persona.clone(),
bound_identity: Some(identity()),
invited_by: Some(authority::PersonaId::new("admin-1")),
created_at_ms: 6,
expires_at_ms: 7,
},
expected: ExpectedEntry::Absent,
},
PersonaMutation::DeleteLinkChallenge {
digest: LinkCodeDigest::new("digest-2"),
expected: Revision::new(8),
},
PersonaMutation::PutLinkAttempts {
key: LinkAttemptKey {
kind: LinkAttemptKind::Mint,
owner: LinkAttemptOwner::Identity(identity()),
},
attempts: LinkAttempts {
count: 2,
window_started_at_ms: 9,
},
expected: ExpectedEntry::Absent,
},
PersonaMutation::DeleteLinkAttempts {
key: LinkAttemptKey {
kind: LinkAttemptKind::RedemptionFailure,
owner: LinkAttemptOwner::Global,
},
expected: Revision::new(10),
},
PersonaMutation::PutPendingInvite {
identity: identity(),
digest: LinkCodeDigest::new("digest-3"),
expected: ExpectedEntry::Absent,
},
PersonaMutation::DeletePendingInvite {
identity: identity(),
expected: Revision::new(11),
},
PersonaMutation::DeleteParticipation {
persona: persona.clone(),
scope: authority::ScopeId::new("conversation-2"),
expected: Revision::new(12),
},
PersonaMutation::DeleteParticipationIndex {
persona: persona.clone(),
expected: Revision::new(13),
},
PersonaMutation::DeleteSearchVisibility {
persona,
scope: authority::ScopeId::new("conversation-3"),
expected: Revision::new(14),
},
];
for value in values {
assert_eq!(mutation_from_wire(mutation_to_wire(&value)).unwrap(), value);
}
}
#[test]
fn missing_or_unknown_required_wire_fields_fail_closed() {
assert!(
mutation_from_wire(pb::PersonaMutation {
mutation: None,
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
.is_err()
);
let mut encoded = mutation_to_wire(&PersonaMutation::PutIdentity {
link: link(),
expected: ExpectedEntry::Absent,
});
if let Some(Mutation::Identity(value)) = &mut encoded.mutation {
value.expected = buffa::MessageField::none();
}
assert!(mutation_from_wire(encoded).is_err());
}
}