[Rust] Implement 'object' type conversion (#6846)

* [Rust] Use serde Value for objects

This hopefully fixes the previous TODO; it at least fixes compilation
errors for the specific swagger model I'm working with.

* [Rust] Update Cargo.toml to specify versions

Letting the version float freely is scary, to say the least.

This gives it a better chance at being future-proof.

When the crate author had a recommended selector I picked that,
otherwise I went semver compatible.

* [Rust] Regenerate the example
This commit is contained in:
Euan Kemp 2017-11-01 07:00:56 -07:00 committed by wing328
parent 7755c7de38
commit c97b63d2bb
18 changed files with 45 additions and 368 deletions

View File

@ -99,8 +99,7 @@ public class RustClientCodegen extends DefaultCodegen implements CodegenConfig {
typeMapping.put("file", "File");
typeMapping.put("binary", "Vec<u8>");
typeMapping.put("ByteArray", "String");
// TODO what should 'object' mapped to
typeMapping.put("object", "Object");
typeMapping.put("object", "Value");
// no need for rust
//importMapping = new HashMap<String, String>();

View File

@ -4,14 +4,14 @@ version = "{{{packageVersion}}}"
authors = ["Swagger Codegen team and contributors"]
[dependencies]
serde = "*"
serde_derive = "*"
serde_yaml = "*"
serde_json = "*"
base64 = "*"
futures = "*"
hyper = "*"
url = "*"
serde = "1.0"
serde_derive = "1.0"
serde_yaml = "0.7"
serde_json = "1.0"
base64 = "~0.7.0"
futures = "0.1.16"
hyper = "0.11.6"
url = "1.5"
[dev-dependencies]
tokio-core = "*"

View File

@ -5,6 +5,9 @@
/// {{{classname}}} : {{{description}}}
{{/description}}
#[allow(unused_imports)]
use serde_json::Value;
#[derive(Debug, Serialize, Deserialize)]
pub struct {{classname}} {
{{#vars}}

View File

@ -4,14 +4,14 @@ version = "1.0.0"
authors = ["Swagger Codegen team and contributors"]
[dependencies]
serde = "*"
serde_derive = "*"
serde_yaml = "*"
serde_json = "*"
base64 = "*"
futures = "*"
hyper = "*"
url = "*"
serde = "1.0"
serde_derive = "1.0"
serde_yaml = "0.7"
serde_json = "1.0"
base64 = "~0.7.0"
futures = "0.1.16"
hyper = "0.11.6"
url = "1.5"
[dev-dependencies]
tokio-core = "*"

View File

@ -10,6 +10,9 @@
/// ApiResponse : Describes the result of uploading an image resource
#[allow(unused_imports)]
use serde_json::Value;
#[derive(Debug, Serialize, Deserialize)]
pub struct ApiResponse {
#[serde(rename = "code")]

View File

@ -10,6 +10,9 @@
/// Category : A category for a pet
#[allow(unused_imports)]
use serde_json::Value;
#[derive(Debug, Serialize, Deserialize)]
pub struct Category {
#[serde(rename = "id")]

View File

@ -10,6 +10,9 @@
/// Order : An order for a pets from the pet store
#[allow(unused_imports)]
use serde_json::Value;
#[derive(Debug, Serialize, Deserialize)]
pub struct Order {
#[serde(rename = "id")]

View File

@ -10,6 +10,9 @@
/// Pet : A pet for sale in the pet store
#[allow(unused_imports)]
use serde_json::Value;
#[derive(Debug, Serialize, Deserialize)]
pub struct Pet {
#[serde(rename = "id")]

View File

@ -10,6 +10,9 @@
/// Tag : A tag for a pet
#[allow(unused_imports)]
use serde_json::Value;
#[derive(Debug, Serialize, Deserialize)]
pub struct Tag {
#[serde(rename = "id")]

View File

@ -10,6 +10,9 @@
/// User : A User who is purchasing from the pet store
#[allow(unused_imports)]
use serde_json::Value;
#[derive(Debug, Serialize, Deserialize)]
pub struct User {
#[serde(rename = "id")]

View File

@ -13,7 +13,7 @@ To see how to make this your own, look here:
[README](https://github.com/swagger-api/swagger-codegen/blob/master/README.md)
- API version: 1.0.0
- Build date: 2017-10-19T17:45:37.995+08:00
- Build date: 2017-10-30T02:13:46.477Z
## Examples
@ -41,8 +41,6 @@ To run a client, follow one of the following simple steps:
```
cargo run --example client TestSpecialTags
cargo run --example client GetXmlFeatures
cargo run --example client PostXmlFeatures
cargo run --example client FakeOuterBooleanSerialize
cargo run --example client FakeOuterCompositeSerialize
cargo run --example client FakeOuterNumberSerialize

View File

@ -139,11 +139,11 @@ paths:
type: "array"
items:
type: "string"
default: "available"
enum:
- "available"
- "pending"
- "sold"
default: "available"
collectionFormat: "csv"
formatString: "{:?}"
example: "&Vec::new()"
@ -893,10 +893,10 @@ paths:
type: "array"
items:
type: "string"
default: "$"
enum:
- ">"
- "$"
default: "$"
formatString: "{:?}"
example: "Some(&Vec::new())"
- name: "enum_form_string"
@ -918,10 +918,10 @@ paths:
type: "array"
items:
type: "string"
default: "$"
enum:
- ">"
- "$"
default: "$"
formatString: "{:?}"
example: "Some(&Vec::new())"
- name: "enum_header_string"
@ -943,10 +943,10 @@ paths:
type: "array"
items:
type: "string"
default: "$"
enum:
- ">"
- "$"
default: "$"
formatString: "{:?}"
example: "Some(&Vec::new())"
- name: "enum_query_string"
@ -1347,60 +1347,6 @@ paths:
path: "/fake/jsonFormData"
HttpMethod: "Get"
httpmethod: "get"
/fake/xmlFeatures:
get:
summary: "Get some XML"
operationId: "getXmlFeatures"
consumes:
- "application/xml"
produces:
- "application/xml"
parameters: []
responses:
200:
description: "Success"
schema:
$ref: "#/definitions/xmlObject"
uppercase_operation_id: "GET_XML_FEATURES"
uppercase_message: "SUCCESS"
uppercase_data_type: "XMLOBJECT"
producesXml: true
operation_id: "get_xml_features"
uppercase_operation_id: "GET_XML_FEATURES"
path: "/fake/xmlFeatures"
HttpMethod: "Get"
httpmethod: "get"
post:
summary: "Post some xml"
operationId: "postXmlFeatures"
consumes:
- "application/xml"
produces:
- "application/xml"
parameters:
- in: "body"
name: "xmlObject"
required: true
schema:
$ref: "#/definitions/xmlObject"
uppercase_data_type: "XMLOBJECT"
refName: "xmlObject"
formatString: "{:?}"
example: "???"
model_key: "OuterBoolean"
uppercase_operation_id: "POST_XML_FEATURES"
consumesXml: true
responses:
200:
description: "Success"
uppercase_operation_id: "POST_XML_FEATURES"
uppercase_message: "SUCCESS"
operation_id: "post_xml_features"
uppercase_operation_id: "POST_XML_FEATURES"
path: "/fake/xmlFeatures"
HttpMethod: "Post"
httpmethod: "post"
noClientExample: true
/fake/inline-additionalProperties:
post:
tags:
@ -1512,7 +1458,7 @@ definitions:
type: "string"
description: "Order Status"
enum:
- "placedtest"
- "placed"
- "approved"
- "delivered"
complete:
@ -1524,7 +1470,7 @@ definitions:
id: 0
shipDate: "2000-01-23T04:56:07.000+00:00"
complete: false
status: "placedtest"
status: "placed"
xml:
name: "Order"
upperCaseName: "ORDER"

View File

@ -15,8 +15,6 @@ use futures::{Future, future, Stream, stream};
use petstore_api::{ApiNoContext, ContextWrapperExt,
ApiError,
TestSpecialTagsResponse,
GetXmlFeaturesResponse,
PostXmlFeaturesResponse,
FakeOuterBooleanSerializeResponse,
FakeOuterCompositeSerializeResponse,
FakeOuterNumberSerializeResponse,
@ -55,7 +53,6 @@ fn main() {
.arg(Arg::with_name("operation")
.help("Sets the operation to run")
.possible_values(&[
"GetXmlFeatures",
"FakeOuterBooleanSerialize",
"FakeOuterCompositeSerialize",
"FakeOuterNumberSerialize",
@ -105,17 +102,6 @@ fn main() {
// println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>")));
// },
Some("GetXmlFeatures") => {
let result = client.get_xml_features().wait();
println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>")));
},
// Disabled because there's no example.
// Some("PostXmlFeatures") => {
// let result = client.post_xml_features(???).wait();
// println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>")));
// },
Some("FakeOuterBooleanSerialize") => {
let result = client.fake_outer_boolean_serialize(None).wait();
println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>")));

View File

@ -11,8 +11,6 @@ use std::io::Error;
use petstore_api::{Api, ApiError, Context,
TestSpecialTagsResponse,
GetXmlFeaturesResponse,
PostXmlFeaturesResponse,
FakeOuterBooleanSerializeResponse,
FakeOuterCompositeSerializeResponse,
FakeOuterNumberSerializeResponse,
@ -59,20 +57,6 @@ impl Api for Server {
Box::new(futures::failed("Generic failure".into()))
}
/// Get some XML
fn get_xml_features(&self, context: &Context) -> Box<Future<Item=GetXmlFeaturesResponse, Error=ApiError> + Send> {
let context = context.clone();
println!("get_xml_features() - X-Span-ID: {:?}", context.x_span_id.unwrap_or(String::from("<none>")).clone());
Box::new(futures::failed("Generic failure".into()))
}
/// Post some xml
fn post_xml_features(&self, xml_object: models::XmlObject, context: &Context) -> Box<Future<Item=PostXmlFeaturesResponse, Error=ApiError> + Send> {
let context = context.clone();
println!("post_xml_features({:?}) - X-Span-ID: {:?}", xml_object, context.x_span_id.unwrap_or(String::from("<none>")).clone());
Box::new(futures::failed("Generic failure".into()))
}
fn fake_outer_boolean_serialize(&self, body: Option<models::OuterBoolean>, context: &Context) -> Box<Future<Item=FakeOuterBooleanSerializeResponse, Error=ApiError> + Send> {
let context = context.clone();

View File

@ -36,8 +36,6 @@ use swagger::{Context, ApiError, XSpanId};
use {Api,
TestSpecialTagsResponse,
GetXmlFeaturesResponse,
PostXmlFeaturesResponse,
FakeOuterBooleanSerializeResponse,
FakeOuterCompositeSerializeResponse,
FakeOuterNumberSerializeResponse,
@ -252,105 +250,6 @@ impl Api for Client {
Box::new(futures::done(result))
}
fn get_xml_features(&self, context: &Context) -> Box<Future<Item=GetXmlFeaturesResponse, Error=ApiError> + Send> {
let url = format!("{}/v2/fake/xmlFeatures?", self.base_path);
let hyper_client = (self.hyper_client)();
let request = hyper_client.request(hyper::method::Method::Get, &url);
let mut custom_headers = hyper::header::Headers::new();
context.x_span_id.as_ref().map(|header| custom_headers.set(XSpanId(header.clone())));
let request = request.headers(custom_headers);
// Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists).
fn parse_response(mut response: hyper::client::response::Response) -> Result<GetXmlFeaturesResponse, ApiError> {
match response.status.to_u16() {
200 => {
let mut buf = String::new();
response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
// ToDo: this will move to swagger-rs and become a standard From conversion trait
// once https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream
let body = serde_xml_rs::from_str::<models::XmlObject>(&buf)
.map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;
Ok(GetXmlFeaturesResponse::Success(body))
},
code => {
let mut buf = [0; 100];
let debug_body = match response.read(&mut buf) {
Ok(len) => match str::from_utf8(&buf[..len]) {
Ok(body) => Cow::from(body),
Err(_) => Cow::from(format!("<Body was not UTF8: {:?}>", &buf[..len].to_vec())),
},
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
};
Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
response.headers,
debug_body)))
}
}
}
let result = request.send().map_err(|e| ApiError(format!("No response received: {}", e))).and_then(parse_response);
Box::new(futures::done(result))
}
fn post_xml_features(&self, param_xml_object: models::XmlObject, context: &Context) -> Box<Future<Item=PostXmlFeaturesResponse, Error=ApiError> + Send> {
let url = format!("{}/v2/fake/xmlFeatures?", self.base_path);
let body = serde_xml_rs::to_string(&param_xml_object).expect("impossible to fail to serialize");
let hyper_client = (self.hyper_client)();
let request = hyper_client.request(hyper::method::Method::Post, &url);
let mut custom_headers = hyper::header::Headers::new();
let request = request.body(&body);
custom_headers.set(ContentType(mimetypes::requests::POST_XML_FEATURES.clone()));
context.x_span_id.as_ref().map(|header| custom_headers.set(XSpanId(header.clone())));
let request = request.headers(custom_headers);
// Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists).
fn parse_response(mut response: hyper::client::response::Response) -> Result<PostXmlFeaturesResponse, ApiError> {
match response.status.to_u16() {
200 => {
Ok(PostXmlFeaturesResponse::Success)
},
code => {
let mut buf = [0; 100];
let debug_body = match response.read(&mut buf) {
Ok(len) => match str::from_utf8(&buf[..len]) {
Ok(body) => Cow::from(body),
Err(_) => Cow::from(format!("<Body was not UTF8: {:?}>", &buf[..len].to_vec())),
},
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
};
Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
response.headers,
debug_body)))
}
}
}
let result = request.send().map_err(|e| ApiError(format!("No response received: {}", e))).and_then(parse_response);
Box::new(futures::done(result))
}
fn fake_outer_boolean_serialize(&self, param_body: Option<models::OuterBoolean>, context: &Context) -> Box<Future<Item=FakeOuterBooleanSerializeResponse, Error=ApiError> + Send> {

View File

@ -38,16 +38,6 @@ pub enum TestSpecialTagsResponse {
SuccessfulOperation ( models::Client ) ,
}
#[derive(Debug, PartialEq)]
pub enum GetXmlFeaturesResponse {
Success ( models::XmlObject ) ,
}
#[derive(Debug, PartialEq)]
pub enum PostXmlFeaturesResponse {
Success ,
}
#[derive(Debug, PartialEq)]
pub enum FakeOuterBooleanSerializeResponse {
OutputBoolean ( models::OuterBoolean ) ,
@ -222,12 +212,6 @@ pub trait Api {
/// To test special tags
fn test_special_tags(&self, body: models::Client, context: &Context) -> Box<Future<Item=TestSpecialTagsResponse, Error=ApiError> + Send>;
/// Get some XML
fn get_xml_features(&self, context: &Context) -> Box<Future<Item=GetXmlFeaturesResponse, Error=ApiError> + Send>;
/// Post some xml
fn post_xml_features(&self, xml_object: models::XmlObject, context: &Context) -> Box<Future<Item=PostXmlFeaturesResponse, Error=ApiError> + Send>;
fn fake_outer_boolean_serialize(&self, body: Option<models::OuterBoolean>, context: &Context) -> Box<Future<Item=FakeOuterBooleanSerializeResponse, Error=ApiError> + Send>;
@ -326,12 +310,6 @@ pub trait ApiNoContext {
/// To test special tags
fn test_special_tags(&self, body: models::Client) -> Box<Future<Item=TestSpecialTagsResponse, Error=ApiError> + Send>;
/// Get some XML
fn get_xml_features(&self) -> Box<Future<Item=GetXmlFeaturesResponse, Error=ApiError> + Send>;
/// Post some xml
fn post_xml_features(&self, xml_object: models::XmlObject) -> Box<Future<Item=PostXmlFeaturesResponse, Error=ApiError> + Send>;
fn fake_outer_boolean_serialize(&self, body: Option<models::OuterBoolean>) -> Box<Future<Item=FakeOuterBooleanSerializeResponse, Error=ApiError> + Send>;
@ -443,16 +421,6 @@ impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> {
self.api().test_special_tags(body, &self.context())
}
/// Get some XML
fn get_xml_features(&self) -> Box<Future<Item=GetXmlFeaturesResponse, Error=ApiError> + Send> {
self.api().get_xml_features(&self.context())
}
/// Post some xml
fn post_xml_features(&self, xml_object: models::XmlObject) -> Box<Future<Item=PostXmlFeaturesResponse, Error=ApiError> + Send> {
self.api().post_xml_features(xml_object, &self.context())
}
fn fake_outer_boolean_serialize(&self, body: Option<models::OuterBoolean>) -> Box<Future<Item=FakeOuterBooleanSerializeResponse, Error=ApiError> + Send> {
self.api().fake_outer_boolean_serialize(body, &self.context())

View File

@ -8,10 +8,6 @@ pub mod responses {
lazy_static! {
pub static ref TEST_SPECIAL_TAGS_SUCCESSFUL_OPERATION: Mime = mime!(Application/Json);
}
/// Create Mime objects for the response content types for GetXmlFeatures
lazy_static! {
pub static ref GET_XML_FEATURES_SUCCESS: Mime = mime!(Application/Xml);
}
/// Create Mime objects for the response content types for TestClientModel
lazy_static! {
pub static ref TEST_CLIENT_MODEL_SUCCESSFUL_OPERATION: Mime = mime!(Application/Json);
@ -65,10 +61,6 @@ pub mod requests {
lazy_static! {
pub static ref TEST_SPECIAL_TAGS: Mime = mime!(Application/Json);
}
/// Create Mime objects for the request content types for PostXmlFeatures
lazy_static! {
pub static ref POST_XML_FEATURES: Mime = mime!(Application/Xml);
}
/// Create Mime objects for the request content types for FakeOuterBooleanSerialize
lazy_static! {
pub static ref FAKE_OUTER_BOOLEAN_SERIALIZE: Mime = mime!(Application/Json);

View File

@ -38,8 +38,6 @@ use swagger::{ApiError, Context, XSpanId};
use {Api,
TestSpecialTagsResponse,
GetXmlFeaturesResponse,
PostXmlFeaturesResponse,
FakeOuterBooleanSerializeResponse,
FakeOuterCompositeSerializeResponse,
FakeOuterNumberSerializeResponse,
@ -180,120 +178,6 @@ fn add_routes<T>(router: &mut Router, api: T) where T: Api + Send + Sync + Clone
},
"TestSpecialTags");
let api_clone = api.clone();
router.get(
"/v2/fake/xmlFeatures",
move |req: &mut Request| {
let mut context = Context::default();
// Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists).
fn handle_request<T>(req: &mut Request, api: &T, context: &mut Context) -> Result<Response, Response> where T: Api {
context.x_span_id = Some(req.headers.get::<XSpanId>().map(XSpanId::to_string).unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()));
context.auth_data = req.extensions.remove::<AuthData>();
context.authorization = req.extensions.remove::<Authorization>();
match api.get_xml_features(context).wait() {
Ok(rsp) => match rsp {
GetXmlFeaturesResponse::Success(body) => {
let body_string = serde_xml_rs::to_string(&body).expect("impossible to fail to serialize");
let mut response = Response::with((status::Status::from_u16(200), body_string));
response.headers.set(ContentType(mimetypes::responses::GET_XML_FEATURES_SUCCESS.clone()));
context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone())));
Ok(response)
},
},
Err(_) => {
// Application code returned an error. This should not happen, as the implementation should
// return a valid response.
Err(Response::with((status::InternalServerError, "An internal error occurred".to_string())))
}
}
}
handle_request(req, &api_clone, &mut context).or_else(|mut response| {
context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone())));
Ok(response)
})
},
"GetXmlFeatures");
let api_clone = api.clone();
router.post(
"/v2/fake/xmlFeatures",
move |req: &mut Request| {
let mut context = Context::default();
// Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists).
fn handle_request<T>(req: &mut Request, api: &T, context: &mut Context) -> Result<Response, Response> where T: Api {
context.x_span_id = Some(req.headers.get::<XSpanId>().map(XSpanId::to_string).unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()));
context.auth_data = req.extensions.remove::<AuthData>();
context.authorization = req.extensions.remove::<Authorization>();
// Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields.
let param_xml_object_raw = req.get::<bodyparser::Raw>().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter xmlObject - not valid UTF-8: {}", e))))?;
let mut unused_elements = Vec::new();
let param_xml_object = if let Some(param_xml_object_raw) = param_xml_object_raw {
let deserializer = &mut serde_xml_rs::de::Deserializer::new_from_reader(param_xml_object_raw.as_bytes());
let param_xml_object: Option<models::XmlObject> = serde_ignored::deserialize(deserializer, |path| {
warn!("Ignoring unknown field in body: {}", path);
unused_elements.push(path.to_string());
}).map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter xmlObject - doesn't match schema: {}", e))))?;
param_xml_object
} else {
None
};
let param_xml_object = param_xml_object.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter xmlObject".to_string())))?;
match api.post_xml_features(param_xml_object, context).wait() {
Ok(rsp) => match rsp {
PostXmlFeaturesResponse::Success => {
let mut response = Response::with((status::Status::from_u16(200)));
context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone())));
if !unused_elements.is_empty() {
response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements)));
}
Ok(response)
},
},
Err(_) => {
// Application code returned an error. This should not happen, as the implementation should
// return a valid response.
Err(Response::with((status::InternalServerError, "An internal error occurred".to_string())))
}
}
}
handle_request(req, &api_clone, &mut context).or_else(|mut response| {
context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone())));
Ok(response)
})
},
"PostXmlFeatures");
let api_clone = api.clone();
router.post(
"/v2/fake/outer/boolean",