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
use serde::{de::DeserializeOwned, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Method {
    Post,
    Get,
    Put,
    Delete,
}

impl std::fmt::Display for Method {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Method::Post => write!(f, "POST"),
            Method::Get => write!(f, "GET"),
            Method::Put => write!(f, "PUT"),
            Method::Delete => write!(f, "DELETE"),
        }
    }
}

pub trait Client: Clone {
    type Error: core::fmt::Debug;

    /// Transmit an authenticated request to a Proxmox VE API endpoint
    /// using the provided method, path, body, and query.
    fn request_with_body_and_query<B, Q, R>(
        &self,
        method: Method,
        path: &str,
        body: Option<&B>,
        query: Option<&Q>,
    ) -> Result<R, Self::Error>
    where
        B: Serialize,
        Q: Serialize,
        R: DeserializeOwned;

    fn request_with_body<B, R>(
        &self,
        method: Method,
        path: &str,
        body: &B,
    ) -> Result<R, Self::Error>
    where
        B: Serialize,
        R: DeserializeOwned,
    {
        self.request_with_body_and_query::<_, (), _>(method, path, Some(body), None)
    }

    fn request_with_query<Q, R>(
        &self,
        method: Method,
        path: &str,
        query: &Q,
    ) -> Result<R, Self::Error>
    where
        Q: Serialize,
        R: DeserializeOwned,
    {
        self.request_with_body_and_query::<(), _, _>(method, path, None, Some(query))
    }

    fn put<B, R>(&self, path: &str, body: &B) -> Result<R, Self::Error>
    where
        B: Serialize,
        R: DeserializeOwned,
    {
        self.request_with_body(Method::Put, path, body)
    }

    fn post<B, R>(&self, path: &str, body: &B) -> Result<R, Self::Error>
    where
        B: Serialize,
        R: DeserializeOwned,
    {
        self.request_with_body(Method::Post, path, body)
    }

    fn delete<B, R>(&self, path: &str, body: &B) -> Result<R, Self::Error>
    where
        B: Serialize,
        R: DeserializeOwned,
    {
        self.request_with_body(Method::Delete, path, body)
    }

    fn get<Q, R>(&self, path: &str, query: &Q) -> Result<R, Self::Error>
    where
        Q: Serialize,
        R: DeserializeOwned,
    {
        self.request_with_query(Method::Get, path, query)
    }
}

impl<T> Client for &T
where
    T: Client,
{
    type Error = <T as Client>::Error;

    fn request_with_body_and_query<B, Q, R>(
        &self,
        method: Method,
        path: &str,
        body: Option<&B>,
        query: Option<&Q>,
    ) -> Result<R, Self::Error>
    where
        B: Serialize,
        Q: Serialize,
        R: DeserializeOwned,
    {
        T::request_with_body_and_query(self, method, path, body, query)
    }
}