xet_client/cas_client/simulation/direct_access_client.rs
1//! Direct Access Client Trait
2//!
3//! This module defines the `DirectAccessClient` trait, which extends the standard
4//! `Client` interface with direct XORB and file access methods. This is used by
5//! the local server and testing utilities to access stored data directly.
6
7use std::ops::Range;
8use std::time::Duration;
9
10use async_trait::async_trait;
11use bytes::Bytes;
12use xet_core_structures::merklehash::MerkleHash;
13use xet_core_structures::xorb_object::XorbObject;
14
15use super::super::interface::Client;
16use crate::cas_types::{
17 FileRange, QueryReconstructionResponse, QueryReconstructionResponseV2, XorbReconstructionFetchInfo,
18};
19use crate::error::Result;
20
21/// A Client with direct access to XORB and file storage.
22///
23/// This trait extends the standard Client interface with methods for:
24/// - Direct XORB access (read, list)
25/// - File data retrieval
26/// - URL expiration control
27/// - API delay simulation
28///
29/// Both `LocalClient` and `MemoryClient` implement this trait, allowing the
30/// local server to work with either backend.
31#[cfg_attr(not(target_family = "wasm"), async_trait)]
32#[cfg_attr(target_family = "wasm", async_trait(?Send))]
33pub trait DirectAccessClient: Client + Send + Sync {
34 /// Sets the expiration duration for fetch term URLs.
35 fn set_fetch_term_url_expiration(&self, expiration: Duration);
36
37 /// Sets a random delay range for all Client API calls.
38 ///
39 /// When set, each Client trait method will sleep for a random duration
40 /// within the specified range before returning. This simulates network latency.
41 ///
42 /// Pass `None` to disable the delay.
43 fn set_api_delay_range(&self, delay_range: Option<Range<Duration>>);
44
45 /// Sets the maximum number of byte ranges per `XorbMultiRangeFetch` entry
46 /// in V2 reconstruction responses.
47 ///
48 /// Default is `usize::MAX` (all ranges in one fetch). When set to N,
49 /// ranges for each xorb are grouped into entries of at most N ranges.
50 /// This simulates the CloudFront URL length limit that forces splitting.
51 fn set_max_ranges_per_fetch(&self, max_ranges: usize);
52
53 /// Sets the expiration duration for global dedup shards.
54 ///
55 /// When set, `query_for_global_dedup_shard` will set the shard footer's
56 /// `shard_key_expiry` to `now + expiration`
57 ///
58 /// Pass `None` to disable (default: returns full shards with no expiration).
59 fn set_global_dedup_shard_expiration(&self, expiration: Option<Duration>);
60
61 /// Disables V2 endpoints (reconstruction and shard upload) with the given HTTP status code.
62 /// When disabled, `/v2/*` handlers return this status, forcing clients to fall back to V1.
63 /// Pass 0 to re-enable.
64 fn disable_v2_endpoints(&self, status_code: u16);
65
66 /// Returns the HTTP status code V2 endpoints should return when disabled,
67 /// or 0 if V2 is enabled.
68 fn v2_disabled_status_code(&self) -> u16 {
69 0
70 }
71
72 /// V1 reconstruction: returns per-range presigned URLs.
73 async fn get_reconstruction_v1(
74 &self,
75 file_id: &MerkleHash,
76 bytes_range: Option<FileRange>,
77 ) -> Result<Option<QueryReconstructionResponse>>;
78
79 /// V2 reconstruction: returns per-xorb multi-range fetch descriptors.
80 async fn get_reconstruction_v2(
81 &self,
82 file_id: &MerkleHash,
83 bytes_range: Option<FileRange>,
84 ) -> Result<Option<QueryReconstructionResponseV2>>;
85
86 /// Applies the configured API delay if set.
87 ///
88 /// This method sleeps for a random duration within the configured delay range.
89 /// If no delay is configured (via `set_api_delay_range`), this returns immediately.
90 async fn apply_api_delay(&self);
91
92 /// Returns all XORB hashes stored in this client.
93 async fn list_xorbs(&self) -> Result<Vec<MerkleHash>>;
94
95 /// Get all uncompressed bytes from a XORB.
96 async fn get_full_xorb(&self, hash: &MerkleHash) -> Result<Bytes>;
97
98 /// Get uncompressed bytes from a XORB within chunk ranges.
99 /// Each tuple represents a chunk index range [start, end).
100 async fn get_xorb_ranges(&self, hash: &MerkleHash, chunk_ranges: Vec<(u32, u32)>) -> Result<Vec<Bytes>>;
101
102 /// Get the length of the uncompressed XORB data.
103 async fn xorb_length(&self, hash: &MerkleHash) -> Result<u32>;
104
105 /// Check if a XORB exists.
106 async fn xorb_exists(&self, hash: &MerkleHash) -> Result<bool>;
107
108 /// Get the XorbObject footer/metadata for a XORB.
109 async fn xorb_footer(&self, hash: &MerkleHash) -> Result<XorbObject>;
110
111 /// Get the file size for a given file hash.
112 async fn get_file_size(&self, hash: &MerkleHash) -> Result<u64>;
113
114 /// Get file data, optionally within a byte range.
115 async fn get_file_data(&self, hash: &MerkleHash, byte_range: Option<FileRange>) -> Result<Bytes>;
116
117 /// Get raw (serialized) bytes from a XORB, optionally within a byte range.
118 ///
119 /// Unlike `get_xorb_ranges` which returns decompressed chunk data, this returns
120 /// the raw bytes as stored (including compression headers). This is used by the
121 /// server's fetch_term endpoint to serve data that clients can then decompress.
122 async fn get_xorb_raw_bytes(&self, hash: &MerkleHash, byte_range: Option<FileRange>) -> Result<Bytes>;
123
124 /// Get the total length of the raw (serialized) XORB data.
125 async fn xorb_raw_length(&self, hash: &MerkleHash) -> Result<u64>;
126
127 /// Fetches term data for a given hash and fetch term.
128 /// Returns (data bytes, chunk byte indices) matching `Client::get_file_term_data`.
129 async fn fetch_term_data(
130 &self,
131 hash: MerkleHash,
132 fetch_term: XorbReconstructionFetchInfo,
133 ) -> Result<(Bytes, Vec<u32>)>;
134}