tarantool_rs/client/dmo/
response.rs1use rmpv::Value;
2use serde::de::DeserializeOwned;
3
4use crate::{errors::DecodingError, utils::extract_iproto_data};
5
6#[derive(Clone, Debug, PartialEq)]
10pub struct DmoResponse(pub(crate) rmpv::Value);
11
12impl DmoResponse {
13 pub fn decode<T>(self) -> Result<T, DecodingError>
17 where
18 T: DeserializeOwned,
19 {
20 let first = self
21 .into_data_tuple()?
22 .into_iter()
23 .next()
24 .ok_or_else(|| DecodingError::invalid_tuple_length(1, 0))?;
25 Ok(rmpv::ext::from_value(first)?)
26 }
27
28 pub fn decode_opt<T>(self) -> Result<Option<T>, DecodingError>
30 where
31 T: DeserializeOwned,
32 {
33 self.into_data_tuple()?
34 .into_iter()
35 .next()
36 .map(rmpv::ext::from_value::<T>)
37 .transpose()
38 .map_err(Into::into)
39 }
40
41 fn into_data_tuple(self) -> Result<Vec<Value>, DecodingError> {
42 match extract_iproto_data(self.0)? {
43 Value::Array(x) => Ok(x),
44 rest => Err(DecodingError::type_mismatch("array", rest.to_string())),
45 }
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use assert_matches::assert_matches;
52
53 use crate::codec::consts::keys::DATA;
54
55 use super::*;
56
57 fn build_tuple_response(data: Vec<Value>) -> Value {
58 Value::Map(vec![(DATA.into(), Value::Array(data))])
59 }
60
61 #[test]
62 fn decode() {
63 let resp = build_tuple_response(vec![Value::Boolean(true)]);
64 assert_matches!(DmoResponse(resp).decode(), Ok(true));
65 }
66
67 #[test]
68 fn decode_err_len() {
69 let resp = build_tuple_response(vec![]);
70 assert_matches!(DmoResponse(resp).decode::<()>(), Err(_));
71 }
72
73 #[test]
74 fn decode_opt() {
75 let resp = build_tuple_response(vec![Value::Boolean(true)]);
76 assert_matches!(DmoResponse(resp).decode_opt(), Ok(Some(true)));
77 }
78
79 #[test]
80 fn decode_opt_none() {
81 let resp = build_tuple_response(vec![]);
82 assert_matches!(DmoResponse(resp).decode_opt::<()>(), Ok(None));
83 }
84}