botx_api_framework/integration/actix/
command_handler.rs

1use 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}