// file: crates/ksp-store-api/src/model/raw_inspection.rs // version: 1 /// Random-access page request dedicated to bounded interactive RAW inspection. /// /// This request is intentionally distinct from [`crate::RawPageRequest`]. It /// provides offset/limit semantics for operator-facing inspection while the /// canonical RAW list APIs keep their opaque backend cursors for replay, /// backfill and worker traversal. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct RawInspectionPageRequest { limit: crate::RawPageLimit, offset: u64, } impl RawInspectionPageRequest { /// Creates one inspection request from an absolute zero-based offset and positive page limit. #[must_use] pub const fn new(offset: u64, limit: crate::RawPageLimit) -> Self { return Self { limit, offset }; } /// Returns the exact caller-requested page size. #[must_use] pub const fn limit(&self) -> crate::RawPageLimit { return self.limit; } /// Returns the absolute zero-based item offset within the filtered result set. #[must_use] pub const fn offset(&self) -> u64 { return self.offset; } } /// One bounded random-access inspection page with exact logical counts. /// /// `total_items` is the exact count inside the mandatory query scope before /// optional filters. `filtered_items` is the exact count after optional query /// filters. The item vector contains only the requested page window. #[derive(Debug)] pub struct RawInspectionPage { filtered_items: u64, items: std::vec::Vec, total_items: u64, } impl RawInspectionPage { /// Creates one inspection page after validating count consistency. pub fn try_new(items: std::vec::Vec, total_items: u64, filtered_items: u64) -> crate::Result { if filtered_items > total_items { return std::result::Result::Err(raw_model_error("filtered_items")); } let item_count = u64::try_from(items.len()); let item_count = match item_count { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return std::result::Result::Err(raw_model_error("items")), }; if item_count > filtered_items { return std::result::Result::Err(raw_model_error("items")); } return std::result::Result::Ok(Self { filtered_items, items, total_items }); } /// Returns the exact count after optional query filters. #[must_use] pub const fn filtered_items(&self) -> u64 { return self.filtered_items; } /// Returns the current inspection page items. #[must_use] pub fn items(&self) -> &[T] { return self.items.as_slice(); } /// Consumes the page and returns its bounded inspection items. #[must_use] pub fn into_items(self) -> std::vec::Vec { return self.items; } /// Returns the exact count before optional query filters inside the mandatory scope. #[must_use] pub const fn total_items(&self) -> u64 { return self.total_items; } } /// Backend-independent random-access inspection query for RAW transactions. #[derive(Clone, Debug, Eq, PartialEq)] pub struct RawTransactionInspectionQuery { direction: crate::RawSortDirection, network: crate::RawNetworkId, page: crate::RawInspectionPageRequest, slots: crate::RawSlotRange, } impl RawTransactionInspectionQuery { /// Creates one transaction inspection query. #[must_use] pub fn new(network: crate::RawNetworkId, slots: crate::RawSlotRange, direction: crate::RawSortDirection, page: crate::RawInspectionPageRequest) -> Self { return Self { direction, network, page, slots }; } /// Returns the requested deterministic traversal direction. #[must_use] pub const fn direction(&self) -> crate::RawSortDirection { return self.direction; } /// Returns the mandatory logical network scope. #[must_use] pub fn network(&self) -> &crate::RawNetworkId { return &self.network; } /// Returns the random-access inspection page request. #[must_use] pub const fn page(&self) -> crate::RawInspectionPageRequest { return self.page; } /// Returns optional inclusive slot filters. #[must_use] pub const fn slots(&self) -> crate::RawSlotRange { return self.slots; } } /// Backend-independent random-access inspection query for RAW account states. #[derive(Clone, Debug, Eq, PartialEq)] pub struct RawAccountStateInspectionQuery { direction: crate::RawSortDirection, network: crate::RawNetworkId, page: crate::RawInspectionPageRequest, pubkey: std::option::Option, slots: crate::RawSlotRange, } impl RawAccountStateInspectionQuery { /// Creates one account-state inspection query. #[must_use] pub fn new( network: crate::RawNetworkId, pubkey: std::option::Option, slots: crate::RawSlotRange, direction: crate::RawSortDirection, page: crate::RawInspectionPageRequest, ) -> Self { return Self { direction, network, page, pubkey, slots }; } /// Returns the requested deterministic traversal direction. #[must_use] pub const fn direction(&self) -> crate::RawSortDirection { return self.direction; } /// Returns the mandatory logical network scope. #[must_use] pub fn network(&self) -> &crate::RawNetworkId { return &self.network; } /// Returns the random-access inspection page request. #[must_use] pub const fn page(&self) -> crate::RawInspectionPageRequest { return self.page; } /// Returns an optional exact account-address filter. #[must_use] pub fn pubkey(&self) -> std::option::Option<&crate::Pubkey> { return self.pubkey.as_ref(); } /// Returns optional inclusive slot filters. #[must_use] pub const fn slots(&self) -> crate::RawSlotRange { return self.slots; } } /// Payload-free summary of one canonical RAW transaction for operator inspection. #[derive(Clone, Debug, Eq, PartialEq)] pub struct RawTransactionSummary { block_time: std::option::Option, content_hash: crate::RawContentHash, format_id: crate::RawFormatId, format_version: u32, payload_size_bytes: std::option::Option, reference: crate::RawTransactionReference, retention_state: crate::RawRetentionState, slot: u64, } impl RawTransactionSummary { /// Creates one payload-free transaction summary after validating summary invariants. pub fn try_new( reference: crate::RawTransactionReference, slot: u64, block_time: std::option::Option, format_id: crate::RawFormatId, format_version: u32, content_hash: crate::RawContentHash, payload_size_bytes: std::option::Option, retention_state: crate::RawRetentionState, ) -> crate::Result { if format_version == 0 { return std::result::Result::Err(raw_model_error("format_version")); } if let std::option::Option::Some(size) = payload_size_bytes { let size = usize::try_from(size); let size = match size { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return std::result::Result::Err(raw_model_error("payload_size_bytes")), }; if size == 0 || size > crate::MAX_RAW_PAYLOAD_BYTES { return std::result::Result::Err(raw_model_error("payload_size_bytes")); } } if retention_state == crate::RawRetentionState::Purged { if payload_size_bytes.is_some() { return std::result::Result::Err(raw_model_error("payload_size_bytes")); } } else if payload_size_bytes.is_none() { return std::result::Result::Err(raw_model_error("payload_size_bytes")); } return std::result::Result::Ok(Self { block_time, content_hash, format_id, format_version, payload_size_bytes, reference, retention_state, slot, }); } /// Returns the optional canonical block timestamp. #[must_use] pub const fn block_time(&self) -> std::option::Option { return self.block_time; } /// Returns the deterministic canonical content digest. #[must_use] pub const fn content_hash(&self) -> crate::RawContentHash { return self.content_hash; } /// Returns the KSP-owned canonical RAW format identifier. #[must_use] pub fn format_id(&self) -> &crate::RawFormatId { return &self.format_id; } /// Returns the KSP-owned canonical RAW format version. #[must_use] pub const fn format_version(&self) -> u32 { return self.format_version; } /// Returns the canonical payload size when payload content is logically retained. #[must_use] pub const fn payload_size_bytes(&self) -> std::option::Option { return self.payload_size_bytes; } /// Returns the durable backend-independent transaction identity. #[must_use] pub fn reference(&self) -> &crate::RawTransactionReference { return &self.reference; } /// Returns the logical RAW transaction retention state. #[must_use] pub const fn retention_state(&self) -> crate::RawRetentionState { return self.retention_state; } /// Returns the Solana slot containing the transaction. #[must_use] pub const fn slot(&self) -> u64 { return self.slot; } } /// Data-free summary of one canonical RAW account state for operator inspection. #[derive(Clone, Debug, Eq, PartialEq)] pub struct RawAccountStateSummary { data_length_bytes: u64, executable: bool, lamports: u64, owner: crate::Pubkey, reference: crate::RawAccountStateReference, rent_epoch: u64, } impl RawAccountStateSummary { /// Creates one data-free account-state summary after validating its bounded data length. pub fn try_new( reference: crate::RawAccountStateReference, lamports: u64, owner: crate::Pubkey, executable: bool, rent_epoch: u64, data_length_bytes: u64, ) -> crate::Result { let data_length = usize::try_from(data_length_bytes); let data_length = match data_length { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return std::result::Result::Err(raw_model_error("data_length_bytes")), }; if data_length > crate::MAX_RAW_ACCOUNT_DATA_BYTES { return std::result::Result::Err(raw_model_error("data_length_bytes")); } return std::result::Result::Ok(Self { data_length_bytes, executable, lamports, owner, reference, rent_epoch }); } /// Returns the complete canonical account-data length without exposing account bytes. #[must_use] pub const fn data_length_bytes(&self) -> u64 { return self.data_length_bytes; } /// Returns whether the account is executable. #[must_use] pub const fn executable(&self) -> bool { return self.executable; } /// Returns the account lamport balance. #[must_use] pub const fn lamports(&self) -> u64 { return self.lamports; } /// Returns the account owner program public key. #[must_use] pub const fn owner(&self) -> &crate::Pubkey { return &self.owner; } /// Returns the durable backend-independent account-state identity. #[must_use] pub fn reference(&self) -> &crate::RawAccountStateReference { return &self.reference; } /// Returns the rent epoch reported for this account state. #[must_use] pub const fn rent_epoch(&self) -> u64 { return self.rent_epoch; } } fn raw_model_error(field: &'static str) -> crate::Error { return crate::Error::new(crate::ERROR_CODE_RAW_MODEL_INVALID, "invalid backend-agnostic RAW inspection model").with_context("field", field); } #[cfg(test)] #[path = "../../unit_tests/model/raw_inspection.rs"] mod tests;