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()),
}
}