botx_api_framework/integration/actix/
command_handler.rs1use std::collections::HashMap;
2
3use actix_web::{web::{Data, self}, post, Responder, HttpResponse, http::{header::ContentType, self}, error};
4use anthill_di::DependencyContext;
5use botx_api::bot::models::CommandRequest;
6use derive_builder::Builder;
7use derive_more::Display;
8use serde::{Serialize, Deserialize};
9
10use crate::contexts::BotXApiFrameworkContext;
11
12pub type CommandResult = Result<CommandOk, CommandError>;
13
14#[derive(Serialize, Display, Deserialize, Debug)]
15#[display(fmt = "{}", "serde_json::to_string(self).unwrap()")]
16pub struct CommandOk {
17 pub result: String,
18}
19
20impl Default for CommandOk {
21 fn default() -> Self { Self { result: "accepted".to_string() } }
22}
23
24impl Responder for CommandOk {
25 type Body = String;
26
27 fn respond_to(self, _: &actix_web::HttpRequest) -> HttpResponse<Self::Body> {
28 let string = serde_json::to_string(&self).unwrap();
29 let res: actix_http::Response<_> = string.into();
30 res.into()
31 }
32}
33
34#[derive(Serialize, Deserialize, Debug, Display, Clone, Builder)]
35#[display(fmt = "{}", "serde_json::to_string(self).unwrap()")]
36#[builder(setter(into, prefix = "with"))]
37pub struct CommandError {
38 #[builder(default = "\"bot_disabled\".to_string()")]
39 pub reason: String,
40
41 #[builder(default = "HashMap::from_iter( [(\"status_message\".to_string(), serde_json::Value::String(\"please stand by\".to_string()))] )")]
42 pub error_data: HashMap<String, serde_json::Value>,
43
44 #[builder(default)]
45 pub errors: Vec<String>,
46}
47
48
49impl CommandErrorBuilder {
50 pub fn with_status_message(mut self, description: String) -> Self {
51 self.error_data = self.error_data.or(Some(Default::default())).map(|mut x| {
52 x.insert("status_message".to_string(), serde_json::Value::String(description));
53 x
54 });
55 self
56 }
57}
58
59impl Default for CommandError {
60 fn default() -> Self {
61 Self {
62 reason: "bot_disabled".to_string(),
63 error_data: HashMap::from_iter( [("status_message".to_string(), serde_json::Value::String("please stand by".to_string()))] ),
64 errors: Default::default()
65 }
66 }
67}
68
69impl error::ResponseError for CommandError {
70 fn error_response(&self) -> HttpResponse {
71 HttpResponse::build(self.status_code())
72 .insert_header(ContentType::json())
73 .body(serde_json::to_string(self).unwrap())
74 }
75
76 fn status_code(&self) -> http::StatusCode {
77 http::StatusCode::SERVICE_UNAVAILABLE
78 }
79}
80
81#[post("/command")]
82pub async fn command(request: web::Json<CommandRequest<serde_json::Value, serde_json::Value>>, ioc_ctx: Data<DependencyContext>) -> CommandResult {
83 log::debug!("{:#?}", request);
84
85 let context = ioc_ctx.resolve::<BotXApiFrameworkContext>().await.expect("Контекст фреймворка не может быть получен из ioc");
86
87 let res = context.process_command(request.0).await;
88
89 match res {
90 Ok(res) => Ok(CommandOk { result: res.result }),
91 Err(err) => Err(CommandErrorBuilder::default().with_status_message(err.status_message).with_reason(err.reason).build().unwrap()),
92 }
93}