1pub mod expect;
17
18#[doc(inline)]
19pub use expect::Expect;
20pub use expect::ExpectedRequest;
21
22use nv_redfish_core::action::ActionTarget;
23use nv_redfish_core::query::ExpandQuery;
24use nv_redfish_core::ActionError;
25use nv_redfish_core::Bmc as NvRedfishBmc;
26use nv_redfish_core::Expandable;
27use nv_redfish_core::ModificationResponse;
28use nv_redfish_core::ODataETag;
29use nv_redfish_core::ODataId;
30use serde::Serialize;
31use serde_json::from_value;
32use serde_json::to_value;
33use serde_json::Error as JsonError;
34use std::collections::VecDeque;
35use std::error::Error as StdError;
36use std::fmt::Display;
37use std::fmt::Formatter;
38use std::fmt::Result as FmtResult;
39use std::sync::Arc;
40use std::sync::Mutex;
41use std::sync::PoisonError;
42
43#[derive(Debug)]
44pub enum Error {
45 NotSupported,
46 ErrorResponse(Box<dyn StdError + Send + Sync>),
47 MutexLock(String),
48 NothingIsExpected,
49 BadResponseJson(JsonError),
50 UnexpectedGet(ODataId, ExpectedRequest),
51 UnexpectedExpand(ODataId, ExpectedRequest),
52 UnexpectedUpdate(ODataId, String, ExpectedRequest),
53 UnexpectedCreate(ODataId, String, ExpectedRequest),
54 UnexpectedDelete(ODataId, ExpectedRequest),
55 UnexpectedAction(ActionTarget, String, ExpectedRequest),
56 UnexpectedStream(String, ExpectedRequest),
57}
58
59impl Display for Error {
60 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
61 match self {
62 Self::ErrorResponse(err) => write!(f, "response: {err}"),
63 Self::NotSupported => write!(f, "not supported"),
64 Self::MutexLock(err) => write!(f, "lock error: {err}"),
65 Self::NothingIsExpected => {
66 write!(f, "nothing is expected to happen but something happened")
67 }
68 Self::BadResponseJson(err) => write!(f, "bad json response: {err}"),
69 Self::UnexpectedGet(id, expected) => {
70 write!(f, "unexpected get: {id}; expected: {expected:?}")
71 }
72 Self::UnexpectedExpand(id, expected) => {
73 write!(f, "unexpected expand: {id}; expected: {expected:?}")
74 }
75 Self::UnexpectedUpdate(id, json, expected) => {
76 write!(
77 f,
78 "unexpected update: {id}; json: {json} expected: {expected:?}"
79 )
80 }
81 Self::UnexpectedCreate(id, json, expected) => {
82 write!(
83 f,
84 "unexpected create: {id}; json: {json} expected: {expected:?}"
85 )
86 }
87 Self::UnexpectedDelete(id, expected) => {
88 write!(f, "unexpected delete: {id}; expected: {expected:?}")
89 }
90 Self::UnexpectedAction(id, json, expected) => {
91 write!(
92 f,
93 "unexpected action: {id}; json: {json} expected: {expected:?}"
94 )
95 }
96 Self::UnexpectedStream(uri, expected) => {
97 write!(f, "unexpected stream: {uri}; expected: {expected:?}")
98 }
99 }
100 }
101}
102
103impl StdError for Error {}
104
105impl Error {
106 pub fn mutex_lock<T>(err: PoisonError<T>) -> Self {
107 Self::MutexLock(err.to_string())
108 }
109}
110
111#[derive(Default)]
112pub struct Bmc<E> {
113 expect: Mutex<VecDeque<Expect<E>>>,
114}
115
116impl<E> Bmc<E> {
117 pub fn expect(&self, exp: Expect<E>) {
118 let expect: &mut VecDeque<Expect<E>> = &mut self.expect.lock().expect("not poisoned");
119 expect.push_back(exp);
120 }
121
122 pub fn debug_expect(&self) {
123 let expect: &VecDeque<Expect<E>> = &self.expect.lock().expect("not poisoned");
124 println!("Expectations (total: {})", expect.len());
125 for v in expect {
126 println!("{:#?}", v.request);
127 }
128 }
129}
130
131impl<E> NvRedfishBmc for Bmc<E>
132where
133 E: StdError + Send + Sync + 'static,
134{
135 type Error = Error;
136
137 async fn expand<T>(&self, in_id: &ODataId, _query: ExpandQuery) -> Result<Arc<T>, Error>
138 where
139 T: Expandable,
140 {
141 let expect = self
142 .expect
143 .lock()
144 .map_err(Error::mutex_lock)?
145 .pop_front()
146 .ok_or(Error::NothingIsExpected)?;
147 match expect {
148 Expect {
149 request: ExpectedRequest::Expand { id },
150 response,
151 } if id == *in_id => {
152 let response = response.map_err(|err| Error::ErrorResponse(Box::new(err)))?;
153 let result: T = from_value(response).map_err(Error::BadResponseJson)?;
154 Ok(Arc::new(result))
155 }
156 _ => Err(Error::UnexpectedExpand(in_id.clone(), expect.request)),
157 }
158 }
159
160 async fn get<T: nv_redfish_core::EntityTypeRef + Sized + for<'a> serde::Deserialize<'a>>(
161 &self,
162 in_id: &ODataId,
163 ) -> Result<Arc<T>, Self::Error> {
164 let expect = self
165 .expect
166 .lock()
167 .map_err(Error::mutex_lock)?
168 .pop_front()
169 .ok_or(Error::NothingIsExpected)?;
170 match expect {
171 Expect {
172 request: ExpectedRequest::Get { id },
173 response,
174 } if id == *in_id => {
175 let response = response.map_err(|err| Error::ErrorResponse(Box::new(err)))?;
176 let result: T = from_value(response).map_err(Error::BadResponseJson)?;
177 Ok(Arc::new(result))
178 }
179 _ => Err(Error::UnexpectedGet(in_id.clone(), expect.request)),
180 }
181 }
182
183 async fn update<
184 V: Sync + Send + Serialize,
185 R: Sync + Send + Sized + for<'a> serde::Deserialize<'a>,
186 >(
187 &self,
188 in_id: &ODataId,
189 _etag: Option<&ODataETag>,
190 update: &V,
191 ) -> Result<ModificationResponse<R>, Self::Error> {
192 let expect = self
193 .expect
194 .lock()
195 .map_err(Error::mutex_lock)?
196 .pop_front()
197 .ok_or(Error::NothingIsExpected)?;
198 let in_request = to_value(update).expect("json serializable");
199 match expect {
200 Expect {
201 request: ExpectedRequest::Update { id, request },
202 response,
203 } if id == *in_id && request == in_request => {
204 let response = response.map_err(|err| Error::ErrorResponse(Box::new(err)))?;
205 let result: R = from_value(response).map_err(Error::BadResponseJson)?;
206 Ok(ModificationResponse::Entity(result))
207 }
208 _ => Err(Error::UnexpectedUpdate(
209 in_id.clone(),
210 in_request.to_string(),
211 expect.request,
212 )),
213 }
214 }
215
216 async fn create<
217 V: Sync + Send + Serialize,
218 R: Sync + Send + Sized + for<'a> serde::Deserialize<'a>,
219 >(
220 &self,
221 in_id: &ODataId,
222 create: &V,
223 ) -> Result<ModificationResponse<R>, Self::Error> {
224 let expect = self
225 .expect
226 .lock()
227 .map_err(Error::mutex_lock)?
228 .pop_front()
229 .ok_or(Error::NothingIsExpected)?;
230 let in_request = to_value(create).expect("json serializable");
231 match expect {
232 Expect {
233 request: ExpectedRequest::Create { id, request },
234 response,
235 } if id == *in_id && request == in_request => {
236 let response = response.map_err(|err| Error::ErrorResponse(Box::new(err)))?;
237 let result: R = from_value(response).map_err(Error::BadResponseJson)?;
238 Ok(ModificationResponse::Entity(result))
239 }
240 _ => Err(Error::UnexpectedCreate(
241 in_id.clone(),
242 in_request.to_string(),
243 expect.request,
244 )),
245 }
246 }
247
248 async fn delete<
249 R: nv_redfish_core::EntityTypeRef + Sync + Send + for<'de> serde::Deserialize<'de>,
250 >(
251 &self,
252 in_id: &ODataId,
253 ) -> Result<ModificationResponse<R>, Self::Error> {
254 let expect = self
255 .expect
256 .lock()
257 .map_err(Error::mutex_lock)?
258 .pop_front()
259 .ok_or(Error::NothingIsExpected)?;
260 match expect {
261 Expect {
262 request: ExpectedRequest::Delete { id },
263 ..
264 } if id == *in_id => Ok(ModificationResponse::Empty),
265 _ => Err(Error::UnexpectedDelete(in_id.clone(), expect.request)),
266 }
267 }
268
269 async fn action<
270 T: Send + Sync + serde::Serialize,
271 R: Send + Sync + Sized + for<'a> serde::Deserialize<'a>,
272 >(
273 &self,
274 action: &nv_redfish_core::Action<T, R>,
275 params: &T,
276 ) -> Result<ModificationResponse<R>, Self::Error> {
277 let expect = self
278 .expect
279 .lock()
280 .map_err(Error::mutex_lock)?
281 .pop_front()
282 .ok_or(Error::NothingIsExpected)?;
283 let in_request = to_value(params).expect("json serializable");
284 match expect {
285 Expect {
286 request: ExpectedRequest::Action { target, request },
287 response,
288 } if target == action.target && request == in_request => {
289 let response = response.map_err(|err| Error::ErrorResponse(Box::new(err)))?;
290 let result: R = from_value(response).map_err(Error::BadResponseJson)?;
291 Ok(ModificationResponse::Entity(result))
292 }
293 _ => Err(Error::UnexpectedAction(
294 action.target.clone(),
295 in_request.to_string(),
296 expect.request,
297 )),
298 }
299 }
300
301 async fn filter<
302 T: nv_redfish_core::EntityTypeRef
303 + Sized
304 + for<'a> serde::Deserialize<'a>
305 + 'static
306 + Send
307 + Sync,
308 >(
309 &self,
310 _id: &ODataId,
311 _query: nv_redfish_core::FilterQuery,
312 ) -> Result<Arc<T>, Self::Error> {
313 todo!("unimplemented")
314 }
315
316 async fn stream<T: Sized + for<'a> serde::Deserialize<'a> + Send + 'static>(
317 &self,
318 in_uri: &str,
319 ) -> Result<nv_redfish_core::BoxTryStream<T, Self::Error>, Self::Error> {
320 let expect = self
321 .expect
322 .lock()
323 .map_err(Error::mutex_lock)?
324 .pop_front()
325 .ok_or(Error::NothingIsExpected)?;
326 match expect {
327 Expect {
328 request: ExpectedRequest::Stream { uri },
329 response,
330 } if uri == *in_uri => {
331 let response = response.map_err(|err| Error::ErrorResponse(Box::new(err)))?;
332 let result: Vec<T> = from_value(response).map_err(Error::BadResponseJson)?;
333 Ok(Box::pin(futures_util::stream::iter(
334 result.into_iter().map(Ok),
335 )))
336 }
337 _ => Err(Error::UnexpectedStream(in_uri.to_string(), expect.request)),
338 }
339 }
340}
341
342impl ActionError for Error {
343 fn not_supported() -> Self {
344 Error::NotSupported
345 }
346}