cloud_sdk_testkit/
recording.rs1use core::sync::atomic::{AtomicUsize, Ordering};
4
5use cloud_sdk::Method;
6use cloud_sdk::transport::{StatusCode, TransportRequest};
7
8pub const MAX_DYNAMIC_RECORDS: usize = 1_024;
10
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub enum RecordedMethod {
14 Get,
16 Post,
18 Put,
20 Delete,
22 Patch,
24 Head,
26 Options,
28 Extension,
30}
31
32impl RecordedMethod {
33 pub(crate) fn from_method(method: Method) -> Self {
34 match method {
35 Method::Get => Self::Get,
36 Method::Post => Self::Post,
37 Method::Put => Self::Put,
38 Method::Delete => Self::Delete,
39 Method::Patch => Self::Patch,
40 Method::Head => Self::Head,
41 Method::Options => Self::Options,
42 _ => Self::Extension,
43 }
44 }
45
46 const fn encode(self) -> usize {
47 match self {
48 Self::Get => 0,
49 Self::Post => 1,
50 Self::Put => 2,
51 Self::Delete => 3,
52 Self::Patch => 4,
53 Self::Head => 5,
54 Self::Options => 6,
55 Self::Extension => 7,
56 }
57 }
58
59 const fn decode(value: usize) -> Self {
60 match value {
61 0 => Self::Get,
62 1 => Self::Post,
63 2 => Self::Put,
64 3 => Self::Delete,
65 4 => Self::Patch,
66 5 => Self::Head,
67 6 => Self::Options,
68 _ => Self::Extension,
69 }
70 }
71}
72
73#[derive(Clone, Copy, Debug, Eq, PartialEq)]
75pub struct RecordedRequest {
76 sequence: usize,
77 method: RecordedMethod,
78 target_len: usize,
79 body_len: usize,
80 header_count: usize,
81 status: StatusCode,
82}
83
84impl RecordedRequest {
85 #[must_use]
87 pub const fn sequence(self) -> usize {
88 self.sequence
89 }
90
91 #[must_use]
93 pub const fn method(self) -> RecordedMethod {
94 self.method
95 }
96
97 #[must_use]
99 pub const fn target_len(self) -> usize {
100 self.target_len
101 }
102
103 #[must_use]
105 pub const fn body_len(self) -> usize {
106 self.body_len
107 }
108
109 #[must_use]
111 pub const fn header_count(self) -> usize {
112 self.header_count
113 }
114
115 #[must_use]
117 pub const fn status(self) -> StatusCode {
118 self.status
119 }
120}
121
122pub struct RequestRecordSlot {
124 ready: AtomicUsize,
125 sequence: AtomicUsize,
126 method: AtomicUsize,
127 target_len: AtomicUsize,
128 body_len: AtomicUsize,
129 header_count: AtomicUsize,
130 status: AtomicUsize,
131}
132
133impl RequestRecordSlot {
134 #[must_use]
136 pub const fn new() -> Self {
137 Self {
138 ready: AtomicUsize::new(0),
139 sequence: AtomicUsize::new(0),
140 method: AtomicUsize::new(0),
141 target_len: AtomicUsize::new(0),
142 body_len: AtomicUsize::new(0),
143 header_count: AtomicUsize::new(0),
144 status: AtomicUsize::new(0),
145 }
146 }
147
148 #[must_use]
150 pub fn get(&self) -> Option<RecordedRequest> {
151 if self.ready.load(Ordering::Acquire) == 0 {
152 return None;
153 }
154 let status = u16::try_from(self.status.load(Ordering::Relaxed)).ok()?;
155 Some(RecordedRequest {
156 sequence: self.sequence.load(Ordering::Relaxed),
157 method: RecordedMethod::decode(self.method.load(Ordering::Relaxed)),
158 target_len: self.target_len.load(Ordering::Relaxed),
159 body_len: self.body_len.load(Ordering::Relaxed),
160 header_count: self.header_count.load(Ordering::Relaxed),
161 status: StatusCode::new(status)?,
162 })
163 }
164
165 pub(crate) fn is_empty(&self) -> bool {
166 self.ready.load(Ordering::Acquire) == 0
167 }
168
169 pub(crate) fn commit(
170 &self,
171 sequence: usize,
172 request: TransportRequest<'_>,
173 status: StatusCode,
174 ) {
175 self.sequence.store(sequence, Ordering::Relaxed);
176 self.method.store(
177 RecordedMethod::from_method(request.method()).encode(),
178 Ordering::Relaxed,
179 );
180 self.target_len
181 .store(request.target().len(), Ordering::Relaxed);
182 self.body_len.store(request.body().len(), Ordering::Relaxed);
183 self.header_count
184 .store(request.headers().as_slice().len(), Ordering::Relaxed);
185 self.status
186 .store(usize::from(status.get()), Ordering::Relaxed);
187 self.ready.store(1, Ordering::Release);
188 }
189}
190
191impl Default for RequestRecordSlot {
192 fn default() -> Self {
193 Self::new()
194 }
195}
196
197impl core::fmt::Debug for RequestRecordSlot {
198 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
199 formatter
200 .debug_tuple("RequestRecordSlot")
201 .field(&self.get())
202 .finish()
203 }
204}