1use serde::{Deserialize, Serialize};
4
5use super::{enums::object_type::IcingaObjectType, metadata::IcingaMetadata};
6
7#[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
9pub struct ResultsWrapper<T> {
10 pub results: Vec<T>,
12}
13
14#[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
16pub struct QueryResultObjectWithJoins<Obj, ObjJoins> {
17 pub attrs: Obj,
19 pub joins: ObjJoins,
21 pub meta: IcingaMetadata,
23 pub name: String,
25 #[serde(rename = "type")]
27 pub object_type: IcingaObjectType,
28}
29
30#[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
32pub struct QueryResultObject<Obj> {
33 pub attrs: Obj,
35 pub meta: IcingaMetadata,
37 pub name: String,
39 #[serde(rename = "type")]
41 pub object_type: IcingaObjectType,
42}
43
44pub trait QueryableObject {
46 type ListEndpoint;
48
49 fn default_query_endpoint() -> Result<Self::ListEndpoint, crate::error::Error>;
56}
57
58macro_rules! query_with_joins {
60 ($name:ident, $builder_name:ident, $object_category:path, $path_component:path, $return_type:ty, $join_types:ty, $join_return_type:ty, $object_type:expr, $url_fragment:expr) => {
61 use std::collections::BTreeMap;
62
63#[rustfmt::skip]
64 use crate::types::{
65 enums::object_type::IcingaObjectType,
66 filter::IcingaFilter,
67 join_types::{
68 add_joins_to_url,
69 $path_component::{$join_return_type, $join_types},
70 IcingaJoins,
71 },
72 metadata::{add_meta_to_url, IcingaMetadataType},
73 query::{QueryableObject, QueryResultObject, QueryResultObjectWithJoins, ResultsWrapper},
74 rest::{RestApiEndpoint, RestApiResponse},
75 $object_category::{
76 $path_component::{
77 $return_type,
78 }
79 }
80 };
81
82 #[allow(clippy::missing_errors_doc)]
84 #[derive(Debug, Clone, derive_builder::Builder)]
85 #[builder(
86 build_fn(error = "crate::error::Error", validate = "Self::validate"),
87 derive(Debug)
88 )]
89 pub struct $name<'a> {
90 #[builder(default, setter(strip_option, into))]
92 joins: Option<IcingaJoins<'a, $join_types>>,
93 #[builder(default, setter(strip_option, into))]
95 meta: Option<Vec<IcingaMetadataType>>,
96 #[builder(default, setter(strip_option, into))]
98 filter: Option<IcingaFilter>,
99 }
100
101 impl<'a> $name<'a> {
102 #[must_use]
106 pub fn builder() -> $builder_name<'a> {
107 $builder_name::default()
108 }
109 }
110
111 impl QueryableObject for $return_type {
112 type ListEndpoint = $name<'static>;
113
114 fn default_query_endpoint() -> Result<Self::ListEndpoint, crate::error::Error> {
115 $name::builder().build()
116 }
117 }
118
119 impl<'a> $builder_name<'a> {
120 pub fn validate(&self) -> Result<(), crate::error::Error> {
126 let expected = $object_type;
127 if let Some(Some(filter)) = &self.filter {
128 if filter.object_type != expected {
129 Err(crate::error::Error::FilterObjectTypeMismatch(
130 vec![expected],
131 filter.object_type.to_owned(),
132 ))
133 } else {
134 Ok(())
135 }
136 } else {
137 Ok(())
138 }
139 }
140 }
141
142 impl<'a> RestApiEndpoint for $name<'a> {
143 type RequestBody = IcingaFilter;
144
145 fn method(&self) -> Result<reqwest::Method, crate::error::Error> {
146 Ok(reqwest::Method::GET)
147 }
148
149 fn url(&self, base_url: &url::Url) -> Result<url::Url, crate::error::Error> {
150 let mut url = base_url
151 .join($url_fragment)
152 .map_err(crate::error::Error::CouldNotParseUrlFragment)?;
153 if let Some(joins) = &self.joins {
154 add_joins_to_url(&mut url, &joins)?;
155 }
156 if let Some(meta) = &self.meta {
157 add_meta_to_url(&mut url, &meta)?;
158 }
159 Ok(url)
160 }
161
162 fn request_body(
163 &self,
164 ) -> Result<Option<std::borrow::Cow<Self::RequestBody>>, crate::error::Error>
165 where
166 Self::RequestBody: Clone + serde::Serialize + std::fmt::Debug,
167 {
168 Ok(self.filter.as_ref().map(|f| std::borrow::Cow::Borrowed(f)))
169 }
170 }
171
172 impl<'a> RestApiResponse<$name<'a>> for ResultsWrapper<QueryResultObject<$return_type>> {}
173
174 impl<'a> RestApiResponse<$name<'a>>
175 for ResultsWrapper<QueryResultObject<BTreeMap<String, serde_json::Value>>>
176 {
177 }
178
179 impl<'a> RestApiResponse<$name<'a>>
180 for ResultsWrapper<QueryResultObjectWithJoins<$return_type, $join_return_type>>
181 {
182 }
183
184 impl<'a> RestApiResponse<$name<'a>>
185 for ResultsWrapper<
186 QueryResultObjectWithJoins<BTreeMap<String, serde_json::Value>, $join_return_type>,
187 >
188 {
189 }
190
191 impl<'a> RestApiResponse<$name<'a>>
192 for ResultsWrapper<
193 QueryResultObjectWithJoins<$return_type, BTreeMap<String, serde_json::Value>>,
194 >
195 {
196 }
197
198 impl<'a> RestApiResponse<$name<'a>>
199 for ResultsWrapper<
200 QueryResultObjectWithJoins<
201 BTreeMap<String, serde_json::Value>,
202 BTreeMap<String, serde_json::Value>,
203 >,
204 >
205 {
206 }
207 };
208}
209pub(crate) use query_with_joins;
210
211macro_rules! query {
213 ($name:ident, $builder_name:ident, $object_category:path, $path_component:path, $return_type:ty, $object_type:expr, $url_fragment:expr) => {
214 use std::collections::BTreeMap;
215
216#[rustfmt::skip]
217 use crate::types::{
218 enums::object_type::IcingaObjectType,
219 filter::IcingaFilter,
220 metadata::{add_meta_to_url, IcingaMetadataType},
221 query::{QueryableObject, QueryResultObject, ResultsWrapper},
222 rest::{RestApiEndpoint, RestApiResponse},
223 $object_category::{
224 $path_component::{
225 $return_type,
226 },
227 },
228 };
229
230 #[allow(clippy::missing_errors_doc)]
232 #[derive(Debug, Clone, derive_builder::Builder)]
233 #[builder(
234 build_fn(error = "crate::error::Error", validate = "Self::validate"),
235 derive(Debug)
236 )]
237 pub struct $name {
238 #[builder(default, setter(strip_option, into))]
240 meta: Option<Vec<IcingaMetadataType>>,
241 #[builder(default, setter(strip_option, into))]
243 filter: Option<IcingaFilter>,
244 }
245
246 impl $name {
247 #[must_use]
251 pub fn builder() -> $builder_name {
252 $builder_name::default()
253 }
254 }
255
256 impl QueryableObject for $return_type {
257 type ListEndpoint = $name;
258
259 fn default_query_endpoint() -> Result<Self::ListEndpoint, crate::error::Error> {
260 $name::builder().build()
261 }
262 }
263
264 impl $builder_name {
265 pub fn validate(&self) -> Result<(), crate::error::Error> {
271 let expected = $object_type;
272 if let Some(Some(filter)) = &self.filter {
273 if filter.object_type != expected {
274 Err(crate::error::Error::FilterObjectTypeMismatch(
275 vec![expected],
276 filter.object_type.to_owned(),
277 ))
278 } else {
279 Ok(())
280 }
281 } else {
282 Ok(())
283 }
284 }
285 }
286
287 impl RestApiEndpoint for $name {
288 type RequestBody = IcingaFilter;
289
290 fn method(&self) -> Result<reqwest::Method, crate::error::Error> {
291 Ok(reqwest::Method::GET)
292 }
293
294 fn url(&self, base_url: &url::Url) -> Result<url::Url, crate::error::Error> {
295 let mut url = base_url
296 .join($url_fragment)
297 .map_err(crate::error::Error::CouldNotParseUrlFragment)?;
298 if let Some(meta) = &self.meta {
299 add_meta_to_url(&mut url, &meta)?;
300 }
301 Ok(url)
302 }
303
304 fn request_body(
305 &self,
306 ) -> Result<Option<std::borrow::Cow<Self::RequestBody>>, crate::error::Error>
307 where
308 Self::RequestBody: Clone + serde::Serialize + std::fmt::Debug,
309 {
310 Ok(self.filter.as_ref().map(|f| std::borrow::Cow::Borrowed(f)))
311 }
312 }
313
314 impl RestApiResponse<$name> for ResultsWrapper<QueryResultObject<$return_type>> {}
315
316 impl RestApiResponse<$name>
317 for ResultsWrapper<QueryResultObject<BTreeMap<String, serde_json::Value>>>
318 {
319 }
320 };
321}
322pub(crate) use query;