use ::quote::Tokens;
use ::syn::Ident;
use ::yaml_rust::Yaml;
use infer::datatype;
use infer::TokensResult;
const UNSPECIFIED_CODE: u16 = 999;
fn parse_response_key(key: &Yaml) -> (u16, Ident) {
match key.as_i64() {
Some(code) => (code as u16, Ident::new(format!("Status{}", code))),
None => (UNSPECIFIED_CODE, Ident::new("UnspecifiedCode")),
}
}
pub(super) fn infer_v3(schema: &Yaml) -> TokensResult {
let mut err_codes: Vec<u16> = Vec::new();
let mut err_variants: Vec<Ident> = Vec::new();
let mut err_models: Vec<Tokens> = Vec::new();
let mut ok_codes: Vec<u16> = Vec::new();
let mut ok_variants: Vec<Ident> = Vec::new();
let mut ok_models: Vec<Tokens> = Vec::new();
let mut additional_types: Vec<Tokens> = Vec::new();
let mut unspecified_err = quote!(UnspecifiedCode(String),);
let mut unspecified_err_deser = quote! {
let mut buf = String::new();
response.read_to_string(&mut buf).ok();
ErrBody::UnspecifiedCode(buf)
};
for (code, schema) in schema.as_hash().expect("Responses must be a map.") {
let (status_code, variant_ident) = parse_response_key(&code);
let inferred_type: Tokens;
let additional_type: Option<Tokens>;
let schema = &schema["content"]["application/json"]["schema"];
if let None = schema.as_hash() {
inferred_type = quote!{ () };
additional_type = None;
} else {
let (ty, at) = datatype::infer_v3(&schema)?;
inferred_type = ty;
additional_type = at;
}
if let Some(t) = additional_type {
additional_types.push(t);
}
if status_code < 400 {
ok_codes.push(status_code);
ok_variants.push(variant_ident);
ok_models.push(inferred_type.clone());
} else {
err_codes.push(status_code);
err_variants.push(variant_ident);
err_models.push(inferred_type.clone());
}
if code == &Yaml::from_str("default") {
unspecified_err = quote!();
unspecified_err_deser = quote! {
deser_into::<#inferred_type>(response)
.map(ErrBody::UnspecifiedCode)
.unwrap_or_else(ErrBody::MalformedJSON)
};
}
}
let ok_models2 = ok_models.clone();
let err_models2 = err_models.clone();
let ok_variants2 = ok_variants.clone();
let err_variants2 = err_variants.clone();
Ok(quote! {
use std::io::Read;
use ::tapioca::response::ClientResponse;
use ::tapioca::response::Response;
use ::tapioca::response::ResponseBody;
use ::tapioca::response::ResponseResult as _ResponseResult;
use ::tapioca::response::Status;
use ::tapioca::response::StatusCode;
use ::tapioca::serde::de::DeserializeOwned;
use ::tapioca::serde_json;
#(#additional_types)*
pub type ResponseResult = _ResponseResult<OkResult, ErrResult>;
fn deser_into<T: DeserializeOwned>(response: &mut ClientResponse) -> Result<T, String> {
let mut buf = String::new();
match response.json::<T>() {
Ok(body) => Ok(body),
Err(_) => match response.read_to_string(&mut buf) {
Err(_)
| Ok(0) => serde_json::from_str::<T>("null").or_else(|_| Err(buf)),
_ => Err(buf),
},
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub enum OkBody {
#(#ok_variants(#ok_models),)*
MalformedJSON(String),
UnspecifiedCode(String),
}
#[derive(Debug)]
#[allow(dead_code)]
pub enum ErrBody {
#(#err_variants(#err_models),)*
#unspecified_err
MalformedJSON(String),
NetworkFailure(),
}
#[derive(Debug)]
pub struct OkResult {
body: OkBody,
status_code: StatusCode,
}
#[derive(Debug)]
pub struct ErrResult {
body: ErrBody,
status_code: StatusCode,
}
impl Response for OkResult {
type BodyType = OkBody;
fn from(maybe_response: &mut Option<&mut ClientResponse>) -> Self {
let status_code = match *maybe_response {
Some(ref response) => StatusCode::of(&Some(response)),
None => panic!("OkResponse requires Some response."),
};
assert!(status_code.is_ok());
Self {
body: <OkBody as ResponseBody>::from(maybe_response),
status_code,
}
}
fn body(self) -> Self::BodyType {
self.body
}
fn status_code(&self) -> StatusCode {
self.status_code
}
}
impl Response for ErrResult {
type BodyType = ErrBody;
fn from(maybe_response: &mut Option<&mut ClientResponse>) -> Self {
let status_code = match *maybe_response {
Some(ref response) => StatusCode::of(&Some(response)),
None => StatusCode::of(&None),
};
assert!(status_code.is_err());
Self {
body: <ErrBody as ResponseBody>::from(maybe_response),
status_code,
}
}
fn body(self) -> Self::BodyType {
self.body
}
fn status_code(&self) -> StatusCode {
self.status_code
}
}
impl ResponseBody for OkBody {
fn from(maybe_response: &mut Option<&mut ClientResponse>) -> Self {
let status_code = match *maybe_response {
Some(ref response) => StatusCode::of(&Some(response)),
None => panic!("OkResponse requires Some response"),
}.to_u16();
match (maybe_response, status_code) {
#(
(&mut Some(ref mut response), #ok_codes) =>
deser_into::<#ok_models2>(response)
.map(OkBody::#ok_variants2)
.unwrap_or_else(OkBody::MalformedJSON),
)*
(&mut Some(ref mut response), _) => {
let mut buf = String::new();
response.read_to_string(&mut buf).ok();
OkBody::UnspecifiedCode(buf)
},
(&mut None, _) => panic!("OkResponse requires Some response"),
}
}
}
impl ResponseBody for ErrBody {
fn from(maybe_response: &mut Option<&mut ClientResponse>) -> Self {
let status_code = match *maybe_response {
Some(ref response) => StatusCode::of(&Some(response)),
None => StatusCode::of(&None),
};
assert!(status_code.is_err());
match (maybe_response, status_code.to_u16()) {
#(
(&mut Some(ref mut response), #err_codes) =>
deser_into::<#err_models2>(response)
.map(ErrBody::#err_variants2)
.unwrap_or_else(ErrBody::MalformedJSON),
)*
(&mut Some(ref mut response), _) => { #unspecified_err_deser },
(&mut None, _) => ErrBody::NetworkFailure(),
}
}
}
})
}