1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use rmpv::Value;
use serde::de::DeserializeOwned;

use crate::{errors::DecodingError, utils::extract_iproto_data};

// TODO: unify with call_response.rs

/// Tuple, returned from all data-manipulation operations (insert, update, upsert, replace, delete).
#[derive(Clone, Debug, PartialEq)]
pub struct DmoResponse(pub(crate) rmpv::Value);

impl DmoResponse {
    /// Decode row into type.
    ///
    /// Raises error if no rows returned.
    pub fn decode<T>(self) -> Result<T, DecodingError>
    where
        T: DeserializeOwned,
    {
        let first = self
            .into_data_tuple()?
            .into_iter()
            .next()
            .ok_or_else(|| DecodingError::invalid_tuple_length(1, 0))?;
        Ok(rmpv::ext::from_value(first)?)
    }

    /// Decode row into type or return `None` if no rows returned.
    pub fn decode_opt<T>(self) -> Result<Option<T>, DecodingError>
    where
        T: DeserializeOwned,
    {
        self.into_data_tuple()?
            .into_iter()
            .next()
            .map(rmpv::ext::from_value::<T>)
            .transpose()
            .map_err(Into::into)
    }

    fn into_data_tuple(self) -> Result<Vec<Value>, DecodingError> {
        match extract_iproto_data(self.0)? {
            Value::Array(x) => Ok(x),
            rest => Err(DecodingError::type_mismatch("array", rest.to_string())),
        }
    }
}

#[cfg(test)]
mod tests {
    use assert_matches::assert_matches;

    use crate::codec::consts::keys::DATA;

    use super::*;

    fn build_tuple_response(data: Vec<Value>) -> Value {
        Value::Map(vec![(DATA.into(), Value::Array(data))])
    }

    #[test]
    fn decode() {
        let resp = build_tuple_response(vec![Value::Boolean(true)]);
        assert_matches!(DmoResponse(resp).decode(), Ok(true));
    }

    #[test]
    fn decode_err_len() {
        let resp = build_tuple_response(vec![]);
        assert_matches!(DmoResponse(resp).decode::<()>(), Err(_));
    }

    #[test]
    fn decode_opt() {
        let resp = build_tuple_response(vec![Value::Boolean(true)]);
        assert_matches!(DmoResponse(resp).decode_opt(), Ok(Some(true)));
    }

    #[test]
    fn decode_opt_none() {
        let resp = build_tuple_response(vec![]);
        assert_matches!(DmoResponse(resp).decode_opt::<()>(), Ok(None));
    }
}