use std::collections::BTreeMap;
use axum::extract::{Extension, Json, Path, Query, Request, State};
use either::Either;
use oas::{MediaType, Parameter, ParameterIn, Referenceable, RequestBody, Schema};
use crate::Schematic;
pub trait ParameterProvider {
fn generate(_url: String) -> Either<Vec<Parameter>, RequestBody> {
Either::Left(vec![])
}
}
fn build_param(name: String, _in: ParameterIn, required: bool, schema: Schema, description: Option<String>) -> Parameter {
Parameter {
name,
_in,
description,
required: Some(required),
deprecated: None,
allow_empty_value: None,
style: None,
explode: None,
allow_reserved: None,
schema: Some(Referenceable::Data(schema)),
example: None,
examples: None,
content: None,
}
}
impl<T1: Schematic> ParameterProvider for Path<(T1,)> {
fn generate(url: String) -> Either<Vec<Parameter>, RequestBody> {
let pattern = regex::Regex::new(r":([^/]+)").unwrap();
let param_names_in_path: Vec<String> = pattern.captures_iter(&url).map(|digits| digits.get(1).unwrap().as_str().to_string()).collect();
let t1_param = build_param(
param_names_in_path.first().cloned().expect("cannot get param in path"),
ParameterIn::Path,
T1::required(),
T1::generate_schema().schema,
T1::doc(),
);
Either::Left(vec![t1_param])
}
}
impl<T1: Schematic, T2: Schematic> ParameterProvider for Path<(T1, T2)> {
fn generate(url: String) -> Either<Vec<Parameter>, RequestBody> {
let pattern = regex::Regex::new(r":([^/]+)").unwrap();
let param_names_in_path: Vec<String> = pattern.captures_iter(&url).map(|digits| digits.get(1).unwrap().as_str().to_string()).collect();
let t1_param = build_param(
param_names_in_path.first().cloned().expect("cannot get param in path"),
ParameterIn::Path,
T1::required(),
T1::generate_schema().schema,
T1::doc(),
);
let t2_param = build_param(
param_names_in_path.get(1).cloned().expect("cannot get param in path"),
ParameterIn::Path,
T2::required(),
T2::generate_schema().schema,
T2::doc(),
);
Either::Left(vec![t1_param, t2_param])
}
}
impl<T: Schematic> ParameterProvider for Path<T> {
fn generate(url: String) -> Either<Vec<Parameter>, RequestBody> {
let mut ret = vec![];
let mut schema = T::generate_schema();
if let Some(mut properties) = schema.schema.extras.remove("properties") {
if let Some(properties) = properties.as_object_mut() {
properties.iter_mut().for_each(|(key, value)| {
let schema = serde_json::from_value(value.clone()).unwrap();
let param = build_param(key.to_string(), ParameterIn::Path, T::required(), schema, T::doc());
ret.push(param);
})
}
} else {
let pattern = regex::Regex::new(r":([^/]+)").unwrap();
let param_names_in_path: Vec<String> = pattern.captures_iter(&url).map(|digits| digits.get(1).unwrap().as_str().to_string()).collect();
if let Some(param_name) = param_names_in_path.first() {
let param = build_param(param_name.clone(), ParameterIn::Path, T::required(), T::generate_schema().schema, T::doc());
ret.push(param);
}
}
Either::Left(ret)
}
}
impl<T: Schematic> ParameterProvider for Json<T> {
fn generate(_url: String) -> Either<Vec<Parameter>, RequestBody> {
let mut contents = BTreeMap::new();
let schema = T::generate_schema();
contents.insert(
"application/json".to_owned(),
MediaType {
schema: Some(Referenceable::Data(schema.schema)),
example: None,
examples: None,
encoding: None,
},
);
let req_body = RequestBody {
description: T::doc(),
required: Some(T::required()),
content: contents,
};
Either::Right(req_body)
}
}
impl<T: Schematic> ParameterProvider for Query<T> {
fn generate(_url: String) -> Either<Vec<Parameter>, RequestBody> {
let fields = T::fields();
if fields.is_empty() {
let mut ret = vec![];
let mut schema = T::generate_schema();
if let Some(mut properties) = schema.schema.extras.remove("properties") {
if let Some(properties) = properties.as_object_mut() {
properties.iter_mut().for_each(|(key, value)| {
let schema = serde_json::from_value(value.clone()).unwrap();
let param = build_param(key.to_string(), ParameterIn::Query, T::required(), schema, T::doc());
ret.push(param);
})
}
}
Either::Left(ret)
} else {
Either::Left(
fields
.into_iter()
.map(|(name, schema)| {
let desc = schema.schema.description.clone();
build_param(name.to_string(), ParameterIn::Query, schema.required, schema.schema, desc)
})
.collect(),
)
}
}
}
impl<T> ParameterProvider for State<T> {}
impl<T> ParameterProvider for Extension<T> {}
impl ParameterProvider for Request {}
impl ParameterProvider for axum::extract::multipart::Multipart {
fn generate(_url: String) -> Either<Vec<Parameter>, RequestBody> {
let mut contents = BTreeMap::new();
contents.insert(
"multipart/form-data".to_owned(),
MediaType {
schema: None,
example: None,
examples: None,
encoding: None,
},
);
let req_body = RequestBody {
description: Some("Multipart form data".to_owned()),
required: Some(true),
content: contents,
};
Either::Right(req_body)
}
}