#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)]
pub use canwu_core::{
ArmyId, BoundaryId, CommandAttemptId, CommandId, CommandRequestId, CoreEntityKind,
DecisionRequestId, DecisionTicketId, DecisionTraceId, DomainEntityKindClass, DomainEntityType,
DomainKindClass, DomainRecordKind, DomainRecordRef, DomainRecordType, DomainRecordVersionRef,
DomainRecordVersionSource, DomainValueKindClass, DomainValueType, EntityRef, EvaluationTerm,
EvaluationTraceRecord, EventId, EvidenceRef, GovernmentId, HolderKnowledgeRecordId, IngressId,
KnowledgeHolderPolicy, KnowledgeHolderRef, KnowledgeRecordId, KnowledgeRecordKind,
KnowledgeSchemaId, LetterId, OrganizationId, PersonId, RandomDrawId, ResourceId, RouteId,
SchemaRegistry, SchemaRegistryError, SimulationGranularity, TerritoryId, TypeSchema,
TypedDomainRecordRef,
};
pub use canwu_event::{CauseRef, EventAudience, EventKind, EventKindError, SimEvent};
pub use canwu_knowledge::{
ActorKnowledge, ArmyKnowledge, EstimateRange, KnowledgeCursor, KnowledgeHistoryView,
KnowledgeOrigin, KnowledgeQuery, KnowledgeQueryError, KnowledgeQueryResult, KnowledgeReadCut,
KnowledgeRecord, KnowledgeRecordDraft, KnowledgeRecordView, KnowledgeSnapshot, KnowledgeSource,
KnowledgeSubject, KnowledgeSubjectTarget, MAX_KNOWLEDGE_PAGE_SIZE,
};
pub use canwu_routing::{
DepartureSlot, DurationSample, PlanningSnapshot, ROUTING_ALGORITHM_VERSION, RouteCost,
RouteLeg, RoutePlan, RoutingAlgorithm, RoutingCache, RoutingConnection, RoutingConnectionRef,
RoutingEndpoint, RoutingEndpointKind, RoutingError, RoutingNetwork, RoutingNodeRef,
RoutingPolicy, RoutingRequest, TransferMode, TraversalModel, plan_route,
};
use canwu_sim::Simulation;
pub use canwu_sim::{
ADMISSION_CURSOR_FORMAT_VERSION, ArchiveProvider, ArchiveReachabilityManifest, ArchiveStore,
ArchiveStoreOutcome, ArchivedEvidenceLocator, ArchivedEvidenceReceipt,
ArchivedPluginIngressProvenance, ArchivedSegmentHeader, Army, ArtifactManifest, BoundaryChange,
BoundaryContext, BoundaryDirective, BoundaryEmission, BoundaryEmissionKind,
BoundaryEvaluationTrace, BoundaryIngressGeneration, BoundaryKnowledgeChange,
BoundaryPersonAvailabilityChange, BoundaryPersonCreation, BoundaryPhase, BoundaryProposal,
BoundaryReceipt, BoundaryRecord, BoundaryRequest, BoundarySystemContract,
BoundarySystemHandler, CHECKPOINT_JOURNAL_FORMAT_VERSION, COMMITMENT_FORMAT_VERSION,
CONTROLLER_AUTHORITY_UNAVAILABLE_REASON, CanwuError, CheckpointJournal, Command,
CommandAttemptOutcome, CommandAttemptRecord, CommandAuthority, CommandContext, CommandEnvelope,
CommandIngress, CommandOutcome, CommandPolicyContext, CommandReceipt, CommandRecord,
CommandRejection, CommandRequest, CommitmentRoots, CompactedSimulation, ControllerDecision,
ControllerPolicy, CreatedPerson, CustodyState, DECISION_ARCHIVE_BUCKET_PAGE_FORMAT_VERSION,
DECISION_ARCHIVE_FORMAT_VERSION, DECISION_MAKER_UNAVAILABLE_REASON,
DECISION_REQUEST_COMMITMENT_DOMAIN, DecisionAction, DecisionArchiveBlob,
DecisionArchiveBucketPage, DecisionArchiveProvider, DecisionArchiveReceipt,
DecisionArchiveRecord, DecisionArchiveStore, DecisionArchiveStoreOutcome,
DecisionAttemptErrorCode, DecisionAttemptOutcome, DecisionAttemptRecord, DecisionAuthority,
DecisionContext, DecisionController, DecisionControllerBinding, DecisionError,
DecisionErrorCode, DecisionEvaluation, DecisionExternalEvidence, DecisionFactorContribution,
DecisionHistoryCursor, DecisionHistoryKey, DecisionHistoryLocation, DecisionHistoryPage,
DecisionHistoryQueryBudget, DecisionHotState, DecisionIngressRequest,
DecisionLocatorScaleMetrics, DecisionMutation, DecisionOption, DecisionOptionEvaluation,
DecisionOptionWeight, DecisionOrigin, DecisionOutcome, DecisionPolicy, DecisionPolicyIdentity,
DecisionPolicyKind, DecisionRandomEvidence, DecisionRule, DecisionStage, DecisionState,
DecisionTicket, DecisionTicketDraft, DecisionTicketState, DecisionTrace, DemoIds, DomainRecord,
DomainRecordChange, DomainRecordClass, DomainRecordCommitmentRoots, DomainRecordDraft,
DomainRecordLifecycle, DomainRecordMutation, DomainRecordMutationPolicy, DomainRecordOperation,
DomainRecordPage, DomainRecordPageRoots, DomainRecordSchema, DomainReference,
DomainReferenceSchema, DomainReferenceTarget, DomainReferenceTargetKind, ENGINE_VERSION,
ErrorCode, EvaluationLimitsV1, EvidenceArchiveIndex, EvidenceCursor, EvidenceIndexEntry,
EvidenceItemLocator, EvidenceJournalKind, EvidenceJournalRoots, EvidenceJournalSegment,
EvidenceNestedLocator, EvidenceSealToken, ExternalDecisionOption, ExternalDecisionRequest,
ExternalDecisionResponse, ExternalPolicy, Government, GuardedUtilityPolicy,
HumanDecisionResponse, HumanPolicy, IDENTITY_EVIDENCE_DEPENDENCIES_FIELD,
IDENTITY_EVIDENCE_DEPENDENCIES_FORMAT_VERSION, IdentityEvidenceDependenciesV1,
IngressCancellationAuthority, IngressClass, IngressPayload, IngressReceipt, IngressRecord,
InteractionPolicy, Issuer, KnowledgeLimitsV1, KnowledgeSubjectSchema,
KnowledgeSubjectTargetKind, KnowledgeWriteGrant, LetterCargo, LetterStatus, LifeState,
LlmModelIdentity, LlmPolicy, MAX_DECISION_ARCHIVE_BATCH_ENTRIES,
MAX_DECISION_HISTORY_PAGE_BYTES, MAX_DECISION_HISTORY_PAGE_SIZE,
MAX_INGRESS_CANCELLATION_REASON_BYTES, MAX_OWNER_AUTHORIZED_MUTATIONS,
MAX_OWNER_AUTHORIZED_PARTICIPANTS, MAX_STATE_DELTA_PAGES, MAX_STATE_PAGE_BYTES,
MaintenanceChangeRecord, MaintenanceDependencyResolverDescriptor, MaintenanceDisposition,
MaintenanceIngressRequest, MaintenanceRejectionReceipt, MapPoint,
OWNER_AUTHORIZED_MAINTENANCE_FORMAT_VERSION, ObservationPolicy, OrderedRulePolicy, OutboxEntry,
OwnerAuthorizedMaintenanceDraft, OwnerAuthorizedMaintenanceParticipant,
OwnerAuthorizedMaintenanceRequest, OwnerAuthorizedMutation, OwnerAuthorizedParticipantDraft,
OwnerAuthorizedParticipantProposal, OwnerAuthorizedParticipantRole,
OwnerAuthorizedRecordExpectation, PAGED_CHECKPOINT_FORMAT_VERSION,
PAYLOAD_REQUIRED_EVIDENCE_CONTINUATION_FIELD,
PAYLOAD_REQUIRED_EVIDENCE_CONTINUATION_FORMAT_VERSION, PLUGIN_DESCRIPTOR_FORMAT_VERSION,
PagedSimulationCheckpoint, PatriciaStoreMetrics, PayloadProperty,
PayloadRequiredEvidenceContinuationV1, PayloadSchema, PayloadValueType,
PersistentDomainRecordStore, Person, PersonAvailability, PersonDraft, PersonTransitState,
PluginActionDescriptor, PluginArchiveObjectProvider, PluginArchiveReachabilityParticipant,
PluginArchiveRetention, PluginCommandHandler, PluginComponentRecord, PluginDescriptor,
PluginIngressDescriptor, PluginIngressPermit, PluginIngressRequest, PluginIngressTarget,
PluginKnowledgeSchema, PluginRegistrar, PluginRegistry, PolicyDecision,
PortablePagedSimulationCheckpoint, PreparedDecisionArchive, PreparedDecisionIngress,
PreparedEvidenceSeal, PreparedPagedSimulationCheckpoint, PreparedStateDelta,
QueuedExternalPolicy, QueuedHumanPolicy, QueuedLlmPolicy, RUN_CONFIGURATION_FORMAT_VERSION,
RUN_MANIFEST_FORMAT_VERSION, RandomAlgorithm, RandomDecisionResolution, RandomDrawAddress,
RandomDrawOutcome, RandomDrawProducer, RandomDrawRecord, RandomOperationAddressV1,
RandomOperationTarget, RandomSample, RandomStreamKey, RandomStreamState, ReplayJournal,
ReservationAllocation, ReservationDisposition, ReservationOffer, ReservationOfferRecord,
ReservationPoolKey, ReservationRef, ReservationRequest, ReservationRequestRecord, Route,
RuleChoice, RulePolicy, RunConfiguration, RunConfigurationSnapshot, RunManifest, RunPurpose,
SNAPSHOT_FORMAT_VERSION, STATE_PAGE_CODEC, STATE_PAGE_FORMAT_VERSION,
STATE_REVISION_FORMAT_VERSION, Scenario, SeatBinding, SeatPolicy, SimulationCheckpoint,
SimulationPlugin, SimulationSnapshot, SimulationSystemHandler, SimulationView, StateKey,
StatePageBlob, StatePageProvider, StatePageRetentionHandle, StatePageRetentionLedger,
StatePageRetentionPhase, StatePageStore, StateVisibility, SystemCadence, SystemContract,
SystemDirective, Territory, TracePolicy, TransitState, UtilityEvaluator, UtilityPolicy,
UtilityProfile, VerifiedDecisionArchiveCommit, VerifiedOwnerAuthorizedMaintenanceCommit,
WeightedUtilityEvaluator, WeightedUtilityPolicy, WorldSnapshot, canonical_byte_hash,
canonical_hash, format8_decision_locator_scale_probe, format8_patricia_scale_probe,
identity_evidence_dependencies_property_v1, payload_required_evidence_continuation_property_v1,
prepare_state_delta, state_page_id, verify_state_delta,
};
pub use canwu_sim::{
MAX_PENDING_TRANSITION_MANIFESTS, MAX_PENDING_TRANSITION_MANIFESTS_PER_COORDINATOR,
MAX_TRANSITION_EXPECTED_VERSIONS, MAX_TRANSITION_LINEAGE_ID_BYTES, MAX_TRANSITION_PARTICIPANTS,
MAX_TRANSITION_READY_HORIZON, PendingTransitionManifest, TransitionAuditOutcome,
TransitionAuditRecord, TransitionManifest, TransitionManifestId, TransitionParticipant,
TransitionParticipantAudit, TransitionRecordVersion,
};
pub use canwu_time::{SimDuration, SimTime};
pub use canwu_transport::{
BookingAllocationV1, CAPACITY_BOOKING_ALLOCATION_DIGEST_DOMAIN, CapacityAllocationFailureV1,
CapacityBooking, CapacityBookingAllocationEvidenceV1, CapacityBookingId,
CapacityBookingRequestV1, CapacityBookingStatus, DeliveryCompletionRequest, DeliverySaga,
Handoff, HandoffId, HandoffKind, ItineraryRevision, ItineraryRevisionId,
ItineraryRevisionReason, LegExecution, LegExecutionId, LegExecutionStatus, MovementInitiative,
MovementOrder, MovementOrderError, MovementOrderId, MovementSubject, MovementSubjectRole,
ReconciliationOutcome, SagaState, TRANSPORT_SEMANTIC_VERSION, TransportCapacityPoolV1,
TransportError, TransportExecution, TransportExecutionId, TransportExecutionState,
allocate_capacity_bookings, capacity_booking_allocation_operation_key,
delivery_completion_operation_key,
};
use serde::{Deserialize, Serialize};
pub struct Canwu {
simulation: Simulation,
}
pub struct CompactedCanwu {
simulation: CompactedSimulation,
}
impl PluginArchiveObjectProvider for Canwu {
fn load_plugin_archive_object(
&self,
namespace: &str,
object_id: &str,
) -> Result<Option<Vec<u8>>, CanwuError> {
self.plugin_archive_object(namespace, object_id)
}
}
impl Canwu {
#[must_use]
pub const fn version() -> &'static str {
ENGINE_VERSION
}
pub fn new(seed: u64, scenario: Scenario) -> Result<Self, CanwuError> {
Ok(Self {
simulation: Simulation::new(seed, scenario)?,
})
}
pub fn into_compacted(self) -> Result<CompactedCanwu, CanwuError> {
Ok(CompactedCanwu {
simulation: self.simulation.into_compacted()?,
})
}
pub fn new_with_plugins(
seed: u64,
scenario: Scenario,
plugins: &[&dyn SimulationPlugin],
) -> Result<Self, CanwuError> {
Ok(Self {
simulation: Simulation::new_with_plugins(seed, scenario, plugins)?,
})
}
pub fn new_with_manifest(
seed: u64,
scenario: Scenario,
run_manifest: RunManifest,
) -> Result<Self, CanwuError> {
Ok(Self {
simulation: Simulation::new_with_manifest(seed, scenario, run_manifest)?,
})
}
pub fn new_with_manifest_and_plugins(
seed: u64,
scenario: Scenario,
run_manifest: RunManifest,
plugins: &[&dyn SimulationPlugin],
) -> Result<Self, CanwuError> {
Ok(Self {
simulation: Simulation::new_with_manifest_and_plugins(
seed,
scenario,
run_manifest,
plugins,
)?,
})
}
pub fn new_with_run_configuration(
seed: u64,
scenario: Scenario,
run_manifest: RunManifest,
run_configuration: RunConfiguration,
) -> Result<Self, CanwuError> {
Ok(Self {
simulation: Simulation::new_with_run_configuration(
seed,
scenario,
run_manifest,
run_configuration,
)?,
})
}
pub fn new_with_run_configuration_and_plugins(
seed: u64,
scenario: Scenario,
run_manifest: RunManifest,
run_configuration: RunConfiguration,
plugins: &[&dyn SimulationPlugin],
) -> Result<Self, CanwuError> {
Ok(Self {
simulation: Simulation::new_with_run_configuration_and_plugins(
seed,
scenario,
run_manifest,
run_configuration,
plugins,
)?,
})
}
pub fn demo(seed: u64) -> Result<Self, CanwuError> {
let (simulation, _) = Simulation::demo(seed)?;
Ok(Self { simulation })
}
#[must_use]
pub fn demo_ids() -> DemoIds {
let (_, ids) = canwu_sim::demo_scenario();
ids
}
#[must_use]
pub const fn time(&self) -> SimTime {
self.simulation.time()
}
#[must_use]
pub const fn run_manifest(&self) -> &RunManifest {
self.simulation.run_manifest()
}
#[must_use]
pub const fn run_configuration(&self) -> &RunConfigurationSnapshot {
self.simulation.run_configuration()
}
#[must_use]
pub fn revision(&self) -> u64 {
self.simulation.revision()
}
#[must_use]
pub fn run_manifest_hash(&self) -> &str {
self.simulation.run_manifest_hash()
}
#[must_use]
pub fn checkpoint_hash(&self) -> &str {
self.simulation.checkpoint_hash()
}
pub fn authoritative_state_hash(&self) -> Result<String, CanwuError> {
self.simulation.authoritative_state_hash()
}
pub fn entities(&self) -> impl Iterator<Item = &EntityRef> {
self.simulation.entities()
}
#[must_use]
pub fn entity_exists(&self, entity: &EntityRef) -> bool {
self.simulation.entity_exists(entity)
}
#[must_use]
pub fn world(&self) -> WorldSnapshot {
self.simulation.world()
}
#[must_use]
pub fn person_availability(&self, person: PersonId) -> Option<&PersonAvailability> {
self.simulation.person_availability(person)
}
pub fn person_availabilities(&self) -> impl Iterator<Item = (&PersonId, &PersonAvailability)> {
self.simulation.person_availabilities()
}
pub fn pending_transition_manifests(&self) -> impl Iterator<Item = &PendingTransitionManifest> {
self.simulation.pending_transition_manifests()
}
#[must_use]
pub fn knowledge(&self) -> &KnowledgeSnapshot {
self.simulation.knowledge()
}
#[must_use]
pub fn events(&self) -> &[SimEvent] {
self.simulation.events()
}
#[must_use]
pub fn commands(&self) -> &[CommandRecord] {
self.simulation.command_log()
}
#[must_use]
pub fn boundaries(&self) -> &[BoundaryRecord] {
self.simulation.boundaries()
}
#[must_use]
pub fn command_attempts(&self) -> &[CommandAttemptRecord] {
self.simulation.command_attempts()
}
#[must_use]
pub fn ingress_log(&self) -> &[IngressRecord] {
self.simulation.ingress_log()
}
#[must_use]
pub fn domain_record(&self, reference: &DomainRecordRef) -> Option<&DomainRecord> {
self.simulation.domain_record(reference)
}
#[must_use]
pub fn domain_record_version_evidence_exists(
&self,
reference: &DomainRecordVersionRef,
) -> bool {
self.simulation
.domain_record_version_evidence_exists(reference)
}
#[must_use]
pub fn evidence_exists(&self, reference: &EvidenceRef) -> bool {
self.simulation.evidence_exists(reference)
}
#[must_use]
pub fn evidence_time(&self, reference: &EvidenceRef) -> Option<SimTime> {
self.simulation.evidence_time(reference)
}
#[must_use]
pub fn domain_record_version(
&self,
reference: &DomainRecordVersionRef,
) -> Option<DomainRecord> {
self.simulation.domain_record_version(reference)
}
pub fn current_domain_record_version(
&self,
reference: &DomainRecordRef,
) -> Result<Option<DomainRecordVersionRef>, CanwuError> {
self.simulation.current_domain_record_version(reference)
}
#[must_use]
pub fn typed_domain_record<T: DomainRecordType>(
&self,
reference: &TypedDomainRecordRef<T>,
) -> Option<&DomainRecord> {
self.simulation.typed_domain_record(reference)
}
pub fn domain_records(&self) -> impl Iterator<Item = &DomainRecord> {
self.simulation.domain_records()
}
pub fn domain_record_page(
&self,
kind: &DomainRecordKind,
after: Option<&DomainRecordRef>,
limit: usize,
expected_revision: Option<u64>,
) -> Result<DomainRecordPage, CanwuError> {
self.simulation
.domain_record_page(kind, after, limit, expected_revision)
}
#[must_use]
pub fn decision_ticket(&self, id: DecisionTicketId) -> Option<&DecisionTicket> {
self.simulation.decision_ticket(id)
}
#[must_use]
pub fn decision_controller(&self, id: &str) -> Option<&DecisionControllerBinding> {
self.simulation.decision_controller(id)
}
#[must_use]
pub fn decision_trace(&self, id: DecisionTraceId) -> Option<&DecisionTrace> {
self.simulation.decision_trace(id)
}
#[must_use]
pub fn decision_attempt(&self, id: DecisionRequestId) -> Option<&DecisionAttemptRecord> {
self.simulation.decision_attempt(id)
}
#[must_use]
pub fn decision_hot_state(&self) -> DecisionHotState {
self.simulation.decision_hot_state()
}
#[must_use]
pub fn decision_history_location(&self, key: &DecisionHistoryKey) -> DecisionHistoryLocation {
self.simulation.decision_history_location(key)
}
pub fn decision_history_location_with_provider(
&self,
key: &DecisionHistoryKey,
provider: &dyn DecisionArchiveProvider,
) -> Result<DecisionHistoryLocation, CanwuError> {
self.simulation
.decision_history_location_with_provider(key, provider)
}
#[must_use]
pub fn random_draws(&self) -> &[RandomDrawRecord] {
self.simulation.random_draws()
}
#[must_use]
pub fn boundary_head_hash(&self) -> Option<&str> {
self.simulation.boundary_head_hash()
}
#[must_use]
pub const fn schema(&self) -> &SchemaRegistry {
self.simulation.schema()
}
pub fn plugin_descriptors(&self) -> impl Iterator<Item = &PluginDescriptor> {
self.simulation.plugin_descriptors()
}
#[must_use]
pub fn replay_journal(&self) -> ReplayJournal {
self.simulation.replay_journal()
}
pub fn outbox_entries(&self) -> Result<Vec<OutboxEntry>, CanwuError> {
self.simulation.outbox_entries()
}
pub fn evidence_cursor(&self) -> Result<EvidenceCursor, CanwuError> {
self.simulation.evidence_cursor()
}
pub fn checkpoint(&self) -> Result<SimulationCheckpoint, CanwuError> {
self.simulation.checkpoint()
}
pub fn archive_reachability_manifest(
&self,
retained_checkpoints: &[SimulationCheckpoint],
page_retention: &StatePageRetentionLedger,
decision_provider: &dyn DecisionArchiveProvider,
plugin_provider: &dyn PluginArchiveObjectProvider,
) -> Result<ArchiveReachabilityManifest, CanwuError> {
self.simulation.archive_reachability_manifest(
retained_checkpoints,
page_retention,
decision_provider,
plugin_provider,
)
}
pub fn journal_segment_since(
&self,
start: EvidenceCursor,
) -> Result<EvidenceJournalSegment, CanwuError> {
self.simulation.journal_segment_since(start)
}
pub fn checkpoint_journal(&self) -> Result<CheckpointJournal, CanwuError> {
self.simulation.checkpoint_journal()
}
pub fn checkpoint_journal_json(&self) -> Result<String, CanwuError> {
self.simulation.checkpoint_journal_json()
}
pub fn register_plugin<P: SimulationPlugin + ?Sized>(
&mut self,
plugin: &P,
) -> Result<(), CanwuError> {
self.simulation.register_plugin(plugin)
}
pub fn set_plugin_archive_object_provider(
&mut self,
provider: std::rc::Rc<dyn PluginArchiveObjectProvider>,
) {
self.simulation.set_plugin_archive_object_provider(provider);
}
pub fn plugin_archive_object(
&self,
namespace: &str,
object_id: &str,
) -> Result<Option<Vec<u8>>, CanwuError> {
self.simulation.plugin_archive_object(namespace, object_id)
}
pub fn submit(&mut self, command: CommandEnvelope) -> Result<CommandReceipt, CanwuError> {
self.simulation.submit(command)
}
pub fn process_command(
&mut self,
request: CommandRequest,
) -> Result<CommandOutcome, CanwuError> {
self.simulation.process_command(request)
}
pub fn enqueue_command(
&mut self,
due_at: SimTime,
priority: i32,
request: CommandRequest,
) -> Result<IngressReceipt, CanwuError> {
self.simulation.enqueue_command(due_at, priority, request)
}
pub fn enqueue_plugin_ingress(
&mut self,
request: PluginIngressRequest,
) -> Result<IngressReceipt, CanwuError> {
self.simulation.enqueue_plugin_ingress(request)
}
pub fn enqueue_permitted_plugin_ingress(
&mut self,
request: PluginIngressRequest,
permit: &PluginIngressPermit,
) -> Result<IngressReceipt, CanwuError> {
self.simulation
.enqueue_permitted_plugin_ingress(request, permit)
}
pub fn cancel_plugin_ingress(
&mut self,
ingress_id: IngressId,
reason: impl Into<String>,
) -> Result<IngressReceipt, CanwuError> {
self.simulation.cancel_plugin_ingress(ingress_id, reason)
}
pub fn cancel_permitted_plugin_ingress(
&mut self,
ingress_id: IngressId,
permit: &PluginIngressPermit,
reason: impl Into<String>,
) -> Result<IngressReceipt, CanwuError> {
self.simulation
.cancel_permitted_plugin_ingress(ingress_id, permit, reason)
}
pub fn prepare_decision(
&self,
decision_request_id: DecisionRequestId,
command_request_id: Option<CommandRequestId>,
ticket_id: DecisionTicketId,
policy: &dyn DecisionPolicy,
) -> Result<DecisionEvaluation, CanwuError> {
self.simulation
.prepare_decision(decision_request_id, command_request_id, ticket_id, policy)
}
pub fn prepare_decision_at(
&self,
due_at: SimTime,
decision_request_id: DecisionRequestId,
command_request_id: Option<CommandRequestId>,
ticket_id: DecisionTicketId,
policy: &dyn DecisionPolicy,
) -> Result<DecisionEvaluation, CanwuError> {
self.simulation.prepare_decision_at(
due_at,
decision_request_id,
command_request_id,
ticket_id,
policy,
)
}
pub fn enqueue_decision(
&mut self,
due_at: SimTime,
priority: i32,
request: DecisionIngressRequest,
) -> Result<IngressReceipt, CanwuError> {
self.simulation.enqueue_decision(due_at, priority, request)
}
pub fn drive_decision(
&mut self,
due_at: SimTime,
priority: i32,
decision_request_id: DecisionRequestId,
command_request_id: Option<CommandRequestId>,
ticket_id: DecisionTicketId,
policy: &dyn DecisionPolicy,
) -> Result<DecisionEvaluation, CanwuError> {
self.simulation.drive_decision(
due_at,
priority,
decision_request_id,
command_request_id,
ticket_id,
policy,
)
}
pub fn schedule_calendar_boundary(
&mut self,
due_at: SimTime,
cadences: Vec<SystemCadence>,
) -> Result<IngressReceipt, CanwuError> {
self.simulation.schedule_calendar_boundary(due_at, cadences)
}
pub fn advance_canonical(
&mut self,
duration: SimDuration,
) -> Result<Vec<BoundaryReceipt>, CanwuError> {
self.simulation.advance_canonical(duration)
}
pub fn step_canonical(&mut self) -> Result<Option<BoundaryReceipt>, CanwuError> {
self.simulation.step_canonical()
}
pub fn advance(&mut self, duration: SimDuration) -> Result<Vec<SimEvent>, CanwuError> {
self.simulation.advance(duration)
}
pub fn settle_boundary(
&mut self,
request: BoundaryRequest,
) -> Result<BoundaryReceipt, CanwuError> {
self.simulation.settle_boundary(request)
}
pub fn wait(&mut self, duration: SimDuration) -> Result<Vec<SimEvent>, CanwuError> {
self.advance(duration)
}
pub fn step(&mut self) -> Result<Vec<SimEvent>, CanwuError> {
self.simulation.step()
}
#[must_use]
pub fn snapshot(&self) -> SimulationSnapshot {
self.simulation.snapshot()
}
pub fn snapshot_json(&self) -> Result<String, CanwuError> {
self.simulation.snapshot_json()
}
pub fn from_snapshot_json(json: &str) -> Result<Self, CanwuError> {
let simulation = Simulation::from_snapshot_json(json)?;
Ok(Self { simulation })
}
pub fn from_snapshot_json_with_plugins(
json: &str,
plugins: &[&dyn SimulationPlugin],
) -> Result<Self, CanwuError> {
Ok(Self {
simulation: Simulation::from_snapshot_json_with_plugins(json, plugins)?,
})
}
pub fn from_checkpoint_and_journal(
checkpoint: SimulationCheckpoint,
segments: Vec<EvidenceJournalSegment>,
) -> Result<Self, CanwuError> {
Ok(Self {
simulation: Simulation::from_checkpoint_and_journal(checkpoint, segments)?,
})
}
pub fn from_checkpoint_journal(bundle: CheckpointJournal) -> Result<Self, CanwuError> {
Ok(Self {
simulation: Simulation::from_checkpoint_journal(bundle)?,
})
}
pub fn from_checkpoint_journal_with_plugins(
bundle: CheckpointJournal,
plugins: &[&dyn SimulationPlugin],
) -> Result<Self, CanwuError> {
Ok(Self {
simulation: Simulation::from_checkpoint_journal_with_plugins(bundle, plugins)?,
})
}
pub fn from_checkpoint_journal_json(json: &str) -> Result<Self, CanwuError> {
Ok(Self {
simulation: Simulation::from_checkpoint_journal_json(json)?,
})
}
pub fn from_checkpoint_journal_json_with_plugins(
json: &str,
plugins: &[&dyn SimulationPlugin],
) -> Result<Self, CanwuError> {
Ok(Self {
simulation: Simulation::from_checkpoint_journal_json_with_plugins(json, plugins)?,
})
}
pub fn replay_from_journal(
plugins: &[&dyn SimulationPlugin],
journal: &ReplayJournal,
) -> Result<Self, CanwuError> {
let simulation = Simulation::replay_from_journal(plugins, journal)?;
Ok(Self { simulation })
}
pub fn replay_from_journal_with_archive_provider(
plugins: &[&dyn SimulationPlugin],
journal: &ReplayJournal,
archive_provider: std::rc::Rc<dyn PluginArchiveObjectProvider>,
) -> Result<Self, CanwuError> {
let simulation = Simulation::replay_from_journal_with_archive_provider(
plugins,
journal,
archive_provider,
)?;
Ok(Self { simulation })
}
pub fn replay_from_journal_json(
plugins: &[&dyn SimulationPlugin],
json: &str,
) -> Result<Self, CanwuError> {
Ok(Self {
simulation: Simulation::replay_from_journal_json(plugins, json)?,
})
}
#[must_use]
pub fn fork(&self) -> Self {
Self {
simulation: self.simulation.fork(),
}
}
pub fn admin_query_knowledge(
&self,
holder: KnowledgeHolderRef,
query: &KnowledgeQuery,
) -> Result<KnowledgeQueryResult, CanwuError> {
self.simulation
.knowledge()
.query_current(
holder,
query,
self.simulation.boundaries().last().map(|value| value.id),
)
.map_err(map_knowledge_query_error)
}
pub fn viewer(&self) -> Result<CanwuViewer<'_>, CanwuError> {
let principal = self.declared_observation_principal()?;
Ok(CanwuViewer {
canwu: self,
context: KnowledgeViewContext { principal },
})
}
pub fn viewer_for_actor(&self, actor: PersonId) -> Result<CanwuViewer<'_>, CanwuError> {
if !self.entity_exists(&EntityRef::Person(actor)) {
return Err(CanwuError::new(
ErrorCode::ActorNotFound,
format!("actor {actor} was not found"),
));
}
let principal = match self.run_configuration().declared() {
Some(configuration)
if configuration.observation == ObservationPolicy::ActorBound
&& configuration.seat == SeatPolicy::CharacterBound
&& configuration
.seat_binding
.as_ref()
.and_then(|binding| binding.actor)
== Some(actor) =>
{
ObservationPrincipal::Person(actor)
}
Some(_) => {
return Err(CanwuError::new(
ErrorCode::InvalidAuthority,
"the persisted run policy does not authorize a character viewer",
));
}
None => ObservationPrincipal::Person(actor),
};
Ok(CanwuViewer {
canwu: self,
context: KnowledgeViewContext { principal },
})
}
pub fn viewer_context(&self, actor: PersonId) -> Result<ViewerContext, CanwuError> {
let viewer = self.viewer_for_actor(actor)?;
Ok(ViewerContext {
principal: viewer.context.principal.clone(),
observation: ObservationPolicy::ActorBound,
checkpoint_hash: self.checkpoint_hash().to_owned(),
})
}
fn declared_observation_principal(&self) -> Result<ObservationPrincipal, CanwuError> {
let Some(configuration) = self.run_configuration().declared() else {
return Err(CanwuError::new(
ErrorCode::InvalidAuthority,
"legacy runs require viewer_for_actor with an existing character",
));
};
match configuration.observation {
ObservationPolicy::ActorBound => match configuration.seat {
SeatPolicy::CharacterBound => configuration
.seat_binding
.as_ref()
.and_then(|binding| binding.actor)
.map(ObservationPrincipal::Person)
.ok_or_else(|| {
CanwuError::new(
ErrorCode::InvalidAuthority,
"character-bound observation lacks an actor binding",
)
}),
SeatPolicy::InstitutionBound => configuration
.seat_binding
.as_ref()
.and_then(|binding| binding.institution.clone())
.map(ObservationPrincipal::Institution)
.ok_or_else(|| {
CanwuError::new(
ErrorCode::InvalidAuthority,
"institution-bound observation lacks an institution binding",
)
}),
SeatPolicy::ObserverSeat | SeatPolicy::AdvisorSeat | SeatPolicy::None => {
Err(CanwuError::new(
ErrorCode::InvalidAuthority,
"actor-bound observation requires a character or institution seat",
))
}
},
ObservationPolicy::PublicObserver => Ok(ObservationPrincipal::Public),
ObservationPolicy::ResearchFull => Ok(ObservationPrincipal::Research),
ObservationPolicy::DeveloperDiagnostic => Ok(ObservationPrincipal::Developer),
}
}
#[must_use]
pub fn explain(&self, request: &ExplanationRequest) -> Explanation {
match request {
ExplanationRequest::Event(event_id) => self.explain_event(*event_id),
ExplanationRequest::Failure(error) => Explanation {
summary: error.message.clone(),
causal_chain: vec![ExplanationStep {
label: format!("Validation failed: {:?}", error.code),
event: None,
}],
},
}
}
fn explain_event(&self, event_id: EventId) -> Explanation {
let mut chain = Vec::new();
let events = self.events();
let mut current = event_by_id(events, event_id);
while let Some(event) = current {
chain.push(ExplanationStep {
label: event.summary.clone(),
event: Some(event.id),
});
current = match &event.cause {
Some(CauseRef::Boundary(boundary)) => {
chain.push(ExplanationStep {
label: format!("Committed by boundary {boundary}"),
event: None,
});
None
}
Some(CauseRef::Event(parent)) => event_by_id(events, *parent),
Some(CauseRef::Command(command)) => {
chain.push(ExplanationStep {
label: format!("Accepted command {command}"),
event: None,
});
None
}
Some(CauseRef::System(system)) => {
chain.push(ExplanationStep {
label: format!("Produced by system {system}"),
event: None,
});
None
}
None => None,
};
}
Explanation {
summary: chain.first().map_or_else(
|| "Event was not found".to_owned(),
|step| step.label.clone(),
),
causal_chain: chain,
}
}
}
fn event_by_id(events: &[SimEvent], event_id: EventId) -> Option<&SimEvent> {
let index = usize::try_from(event_id.get().checked_sub(1)?).ok()?;
events.get(index).filter(|event| event.id == event_id)
}
impl CompactedCanwu {
pub fn from_checkpoint_and_journal(
checkpoint: SimulationCheckpoint,
segments: Vec<EvidenceJournalSegment>,
) -> Result<Self, CanwuError> {
Ok(Self {
simulation: CompactedSimulation::from_checkpoint_and_journal(checkpoint, segments)?,
})
}
pub fn from_checkpoint_and_journal_with_plugins(
checkpoint: SimulationCheckpoint,
segments: Vec<EvidenceJournalSegment>,
plugins: &[&dyn SimulationPlugin],
) -> Result<Self, CanwuError> {
Ok(Self {
simulation: CompactedSimulation::from_checkpoint_and_journal_with_plugins(
checkpoint, segments, plugins,
)?,
})
}
pub fn evidence_cursor(&self) -> Result<EvidenceCursor, CanwuError> {
self.simulation.evidence_cursor()
}
pub fn checkpoint(&self) -> Result<SimulationCheckpoint, CanwuError> {
self.simulation.checkpoint()
}
pub fn outbox_entries(&self) -> Result<Vec<OutboxEntry>, CanwuError> {
self.simulation.outbox_entries()
}
pub fn outbox_entries_for_segment(
&self,
segment: &EvidenceJournalSegment,
) -> Result<Vec<OutboxEntry>, CanwuError> {
self.simulation.outbox_entries_for_segment(segment)
}
#[must_use]
pub fn archived_evidence_receipt(
&self,
reference: &EvidenceRef,
) -> Option<&ArchivedEvidenceReceipt> {
self.simulation.archived_evidence_receipt(reference)
}
pub fn load_archived_evidence_segment(
&self,
reference: &EvidenceRef,
provider: &dyn ArchiveProvider,
) -> Result<EvidenceJournalSegment, CanwuError> {
self.simulation
.load_archived_evidence_segment(reference, provider)
}
pub fn seal_evidence(&mut self) -> Result<Option<EvidenceJournalSegment>, CanwuError> {
self.simulation.seal_evidence()
}
pub fn prepare_evidence_seal(&self) -> Result<Option<PreparedEvidenceSeal>, CanwuError> {
self.simulation.prepare_evidence_seal()
}
pub fn commit_evidence_seal(
&mut self,
token: &EvidenceSealToken,
provider: &dyn ArchiveProvider,
) -> Result<(), CanwuError> {
self.simulation.commit_evidence_seal(token, provider)
}
pub fn snapshot_with_segments(
&self,
segments: Vec<EvidenceJournalSegment>,
) -> Result<SimulationSnapshot, CanwuError> {
self.simulation.snapshot_with_segments(segments)
}
pub fn replay_journal_with_segments(
&self,
segments: Vec<EvidenceJournalSegment>,
) -> Result<ReplayJournal, CanwuError> {
self.simulation.replay_journal_with_segments(segments)
}
#[must_use]
pub const fn time(&self) -> SimTime {
self.simulation.time()
}
#[must_use]
pub const fn revision(&self) -> u64 {
self.simulation.revision()
}
#[must_use]
pub fn checkpoint_hash(&self) -> &str {
self.simulation.checkpoint_hash()
}
#[must_use]
pub fn boundary_head_hash(&self) -> Option<&str> {
self.simulation.boundary_head_hash()
}
pub fn entities(&self) -> impl Iterator<Item = &EntityRef> {
self.simulation.entities()
}
#[must_use]
pub fn entity_exists(&self, entity: &EntityRef) -> bool {
self.simulation.entity_exists(entity)
}
#[must_use]
pub fn world(&self) -> WorldSnapshot {
self.simulation.world()
}
#[must_use]
pub fn person_availability(&self, person: PersonId) -> Option<&PersonAvailability> {
self.simulation.person_availability(person)
}
pub fn person_availabilities(&self) -> impl Iterator<Item = (&PersonId, &PersonAvailability)> {
self.simulation.person_availabilities()
}
pub fn pending_transition_manifests(&self) -> impl Iterator<Item = &PendingTransitionManifest> {
self.simulation.pending_transition_manifests()
}
#[must_use]
pub fn knowledge(&self) -> &KnowledgeSnapshot {
self.simulation.knowledge()
}
#[must_use]
pub fn domain_record(&self, reference: &DomainRecordRef) -> Option<&DomainRecord> {
self.simulation.domain_record(reference)
}
#[must_use]
pub fn typed_domain_record<T: DomainRecordType>(
&self,
reference: &TypedDomainRecordRef<T>,
) -> Option<&DomainRecord> {
self.simulation.typed_domain_record(reference)
}
#[must_use]
pub fn decision_ticket(&self, id: DecisionTicketId) -> Option<&DecisionTicket> {
self.simulation.decision_ticket(id)
}
#[must_use]
pub fn decision_controller(&self, id: &str) -> Option<&DecisionControllerBinding> {
self.simulation.decision_controller(id)
}
#[must_use]
pub fn decision_trace(&self, id: DecisionTraceId) -> Option<&DecisionTrace> {
self.simulation.decision_trace(id)
}
#[must_use]
pub fn decision_attempt(&self, id: DecisionRequestId) -> Option<&DecisionAttemptRecord> {
self.simulation.decision_attempt(id)
}
#[must_use]
pub fn decision_hot_state(&self) -> DecisionHotState {
self.simulation.decision_hot_state()
}
#[must_use]
pub fn decision_history_location(&self, key: &DecisionHistoryKey) -> DecisionHistoryLocation {
self.simulation.decision_history_location(key)
}
pub fn decision_history_location_with_provider(
&self,
key: &DecisionHistoryKey,
provider: &dyn DecisionArchiveProvider,
) -> Result<DecisionHistoryLocation, CanwuError> {
self.simulation
.decision_history_location_with_provider(key, provider)
}
pub fn submit(&mut self, envelope: CommandEnvelope) -> Result<CommandReceipt, CanwuError> {
self.simulation.submit(envelope)
}
pub fn process_command(
&mut self,
request: CommandRequest,
) -> Result<CommandOutcome, CanwuError> {
self.simulation.process_command(request)
}
pub fn enqueue_command(
&mut self,
due_at: SimTime,
priority: i32,
request: CommandRequest,
) -> Result<IngressReceipt, CanwuError> {
self.simulation.enqueue_command(due_at, priority, request)
}
pub fn enqueue_plugin_ingress(
&mut self,
request: PluginIngressRequest,
) -> Result<IngressReceipt, CanwuError> {
self.simulation.enqueue_plugin_ingress(request)
}
pub fn enqueue_permitted_plugin_ingress(
&mut self,
request: PluginIngressRequest,
permit: &PluginIngressPermit,
) -> Result<IngressReceipt, CanwuError> {
self.simulation
.enqueue_permitted_plugin_ingress(request, permit)
}
pub fn cancel_plugin_ingress(
&mut self,
ingress_id: IngressId,
reason: impl Into<String>,
) -> Result<IngressReceipt, CanwuError> {
self.simulation.cancel_plugin_ingress(ingress_id, reason)
}
pub fn cancel_permitted_plugin_ingress(
&mut self,
ingress_id: IngressId,
permit: &PluginIngressPermit,
reason: impl Into<String>,
) -> Result<IngressReceipt, CanwuError> {
self.simulation
.cancel_permitted_plugin_ingress(ingress_id, permit, reason)
}
pub fn prepare_decision(
&self,
decision_request_id: DecisionRequestId,
command_request_id: Option<CommandRequestId>,
ticket_id: DecisionTicketId,
policy: &dyn DecisionPolicy,
) -> Result<DecisionEvaluation, CanwuError> {
self.simulation
.prepare_decision(decision_request_id, command_request_id, ticket_id, policy)
}
pub fn prepare_decision_at(
&self,
due_at: SimTime,
decision_request_id: DecisionRequestId,
command_request_id: Option<CommandRequestId>,
ticket_id: DecisionTicketId,
policy: &dyn DecisionPolicy,
) -> Result<DecisionEvaluation, CanwuError> {
self.simulation.prepare_decision_at(
due_at,
decision_request_id,
command_request_id,
ticket_id,
policy,
)
}
pub fn enqueue_decision(
&mut self,
due_at: SimTime,
priority: i32,
request: DecisionIngressRequest,
) -> Result<IngressReceipt, CanwuError> {
self.simulation.enqueue_decision(due_at, priority, request)
}
pub fn drive_decision(
&mut self,
due_at: SimTime,
priority: i32,
decision_request_id: DecisionRequestId,
command_request_id: Option<CommandRequestId>,
ticket_id: DecisionTicketId,
policy: &dyn DecisionPolicy,
) -> Result<DecisionEvaluation, CanwuError> {
self.simulation.drive_decision(
due_at,
priority,
decision_request_id,
command_request_id,
ticket_id,
policy,
)
}
pub fn schedule_calendar_boundary(
&mut self,
due_at: SimTime,
cadences: Vec<SystemCadence>,
) -> Result<IngressReceipt, CanwuError> {
self.simulation.schedule_calendar_boundary(due_at, cadences)
}
pub fn advance(&mut self, duration: SimDuration) -> Result<Vec<SimEvent>, CanwuError> {
self.simulation.advance(duration)
}
pub fn advance_canonical(
&mut self,
duration: SimDuration,
) -> Result<Vec<BoundaryReceipt>, CanwuError> {
self.simulation.advance_canonical(duration)
}
pub fn step_canonical(&mut self) -> Result<Option<BoundaryReceipt>, CanwuError> {
self.simulation.step_canonical()
}
pub fn settle_boundary(
&mut self,
request: BoundaryRequest,
) -> Result<BoundaryReceipt, CanwuError> {
self.simulation.settle_boundary(request)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ObservationPrincipal {
Person(PersonId),
Institution(EntityRef),
Public,
Research,
Developer,
}
impl ObservationPrincipal {
const fn person(&self) -> Option<PersonId> {
match self {
Self::Person(actor) => Some(*actor),
Self::Institution(_) | Self::Public | Self::Research | Self::Developer => None,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ViewerContext {
principal: ObservationPrincipal,
observation: ObservationPolicy,
checkpoint_hash: String,
}
impl ViewerContext {
#[must_use]
pub const fn principal(&self) -> &ObservationPrincipal {
&self.principal
}
#[must_use]
pub const fn actor(&self) -> Option<PersonId> {
self.principal.person()
}
#[must_use]
pub const fn observation(&self) -> ObservationPolicy {
self.observation
}
}
#[derive(Clone, Debug)]
struct KnowledgeViewContext {
principal: ObservationPrincipal,
}
pub struct CanwuViewer<'a> {
canwu: &'a Canwu,
context: KnowledgeViewContext,
}
impl CanwuViewer<'_> {
#[must_use]
pub const fn principal(&self) -> &ObservationPrincipal {
&self.context.principal
}
pub fn query_knowledge(
&self,
query: &KnowledgeQuery,
) -> Result<KnowledgeQueryResult, CanwuError> {
let holder = match &self.context.principal {
ObservationPrincipal::Person(actor) => KnowledgeHolderRef::Person(*actor),
ObservationPrincipal::Institution(entity) => KnowledgeHolderRef::Entity(entity.clone()),
ObservationPrincipal::Public => return Err(invalid_knowledge_authority()),
ObservationPrincipal::Research | ObservationPrincipal::Developer => {
return Err(CanwuError::new(
ErrorCode::InvalidKnowledgeAuthority,
"diagnostic viewers must select a holder explicitly",
));
}
};
self.canwu.admin_query_knowledge(holder, query)
}
pub fn query_holder_knowledge(
&self,
holder: KnowledgeHolderRef,
query: &KnowledgeQuery,
) -> Result<KnowledgeQueryResult, CanwuError> {
match self.context.principal {
ObservationPrincipal::Research | ObservationPrincipal::Developer => {}
ObservationPrincipal::Person(_)
| ObservationPrincipal::Institution(_)
| ObservationPrincipal::Public => return Err(invalid_knowledge_authority()),
}
if !knowledge_holder_exists(self.canwu, &holder) {
return Err(CanwuError::new(
ErrorCode::InvalidKnowledgeHolder,
"the requested knowledge holder does not exist",
));
}
self.canwu.admin_query_knowledge(holder, query)
}
pub fn audit_knowledge_record(
&self,
holder: &KnowledgeHolderRef,
record: HolderKnowledgeRecordId,
) -> Result<KnowledgeRecord, CanwuError> {
match self.context.principal {
ObservationPrincipal::Research | ObservationPrincipal::Developer => {}
ObservationPrincipal::Person(_)
| ObservationPrincipal::Institution(_)
| ObservationPrincipal::Public => return Err(invalid_knowledge_authority()),
}
if !knowledge_holder_exists(self.canwu, holder) {
return Err(CanwuError::new(
ErrorCode::InvalidKnowledgeHolder,
"the requested knowledge holder does not exist",
));
}
let index = usize::try_from(record.get().saturating_sub(1)).map_err(|_| {
CanwuError::new(
ErrorCode::KnowledgeRecordNotFound,
"holder-relative knowledge record ID is outside the supported range",
)
})?;
self.canwu
.knowledge()
.for_holder(holder)
.and_then(|records| records.values().nth(index))
.cloned()
.ok_or_else(|| {
CanwuError::new(
ErrorCode::KnowledgeRecordNotFound,
"holder-relative knowledge record was not found",
)
})
}
#[must_use]
pub fn visible_changes_since(&self, since: SimTime) -> Vec<VisibleChange> {
let context = ViewerContext {
principal: self.context.principal.clone(),
observation: observation_for_principal(&self.context.principal),
checkpoint_hash: self.canwu.checkpoint_hash().to_owned(),
};
self.canwu
.events()
.iter()
.filter(|event| event.timestamp > since)
.filter_map(|event| {
let audience = self.canwu.simulation.event_audience(event);
visible_change(&context, event, &audience)
})
.collect()
}
pub fn evaluation_traces(
&self,
subject: &EntityRef,
after: Option<BoundaryId>,
) -> Result<Vec<EvaluationTraceView>, CanwuError> {
let (own, holder) = match &self.context.principal {
ObservationPrincipal::Person(actor) => (
EntityRef::Person(*actor),
KnowledgeHolderRef::Person(*actor),
),
ObservationPrincipal::Institution(entity) => {
(entity.clone(), KnowledgeHolderRef::Entity(entity.clone()))
}
ObservationPrincipal::Research | ObservationPrincipal::Developer => {
return Ok(self.canwu.evaluation_trace_views(subject, after, None));
}
ObservationPrincipal::Public => return Err(invalid_knowledge_authority()),
};
if &own == subject {
return Ok(self.canwu.evaluation_trace_views(subject, after, None));
}
let learned = self.canwu.boundaries().iter().find_map(|boundary| {
boundary
.knowledge_changes
.iter()
.filter(|change| {
change.holder == holder
&& change
.records
.iter()
.any(|record| knowledge_names_subject(record, subject))
})
.map(|change| match change.visibility {
StateVisibility::SameBoundary => (boundary.id, change.phase),
StateVisibility::NextBoundary => {
(boundary.id, BoundaryPhase::SaveReplayAndDiagnosticHashing)
}
})
.min()
});
Ok(learned.map_or_else(Vec::new, |learned| {
self.canwu
.evaluation_trace_views(subject, after, Some(learned))
}))
}
}
impl Canwu {
fn evaluation_trace_views(
&self,
subject: &EntityRef,
after: Option<BoundaryId>,
learned: Option<(BoundaryId, BoundaryPhase)>,
) -> Vec<EvaluationTraceView> {
let boundaries = self.boundaries();
let start = after.map_or(0, |after| {
boundaries.partition_point(|boundary| boundary.id <= after)
});
boundaries[start..]
.iter()
.flat_map(|boundary| {
boundary
.evaluation_traces
.iter()
.map(move |entry| (boundary.id, entry))
})
.filter(|(boundary, entry)| {
&entry.trace.subject == subject
&& learned.is_none_or(|learned| learned < (*boundary, entry.phase))
})
.map(|(_, entry)| EvaluationTraceView::from(&entry.trace))
.collect()
}
}
fn knowledge_names_subject(record: &KnowledgeRecord, subject: &EntityRef) -> bool {
record.subjects.iter().any(|named| match &named.target {
KnowledgeSubjectTarget::Entity(entity) => entity == subject,
KnowledgeSubjectTarget::DomainRecord(reference) => {
matches!(subject, EntityRef::Domain(domain) if domain == reference)
}
KnowledgeSubjectTarget::Event(_) => false,
})
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct EvaluationTraceView {
pub rule_id: String,
pub rule_version: String,
pub subject: EntityRef,
pub terms: Vec<EvaluationTermView>,
pub result: i64,
pub boundary: BoundaryId,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct EvaluationTermView {
pub term_id: String,
pub contribution: i64,
}
impl From<&EvaluationTraceRecord> for EvaluationTraceView {
fn from(record: &EvaluationTraceRecord) -> Self {
Self {
rule_id: record.rule_id.clone(),
rule_version: record.rule_version.clone(),
subject: record.subject.clone(),
terms: record
.terms
.iter()
.map(|term| EvaluationTermView {
term_id: term.term_id.clone(),
contribution: term.contribution,
})
.collect(),
result: record.result,
boundary: record.boundary,
}
}
}
const fn observation_for_principal(principal: &ObservationPrincipal) -> ObservationPolicy {
match principal {
ObservationPrincipal::Person(_) | ObservationPrincipal::Institution(_) => {
ObservationPolicy::ActorBound
}
ObservationPrincipal::Public => ObservationPolicy::PublicObserver,
ObservationPrincipal::Research => ObservationPolicy::ResearchFull,
ObservationPrincipal::Developer => ObservationPolicy::DeveloperDiagnostic,
}
}
fn invalid_knowledge_authority() -> CanwuError {
CanwuError::new(
ErrorCode::InvalidKnowledgeAuthority,
"this observation principal cannot read a private knowledge ledger",
)
}
fn knowledge_holder_exists(canwu: &Canwu, holder: &KnowledgeHolderRef) -> bool {
match holder {
KnowledgeHolderRef::Person(actor) => canwu.entity_exists(&EntityRef::Person(*actor)),
KnowledgeHolderRef::Entity(entity) => canwu.entity_exists(entity),
}
}
fn map_knowledge_query_error(error: KnowledgeQueryError) -> CanwuError {
match error {
KnowledgeQueryError::ReadCutUnavailable => CanwuError::new(
ErrorCode::KnowledgeReadCutUnavailable,
"knowledge cursor read cut is no longer available",
),
KnowledgeQueryError::InvalidLimit => CanwuError::new(
ErrorCode::KnowledgeLimitExceeded,
"knowledge query page size is outside the supported range",
),
KnowledgeQueryError::InvalidCursor
| KnowledgeQueryError::InvalidLedger
| KnowledgeQueryError::Encoding => CanwuError::new(
ErrorCode::InvalidKnowledgeRecord,
"knowledge query, cursor, or ledger is invalid",
),
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct VisibleChange {
pub timestamp: SimTime,
pub summary: String,
pub source_event: EventId,
}
fn visible_change(
viewer: &ViewerContext,
event: &SimEvent,
plugin_audience: &EventAudience,
) -> Option<VisibleChange> {
let visible = event_visible_to(viewer, event, plugin_audience);
visible.then(|| VisibleChange {
timestamp: event.timestamp,
summary: event.summary.clone(),
source_event: event.id,
})
}
fn event_visible_to(viewer: &ViewerContext, event: &SimEvent, audience: &EventAudience) -> bool {
if matches!(
viewer.observation,
ObservationPolicy::ResearchFull | ObservationPolicy::DeveloperDiagnostic
) {
return true;
}
match audience {
EventAudience::Public => true,
EventAudience::Actor(actor) => viewer.principal.person() == Some(*actor),
EventAudience::Actors(actors) => viewer
.principal
.person()
.is_some_and(|actor| actors.binary_search(&actor).is_ok()),
EventAudience::KnowledgeHolder(holder) => {
principal_matches_holder(&viewer.principal, holder)
}
EventAudience::AffectedActors => viewer
.principal
.person()
.is_some_and(|actor| event.affected_entities.contains(&EntityRef::Person(actor))),
EventAudience::Private => false,
}
}
fn principal_matches_holder(principal: &ObservationPrincipal, holder: &KnowledgeHolderRef) -> bool {
match (principal, holder) {
(ObservationPrincipal::Person(actor), KnowledgeHolderRef::Person(holder)) => {
actor == holder
}
(ObservationPrincipal::Institution(institution), KnowledgeHolderRef::Entity(holder)) => {
institution == holder
}
(ObservationPrincipal::Research | ObservationPrincipal::Developer, _) => true,
(
ObservationPrincipal::Person(_)
| ObservationPrincipal::Institution(_)
| ObservationPrincipal::Public,
_,
) => false,
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(tag = "type", content = "value", rename_all = "snake_case")]
pub enum ExplanationRequest {
Event(EventId),
Failure(CanwuError),
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ExplanationStep {
pub label: String,
pub event: Option<EventId>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Explanation {
pub summary: String,
pub causal_chain: Vec<ExplanationStep>,
}