use std::collections::BTreeMap;
use std::error::Error;
use std::fmt;
use crate::capability::CapabilityId;
use crate::fingerprint::Fingerprint;
use crate::id::{RoleId, TypeId};
use crate::schema::OwnsFactId;
use crate::value::ValueTypeTag;
pub const MAX_SDK_DIAGNOSTIC_CODE_BYTES: usize = 128;
pub const MAX_SDK_DIAGNOSTIC_MESSAGE_BYTES: usize = 512;
pub const MAX_SDK_DIAGNOSTIC_NAME_BYTES: usize = 128;
pub const MAX_SDK_DIAGNOSTIC_PATH_SEGMENTS: usize = 32;
pub const MAX_SDK_DIAGNOSTIC_DETAILS: usize = 32;
pub const MAX_SDK_QUERY_DIAGNOSTIC_IDENTITY_BYTES: usize = 512;
pub const MAX_SDK_QUERY_DIAGNOSTIC_IDENTITY_LIST: usize = 32;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SdkDiagnosticVersion(u16);
impl SdkDiagnosticVersion {
pub const V1: Self = Self(1);
pub const CURRENT: Self = Self::V1;
#[must_use]
pub const fn get(self) -> u16 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum SdkDiagnosticCategory {
InvalidInput,
UnsupportedCapability,
ResourceLimit,
Integrity,
Provider,
Transaction,
Cancelled,
Internal,
}
impl SdkDiagnosticCategory {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::InvalidInput => "invalid_input",
Self::UnsupportedCapability => "unsupported_capability",
Self::ResourceLimit => "resource_limit",
Self::Integrity => "integrity",
Self::Provider => "provider",
Self::Transaction => "transaction",
Self::Cancelled => "cancelled",
Self::Internal => "internal",
}
}
}
impl fmt::Display for SdkDiagnosticCategory {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum SdkDiagnosticBuildError {
InvalidCode,
InvalidMessage,
InvalidName,
PathLimitExceeded,
DetailLimitExceeded,
DuplicateDetail,
}
impl fmt::Display for SdkDiagnosticBuildError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let message = match self {
Self::InvalidCode => {
"SDK diagnostic code must be bounded canonical lowercase snake case"
}
Self::InvalidMessage => {
"SDK diagnostic message must be bounded, trimmed, single-line, and path-free static text"
}
Self::InvalidName => {
"SDK diagnostic name must be bounded canonical lowercase snake case"
}
Self::PathLimitExceeded => "SDK diagnostic path exceeds its segment ceiling",
Self::DetailLimitExceeded => "SDK diagnostic details exceed their entry ceiling",
Self::DuplicateDetail => "SDK diagnostic detail key is duplicated",
};
formatter.write_str(message)
}
}
impl Error for SdkDiagnosticBuildError {}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SdkDiagnosticCode(String);
impl SdkDiagnosticCode {
pub fn new(value: impl Into<String>) -> Result<Self, SdkDiagnosticBuildError> {
let value = value.into();
if is_canonical_snake_name(&value, MAX_SDK_DIAGNOSTIC_CODE_BYTES) {
Ok(Self(value))
} else {
Err(SdkDiagnosticBuildError::InvalidCode)
}
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for SdkDiagnosticCode {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SdkDiagnosticMessage(&'static str);
impl SdkDiagnosticMessage {
pub fn new(value: &'static str) -> Result<Self, SdkDiagnosticBuildError> {
let valid = !value.is_empty()
&& value.len() <= MAX_SDK_DIAGNOSTIC_MESSAGE_BYTES
&& value.trim() == value
&& !value
.chars()
.any(|character| character.is_control() || matches!(character, '/' | '\\'));
if valid {
Ok(Self(value))
} else {
Err(SdkDiagnosticBuildError::InvalidMessage)
}
}
#[must_use]
pub const fn as_str(self) -> &'static str {
self.0
}
}
impl fmt::Display for SdkDiagnosticMessage {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SdkDiagnosticName(String);
impl SdkDiagnosticName {
pub fn new(value: impl Into<String>) -> Result<Self, SdkDiagnosticBuildError> {
let value = value.into();
if is_canonical_snake_name(&value, MAX_SDK_DIAGNOSTIC_NAME_BYTES) {
Ok(Self(value))
} else {
Err(SdkDiagnosticBuildError::InvalidName)
}
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for SdkDiagnosticName {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SdkQueryDiagnosticIdentity(String);
impl SdkQueryDiagnosticIdentity {
pub fn new(value: impl Into<String>) -> Result<Self, SdkDiagnosticBuildError> {
let value = value.into();
let valid = !value.is_empty()
&& value.len() <= MAX_SDK_QUERY_DIAGNOSTIC_IDENTITY_BYTES
&& !value.chars().any(|character| {
character.is_control()
|| character.is_whitespace()
|| matches!(character, '/' | '\\' | '@')
});
if valid {
Ok(Self(value))
} else {
Err(SdkDiagnosticBuildError::InvalidName)
}
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for SdkQueryDiagnosticIdentity {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum SdkQueryDiagnosticCategory {
InvalidPlan,
Cardinality,
UnsupportedCapability,
StaleSchema,
ResourceLimit,
Cancelled,
Provider,
ResultDecode,
}
impl SdkQueryDiagnosticCategory {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::InvalidPlan => "invalid_plan",
Self::Cardinality => "cardinality",
Self::UnsupportedCapability => "unsupported_capability",
Self::StaleSchema => "stale_schema",
Self::ResourceLimit => "resource_limit",
Self::Cancelled => "cancelled",
Self::Provider => "provider",
Self::ResultDecode => "result_decode",
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum SdkQueryDiagnosticPathKind {
Request,
Plan,
Operation,
Predicate,
Output,
ProviderEvidence,
Result,
}
impl SdkQueryDiagnosticPathKind {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Request => "request",
Self::Plan => "plan",
Self::Operation => "operation",
Self::Predicate => "predicate",
Self::Output => "output",
Self::ProviderEvidence => "provider_evidence",
Self::Result => "result",
}
}
}
fn is_canonical_snake_name(value: impl AsRef<str>, maximum_bytes: usize) -> bool {
let value = value.as_ref();
let bytes = value.as_bytes();
if bytes.is_empty()
|| bytes.len() > maximum_bytes
|| !bytes[0].is_ascii_lowercase()
|| !bytes[bytes.len() - 1].is_ascii_lowercase() && !bytes[bytes.len() - 1].is_ascii_digit()
{
return false;
}
let mut previous_was_underscore = false;
for byte in bytes {
if *byte == b'_' {
if previous_was_underscore {
return false;
}
previous_was_underscore = true;
} else if byte.is_ascii_lowercase() || byte.is_ascii_digit() {
previous_was_underscore = false;
} else {
return false;
}
}
true
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum SdkDiagnosticPathSegment {
Argument(SdkDiagnosticName),
Index(u64),
Type(TypeId),
Field(OwnsFactId),
Role(RoleId),
Query(SdkQueryDiagnosticPathKind),
QueryBinding(u16),
QueryField {
owner: SdkQueryDiagnosticIdentity,
name: SdkQueryDiagnosticIdentity,
},
QueryRole {
owner: SdkQueryDiagnosticIdentity,
name: SdkQueryDiagnosticIdentity,
},
QueryRoleEdge(u16),
QueryOutputSlot(u64),
QueryOutputName(SdkQueryDiagnosticIdentity),
ContractField(SdkQueryDiagnosticIdentity),
ContractIdentity(SdkQueryDiagnosticIdentity),
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum SdkProviderOperation {
Connect,
Schema,
OpenReadTransaction,
OpenWriteTransaction,
Read,
Write,
Rollback,
Close,
}
impl SdkProviderOperation {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Connect => "connect",
Self::Schema => "schema",
Self::OpenReadTransaction => "open_read_transaction",
Self::OpenWriteTransaction => "open_write_transaction",
Self::Read => "read",
Self::Write => "write",
Self::Rollback => "rollback",
Self::Close => "close",
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum SdkCommitFailureOutcome {
DefinitelyAborted,
Unknown,
}
impl SdkCommitFailureOutcome {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::DefinitelyAborted => "definitely_aborted",
Self::Unknown => "unknown",
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum SdkDiagnosticDetailValue {
Boolean(bool),
Count(u64),
ByteCount(u64),
Capability(CapabilityId),
ValueType(ValueTypeTag),
Type(TypeId),
Field(OwnsFactId),
Role(RoleId),
Fingerprint(Fingerprint),
ProviderOperation(SdkProviderOperation),
CommitOutcome(SdkCommitFailureOutcome),
Signed(i64),
QueryCategory(SdkQueryDiagnosticCategory),
QueryIdentity(SdkQueryDiagnosticIdentity),
QueryIdentityList(Vec<SdkQueryDiagnosticIdentity>),
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SdkProjectionEvidenceSlotPresence {
Absent,
Present,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SdkExecutionDiagnostic {
version: SdkDiagnosticVersion,
category: SdkDiagnosticCategory,
code: SdkDiagnosticCode,
message: SdkDiagnosticMessage,
path: Vec<SdkDiagnosticPathSegment>,
details: BTreeMap<SdkDiagnosticName, SdkDiagnosticDetailValue>,
}
impl SdkExecutionDiagnostic {
fn stable(
category: SdkDiagnosticCategory,
code: SdkDiagnosticCode,
message: SdkDiagnosticMessage,
) -> Self {
Self {
version: SdkDiagnosticVersion::CURRENT,
category,
code,
message,
path: Vec::new(),
details: BTreeMap::new(),
}
}
#[must_use]
pub fn invalid_input(code: SdkDiagnosticCode, message: SdkDiagnosticMessage) -> Self {
Self::stable(SdkDiagnosticCategory::InvalidInput, code, message)
}
#[must_use]
pub fn unsupported_capability(code: SdkDiagnosticCode, message: SdkDiagnosticMessage) -> Self {
Self::stable(SdkDiagnosticCategory::UnsupportedCapability, code, message)
}
#[must_use]
pub fn resource_limit(code: SdkDiagnosticCode, message: SdkDiagnosticMessage) -> Self {
Self::stable(SdkDiagnosticCategory::ResourceLimit, code, message)
}
#[must_use]
pub fn integrity(code: SdkDiagnosticCode, message: SdkDiagnosticMessage) -> Self {
Self::stable(SdkDiagnosticCategory::Integrity, code, message)
}
#[must_use]
pub fn projection_evidence_mismatch() -> Self {
Self::stable(
SdkDiagnosticCategory::Integrity,
static_code("projection_evidence_mismatch"),
static_message(
"Generated projection evidence does not match the verified schema package",
),
)
.try_at(SdkDiagnosticPathSegment::Argument(static_name(
"projection_evidence",
)))
.expect("the fixed projection-evidence diagnostic path is bounded")
}
#[must_use]
pub fn projection_evidence_missing(
index: u64,
identity: SdkQueryDiagnosticIdentity,
foreign_package: bool,
) -> Self {
Self::projection_evidence_mismatch()
.try_at(SdkDiagnosticPathSegment::Index(index))
.and_then(|diagnostic| {
diagnostic.try_at(SdkDiagnosticPathSegment::ContractIdentity(identity))
})
.expect("the fixed missing-evidence diagnostic path is bounded")
.with_static_detail(
static_name("actual_occurrence_count"),
SdkDiagnosticDetailValue::Count(0),
)
.with_static_detail(
static_name("expected_occurrence_count"),
SdkDiagnosticDetailValue::Count(1),
)
.with_static_detail(
static_name("foreign_package"),
SdkDiagnosticDetailValue::Boolean(foreign_package),
)
}
#[must_use]
pub fn classify_detached_semantic_schema_fingerprint_rejection(
presence: SdkProjectionEvidenceSlotPresence,
) -> Self {
match presence {
SdkProjectionEvidenceSlotPresence::Absent => Self::projection_evidence_missing(
0,
SdkQueryDiagnosticIdentity::new("semantic_schema_fingerprint")
.expect("the fixed projection-evidence identity is canonical"),
false,
),
SdkProjectionEvidenceSlotPresence::Present => Self::projection_evidence_mismatch(),
}
}
#[must_use]
pub fn generated_token_package_mismatch() -> Self {
Self::stable(
SdkDiagnosticCategory::Integrity,
static_code("generated_token_package_mismatch"),
static_message("The generated token belongs to a different installed schema package"),
)
}
#[must_use]
pub fn provider_failure(operation: SdkProviderOperation) -> Self {
Self::stable(
SdkDiagnosticCategory::Provider,
static_code("provider_operation_failed"),
static_message("The database provider could not complete the requested operation"),
)
.with_static_detail(
static_name("operation"),
SdkDiagnosticDetailValue::ProviderOperation(operation),
)
}
#[must_use]
pub fn commit_failure(outcome: SdkCommitFailureOutcome) -> Self {
let (code, message) = match outcome {
SdkCommitFailureOutcome::DefinitelyAborted => (
"commit_definitely_aborted",
"The provider proves that the transaction did not commit",
),
SdkCommitFailureOutcome::Unknown => (
"commit_outcome_unknown",
"The transaction commit outcome is unknown and state must be reconciled before retry",
),
};
Self::stable(
SdkDiagnosticCategory::Transaction,
static_code(code),
static_message(message),
)
.with_static_detail(
static_name("commit_outcome"),
SdkDiagnosticDetailValue::CommitOutcome(outcome),
)
}
#[must_use]
pub fn transaction_failure(code: SdkDiagnosticCode, message: SdkDiagnosticMessage) -> Self {
Self::stable(SdkDiagnosticCategory::Transaction, code, message)
}
#[must_use]
pub fn query_failure(category: SdkQueryDiagnosticCategory, code: SdkDiagnosticCode) -> Self {
let (outer, message) = match category {
SdkQueryDiagnosticCategory::InvalidPlan => (
SdkDiagnosticCategory::InvalidInput,
"The typed query plan does not satisfy the generated query contract",
),
SdkQueryDiagnosticCategory::Cardinality => (
SdkDiagnosticCategory::InvalidInput,
"The typed query result does not satisfy the requested cardinality",
),
SdkQueryDiagnosticCategory::UnsupportedCapability => (
SdkDiagnosticCategory::UnsupportedCapability,
"The provider cannot execute a capability required by this typed query",
),
SdkQueryDiagnosticCategory::StaleSchema => (
SdkDiagnosticCategory::Integrity,
"The typed query schema authority changed after request validation",
),
SdkQueryDiagnosticCategory::ResourceLimit => (
SdkDiagnosticCategory::ResourceLimit,
"The typed query exceeded a binding neutral execution limit",
),
SdkQueryDiagnosticCategory::Cancelled => (
SdkDiagnosticCategory::Cancelled,
"The typed query was cancelled during execution",
),
SdkQueryDiagnosticCategory::Provider => (
SdkDiagnosticCategory::Provider,
"The database provider could not complete the typed query",
),
SdkQueryDiagnosticCategory::ResultDecode => (
SdkDiagnosticCategory::Integrity,
"Typed query evidence does not match the validated request invocation",
),
};
Self::stable(outer, code, static_message(message)).with_static_detail(
static_name("query_category"),
SdkDiagnosticDetailValue::QueryCategory(category),
)
}
#[must_use]
pub fn cancelled_before_dispatch() -> Self {
Self::stable(
SdkDiagnosticCategory::Cancelled,
static_code("cancelled_before_dispatch"),
static_message("The operation was cancelled before provider dispatch"),
)
}
#[must_use]
pub fn data_operation_cancelled() -> Self {
Self::stable(
SdkDiagnosticCategory::Cancelled,
static_code("provider_cancelled"),
static_message("The data operation was cancelled"),
)
}
#[must_use]
pub fn data_operation_deadline_exceeded() -> Self {
Self::stable(
SdkDiagnosticCategory::ResourceLimit,
static_code("transaction_deadline_exceeded"),
static_message("The data operation exceeded its absolute execution deadline"),
)
}
#[must_use]
pub fn projected_codec_cancelled() -> Self {
Self::stable(
SdkDiagnosticCategory::Cancelled,
static_code("projected_codec_cancelled"),
static_message("Canonical projected codec work was cancelled before publication"),
)
}
#[must_use]
pub fn projected_codec_deadline_exceeded() -> Self {
Self::resource_limit(
static_code("projected_codec_deadline_exceeded"),
static_message("Canonical projected codec work exceeded its absolute deadline"),
)
}
fn projected_codec_limit(code: &'static str) -> Self {
Self::resource_limit(
static_code(code),
static_message("Canonical projected codec work exceeded a tightened resource limit"),
)
}
#[must_use]
pub fn projected_codec_input_limit() -> Self {
Self::projected_codec_limit("projected_codec_input_limit")
}
#[must_use]
pub fn projected_codec_output_limit() -> Self {
Self::projected_codec_limit("projected_codec_output_limit")
}
#[must_use]
pub fn projected_codec_member_limit() -> Self {
Self::projected_codec_limit("projected_codec_member_limit")
}
#[must_use]
pub fn projected_codec_depth_limit() -> Self {
Self::projected_codec_limit("projected_codec_depth_limit")
}
#[must_use]
pub fn projected_record_schema_mismatch() -> Self {
Self::invalid_input(
static_code("projected_record_schema_mismatch"),
static_message("Canonical projected record schema identity does not match the package"),
)
.try_at(SdkDiagnosticPathSegment::ContractField(
SdkQueryDiagnosticIdentity::new("declared_schema_identity")
.expect("the fixed projected record schema path is canonical"),
))
.expect("the fixed projected record schema path is bounded")
}
#[must_use]
pub fn projected_snapshot_detached() -> Self {
Self::invalid_input(
static_code("projected_snapshot_detached"),
static_message(
"A decoded canonical snapshot is detached and cannot authorize a mutation",
),
)
}
#[must_use]
pub fn internal_failure() -> Self {
Self::stable(
SdkDiagnosticCategory::Internal,
static_code("internal_failure"),
static_message("The operation failed inside the TypeBridge runtime"),
)
}
pub fn try_at(
mut self,
segment: SdkDiagnosticPathSegment,
) -> Result<Self, SdkDiagnosticBuildError> {
if self.path.len() == MAX_SDK_DIAGNOSTIC_PATH_SEGMENTS {
return Err(SdkDiagnosticBuildError::PathLimitExceeded);
}
self.path.push(segment);
Ok(self)
}
pub fn try_with_path_prefix<I>(mut self, prefix: I) -> Result<Self, SdkDiagnosticBuildError>
where
I: IntoIterator<Item = SdkDiagnosticPathSegment>,
{
let mut bounded = Vec::new();
for segment in prefix {
if bounded.len().saturating_add(self.path.len()) == MAX_SDK_DIAGNOSTIC_PATH_SEGMENTS {
return Err(SdkDiagnosticBuildError::PathLimitExceeded);
}
bounded.push(segment);
}
for segment in bounded.into_iter().rev() {
self.path.insert(0, segment);
}
Ok(self)
}
pub fn try_with_detail(
mut self,
key: SdkDiagnosticName,
value: SdkDiagnosticDetailValue,
) -> Result<Self, SdkDiagnosticBuildError> {
if self.details.contains_key(&key) {
return Err(SdkDiagnosticBuildError::DuplicateDetail);
}
if self.details.len() == MAX_SDK_DIAGNOSTIC_DETAILS {
return Err(SdkDiagnosticBuildError::DetailLimitExceeded);
}
self.details.insert(key, value);
Ok(self)
}
fn with_static_detail(
mut self,
key: SdkDiagnosticName,
value: SdkDiagnosticDetailValue,
) -> Self {
let replaced = self.details.insert(key, value);
debug_assert!(replaced.is_none());
self
}
#[must_use]
pub const fn version(&self) -> SdkDiagnosticVersion {
self.version
}
#[must_use]
pub const fn category(&self) -> SdkDiagnosticCategory {
self.category
}
#[must_use]
pub const fn code(&self) -> &SdkDiagnosticCode {
&self.code
}
#[must_use]
pub const fn message(&self) -> SdkDiagnosticMessage {
self.message
}
#[must_use]
pub fn path(&self) -> &[SdkDiagnosticPathSegment] {
&self.path
}
#[must_use]
pub const fn details(&self) -> &BTreeMap<SdkDiagnosticName, SdkDiagnosticDetailValue> {
&self.details
}
}
impl fmt::Display for SdkExecutionDiagnostic {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"{} [{}]: {}",
self.category, self.code, self.message
)
}
}
impl Error for SdkExecutionDiagnostic {}
fn static_code(value: &'static str) -> SdkDiagnosticCode {
SdkDiagnosticCode::new(value).expect("static SDK diagnostic code is canonical")
}
fn static_message(value: &'static str) -> SdkDiagnosticMessage {
SdkDiagnosticMessage::new(value).expect("static SDK diagnostic message is valid")
}
fn static_name(value: &'static str) -> SdkDiagnosticName {
SdkDiagnosticName::new(value).expect("static SDK diagnostic name is canonical")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generated_package_diagnostics_have_fixed_binding_neutral_vocabulary() {
let evidence = SdkExecutionDiagnostic::projection_evidence_mismatch();
assert_eq!(evidence.category(), SdkDiagnosticCategory::Integrity);
assert_eq!(evidence.code().as_str(), "projection_evidence_mismatch");
assert_eq!(
evidence.message().as_str(),
"Generated projection evidence does not match the verified schema package",
);
assert!(matches!(
evidence.path(),
[SdkDiagnosticPathSegment::Argument(name)]
if name.as_str() == "projection_evidence"
));
assert!(evidence.details().is_empty());
let token = SdkExecutionDiagnostic::generated_token_package_mismatch();
assert_eq!(token.category(), SdkDiagnosticCategory::Integrity);
assert_eq!(token.code().as_str(), "generated_token_package_mismatch");
assert_eq!(
token.message().as_str(),
"The generated token belongs to a different installed schema package",
);
assert!(token.path().is_empty());
assert!(token.details().is_empty());
}
#[test]
fn detached_semantic_fingerprint_classifier_matches_v3_representative() {
let missing =
SdkExecutionDiagnostic::classify_detached_semantic_schema_fingerprint_rejection(
SdkProjectionEvidenceSlotPresence::Absent,
);
assert_eq!(missing.category(), SdkDiagnosticCategory::Integrity);
assert_eq!(missing.code().as_str(), "projection_evidence_mismatch");
assert!(matches!(
missing.path(),
[
SdkDiagnosticPathSegment::Argument(argument),
SdkDiagnosticPathSegment::Index(0),
SdkDiagnosticPathSegment::ContractIdentity(identity),
] if argument.as_str() == "projection_evidence"
&& identity.as_str() == "semantic_schema_fingerprint"
));
assert_eq!(
missing
.details()
.iter()
.map(|(name, value)| (name.as_str(), value))
.collect::<Vec<_>>(),
vec![
(
"actual_occurrence_count",
&SdkDiagnosticDetailValue::Count(0),
),
(
"expected_occurrence_count",
&SdkDiagnosticDetailValue::Count(1),
),
("foreign_package", &SdkDiagnosticDetailValue::Boolean(false),),
],
);
let present =
SdkExecutionDiagnostic::classify_detached_semantic_schema_fingerprint_rejection(
SdkProjectionEvidenceSlotPresence::Present,
);
assert_eq!(
present,
SdkExecutionDiagnostic::projection_evidence_mismatch()
);
}
#[test]
fn projected_codec_diagnostics_match_the_closed_v5_algebra() {
for (diagnostic, category, code) in [
(
SdkExecutionDiagnostic::projected_codec_cancelled(),
SdkDiagnosticCategory::Cancelled,
"projected_codec_cancelled",
),
(
SdkExecutionDiagnostic::projected_codec_input_limit(),
SdkDiagnosticCategory::ResourceLimit,
"projected_codec_input_limit",
),
(
SdkExecutionDiagnostic::projected_codec_output_limit(),
SdkDiagnosticCategory::ResourceLimit,
"projected_codec_output_limit",
),
(
SdkExecutionDiagnostic::projected_codec_member_limit(),
SdkDiagnosticCategory::ResourceLimit,
"projected_codec_member_limit",
),
(
SdkExecutionDiagnostic::projected_codec_depth_limit(),
SdkDiagnosticCategory::ResourceLimit,
"projected_codec_depth_limit",
),
] {
assert_eq!(diagnostic.category(), category);
assert_eq!(diagnostic.code().as_str(), code);
assert!(diagnostic.path().is_empty());
assert!(diagnostic.details().is_empty());
}
let mismatch = SdkExecutionDiagnostic::projected_record_schema_mismatch();
assert_eq!(mismatch.category(), SdkDiagnosticCategory::InvalidInput);
assert_eq!(mismatch.code().as_str(), "projected_record_schema_mismatch");
assert!(matches!(
mismatch.path(),
[SdkDiagnosticPathSegment::ContractField(field)]
if field.as_str() == "declared_schema_identity"
));
assert!(mismatch.details().is_empty());
let detached = SdkExecutionDiagnostic::projected_snapshot_detached();
assert_eq!(detached.category(), SdkDiagnosticCategory::InvalidInput);
assert_eq!(detached.code().as_str(), "projected_snapshot_detached");
assert!(detached.path().is_empty());
assert!(detached.details().is_empty());
}
}