mirror of
https://github.com/OpenAPITools/openapi-generator.git
synced 2025-11-30 07:23:37 +00:00
* End use of deprecated openssl method * Enhance rust-server to use hyper 0.11 to support handling operations asynchronously The changes are complete and working (at least for microservices tested within Metaswitch). This isn't completely compatible with the (previous/current) synchronous swagger-codegen. Specifically, * `Client` is no longer `Send + Sync` * Api implementations used by Server are no longer expected to be `Send + Sync` (which is good, because it's quite hard if `Client` isn't) * the code to create `Client`s and `Server`s, and hook them into `hyper` or `tokio` is different. Importantly, though, the business logic itself should be unchanged. * Re-adds the `basePath` element to all server endpoints. This mean clients and servers can talk to each other again. * Fix multipart formdata codegen * Fix up handling of multipart messages * Fix server -> client multipart message response * Correct handling of optional file types * Add authorization header to requests with basic auth * Add client support for `application/x-www-form-urlencoded` * Import uuid library if headers use UUID type * Add BASE_PATH to the server module. * Wrap client connector * Support both query and body parameters on the same operation
438 lines
22 KiB
Plaintext
438 lines
22 KiB
Plaintext
#![allow(unused_extern_crates)]
|
|
extern crate tokio_core;
|
|
extern crate native_tls;
|
|
extern crate hyper_tls;
|
|
extern crate openssl;
|
|
extern crate mime;
|
|
extern crate chrono;
|
|
extern crate url;
|
|
{{#apiHasFile}}extern crate multipart;{{/apiHasFile}}
|
|
{{#usesUrlEncodedForm}}extern crate serde_urlencoded;{{/usesUrlEncodedForm}}
|
|
|
|
{{#apiUsesUuid}}use uuid;{{/apiUsesUuid}}
|
|
{{#apiHasFile}}use self::multipart::client::lazy::Multipart;{{/apiHasFile}}
|
|
use hyper;
|
|
use hyper::header::{Headers, ContentType};
|
|
use hyper::Uri;
|
|
use self::url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
|
|
use futures;
|
|
use futures::{Future, Stream};
|
|
use futures::{future, stream};
|
|
use self::tokio_core::reactor::Handle;
|
|
use std::borrow::Cow;
|
|
use std::io::{Read, Error, ErrorKind};
|
|
use std::error;
|
|
use std::fmt;
|
|
use std::path::Path;
|
|
use std::sync::Arc;
|
|
use std::str;
|
|
use std::str::FromStr;
|
|
|
|
use mimetypes;
|
|
|
|
use serde_json;
|
|
{{#usesXml}}use serde_xml_rs;{{/usesXml}}
|
|
|
|
#[allow(unused_imports)]
|
|
use std::collections::{HashMap, BTreeMap};
|
|
#[allow(unused_imports)]
|
|
use swagger;
|
|
|
|
use swagger::{Context, ApiError, XSpanId};
|
|
|
|
use {Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
|
|
{{operationId}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
|
|
};
|
|
use models;
|
|
|
|
/// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes.
|
|
fn into_base_path(input: &str, correct_scheme: Option<&'static str>) -> Result<String, ClientInitError> {
|
|
// First convert to Uri, since a base path is a subset of Uri.
|
|
let uri = Uri::from_str(input)?;
|
|
|
|
let scheme = uri.scheme().ok_or(ClientInitError::InvalidScheme)?;
|
|
|
|
// Check the scheme if necessary
|
|
if let Some(correct_scheme) = correct_scheme {
|
|
if scheme != correct_scheme {
|
|
return Err(ClientInitError::InvalidScheme);
|
|
}
|
|
}
|
|
|
|
let host = uri.host().ok_or_else(|| ClientInitError::MissingHost)?;
|
|
let port = uri.port().map(|x| format!(":{}", x)).unwrap_or_default();
|
|
Ok(format!("{}://{}{}", scheme, host, port))
|
|
}
|
|
|
|
/// A client that implements the API by making HTTP calls out to a server.
|
|
#[derive(Clone)]
|
|
pub struct Client {
|
|
hyper_client: Arc<Fn(&Handle) -> Box<hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>> + Sync + Send>,
|
|
handle: Arc<Handle>,
|
|
base_path: String,
|
|
}
|
|
|
|
impl fmt::Debug for Client {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(f, "Client {{ base_path: {} }}", self.base_path)
|
|
}
|
|
}
|
|
|
|
impl Client {
|
|
|
|
/// Create an HTTP client.
|
|
///
|
|
/// # Arguments
|
|
/// * `handle` - tokio reactor handle to use for execution
|
|
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
|
pub fn try_new_http(handle: Handle, base_path: &str) -> Result<Client, ClientInitError> {
|
|
let http_connector = swagger::http_connector();
|
|
Self::try_new_with_connector::<hyper::client::HttpConnector>(
|
|
handle,
|
|
base_path,
|
|
Some("http"),
|
|
http_connector,
|
|
)
|
|
}
|
|
|
|
/// Create a client with a TLS connection to the server.
|
|
///
|
|
/// # Arguments
|
|
/// * `handle` - tokio reactor handle to use for execution
|
|
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
|
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
|
pub fn try_new_https<CA>(
|
|
handle: Handle,
|
|
base_path: &str,
|
|
ca_certificate: CA,
|
|
) -> Result<Client, ClientInitError>
|
|
where
|
|
CA: AsRef<Path>,
|
|
{
|
|
let https_connector = swagger::https_connector(ca_certificate);
|
|
Self::try_new_with_connector::<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>(
|
|
handle,
|
|
base_path,
|
|
Some("https"),
|
|
https_connector,
|
|
)
|
|
}
|
|
|
|
/// Create a client with a mutually authenticated TLS connection to the server.
|
|
///
|
|
/// # Arguments
|
|
/// * `handle` - tokio reactor handle to use for execution
|
|
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
|
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
|
/// * `client_key` - Path to the client private key
|
|
/// * `client_certificate` - Path to the client's public certificate associated with the private key
|
|
pub fn try_new_https_mutual<CA, K, C, T>(
|
|
handle: Handle,
|
|
base_path: &str,
|
|
ca_certificate: CA,
|
|
client_key: K,
|
|
client_certificate: C,
|
|
) -> Result<Client, ClientInitError>
|
|
where
|
|
CA: AsRef<Path>,
|
|
K: AsRef<Path>,
|
|
C: AsRef<Path>,
|
|
{
|
|
let https_connector =
|
|
swagger::https_mutual_connector(ca_certificate, client_key, client_certificate);
|
|
Self::try_new_with_connector::<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>(
|
|
handle,
|
|
base_path,
|
|
Some("https"),
|
|
https_connector,
|
|
)
|
|
}
|
|
|
|
/// Create a client with a custom implementation of hyper::client::Connect.
|
|
///
|
|
/// Intended for use with custom implementations of connect for e.g. protocol logging
|
|
/// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
|
|
/// this function should be used in conjunction with
|
|
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
|
|
///
|
|
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https`
|
|
/// and `try_new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `handle` - tokio reactor handle to use for execution
|
|
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
|
/// * `protocol` - Which protocol to use when constructing the request url, e.g. `Some("http")`
|
|
/// * `connector_fn` - Function which returns an implementation of `hyper::client::Connect`
|
|
pub fn try_new_with_connector<C>(
|
|
handle: Handle,
|
|
base_path: &str,
|
|
protocol: Option<&'static str>,
|
|
connector_fn: Box<Fn(&Handle) -> C + Send + Sync>,
|
|
) -> Result<Client, ClientInitError>
|
|
where
|
|
C: hyper::client::Connect + hyper::client::Service,
|
|
{
|
|
let hyper_client = {
|
|
move |handle: &Handle| -> Box<
|
|
hyper::client::Service<
|
|
Request = hyper::Request<hyper::Body>,
|
|
Response = hyper::Response,
|
|
Error = hyper::Error,
|
|
Future = hyper::client::FutureResponse,
|
|
>,
|
|
> {
|
|
let connector = connector_fn(handle);
|
|
Box::new(hyper::Client::configure().connector(connector).build(
|
|
handle,
|
|
))
|
|
}
|
|
};
|
|
|
|
Ok(Client {
|
|
hyper_client: Arc::new(hyper_client),
|
|
handle: Arc::new(handle),
|
|
base_path: into_base_path(base_path, protocol)?,
|
|
})
|
|
}
|
|
|
|
/// Constructor for creating a `Client` by passing in a pre-made `hyper` client.
|
|
///
|
|
/// One should avoid relying on this function if possible, since it adds a dependency on the underlying transport
|
|
/// implementation, which it would be better to abstract away. Therefore, using this function may lead to a loss of
|
|
/// code generality, which may make it harder to move the application to a serverless environment, for example.
|
|
///
|
|
/// The reason for this function's existence is to support legacy test code, which did mocking at the hyper layer.
|
|
/// This is not a recommended way to write new tests. If other reasons are found for using this function, they
|
|
/// should be mentioned here.
|
|
pub fn try_new_with_hyper_client(hyper_client: Arc<Fn(&Handle) -> Box<hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>> + Sync + Send>,
|
|
handle: Handle,
|
|
base_path: &str)
|
|
-> Result<Client, ClientInitError>
|
|
{
|
|
Ok(Client {
|
|
hyper_client: hyper_client,
|
|
handle: Arc::new(handle),
|
|
base_path: into_base_path(base_path, None)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Api for Client {
|
|
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
|
|
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, param_{{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
|
|
{{#queryParams}}{{#-first}}
|
|
// Query parameters
|
|
{{/-first}}{{#required}} let query_{{paramName}} = format!("{{baseName}}={{=<% %>=}}{<% paramName %>}<%={{ }}=%>&", {{paramName}}=param_{{paramName}}{{#isListContainer}}.join(","){{/isListContainer}}{{^isListContainer}}.to_string(){{/isListContainer}});
|
|
{{/required}}{{^required}} let query_{{paramName}} = param_{{paramName}}.map_or_else(String::new, |query| format!("{{baseName}}={{=<% %>=}}{<% paramName %>}<%={{ }}=%>&", {{paramName}}=query{{#isListContainer}}.join(","){{/isListContainer}}{{^isListContainer}}.to_string(){{/isListContainer}}));
|
|
{{/required}}{{/queryParams}}
|
|
|
|
let uri = format!(
|
|
"{}{{basePathWithoutHost}}{{path}}{{#queryParams}}{{#-first}}?{{/-first}}{{=<% %>=}}{<% paramName %>}<%={{ }}=%>{{/queryParams}}",
|
|
self.base_path{{#pathParams}}, {{baseName}}=utf8_percent_encode(¶m_{{paramName}}.to_string(), PATH_SEGMENT_ENCODE_SET){{/pathParams}}{{#queryParams}},
|
|
{{paramName}}=utf8_percent_encode(&query_{{paramName}}, QUERY_ENCODE_SET){{/queryParams}}
|
|
);
|
|
|
|
let uri = match Uri::from_str(&uri) {
|
|
Ok(uri) => uri,
|
|
Err(err) => return Box::new(futures::done(Err(ApiError(format!("Unable to build URI: {}", err))))),
|
|
};
|
|
|
|
let mut request = hyper::Request::new(hyper::Method::{{#vendorExtensions}}{{HttpMethod}}{{/vendorExtensions}}, uri);
|
|
|
|
{{#vendorExtensions}}{{#hasFile}} // Form data body
|
|
let mut multipart = Multipart::new();
|
|
|
|
// Helper function to convert a Stream into a String. The String can then be used to build the HTTP body.
|
|
fn convert_stream_to_string(stream: Box<Stream<Item=Vec<u8>, Error=Error> + Send>) -> Result<String, ApiError> {
|
|
|
|
stream.concat2()
|
|
.wait()
|
|
.map_err(|e| ApiError(format!("Unable to collect stream: {}", e)))
|
|
.and_then(|body| String::from_utf8(body)
|
|
.map_err(|e| ApiError(format!("Failed to convert utf8 stream to String: {}", e))))
|
|
}{{/hasFile}}{{/vendorExtensions}}{{#formParams}}{{#isFile}}
|
|
|
|
{{^required}} if let Ok(Some(param_{{paramName}})) = param_{{paramName}}.wait() { {{/required}}
|
|
{{^required}} {{/required}} match convert_stream_to_string(param_{{paramName}}) {
|
|
{{^required}} {{/required}} Ok(param_{{paramName}}) => {
|
|
// Add file to multipart form.
|
|
multipart.add_text("{{paramName}}", param_{{paramName}});
|
|
},
|
|
{{^required}} {{/required}} Err(err) => return Box::new(futures::done(Err(err))),
|
|
{{^required}} {{/required}} }
|
|
{{^required}}}{{/required}}{{/isFile}}{{/formParams}}{{#vendorExtensions}}{{#hasFile}}
|
|
|
|
let mut fields = match multipart.prepare() {
|
|
Ok(fields) => fields,
|
|
Err(err) => return Box::new(futures::done(Err(ApiError(format!("Unable to build request: {}", err))))),
|
|
};
|
|
|
|
let mut body_string = String::new();
|
|
let body = fields.to_body().read_to_string(&mut body_string);
|
|
let boundary = fields.boundary();
|
|
let multipart_header = match mime::Mime::from_str(&format!("multipart/form-data;boundary={}", boundary)) {
|
|
Ok(multipart_header) => multipart_header,
|
|
Err(err) => return Box::new(futures::done(Err(ApiError(format!("Unable to build multipart header: {:?}", err))))),
|
|
};{{/hasFile}}{{^hasFile}}{{#formParams}}{{#-first}} let params = &[{{/-first}}
|
|
("{{baseName}}", {{#vendorExtensions}}{{#required}}Some({{#isString}}param_{{paramName}}{{/isString}}{{^isString}}format!("{:?}", param_{{paramName}}){{/isString}}){{/required}}{{^required}}{{#isString}}param_{{paramName}}{{/isString}}{{^isString}}param_{{paramName}}.map(|param| format!("{:?}", param)){{/isString}}{{/required}}),{{/vendorExtensions}}{{#-last}}
|
|
];
|
|
let body = serde_urlencoded::to_string(params).expect("impossible to fail to serialize");
|
|
|
|
request.headers_mut().set(ContentType(mimetypes::requests::{{#vendorExtensions}}{{uppercase_operation_id}}{{/vendorExtensions}}.clone()));
|
|
request.set_body(body.into_bytes());{{/-last}}{{/formParams}}{{/hasFile}}{{/vendorExtensions}}{{#bodyParam}}{{#-first}}
|
|
// Body parameter
|
|
{{/-first}}{{#vendorExtensions}}{{#required}}{{#consumesPlainText}} let body = param_{{paramName}};{{/consumesPlainText}}{{#consumesXml}}
|
|
{{^has_namespace}} let body = serde_xml_rs::to_string(¶m_{{paramName}}).expect("impossible to fail to serialize");{{/has_namespace}}{{#has_namespace}}
|
|
let mut namespaces = BTreeMap::new();
|
|
// An empty string is used to indicate a global namespace in xmltree.
|
|
namespaces.insert("".to_string(), models::namespaces::{{uppercase_data_type}}.clone());
|
|
let body = serde_xml_rs::to_string_with_namespaces(¶m_{{paramName}}, namespaces).expect("impossible to fail to serialize");{{/has_namespace}}{{/consumesXml}}{{#consumesJson}}
|
|
let body = serde_json::to_string(¶m_{{paramName}}).expect("impossible to fail to serialize");{{/consumesJson}}
|
|
{{/required}}{{^required}}{{#consumesPlainText}} let body = param_{{paramName}};
|
|
{{/consumesPlainText}}{{^consumesPlainText}} let body = param_{{paramName}}.map(|ref body| {
|
|
{{#consumesXml}}
|
|
{{^has_namespace}} serde_xml_rs::to_string(body).expect("impossible to fail to serialize"){{/has_namespace}}{{#has_namespace}}
|
|
let mut namespaces = BTreeMap::new();
|
|
// An empty string is used to indicate a global namespace in xmltree.
|
|
namespaces.insert("".to_string(), models::namespaces::{{uppercase_data_type}}.clone());
|
|
serde_xml_rs::to_string_with_namespaces(body, namespaces).expect("impossible to fail to serialize"){{/has_namespace}}{{/consumesXml}}{{#consumesJson}}
|
|
serde_json::to_string(body).expect("impossible to fail to serialize"){{/consumesJson}}
|
|
});{{/consumesPlainText}}{{/required}}{{/vendorExtensions}}{{/bodyParam}}
|
|
|
|
{{#bodyParam}}{{^required}}if let Some(body) = body {
|
|
{{/required}} request.set_body(body.into_bytes());
|
|
{{^required}} }{{/required}}
|
|
|
|
request.headers_mut().set(ContentType(mimetypes::requests::{{#vendorExtensions}}{{uppercase_operation_id}}{{/vendorExtensions}}.clone()));
|
|
{{/bodyParam}}
|
|
context.x_span_id.as_ref().map(|header| request.headers_mut().set(XSpanId(header.clone())));
|
|
{{#authMethods}}{{#isBasic}} context.auth_data.as_ref().map(|auth_data| {
|
|
if let &swagger::AuthData::Basic(ref basic_header) = auth_data {
|
|
request.headers_mut().set(hyper::header::Authorization(
|
|
basic_header.clone(),
|
|
))
|
|
}
|
|
});{{/isBasic}}{{/authMethods}}{{#headerParams}}{{#-first}}
|
|
// Header parameters
|
|
{{/-first}}{{^isMapContainer}} header! { (Request{{vendorExtensions.typeName}}, "{{baseName}}") => {{#isListContainer}}({{{baseType}}})*{{/isListContainer}}{{^isListContainer}}[{{{dataType}}}]{{/isListContainer}} }
|
|
{{#required}} request.headers_mut().set(Request{{vendorExtensions.typeName}}(param_{{paramName}}{{#isListContainer}}.clone(){{/isListContainer}}));
|
|
{{/required}}{{^required}} param_{{paramName}}.map(|header| request.headers_mut().set(Request{{vendorExtensions.typeName}}(header{{#isListContainer}}.clone(){{/isListContainer}})));
|
|
{{/required}}{{/isMapContainer}}{{#isMapContainer}} let param_{{paramName}}: Option<{{{dataType}}}> = None;
|
|
{{/isMapContainer}}{{/headerParams}}
|
|
|
|
{{#vendorExtensions}}{{#hasFile}}
|
|
request.headers_mut().set(ContentType(multipart_header));
|
|
request.set_body(body_string.into_bytes());
|
|
{{/hasFile}}{{/vendorExtensions}}
|
|
|
|
let hyper_client = (self.hyper_client)(&*self.handle);
|
|
Box::new(hyper_client.call(request)
|
|
.map_err(|e| ApiError(format!("No response received: {}", e)))
|
|
.and_then(|mut response| {
|
|
match response.status().as_u16() {
|
|
{{#responses}}
|
|
{{code}} => {
|
|
{{#headers}} header! { (Response{{nameInCamelCase}}, "{{baseName}}") => [{{{datatype}}}] }
|
|
let response_{{name}} = match response.headers().get::<Response{{nameInCamelCase}}>() {
|
|
Some(response_{{name}}) => response_{{name}}.0.clone(),
|
|
None => return Box::new(future::err(ApiError(String::from("Required response header {{baseName}} for response {{code}} was not found.")))) as Box<Future<Item=_, Error=_>>,
|
|
};
|
|
{{/headers}}
|
|
{{^isFile}}let body = response.body();{{/isFile}}{{#isFile}}let body = Box::new(response.body()
|
|
.map(|chunk| chunk.to_vec())
|
|
.map_err(|_|
|
|
Error::new(ErrorKind::Other, "Received error reading response.")
|
|
));{{/isFile}}
|
|
Box::new(
|
|
{{#dataType}}{{^isFile}}
|
|
body
|
|
.concat2()
|
|
.map_err(|e| ApiError(format!("Failed to read response: {}", e)))
|
|
.and_then(|body| str::from_utf8(&body)
|
|
.map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))
|
|
.and_then(|body|
|
|
{{#vendorExtensions}}{{#producesXml}}
|
|
// 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
|
|
serde_xml_rs::from_str::<{{{dataType}}}>(body)
|
|
.map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))
|
|
{{/producesXml}}{{#producesJson}}
|
|
serde_json::from_str::<{{{dataType}}}>(body)
|
|
.map_err(|e| e.into())
|
|
{{/producesJson}}{{#producesPlainText}}
|
|
Ok(body.to_string())
|
|
{{/producesPlainText}}{{/vendorExtensions}}
|
|
))
|
|
.map(move |body|
|
|
{{/isFile}}{{#isFile}}
|
|
future::ok(
|
|
{{/isFile}}
|
|
{{operationId}}Response::{{#vendorExtensions}}{{x-responseId}}{{/vendorExtensions}}{{^headers}}(body){{/headers}}{{#headers}}{{#-first}}{ body: body, {{/-first}}{{name}}: response_{{name}}{{^-last}}, {{/-last}}{{#-last}} }{{/-last}}{{/headers}}
|
|
)
|
|
{{/dataType}}{{^dataType}}
|
|
future::ok(
|
|
{{operationId}}Response::{{#vendorExtensions}}{{x-responseId}}{{/vendorExtensions}}{{#headers}}{{#-first}}{ {{/-first}}{{^-first}}, {{/-first}}{{name}}: response_{{name}}{{#-last}} }{{/-last}}{{/headers}}
|
|
)
|
|
{{/dataType}}
|
|
) as Box<Future<Item=_, Error=_>>
|
|
},
|
|
{{/responses}}
|
|
code => {
|
|
let headers = response.headers().clone();
|
|
Box::new(response.body()
|
|
.take(100)
|
|
.concat2()
|
|
.then(move |body|
|
|
future::err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
|
|
code,
|
|
headers,
|
|
match body {
|
|
Ok(ref body) => match str::from_utf8(body) {
|
|
Ok(body) => Cow::from(body),
|
|
Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
|
|
},
|
|
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
|
|
})))
|
|
)
|
|
) as Box<Future<Item=_, Error=_>>
|
|
}
|
|
}
|
|
}))
|
|
|
|
}
|
|
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum ClientInitError {
|
|
InvalidScheme,
|
|
InvalidUri(hyper::error::UriError),
|
|
MissingHost,
|
|
SslError(openssl::error::ErrorStack)
|
|
}
|
|
|
|
impl From<hyper::error::UriError> for ClientInitError {
|
|
fn from(err: hyper::error::UriError) -> ClientInitError {
|
|
ClientInitError::InvalidUri(err)
|
|
}
|
|
}
|
|
|
|
impl From<openssl::error::ErrorStack> for ClientInitError {
|
|
fn from(err: openssl::error::ErrorStack) -> ClientInitError {
|
|
ClientInitError::SslError(err)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for ClientInitError {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
(self as &fmt::Debug).fmt(f)
|
|
}
|
|
}
|
|
|
|
impl error::Error for ClientInitError {
|
|
fn description(&self) -> &str {
|
|
"Failed to produce a hyper client."
|
|
}
|
|
}
|