use std::collections::BTreeMap;
use anyhow::Result;
use http::{HeaderName, HeaderValue, StatusCode};
use reblessive::tree::Stk;
use crate::api::err::ApiError;
use crate::api::middleware::common::BodyStrategy;
use crate::api::middleware::res::{convert_response_value, output_body_strategy};
use crate::api::request::ApiRequest;
use crate::api::response::ApiResponse;
use crate::ctx::FrozenContext;
use crate::dbs::Options;
use crate::doc::CursorDoc;
use crate::fnc::args::{FromPublic, Optional};
use crate::val::{Closure, Value};
pub async fn body(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(FromPublic(req), next, Optional(strategy)): (
FromPublic<ApiRequest>,
Box<Closure>,
Optional<FromPublic<BodyStrategy>>,
),
) -> Result<Value> {
let res = next.invoke(stk, ctx, opt, doc, vec![req.clone().into()]).await?;
let mut res: ApiResponse = res.try_into()?;
let strategy = strategy.map(|x| x.0).unwrap_or_default();
let Some(strategy) = output_body_strategy(&req.headers, strategy) else {
return Err(ApiError::NoOutputStrategy.into());
};
convert_response_value(&mut res, strategy)?;
Ok(res.into())
}
pub async fn status(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(req, next, status): (Value, Box<Closure>, i64),
) -> Result<Value> {
let res = next.invoke(stk, ctx, opt, doc, vec![req]).await?;
let mut res: ApiResponse = res.try_into()?;
let status = u16::try_from(status)
.ok()
.and_then(|s| StatusCode::from_u16(s).ok())
.ok_or(ApiError::InvalidStatusCode(status))?;
res.status = status;
Ok(res.into())
}
pub async fn header(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(req, next, name, Optional(value)): (Value, Box<Closure>, String, Optional<String>),
) -> Result<Value> {
let res = next.invoke(stk, ctx, opt, doc, vec![req]).await?;
let mut res: ApiResponse = res.try_into()?;
let name: HeaderName =
name.parse().map_err(|e| ApiError::InvalidHeaderName(format!("{}: {}", name, e)))?;
if let Some(value) = value {
let value: HeaderValue = value.parse().map_err(|e| ApiError::InvalidHeaderValue {
name: name.to_string(),
value: format!("{}: {}", value, e),
})?;
res.headers.insert(name, value)
} else {
res.headers.remove(name)
};
Ok(res.into())
}
pub async fn headers(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(req, next, headers): (Value, Box<Closure>, BTreeMap<String, Option<String>>),
) -> Result<Value> {
let res = next.invoke(stk, ctx, opt, doc, vec![req]).await?;
let mut res: ApiResponse = res.try_into()?;
for (k, value) in headers {
let name: HeaderName =
k.parse().map_err(|e| ApiError::InvalidHeaderName(format!("{}: {}", k, e)))?;
if let Some(value) = value {
let value: HeaderValue = value.parse().map_err(|e| ApiError::InvalidHeaderValue {
name: k.clone(),
value: format!("{}: {}", value, e),
})?;
res.headers.insert(name, value);
} else {
res.headers.remove(name);
}
}
Ok(res.into())
}