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
use std::collections::HashMap;

use actix_web::{web::{Data, self}, post, Responder, HttpResponse, http::{header::ContentType, self}, error};
use anthill_di::DependencyContext;
use botx_api::bot::models::CommandRequest;
use derive_builder::Builder;
use derive_more::Display;
use serde::{Serialize, Deserialize};

use crate::contexts::BotXApiFrameworkContext;

pub type CommandResult = Result<CommandOk, CommandError>;

#[derive(Serialize, Display, Deserialize, Debug)]
#[display(fmt = "{}", "serde_json::to_string(self).unwrap()")]
pub struct CommandOk {
    pub result: String,
}

impl Default for CommandOk {
    fn default() -> Self { Self { result: "accepted".to_string() } }
}

impl Responder for CommandOk {
    type Body = String;

    fn respond_to(self, _: &actix_web::HttpRequest) -> HttpResponse<Self::Body> {
        let string = serde_json::to_string(&self).unwrap();
        let res: actix_http::Response<_> = string.into();
        res.into()
    }
}

#[derive(Serialize, Deserialize, Debug, Display, Clone, Builder)]
#[display(fmt = "{}", "serde_json::to_string(self).unwrap()")]
#[builder(setter(into, prefix = "with"))]
pub struct CommandError {
    #[builder(default = "\"bot_disabled\".to_string()")]
    pub reason: String,

    #[builder(default = "HashMap::from_iter( [(\"status_message\".to_string(), serde_json::Value::String(\"please stand by\".to_string()))] )")]
    pub error_data: HashMap<String, serde_json::Value>,

    #[builder(default)]
    pub errors: Vec<String>,
}


impl CommandErrorBuilder {
    pub fn with_status_message(mut self, description: String) -> Self {
        self.error_data = self.error_data.or(Some(Default::default())).map(|mut x| {
            x.insert("status_message".to_string(), serde_json::Value::String(description));
            x
        });
        self
    }
}

impl Default for CommandError {
    fn default() -> Self {
        Self {
            reason: "bot_disabled".to_string(),
            error_data: HashMap::from_iter( [("status_message".to_string(), serde_json::Value::String("please stand by".to_string()))] ),
            errors: Default::default()
        }
    }
}

impl error::ResponseError for CommandError {
    fn error_response(&self) -> HttpResponse {
        HttpResponse::build(self.status_code())
            .insert_header(ContentType::json())
            .body(serde_json::to_string(self).unwrap())
    }

    fn status_code(&self) -> http::StatusCode {
        http::StatusCode::SERVICE_UNAVAILABLE
    }
}

#[post("/command")]
pub async fn command(request: web::Json<CommandRequest<serde_json::Value, serde_json::Value>>, ioc_ctx: Data<DependencyContext>) -> CommandResult {
    log::debug!("{:#?}", request);

    let context = ioc_ctx.resolve::<BotXApiFrameworkContext>().await.expect("Контекст фреймворка не может быть получен из ioc");

    let res = context.process_command(request.0).await;

    match res {
        Ok(res) => Ok(CommandOk { result: res.result }),
        Err(err) => Err(CommandErrorBuilder::default().with_status_message(err.status_message).with_reason(err.reason).build().unwrap()),
    }
}