mirror of
https://github.com/OMGeeky/google-apis-rs.git
synced 2026-02-23 15:49:49 +01:00
* do not emit unused types. Sometimes though, rustc doesn't seem to detect that attributses are actually used * ToParts trait is used and implemented only when needed. Linters are back to 'normal'. Fixes #35
293 lines
11 KiB
Rust
293 lines
11 KiB
Rust
use std::marker::MarkerTrait;
|
|
use std::io::{self, Read, Seek, Cursor, Write, SeekFrom};
|
|
use std;
|
|
|
|
use mime::{Mime, TopLevel, SubLevel, Attr, Value};
|
|
use oauth2;
|
|
use hyper;
|
|
use hyper::header::{ContentType, ContentLength, Headers};
|
|
use hyper::http::LINE_ENDING;
|
|
use hyper::method::Method;
|
|
|
|
use serde;
|
|
|
|
/// Identifies the Hub. There is only one per library, this trait is supposed
|
|
/// to make intended use more explicit.
|
|
/// The hub allows to access all resource methods more easily.
|
|
pub trait Hub: MarkerTrait {}
|
|
|
|
/// Identifies types for building methods of a particular resource type
|
|
pub trait ResourceMethodsBuilder: MarkerTrait {}
|
|
|
|
/// Identifies types which represent builders for a particular resource method
|
|
pub trait CallBuilder: MarkerTrait {}
|
|
|
|
/// Identifies types which can be inserted and deleted.
|
|
/// Types with this trait are most commonly used by clients of this API.
|
|
pub trait Resource: MarkerTrait {}
|
|
|
|
/// Identifies types which are used in API responses.
|
|
pub trait ResponseResult: MarkerTrait {}
|
|
|
|
/// Identifies types which are used in API requests.
|
|
pub trait RequestValue: MarkerTrait {}
|
|
|
|
/// Identifies types which are not actually used by the API
|
|
/// This might be a bug within the google API schema.
|
|
pub trait UnusedType: MarkerTrait {}
|
|
|
|
/// Identifies types which are only used as part of other types, which
|
|
/// usually are carrying the `Resource` trait.
|
|
pub trait Part: MarkerTrait {}
|
|
|
|
/// Identifies types which are only used by other types internally.
|
|
/// They have no special meaning, this trait just marks them for completeness.
|
|
pub trait NestedType: MarkerTrait {}
|
|
|
|
/// A utility to specify reader types which provide seeking capabilities too
|
|
pub trait ReadSeek: Seek + Read {}
|
|
impl<T: Seek + Read> ReadSeek for T {}
|
|
|
|
/// A trait for all types that can convert themselves into a *parts* string
|
|
pub trait ToParts {
|
|
fn to_parts(&self) -> String;
|
|
}
|
|
|
|
|
|
/// A utility type which can decode a server response that indicates error
|
|
#[derive(Deserialize)]
|
|
pub struct JsonServerError {
|
|
pub error: String,
|
|
pub error_description: Option<String>
|
|
}
|
|
|
|
|
|
/// A trait specifying functionality to help controlling any request performed by the API.
|
|
/// The trait has a conservative default implementation.
|
|
///
|
|
/// It contains methods to deal with all common issues, as well with the ones related to
|
|
/// 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.
|
|
fn http_error(&mut self, &hyper::HttpError) -> oauth2::Retry {
|
|
oauth2::Retry::Abort
|
|
}
|
|
|
|
/// Called whenever there is the need for your applications API key after
|
|
/// the official authenticator implementation didn't provide one, for some reason.
|
|
/// If this method returns None as well, the underlying operation will fail
|
|
fn api_key(&mut self) -> Option<String> {
|
|
None
|
|
}
|
|
|
|
/// Called whenever the Authenticator didn't yield a token. The delegate
|
|
/// may attempt to provide one, or just take is a general information about the
|
|
/// pending impending failure
|
|
fn token(&mut self) -> Option<oauth2::Token> {
|
|
None
|
|
}
|
|
|
|
/// Called whenever a server response could not be decoded from json.
|
|
/// It's for informational purposes only, the caller will return with an error
|
|
/// accordingly.
|
|
/// # Arguments
|
|
/// `&str` - The json-encoded value which failed to decode.
|
|
/// `Error` - The decoder error
|
|
fn response_json_decode_error(&mut self, _: &str, _: &serde::json::Error) {}
|
|
|
|
/// Called whenever the http request returns with a non-success status code.
|
|
/// This can involve authentication issues, or anything else that very much
|
|
/// depends on the used API method.
|
|
/// The delegate should check the status, header and decoded json error to decide
|
|
/// whether to retry or not. In the latter case, the underlying call will fail.
|
|
fn http_failure(&mut self, _: &hyper::client::Response, JsonServerError) -> oauth2::Retry {
|
|
oauth2::Retry::Abort
|
|
}
|
|
|
|
/// Called prior to sending the main request of the given method. It can be used to time
|
|
/// the call or to print progress information.
|
|
/// 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) {}
|
|
}
|
|
|
|
/// A delegate with a conservative default implementation, which is used if no other delegate is
|
|
/// set.
|
|
#[derive(Default)]
|
|
pub struct DefaultDelegate;
|
|
|
|
impl Delegate for DefaultDelegate {}
|
|
|
|
|
|
/// A universal result type used as return for all action method results.
|
|
pub enum Result<T = ()> {
|
|
/// The http connection failed
|
|
HttpError(hyper::HttpError),
|
|
|
|
/// We needed an API key for authentication, but didn't obtain one.
|
|
/// Neither through the authenticator, nor through the Delegate.
|
|
MissingAPIKey,
|
|
|
|
/// We required a Token, but didn't get one from the Authenticator
|
|
MissingToken,
|
|
|
|
/// An additional, free form field clashed with one of the built-in optional ones
|
|
FieldClash(&'static str),
|
|
|
|
/// Shows that we failed to decode the server response.
|
|
/// This can happen if the protocol changes in conjunction with strict json decoding.
|
|
JsonDecodeError(serde::json::Error),
|
|
|
|
/// Indicates an HTTP repsonse with a non-success status code
|
|
Failure(hyper::client::Response),
|
|
|
|
/// It worked !
|
|
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
|
|
/// identified by [RFC2387](https://tools.ietf.org/html/rfc2387).
|
|
/// **Note**: This implementation is just as rich as it needs to be to perform uploads
|
|
/// to google APIs, and might not be a fully-featured implementation.
|
|
#[derive(Default)]
|
|
pub struct MultiPartReader<'a> {
|
|
raw_parts: Vec<(Headers, &'a mut Read)>,
|
|
current_part: Option<(Cursor<Vec<u8>>, &'a mut Read)>,
|
|
last_part_boundary: Option<Cursor<Vec<u8>>>,
|
|
}
|
|
|
|
impl<'a> MultiPartReader<'a> {
|
|
|
|
/// Reserve memory for exactly the given amount of parts
|
|
pub fn reserve_exact(&mut self, cap: usize) {
|
|
self.raw_parts.reserve_exact(cap);
|
|
}
|
|
|
|
/// Add a new part to the queue of parts to be read on the first `read` call.
|
|
///
|
|
/// # Arguments
|
|
/// `headers` - identifying the body of the part. It's similar to the header
|
|
/// in an ordinary single-part call, and should thus contain the
|
|
/// same information.
|
|
/// `reader` - a reader providing the part's body
|
|
/// `size` - the amount of bytes provided by the reader. It will be put onto the header as
|
|
/// content-size.
|
|
/// `mime` - It will be put onto the content type
|
|
/// # Panics
|
|
///
|
|
/// If this method is called after the first `read` call, it will panic
|
|
pub fn add_part(&mut self, reader: &'a mut Read, size: u64, mime_type: Mime) -> &mut MultiPartReader<'a> {
|
|
let mut headers = Headers::new();
|
|
headers.set(ContentType(mime_type));
|
|
headers.set(ContentLength(size));
|
|
self.raw_parts.push((headers, reader));
|
|
self
|
|
}
|
|
|
|
/// Returns the mime-type representing our multi-part message.
|
|
/// Use it with the ContentType header.
|
|
pub fn mime_type(&self) -> Mime {
|
|
Mime(
|
|
TopLevel::Multipart,
|
|
SubLevel::Ext("Related".to_string()),
|
|
vec![(Attr::Ext("boundary".to_string()), Value::Ext(BOUNDARY.to_string()))],
|
|
)
|
|
}
|
|
|
|
/// Returns true if we are totally used
|
|
fn is_depleted(&self) -> bool {
|
|
self.raw_parts.len() == 0 && self.current_part.is_none() && self.last_part_boundary.is_none()
|
|
}
|
|
|
|
/// Returns true if we are handling our last part
|
|
fn is_last_part(&self) -> bool {
|
|
self.raw_parts.len() == 0 && self.current_part.is_some()
|
|
}
|
|
}
|
|
|
|
impl<'a> Read for MultiPartReader<'a> {
|
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
match (self.raw_parts.len(),
|
|
self.current_part.is_none(),
|
|
self.last_part_boundary.is_none()) {
|
|
(_, _, false) => {
|
|
let br = self.last_part_boundary.as_mut().unwrap().read(buf).unwrap_or(0);
|
|
if br < buf.len() {
|
|
self.last_part_boundary = None;
|
|
}
|
|
return Ok(br)
|
|
},
|
|
(0, true, true) => return Ok(0),
|
|
(n, true, _) if n > 0 => {
|
|
let (headers, reader) = self.raw_parts.remove(0);
|
|
let mut c = Cursor::new(Vec::<u8>::new());
|
|
write!(&mut c, "{}--{}{}{}{}", LINE_ENDING, BOUNDARY, LINE_ENDING,
|
|
headers, LINE_ENDING).unwrap();
|
|
c.seek(SeekFrom::Start(0)).unwrap();
|
|
self.current_part = Some((c, reader));
|
|
}
|
|
_ => {},
|
|
}
|
|
|
|
// read headers as long as possible
|
|
let (hb, rr) = {
|
|
let &mut (ref mut c, ref mut reader) = self.current_part.as_mut().unwrap();
|
|
let b = c.read(buf).unwrap_or(0);
|
|
(b, reader.read(&mut buf[b..]))
|
|
};
|
|
|
|
match rr {
|
|
Ok(bytes_read) => {
|
|
if hb < buf.len() && bytes_read == 0 {
|
|
if self.is_last_part() {
|
|
// before clearing the last part, we will add the boundary that
|
|
// will be written last
|
|
self.last_part_boundary = Some(Cursor::new(
|
|
format!("{}--{}", LINE_ENDING, BOUNDARY).into_bytes()))
|
|
}
|
|
// We are depleted - this can trigger the next part to come in
|
|
self.current_part = None;
|
|
}
|
|
let mut total_bytes_read = hb + bytes_read;
|
|
while total_bytes_read < buf.len() && !self.is_depleted() {
|
|
match self.read(&mut buf[total_bytes_read ..]) {
|
|
Ok(br) => total_bytes_read += br,
|
|
Err(err) => return Err(err),
|
|
}
|
|
}
|
|
Ok(total_bytes_read)
|
|
}
|
|
Err(err) => {
|
|
// fail permanently
|
|
self.current_part = None;
|
|
self.last_part_boundary = None;
|
|
self.raw_parts.clear();
|
|
Err(err)
|
|
}
|
|
}
|
|
}
|
|
} |