sla-escrow-api 0.2.7

SLA-Escrow: Service Level Agreement Enforcer for AI Agents
Documentation
/// Resolution reason for oracle verdicts.
///
/// Follows Solana's `ProgramError::Custom(u32)` pattern: well-known variants
/// for cross-oracle interoperability, plus `Custom(u16)` for domain-specific
/// codes that each oracle defines independently.
///
/// On-chain encoding (u16):
///   0       = None (default for approvals)
///   1-255   = Standard well-known reasons (interoperable across all oracles)
///   256+    = Custom oracle-specific codes (meaning defined by each oracle)
///
/// # For Oracle Developers
///
/// Use standard variants when your rejection reason maps to a well-known
/// category. Use `Custom(code)` for domain-specific reasons:
///
/// ```rust
/// use sla_escrow_api::resolution::ResolutionReason;
///
/// // Standard: any oracle can emit this
/// let reason = ResolutionReason::LatencyExceeded;
///
/// // Custom: only your oracle defines what 1001 means
/// let reason = ResolutionReason::Custom(1001);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResolutionReason {
    /// No reason — used for approvals or when no specific reason applies.
    None,
    /// HTTP status code fell outside the SLA-specified range.
    StatusCodeOutOfRange,
    /// Response latency exceeded the SLA threshold.
    LatencyExceeded,
    /// Response body failed JSON Schema validation.
    SchemaValidationFailed,
    /// One or more required fields were missing from the response.
    RequiredFieldsMissing,
    /// Response body was shorter than the minimum length.
    BodyTooShort,
    /// Delivery evidence hash did not match the on-chain commitment.
    HashMismatch,
    /// Off-chain evidence could not be fetched or was unavailable.
    EvidenceUnavailable,
    /// General/unspecified rejection (catch-all for standard rejections).
    GeneralRejection,
    /// Oracle-specific reason code. Each oracle defines its own code meanings.
    /// Values should be >= `CUSTOM_REASON_BASE` (256).
    Custom(u16),
}

/// Boundary between standard and custom reason codes.
/// Codes 0-255 are reserved for well-known interoperable reasons.
/// Codes 256-65535 are available for oracle-specific semantics.
pub const CUSTOM_REASON_BASE: u16 = 256;

impl From<ResolutionReason> for u16 {
    fn from(reason: ResolutionReason) -> u16 {
        match reason {
            ResolutionReason::None => 0,
            ResolutionReason::StatusCodeOutOfRange => 1,
            ResolutionReason::LatencyExceeded => 2,
            ResolutionReason::SchemaValidationFailed => 3,
            ResolutionReason::RequiredFieldsMissing => 4,
            ResolutionReason::BodyTooShort => 5,
            ResolutionReason::HashMismatch => 6,
            ResolutionReason::EvidenceUnavailable => 7,
            ResolutionReason::GeneralRejection => 255,
            ResolutionReason::Custom(code) => code,
        }
    }
}

impl From<u16> for ResolutionReason {
    fn from(code: u16) -> Self {
        match code {
            0 => Self::None,
            1 => Self::StatusCodeOutOfRange,
            2 => Self::LatencyExceeded,
            3 => Self::SchemaValidationFailed,
            4 => Self::RequiredFieldsMissing,
            5 => Self::BodyTooShort,
            6 => Self::HashMismatch,
            7 => Self::EvidenceUnavailable,
            255 => Self::GeneralRejection,
            code => Self::Custom(code),
        }
    }
}

impl std::fmt::Display for ResolutionReason {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::None => write!(f, "none"),
            Self::StatusCodeOutOfRange => write!(f, "status_code_out_of_range"),
            Self::LatencyExceeded => write!(f, "latency_exceeded"),
            Self::SchemaValidationFailed => write!(f, "schema_validation_failed"),
            Self::RequiredFieldsMissing => write!(f, "required_fields_missing"),
            Self::BodyTooShort => write!(f, "body_too_short"),
            Self::HashMismatch => write!(f, "hash_mismatch"),
            Self::EvidenceUnavailable => write!(f, "evidence_unavailable"),
            Self::GeneralRejection => write!(f, "general_rejection"),
            Self::Custom(code) => write!(f, "custom({})", code),
        }
    }
}

impl ResolutionReason {
    /// Returns true if this is a standard (well-known) reason code.
    pub fn is_standard(&self) -> bool {
        !matches!(self, Self::Custom(_))
    }

    /// Returns true if this is a custom oracle-specific reason code.
    pub fn is_custom(&self) -> bool {
        matches!(self, Self::Custom(_))
    }

    /// Returns the raw u16 code.
    pub fn code(&self) -> u16 {
        u16::from(*self)
    }
}