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
use crate::response;
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 Call<T>(T);

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

impl<T: ApiCall + Serialize> Serialize for Call<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 Call<List> {
    pub fn list() -> Self {
        Self(List)
    }
}

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

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

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

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

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

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

#[derive(Copy, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Invoke<'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 Invoke<'_, R> {
    const KIND: &'static str = "invoke";
    type Response = response::Return<R>;
}

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

    use super::{Invoke, List, Methods};

    pub trait Sealed {}

    impl Sealed for List {}
    impl Sealed for Methods {}
    impl<R: DeserializeOwned + 'static> Sealed for Invoke<'_, R> {}
}