1#![allow(clippy::pedantic, clippy::nursery, clippy::all)]
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub struct Params {
12 #[serde(skip_serializing_if = "Option::is_none")]
13 pub age_assurance_state: Option<String>,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub appealed: Option<bool>,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub collections: Option<Vec<proto_blue_syntax::Nsid>>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub comment: Option<String>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub cursor: Option<String>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub exclude_tags: Option<Vec<String>>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub hosting_deleted_after: Option<proto_blue_syntax::Datetime>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub hosting_deleted_before: Option<proto_blue_syntax::Datetime>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub hosting_statuses: Option<Vec<String>>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub hosting_updated_after: Option<proto_blue_syntax::Datetime>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub hosting_updated_before: Option<proto_blue_syntax::Datetime>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub ignore_subjects: Option<Vec<String>>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub include_all_user_records: Option<bool>,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub include_muted: Option<bool>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub last_reviewed_by: Option<proto_blue_syntax::Did>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub limit: Option<i64>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub min_account_suspend_count: Option<i64>,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub min_priority_score: Option<i64>,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub min_reported_records_count: Option<i64>,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub min_strike_count: Option<i64>,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub min_takendown_records_count: Option<i64>,
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub only_muted: Option<bool>,
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub queue_count: Option<i64>,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub queue_index: Option<i64>,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub queue_seed: Option<String>,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub reported_after: Option<proto_blue_syntax::Datetime>,
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub reported_before: Option<proto_blue_syntax::Datetime>,
66 #[serde(skip_serializing_if = "Option::is_none")]
67 pub review_state: Option<String>,
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub reviewed_after: Option<proto_blue_syntax::Datetime>,
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub reviewed_before: Option<proto_blue_syntax::Datetime>,
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub sort_direction: Option<String>,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub sort_field: Option<String>,
76 #[serde(skip_serializing_if = "Option::is_none")]
77 pub subject: Option<String>,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 pub subject_type: Option<String>,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub tags: Option<Vec<String>>,
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub takendown: Option<bool>,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
87#[serde(rename_all = "camelCase")]
88pub struct Output {
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub cursor: Option<String>,
91 pub subject_statuses: Vec<crate::tools::ozone::moderation::defs::SubjectStatusView>,
92}
93
94#[derive(Debug, thiserror::Error)]
96pub enum CallError {
97 #[error("{0}")]
98 Xrpc(proto_blue_xrpc::XrpcError),
99 #[error(transparent)]
100 Transport(#[from] proto_blue_xrpc::Error),
101 #[error(transparent)]
102 Json(#[from] serde_json::Error),
103}
104
105fn map_xrpc_error(err: proto_blue_xrpc::XrpcError) -> CallError {
106 CallError::Xrpc(err)
107}
108
109fn to_query_params(p: &Params) -> proto_blue_xrpc::QueryParams {
110 let mut qp = proto_blue_xrpc::QueryParams::new();
111 if let Some(v) = &p.age_assurance_state {
112 qp.insert(
113 "ageAssuranceState".to_string(),
114 proto_blue_xrpc::QueryValue::String(v.to_string()),
115 );
116 }
117 if let Some(v) = &p.appealed {
118 qp.insert(
119 "appealed".to_string(),
120 proto_blue_xrpc::QueryValue::Boolean(*v),
121 );
122 }
123 if let Some(v) = &p.collections {
124 qp.insert(
125 "collections".to_string(),
126 proto_blue_xrpc::QueryValue::Array(
127 v.iter()
128 .map(|x| proto_blue_xrpc::QueryValue::String(x.to_string()))
129 .collect(),
130 ),
131 );
132 }
133 if let Some(v) = &p.comment {
134 qp.insert(
135 "comment".to_string(),
136 proto_blue_xrpc::QueryValue::String(v.to_string()),
137 );
138 }
139 if let Some(v) = &p.cursor {
140 qp.insert(
141 "cursor".to_string(),
142 proto_blue_xrpc::QueryValue::String(v.to_string()),
143 );
144 }
145 if let Some(v) = &p.exclude_tags {
146 qp.insert(
147 "excludeTags".to_string(),
148 proto_blue_xrpc::QueryValue::Array(
149 v.iter()
150 .map(|x| proto_blue_xrpc::QueryValue::String(x.to_string()))
151 .collect(),
152 ),
153 );
154 }
155 if let Some(v) = &p.hosting_deleted_after {
156 qp.insert(
157 "hostingDeletedAfter".to_string(),
158 proto_blue_xrpc::QueryValue::String(v.to_string()),
159 );
160 }
161 if let Some(v) = &p.hosting_deleted_before {
162 qp.insert(
163 "hostingDeletedBefore".to_string(),
164 proto_blue_xrpc::QueryValue::String(v.to_string()),
165 );
166 }
167 if let Some(v) = &p.hosting_statuses {
168 qp.insert(
169 "hostingStatuses".to_string(),
170 proto_blue_xrpc::QueryValue::Array(
171 v.iter()
172 .map(|x| proto_blue_xrpc::QueryValue::String(x.to_string()))
173 .collect(),
174 ),
175 );
176 }
177 if let Some(v) = &p.hosting_updated_after {
178 qp.insert(
179 "hostingUpdatedAfter".to_string(),
180 proto_blue_xrpc::QueryValue::String(v.to_string()),
181 );
182 }
183 if let Some(v) = &p.hosting_updated_before {
184 qp.insert(
185 "hostingUpdatedBefore".to_string(),
186 proto_blue_xrpc::QueryValue::String(v.to_string()),
187 );
188 }
189 if let Some(v) = &p.ignore_subjects {
190 qp.insert(
191 "ignoreSubjects".to_string(),
192 proto_blue_xrpc::QueryValue::Array(
193 v.iter()
194 .map(|x| proto_blue_xrpc::QueryValue::String(x.to_string()))
195 .collect(),
196 ),
197 );
198 }
199 if let Some(v) = &p.include_all_user_records {
200 qp.insert(
201 "includeAllUserRecords".to_string(),
202 proto_blue_xrpc::QueryValue::Boolean(*v),
203 );
204 }
205 if let Some(v) = &p.include_muted {
206 qp.insert(
207 "includeMuted".to_string(),
208 proto_blue_xrpc::QueryValue::Boolean(*v),
209 );
210 }
211 if let Some(v) = &p.last_reviewed_by {
212 qp.insert(
213 "lastReviewedBy".to_string(),
214 proto_blue_xrpc::QueryValue::String(v.to_string()),
215 );
216 }
217 if let Some(v) = &p.limit {
218 qp.insert(
219 "limit".to_string(),
220 proto_blue_xrpc::QueryValue::Integer(*v),
221 );
222 }
223 if let Some(v) = &p.min_account_suspend_count {
224 qp.insert(
225 "minAccountSuspendCount".to_string(),
226 proto_blue_xrpc::QueryValue::Integer(*v),
227 );
228 }
229 if let Some(v) = &p.min_priority_score {
230 qp.insert(
231 "minPriorityScore".to_string(),
232 proto_blue_xrpc::QueryValue::Integer(*v),
233 );
234 }
235 if let Some(v) = &p.min_reported_records_count {
236 qp.insert(
237 "minReportedRecordsCount".to_string(),
238 proto_blue_xrpc::QueryValue::Integer(*v),
239 );
240 }
241 if let Some(v) = &p.min_strike_count {
242 qp.insert(
243 "minStrikeCount".to_string(),
244 proto_blue_xrpc::QueryValue::Integer(*v),
245 );
246 }
247 if let Some(v) = &p.min_takendown_records_count {
248 qp.insert(
249 "minTakendownRecordsCount".to_string(),
250 proto_blue_xrpc::QueryValue::Integer(*v),
251 );
252 }
253 if let Some(v) = &p.only_muted {
254 qp.insert(
255 "onlyMuted".to_string(),
256 proto_blue_xrpc::QueryValue::Boolean(*v),
257 );
258 }
259 if let Some(v) = &p.queue_count {
260 qp.insert(
261 "queueCount".to_string(),
262 proto_blue_xrpc::QueryValue::Integer(*v),
263 );
264 }
265 if let Some(v) = &p.queue_index {
266 qp.insert(
267 "queueIndex".to_string(),
268 proto_blue_xrpc::QueryValue::Integer(*v),
269 );
270 }
271 if let Some(v) = &p.queue_seed {
272 qp.insert(
273 "queueSeed".to_string(),
274 proto_blue_xrpc::QueryValue::String(v.to_string()),
275 );
276 }
277 if let Some(v) = &p.reported_after {
278 qp.insert(
279 "reportedAfter".to_string(),
280 proto_blue_xrpc::QueryValue::String(v.to_string()),
281 );
282 }
283 if let Some(v) = &p.reported_before {
284 qp.insert(
285 "reportedBefore".to_string(),
286 proto_blue_xrpc::QueryValue::String(v.to_string()),
287 );
288 }
289 if let Some(v) = &p.review_state {
290 qp.insert(
291 "reviewState".to_string(),
292 proto_blue_xrpc::QueryValue::String(v.to_string()),
293 );
294 }
295 if let Some(v) = &p.reviewed_after {
296 qp.insert(
297 "reviewedAfter".to_string(),
298 proto_blue_xrpc::QueryValue::String(v.to_string()),
299 );
300 }
301 if let Some(v) = &p.reviewed_before {
302 qp.insert(
303 "reviewedBefore".to_string(),
304 proto_blue_xrpc::QueryValue::String(v.to_string()),
305 );
306 }
307 if let Some(v) = &p.sort_direction {
308 qp.insert(
309 "sortDirection".to_string(),
310 proto_blue_xrpc::QueryValue::String(v.to_string()),
311 );
312 }
313 if let Some(v) = &p.sort_field {
314 qp.insert(
315 "sortField".to_string(),
316 proto_blue_xrpc::QueryValue::String(v.to_string()),
317 );
318 }
319 if let Some(v) = &p.subject {
320 qp.insert(
321 "subject".to_string(),
322 proto_blue_xrpc::QueryValue::String(v.to_string()),
323 );
324 }
325 if let Some(v) = &p.subject_type {
326 qp.insert(
327 "subjectType".to_string(),
328 proto_blue_xrpc::QueryValue::String(v.to_string()),
329 );
330 }
331 if let Some(v) = &p.tags {
332 qp.insert(
333 "tags".to_string(),
334 proto_blue_xrpc::QueryValue::Array(
335 v.iter()
336 .map(|x| proto_blue_xrpc::QueryValue::String(x.to_string()))
337 .collect(),
338 ),
339 );
340 }
341 if let Some(v) = &p.takendown {
342 qp.insert(
343 "takendown".to_string(),
344 proto_blue_xrpc::QueryValue::Boolean(*v),
345 );
346 }
347 qp
348}
349
350pub async fn call(
352 client: &proto_blue_xrpc::XrpcClient,
353 params: Option<&Params>,
354 opts: Option<&proto_blue_xrpc::CallOptions>,
355) -> Result<Output, CallError> {
356 let qp = params.map(to_query_params);
357 let response = match client
358 .query("tools.ozone.moderation.queryStatuses", qp.as_ref(), opts)
359 .await
360 {
361 Ok(r) => r,
362 Err(proto_blue_xrpc::Error::Xrpc(x)) => return Err(map_xrpc_error(x)),
363 Err(e) => return Err(CallError::Transport(e)),
364 };
365 Ok(serde_json::from_value(response.data)?)
366}
367
368#[cfg(feature = "server")]
370pub fn register<F, Fut>(
371 server: proto_blue_xrpc::XrpcServer,
372 handler: F,
373) -> proto_blue_xrpc::XrpcServer
374where
375 F: Fn(proto_blue_xrpc::HandlerContext, Option<Params>) -> Fut + Send + Sync + 'static,
376 Fut: std::future::Future<Output = Result<Output, proto_blue_xrpc::XrpcServerError>>
377 + Send
378 + 'static,
379{
380 let handler = std::sync::Arc::new(handler);
381 server.query("tools.ozone.moderation.queryStatuses", move |ctx| {
382 let handler = handler.clone();
383 async move {
384 let params = params_from_ctx(&ctx);
385 let out = handler(ctx, params).await?;
386 let value = serde_json::to_value(&out).map_err(|e| {
387 proto_blue_xrpc::XrpcServerError::new(
388 proto_blue_xrpc::ResponseType::InternalServerError,
389 format!("output serialize: {e}"),
390 )
391 })?;
392 Ok::<_, proto_blue_xrpc::XrpcServerError>(value)
393 }
394 })
395}
396
397#[cfg(feature = "server")]
398fn params_from_ctx(ctx: &proto_blue_xrpc::HandlerContext) -> Option<Params> {
399 Some(Params {
403 age_assurance_state: ctx.params.get("ageAssuranceState").cloned(),
404 appealed: ctx
405 .params
406 .get("appealed")
407 .and_then(|v| v.parse::<bool>().ok()),
408 collections: ctx.params.get("collections").and_then(|v| {
409 v.split(',')
410 .map(proto_blue_syntax::Nsid::new)
411 .collect::<Result<Vec<_>, _>>()
412 .ok()
413 }),
414 comment: ctx.params.get("comment").cloned(),
415 cursor: ctx.params.get("cursor").cloned(),
416 exclude_tags: Some(
417 ctx.params
418 .get("excludeTags")
419 .map(|v| v.split(',').map(String::from).collect::<Vec<_>>())
420 .unwrap_or_default(),
421 ),
422 hosting_deleted_after: ctx
423 .params
424 .get("hostingDeletedAfter")
425 .and_then(|v| proto_blue_syntax::Datetime::new(v).ok()),
426 hosting_deleted_before: ctx
427 .params
428 .get("hostingDeletedBefore")
429 .and_then(|v| proto_blue_syntax::Datetime::new(v).ok()),
430 hosting_statuses: Some(
431 ctx.params
432 .get("hostingStatuses")
433 .map(|v| v.split(',').map(String::from).collect::<Vec<_>>())
434 .unwrap_or_default(),
435 ),
436 hosting_updated_after: ctx
437 .params
438 .get("hostingUpdatedAfter")
439 .and_then(|v| proto_blue_syntax::Datetime::new(v).ok()),
440 hosting_updated_before: ctx
441 .params
442 .get("hostingUpdatedBefore")
443 .and_then(|v| proto_blue_syntax::Datetime::new(v).ok()),
444 ignore_subjects: Some(
445 ctx.params
446 .get("ignoreSubjects")
447 .map(|v| v.split(',').map(String::from).collect::<Vec<_>>())
448 .unwrap_or_default(),
449 ),
450 include_all_user_records: ctx
451 .params
452 .get("includeAllUserRecords")
453 .and_then(|v| v.parse::<bool>().ok()),
454 include_muted: ctx
455 .params
456 .get("includeMuted")
457 .and_then(|v| v.parse::<bool>().ok()),
458 last_reviewed_by: ctx
459 .params
460 .get("lastReviewedBy")
461 .and_then(|v| proto_blue_syntax::Did::new(v).ok()),
462 limit: ctx.params.get("limit").and_then(|v| v.parse::<i64>().ok()),
463 min_account_suspend_count: ctx
464 .params
465 .get("minAccountSuspendCount")
466 .and_then(|v| v.parse::<i64>().ok()),
467 min_priority_score: ctx
468 .params
469 .get("minPriorityScore")
470 .and_then(|v| v.parse::<i64>().ok()),
471 min_reported_records_count: ctx
472 .params
473 .get("minReportedRecordsCount")
474 .and_then(|v| v.parse::<i64>().ok()),
475 min_strike_count: ctx
476 .params
477 .get("minStrikeCount")
478 .and_then(|v| v.parse::<i64>().ok()),
479 min_takendown_records_count: ctx
480 .params
481 .get("minTakendownRecordsCount")
482 .and_then(|v| v.parse::<i64>().ok()),
483 only_muted: ctx
484 .params
485 .get("onlyMuted")
486 .and_then(|v| v.parse::<bool>().ok()),
487 queue_count: ctx
488 .params
489 .get("queueCount")
490 .and_then(|v| v.parse::<i64>().ok()),
491 queue_index: ctx
492 .params
493 .get("queueIndex")
494 .and_then(|v| v.parse::<i64>().ok()),
495 queue_seed: ctx.params.get("queueSeed").cloned(),
496 reported_after: ctx
497 .params
498 .get("reportedAfter")
499 .and_then(|v| proto_blue_syntax::Datetime::new(v).ok()),
500 reported_before: ctx
501 .params
502 .get("reportedBefore")
503 .and_then(|v| proto_blue_syntax::Datetime::new(v).ok()),
504 review_state: ctx.params.get("reviewState").cloned(),
505 reviewed_after: ctx
506 .params
507 .get("reviewedAfter")
508 .and_then(|v| proto_blue_syntax::Datetime::new(v).ok()),
509 reviewed_before: ctx
510 .params
511 .get("reviewedBefore")
512 .and_then(|v| proto_blue_syntax::Datetime::new(v).ok()),
513 sort_direction: ctx.params.get("sortDirection").cloned(),
514 sort_field: ctx.params.get("sortField").cloned(),
515 subject: ctx.params.get("subject").cloned(),
516 subject_type: ctx.params.get("subjectType").cloned(),
517 tags: Some(
518 ctx.params
519 .get("tags")
520 .map(|v| v.split(',').map(String::from).collect::<Vec<_>>())
521 .unwrap_or_default(),
522 ),
523 takendown: ctx
524 .params
525 .get("takendown")
526 .and_then(|v| v.parse::<bool>().ok()),
527 })
528}