diff --git a/src/mako/lib/mbuild.mako b/src/mako/lib/mbuild.mako index 5ac344a4de6..b98e20a9d8a 100644 --- a/src/mako/lib/mbuild.mako +++ b/src/mako/lib/mbuild.mako @@ -9,7 +9,8 @@ hub_type_params_s, method_media_params, enclose_in, mb_type_bounds, method_response, METHOD_BUILDER_MARKERT_TRAIT, pass_through, markdown_rust_block, parts_from_params, DELEGATE_PROPERTY_NAME, struct_type_bounds_s, supports_scopes, scope_url_to_variant, - re_find_replacements, ADD_PARAM_FN, ADD_PARAM_MEDIA_EXAMPLE, upload_action_fn, METHODS_RESOURCE) + re_find_replacements, ADD_PARAM_FN, ADD_PARAM_MEDIA_EXAMPLE, upload_action_fn, METHODS_RESOURCE, + method_name_to_variant) def get_parts(part_prop): if not part_prop: @@ -419,6 +420,7 @@ match result { paddfields = 'self.' + api.properties.params delegate = 'self.' + property(DELEGATE_PROPERTY_NAME) + delegate_finish = 'if let Some(ref mut d) = ' + delegate + ' { d.finished(); }' auth_call = 'self.hub.auth.borrow_mut()' if supports_scopes(auth): @@ -479,6 +481,10 @@ match result { use hyper::client::IntoBody; use std::io::{Read, Seek}; use hyper::header::{ContentType, ContentLength, Authorization, UserAgent}; + if let Some(ref mut d) = ${delegate} { + d.begin(cmn::MethodInfo { id: "${m.id}", + http_method: ${method_name_to_variant(m.httpMethod)} }); + } let mut params: Vec<(&str, String)> = Vec::with_capacity((${len(params) + len(reserved_params)} + ${paddfields}.len())); % for p in field_params: <% @@ -515,6 +521,7 @@ match result { ## Additional params - may not overlap with optional params for &field in [${', '.join(enclose_in('"', reserved_params + [p.name for p in field_params]))}].iter() { if ${paddfields}.contains_key(field) { + ${delegate_finish} return cmn::Result::FieldClash(field); } } @@ -570,12 +577,15 @@ else { assert 'key' in parameters, "Expected 'key' parameter if there are no scopes" %> let mut key = ${auth_call}.api_key(); - if key.is_none() { if let Some(ref mut dlg) = ${delegate} { - key = dlg.api_key(); + if key.is_none() { if let Some(ref mut d) = ${delegate} { + key = d.api_key(); }} match key { Some(value) => params.push(("key", value)), - None => return cmn::Result::MissingAPIKey, + None => { + ${delegate_finish} + return cmn::Result::MissingAPIKey + } } % else: if self.${api.properties.scopes}.len() == 0 { @@ -643,10 +653,11 @@ else { loop { % if supports_scopes(auth): let mut token = ${auth_call}.token(self.${api.properties.scopes}.keys()); - if token.is_none() { if let Some(ref mut dlg) = ${delegate} { - token = dlg.token(); + if token.is_none() { if let Some(ref mut d) = ${delegate} { + token = d.token(); }} if token.is_none() { + ${delegate_finish} return cmn::Result::MissingToken } let auth_header = Authorization(token.unwrap().access_token); @@ -669,7 +680,7 @@ else { }; % endif - let mut req = client.borrow_mut().request(hyper::method::Method::Extension("${m.httpMethod}".to_string()), url.as_slice()) + let mut req = client.borrow_mut().request(${method_name_to_variant(m.httpMethod)}, url.as_slice()) .header(UserAgent(self.hub._user_agent.clone()))\ % if supports_scopes(auth): @@ -697,32 +708,33 @@ else { } % endif ## media upload handling - match ${delegate} { - Some(ref mut d) => d.pre_request("${m.id}"), - None => {} + if let Some(ref mut d) = ${delegate} { + d.pre_request(); } match req.send() { Err(err) => { - if let Some(ref mut dlg) = ${delegate} { - if let oauth2::Retry::After(d) = dlg.http_error(&err) { + if let Some(ref mut d) = ${delegate} { + if let oauth2::Retry::After(d) = d.http_error(&err) { sleep(d); continue; } } + ${delegate_finish} return cmn::Result::HttpError(err) } Ok(mut res) => { if !res.status.is_success() { - if let Some(ref mut dlg) = ${delegate} { + if let Some(ref mut d) = ${delegate} { let mut json_err = String::new(); res.read_to_string(&mut json_err).unwrap(); let error_info: cmn::JsonServerError = json::decode(&json_err).unwrap(); - if let oauth2::Retry::After(d) = dlg.http_failure(&res, error_info) { + if let oauth2::Retry::After(d) = d.http_failure(&res, error_info) { sleep(d); continue; } } + ${delegate_finish} return cmn::Result::Failure(res) } % if response_schema: @@ -743,6 +755,7 @@ if enable_resource_parsing \ % else: let result_value = res; % endif + ${delegate_finish} return cmn::Result::Success(result_value) } } diff --git a/src/mako/lib/util.py b/src/mako/lib/util.py index fe26fa086fd..82664d7d29a 100644 --- a/src/mako/lib/util.py +++ b/src/mako/lib/util.py @@ -12,7 +12,7 @@ re_find_replacements = re.compile("\{[/\+]?\w+\*?\}") - +HTTP_METHODS = set(("OPTIONS", "GET", "POST", "PUT", "DELETE", "HEAD", "TRACE", "CONNECT", "PATCH" )) USE_FORMAT = 'use_format_field' TYPE_MAP = {'boolean' : 'bool', @@ -879,6 +879,12 @@ def scope_url_to_variant(name, url, fully_qualified=True): return fqvn(FULL) return fqvn(dot_sep_to_canonical_type_name(repl(base))) +def method_name_to_variant(name): + fmt = 'hyper::method::Method::Extension("%s")' + if name in HTTP_METHODS: + name = name.capitalize() + fmt = 'hyper::method::Method::%s' + return fmt % name.capitalize() # given a rust type-name (no optional, as from to_rust_type), you will get a suitable random default value # as string suitable to be passed as reference (or copy, where applicable) diff --git a/src/rust/cmn.rs b/src/rust/cmn.rs index 33b85d6f126..5b49e8b5d15 100644 --- a/src/rust/cmn.rs +++ b/src/rust/cmn.rs @@ -6,6 +6,7 @@ use oauth2; use hyper; use hyper::header::{ContentType, ContentLength, Headers}; use hyper::http::LINE_ENDING; +use hyper::method::Method; /// Identifies the Hub. There is only one per library, this trait is supposed /// to make intended use more explicit. @@ -56,6 +57,14 @@ pub struct JsonServerError { /// uploading media pub trait Delegate { + /// Called at the beginning of any API request. The delegate should store the method + /// information if he is interesting in knowing more context when further calls to it + /// are made. + /// The matching `finished()` call will always be made, no matter whether or not the API + /// request was sucessfull. That way, the delgate may easily maintain a clean state + /// between various API calls. + fn begin(&mut self, MethodInfo) {} + /// Called whenever there is an [HttpError](http://hyperium.github.io/hyper/hyper/error/enum.HttpError.html), usually if there are network problems. /// /// Return retry information. @@ -88,7 +97,14 @@ pub trait Delegate { /// Called prior to sending the main request of the given method. It can be used to time /// the call or to print progress information. - fn pre_request(&mut self, method_name: &str) { let _ = method_name; } + /// It's also useful as you can be sure that a request will definitely be made. + fn pre_request(&mut self) { } + + + /// Called before the API request method returns, in every case. It can be used to clean up + /// internal state between calls to the API. + /// This call always has a matching call to `begin(...)`. + fn finished(&mut self) {} } #[derive(Default)] @@ -119,6 +135,12 @@ pub enum Result { Success(T), } +/// Contains information about an API request. +pub struct MethodInfo { + pub id: &'static str, + pub http_method: Method, +} + const BOUNDARY: &'static str = "MDuXWGyeE33QFXGchb2VFWc4Z7945d"; /// Provides a `Read` interface that converts multiple parts into the protocol