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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::response::{InvokeResponse, ListResponse, MethodsResponse};
use erased_serde::Serialize as ErasedSerialize;
use serde::de::DeserializeOwned;
use serde::ser::SerializeStruct;
use serde::Serialize;
use std::marker::PhantomData;

pub trait ApiCall: sealed::Sealed {
    const KIND: &str;
    type Response: DeserializeOwned + 'static;
}

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct RpcCall<T>(T);

impl<T: ApiCall + Serialize> RpcCall<T> {
    pub fn new(data: T) -> Self {
        Self(data)
    }
}

impl<T: ApiCall + Serialize> Serialize for RpcCall<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let zst = std::mem::size_of::<T>() == 0;
        let len = if zst { 1 } else { 2 };

        let mut s = serializer.serialize_struct("RpcCall", len)?;

        s.serialize_field("type", T::KIND)?;

        if !zst {
            s.serialize_field("data", &self.0)?;
        }

        s.end()
    }
}

impl RpcCall<ListCall> {
    pub fn list() -> Self {
        Self(ListCall)
    }
}

impl RpcCall<MethodsCall> {
    pub fn methods(device_id: uuid::Uuid) -> Self {
        Self(MethodsCall { device_id })
    }
}

impl<'a, R: DeserializeOwned + 'static> RpcCall<InvokeCall<'a, R>> {
    pub fn invoke(
        device_id: uuid::Uuid,
        method_name: &'a str,
        parameters: &'a [&'a dyn ErasedSerialize],
    ) -> Self {
        Self(InvokeCall {
            device_id,
            name: method_name,
            parameters,
            _ret_value: PhantomData,
        })
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum RpcCallKind {
    List,
    Methods,
    Invoke,
}

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Serialize)]
pub struct ListCall;

impl ApiCall for ListCall {
    const KIND: &'static str = "list";
    type Response = ListResponse;
}

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Serialize)]
#[serde(transparent)]
pub struct MethodsCall {
    #[serde(rename = "deviceId")]
    pub device_id: uuid::Uuid,
}

impl ApiCall for MethodsCall {
    const KIND: &'static str = "methods";
    type Response = MethodsResponse;
}

#[derive(Copy, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InvokeCall<'a, R> {
    pub device_id: uuid::Uuid,
    pub name: &'a str,
    pub parameters: &'a [&'a dyn ErasedSerialize],
    // Unused, but this is required for the API for RpcResponse to be the way I'd like it.
    _ret_value: PhantomData<fn() -> R>,
}

impl<R: DeserializeOwned + 'static> ApiCall for InvokeCall<'_, R> {
    const KIND: &'static str = "invoke";
    type Response = InvokeResponse<R>;
}

mod sealed {
    use serde::de::DeserializeOwned;

    use super::{InvokeCall, ListCall, MethodsCall};

    pub trait Sealed {}

    impl Sealed for ListCall {}
    impl Sealed for MethodsCall {}
    impl<R: DeserializeOwned + 'static> Sealed for InvokeCall<'_, R> {}
}