Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions samples/client/petstore/rust/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target/
**/*.rs.bk
Cargo.lock
17 changes: 17 additions & 0 deletions samples/client/petstore/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "petstore-client"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these settings would be exposed as flags.

version = "0.1.0"
authors = ["Vladimir Pouzanov <farcaller@gmail.com>"]

[dependencies]
serde = "*"
serde_derive = "*"
serde_yaml = "*"
serde_json = "*"
base64 = "*"
futures = "*"
hyper = "*"
url = "*"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind annotating which dependency version is the codegen targeting?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, as soon as I figure those myself :-) so far the generated code doesn't compile


[dev-dependencies]
tokio-core = "*"
48 changes: 48 additions & 0 deletions samples/client/petstore/rust/examples/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
extern crate petstore_client;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a sample file, not to be part of generated api

extern crate hyper;
extern crate tokio_core;
extern crate futures;

use hyper::Client;
use hyper::client::HttpConnector;
use tokio_core::reactor::Core;
use futures::{Future};

fn main() {
let mut core = Core::new().expect("failed to init core");
let handle = core.handle();

let http_connector = HttpConnector::new(4, &handle);

let client = Client::configure().connector(http_connector).build(&handle);
let api = "http://petstore.swagger.io:80/v2";

let new_pet = petstore_client::models::Pet::new("barker".to_owned(), vec![]).with_id(1337);

let apicli = petstore_client::apis::client::APIClient::new(
petstore_client::apis::configuration::Configuration::new(
Client::configure().connector(HttpConnector::new(4, &handle)).build(&handle)));

let work = apicli.pet_api().add_pet(&new_pet)
// petstore_client::apis::add_pet(api, &client, &new_pet)
.and_then(|_| {
petstore_client::apis::update_pet_with_form(
api,
&client,
1337,
"barko",
"escaped")
})
.and_then(|_| {
petstore_client::apis::get_pet_by_id(
api,
&client,
1337)
})
.and_then(|pet| {
println!("pet: {:?}", pet);
futures::future::ok(())
});

core.run(work).expect("failed to run core");
}
27 changes: 27 additions & 0 deletions samples/client/petstore/rust/src/apis/add_pet_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use hyper;
use serde_json;
use futures::{Future, Stream};
use futures;

use super::models;
use super::Error;

pub fn add_pet<C: hyper::client::Connect>(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rust naming guidelines dictate snake_case in function names

prefix: &str,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about what's the common pattern for api use, really. This sample client will require two things: a http (or https) prefix as a string and the hyper.rs Client (the web client) plus any arguments.

cli: &hyper::client::Client<C>,
pet: &models::Pet,
) -> Box<Future<Item = (), Error = Error>> {
let mut req = hyper::Request::new(
hyper::Method::Post,
format!("{}/pet", prefix).parse().unwrap());
let serialized = serde_json::to_string(pet).unwrap();
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no error handling for brevity of the example.

req.headers_mut().set(hyper::header::ContentType::json());
req.headers_mut().set(hyper::header::ContentLength(serialized.len() as u64));
req.set_body(serialized);

Box::new(
cli.request(req).and_then(|res| { res.body().concat2() })
.map_err(|e| Error::from(e))
.and_then(|_| futures::future::ok(()))
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the api doesn't dictate the return value so nothing is returned even though the sample server returns a pet instance.

npm server choked up on {"id":1337,"category":null,"name":"barker","photoUrls":[],"tags":[],"status":null} here, http://petstore.swagger.io worked fine.

)
}
26 changes: 26 additions & 0 deletions samples/client/petstore/rust/src/apis/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::rc::Rc;

use hyper;
use super::configuration::Configuration;
use super::pet_api;

pub struct APIClient<C: hyper::client::Connect> {
configuration: Rc<Configuration<C>>,

pet_api: Box<pet_api::PetAPI>,
}

impl<C: hyper::client::Connect> APIClient<C> {
pub fn new(configuration: Configuration<C>) -> APIClient<C> {
let rc = Rc::new(configuration);

APIClient {
configuration: rc.clone(),
pet_api: Box::new(pet_api::PetAPIImpl::new(rc.clone())),
}
}

pub fn pet_api(&self) -> &pet_api::PetAPI {
self.pet_api.as_ref()
}
}
15 changes: 15 additions & 0 deletions samples/client/petstore/rust/src/apis/configuration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use hyper;

pub struct Configuration<C: hyper::client::Connect> {
pub base_path: String,
pub client: hyper::client::Client<C>,
}

impl<C: hyper::client::Connect> Configuration<C> {
pub fn new(client: hyper::client::Client<C>) -> Configuration<C> {
Configuration {
base_path: "http://petstore.swagger.io:80/v2".to_owned(),
client: client,
}
}
}
23 changes: 23 additions & 0 deletions samples/client/petstore/rust/src/apis/get_pet_by_id_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use hyper;
use serde_json;
use futures::{Future, Stream};

use super::models;
use super::Error;

pub fn get_pet_by_id<C: hyper::client::Connect>(
prefix: &str,
cli: &hyper::client::Client<C>,
pet_id: i64,
) -> Box<Future<Item = models::Pet, Error = Error>> {
Copy link
Copy Markdown

@lucab lucab Jul 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Here and in other generated methods)
Would you consider returning a more complex type? The Item here is losing any additional information about the response (header, status, etc) which I've found useful in some situations (e.g. token authenticated endpoints redirecting to token provider). For reference, reqwest does this with a richer Response struct + trait.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For other generators (e.g. PHP, Ruby), there's a corresponding "WithHttpInfo" method (e.g. getPetByIdWithHttpInfo) to return a list with the response (if any), HTTP status code and HTTP headers.

Not sure if Rust supports returning a list. If not, we will need to create an ApiResponse object with response, status and headers as properties.

For Rust, we can follow similar approach by creating a corresponding WithHttpInfo method or we can default the method (e.g. getPetById) to return the response together with status code and headers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can return response object along with the parsed body. Let's focus on making the current implementation compileable and then we can improve.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's also worth sharing other API clients usually come with a corresponding "Async" method (e.g. addPetAsync) for async function call. Not sure how that should be done in Rust but worth thinking ahead with this future requirement.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All this code is async-only. I don't think that it makes sense to have a matching sync version.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok. Good to know. One less thing to consider then.

Box::new(
cli.get(format!("{}/pet/{}", prefix, pet_id).parse().unwrap())

.and_then(|res| { res.body().concat2() }).map_err(|e| Error::from(e))

.and_then(|body| {
let parsed: Result<models::Pet, _> = serde_json::from_slice(&body);
parsed.map_err(|e| Error::from(e))
}).map_err(|e| Error::from(e))
)
}
37 changes: 37 additions & 0 deletions samples/client/petstore/rust/src/apis/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use hyper;
use serde_json;

#[derive(Debug)]
pub enum Error {
Hyper(hyper::Error),
Serde(serde_json::Error),
}

impl From<hyper::Error> for Error {
fn from(e: hyper::Error) -> Self {
return Error::Hyper(e)
}
}

impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Self {
return Error::Serde(e)
}
}

use super::models;

mod get_pet_by_id_api;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's a single namespace, so modules (and files) would have to be named differently from the actual functions.

pub use self::get_pet_by_id_api::get_pet_by_id;

mod update_pet_with_form_api;
pub use self::update_pet_with_form_api::update_pet_with_form;

mod add_pet_api;
pub use self::add_pet_api::add_pet;

pub mod configuration;
pub mod client;

mod pet_api;
pub use self::pet_api::PetAPI;
44 changes: 44 additions & 0 deletions samples/client/petstore/rust/src/apis/pet_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use std::rc::Rc;
use std::borrow::Borrow;

use hyper;
use serde_json;
use futures;
use futures::{Future, Stream};

use super::{Error, configuration, models};

pub trait PetAPI {
fn add_pet(&self, pet: &models::Pet) -> Box<Future<Item = (), Error = Error>>;
}

pub struct PetAPIImpl<C: hyper::client::Connect> {
configuration: Rc<configuration::Configuration<C>>,
}

impl<C: hyper::client::Connect> PetAPIImpl<C> {
pub fn new(configuration: Rc<configuration::Configuration<C>>) -> PetAPIImpl<C> {
PetAPIImpl {
configuration: configuration,
}
}
}

impl<C: hyper::client::Connect>PetAPI for PetAPIImpl<C> {
fn add_pet(&self, pet: &models::Pet) -> Box<Future<Item = (), Error = Error>> {
let configuration: &configuration::Configuration<C> = self.configuration.borrow();
let mut req = hyper::Request::new(
hyper::Method::Post,
format!("{}/pet", configuration.base_path).parse().unwrap());
let serialized = serde_json::to_string(pet).unwrap();
req.headers_mut().set(hyper::header::ContentType::json());
req.headers_mut().set(hyper::header::ContentLength(serialized.len() as u64));
req.set_body(serialized);

Box::new(
configuration.client.request(req).and_then(|res| { res.body().concat2() })
.map_err(|e| Error::from(e))
.and_then(|_| futures::future::ok(()))
)
}
}
31 changes: 31 additions & 0 deletions samples/client/petstore/rust/src/apis/update_pet_with_form_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use hyper;
use futures::{Future, Stream};
use url;
use futures;

use super::Error;

pub fn update_pet_with_form<C: hyper::client::Connect>(
prefix: &str,
cli: &hyper::client::Client<C>,
pet_id: i64,
name: &str,
status: &str,
) -> Box<Future<Item = (), Error = Error>> {
let mut req = hyper::Request::new(
hyper::Method::Post,
format!("{}/pet/{}", prefix, pet_id).parse().unwrap());
let body = url::form_urlencoded::Serializer::new(String::new())
.append_pair("name", name)
.append_pair("status", status)
.finish();
req.headers_mut().set(hyper::header::ContentType::form_url_encoded());
req.headers_mut().set(hyper::header::ContentLength(body.len() as u64));
req.set_body(body);

Box::new(
cli.request(req).and_then(|res| { res.body().concat2() })
.map_err(|e| Error::from(e))
.and_then(|_| futures::future::ok(()))
)
}
10 changes: 10 additions & 0 deletions samples/client/petstore/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#[macro_use]
extern crate serde_derive;

extern crate hyper;
extern crate serde_json;
extern crate futures;
extern crate url;

pub mod apis;
pub mod models;
8 changes: 8 additions & 0 deletions samples/client/petstore/rust/src/models/category.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// Pet catehgry
///
/// A category for a pet
#[derive(Debug, Serialize, Deserialize)]
pub struct Category {
id: Option<i64>,
name: Option<String>,
}
8 changes: 8 additions & 0 deletions samples/client/petstore/rust/src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
mod pet;
pub use self::pet::Pet;

mod category;
pub use self::category::Category;

mod tag;
pub use self::tag::Tag;
34 changes: 34 additions & 0 deletions samples/client/petstore/rust/src/models/pet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/// a Pet
///
/// A pet for sale in the pet store
#[derive(Debug, Serialize, Deserialize)]
pub struct Pet {
id: Option<i64>,
category: Option<super::Category>,
name: String,
#[serde(rename = "photoUrls")] photo_urls: Vec<String>,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, snake case here, and renamed fields need an annotation.

tags: Vec<super::Tag>,
status: Option<String>,
}

impl Pet {
pub fn new(name: String, photo_urls: Vec<String>) -> Pet {
Pet {
id: None,
category: None,
name: name,
photo_urls: photo_urls,
tags: Vec::new(),
status: None,
}
}

pub fn set_id(&mut self, id: i64) {
self.id = Some(id);
}

pub fn with_id(mut self, id: i64) -> Pet {
self.id = Some(id);
self
}
}
8 changes: 8 additions & 0 deletions samples/client/petstore/rust/src/models/tag.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// Pet Tag
///
/// A tag for a pet
#[derive(Debug, Serialize, Deserialize)]
pub struct Tag {
id: Option<i64>,
name: Option<String>,
}