mirror of
https://github.com/OMGeeky/google-apis-rs.git
synced 2026-02-23 15:49:49 +01:00
feat(doit): initial part writing
We are a state-machine, and handle parts of it correctly. However, we don't yet write the boundary at all, and could improve our use of match.
This commit is contained in:
@@ -21,6 +21,3 @@ mime = "*"
|
||||
url = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-oauth2 = "*"
|
||||
|
||||
[dev-dependencies]
|
||||
yup-hyper-mock = "*"
|
||||
|
||||
@@ -619,9 +619,9 @@ else {
|
||||
let mut body_reader: &mut io::Read = match ${simple_media_param.type.arg_name}.as_mut() {
|
||||
Some(&mut (ref mut reader, size, ref mime)) => {
|
||||
let rsize = request_value_reader.seek(io::SeekFrom::End(0)).unwrap();
|
||||
request_value_reader.seek(io::SeekFrom::Start(0)).ok();
|
||||
mp_reader = mp_reader.add_part(&mut request_value_reader, rsize, &json_mime_type)
|
||||
.add_part(reader, size, mime);
|
||||
request_value_reader.seek(io::SeekFrom::Start(0)).unwrap();
|
||||
mp_reader.add_part(&mut request_value_reader, rsize, &json_mime_type)
|
||||
.add_part(reader, size, mime);
|
||||
content_type = ContentType(mp_reader.mime_type());
|
||||
&mut mp_reader
|
||||
}
|
||||
@@ -675,7 +675,7 @@ else {
|
||||
if !res.status.is_success() {
|
||||
if ${delegate}.is_some() {
|
||||
let mut json_err = String::new();
|
||||
res.read_to_string(&mut json_err).ok();
|
||||
res.read_to_string(&mut json_err).unwrap();
|
||||
let error_info: cmn::JsonServerError = json::decode(&json_err).unwrap();
|
||||
if let oauth2::Retry::After(d) = ${delegate_call}.http_failure(&res, error_info) {
|
||||
sleep(d);
|
||||
@@ -686,7 +686,7 @@ else {
|
||||
}
|
||||
% if response_schema:
|
||||
let mut json_response = String::new();
|
||||
res.read_to_string(&mut json_response).ok();
|
||||
res.read_to_string(&mut json_response).unwrap();
|
||||
let result_value = (res, json::decode(&json_response).unwrap());
|
||||
% else:
|
||||
let result_value = res;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use std::marker::MarkerTrait;
|
||||
use std::io::{self, Read, Seek, Cursor};
|
||||
use std::io::{self, Read, Seek, Cursor, Write, SeekFrom};
|
||||
|
||||
use mime::{Mime, TopLevel, SubLevel, Attr, Value};
|
||||
use oauth2;
|
||||
use hyper;
|
||||
use hyper::header::{ContentType, ContentLength, Headers};
|
||||
use hyper::http::LINE_ENDING;
|
||||
|
||||
/// Identifies the Hub. There is only one per library, this trait is supposed
|
||||
/// to make intended use more explicit.
|
||||
@@ -145,7 +146,7 @@ impl<'a> MultiPartReader<'a> {
|
||||
/// # 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) -> MultiPartReader<'a> {
|
||||
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.clone()));
|
||||
headers.set(ContentLength(size));
|
||||
@@ -166,6 +167,36 @@ impl<'a> MultiPartReader<'a> {
|
||||
|
||||
impl<'a> Read for MultiPartReader<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
Err(io::Error::from_os_error(0))
|
||||
if self.raw_parts.len() == 0 && self.current_part.is_none() {
|
||||
return Ok(0)
|
||||
} else if self.raw_parts.len() > 0 && self.current_part.is_none() {
|
||||
let (headers, reader) = self.raw_parts.remove(0);
|
||||
let mut c = Cursor::new(Vec::<u8>::new());
|
||||
write!(&mut c, "{}{}", 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 bytes_read == 0 {
|
||||
// We are depleted - this can trigger the next part to come in
|
||||
self.current_part = None;
|
||||
}
|
||||
Ok(hb + bytes_read)
|
||||
}
|
||||
Err(err) => {
|
||||
// fail permanently
|
||||
self.current_part = None;
|
||||
self.raw_parts.clear();
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/// Identifies the an OAuth2 authorization scope.
|
||||
/// A scope is needed when requesting an
|
||||
/// [authorization token](https://developers.google.com/youtube/v3/guides/authentication).
|
||||
pub enum Scope {
|
||||
/// Manage your YouTube account
|
||||
Account,
|
||||
/// View your YouTube account
|
||||
AccountReadOnly,
|
||||
/// Manage your YouTube videos, which includes uploads and meta-data changes
|
||||
Video,
|
||||
/// View and manage your assets and associated content on YouTube
|
||||
Partner,
|
||||
/// View private information of your YouTube channel relevant during the
|
||||
/// audit process with a YouTube partner.
|
||||
Audit,
|
||||
}
|
||||
|
||||
impl Str for Scope {
|
||||
fn as_slice(&self) -> &str {
|
||||
match *self {
|
||||
Scope::Account => "https://www.googleapis.com/auth/youtube",
|
||||
Scope::AccountReadOnly => "https://www.googleapis.com/auth/youtube.readonly",
|
||||
Scope::Video => "https://www.googleapis.com/auth/youtube.upload",
|
||||
Scope::Partner => "https://www.googleapis.com/auth/youtubepartner",
|
||||
Scope::Audit => "https://www.googleapis.com/auth/youtubepartner-channel-audit",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
#![feature(core)]
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! ```test_harness
|
||||
//! extern crate "youtube3-dev" as youtube3;
|
||||
//! extern crate hyper;
|
||||
//!
|
||||
//! # #[test]
|
||||
//! # fn test() {
|
||||
//! # // TODO - generate !
|
||||
//! # }
|
||||
//! ```
|
||||
use std::marker::PhantomData;
|
||||
use std::cell::RefCell;
|
||||
use std::borrow::BorrowMut;
|
||||
use std::default::Default;
|
||||
|
||||
use hyper;
|
||||
use oauth2;
|
||||
|
||||
mod common;
|
||||
pub mod videos;
|
||||
|
||||
|
||||
/// Central instance to access all youtube related services
|
||||
pub struct YouTube<C, NC, A> {
|
||||
client: RefCell<C>,
|
||||
auth: RefCell<A>,
|
||||
_m: PhantomData<NC>
|
||||
}
|
||||
|
||||
impl<'a, C, NC, A> YouTube<C, NC, A>
|
||||
where NC: hyper::net::NetworkConnector,
|
||||
C: BorrowMut<hyper::Client<NC>> + 'a,
|
||||
A: oauth2::GetToken {
|
||||
|
||||
pub fn new(client: C, authenticator: A) -> YouTube<C, NC, A> {
|
||||
YouTube {
|
||||
client: RefCell::new(client),
|
||||
auth: RefCell::new(authenticator),
|
||||
_m: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn videos(&'a self) -> videos::Service<'a, C, NC, A> {
|
||||
videos::Service::new(&self)
|
||||
}
|
||||
|
||||
pub fn channel_sections(&'a self) -> ChannelSectionMethodsBuilder<'a, C, NC, A> {
|
||||
ChannelSectionMethodsBuilder { hub: &self }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn new<C, NC, A>(client: C, authenticator: A) -> YouTube<C, NC, A>
|
||||
where NC: hyper::net::NetworkConnector,
|
||||
C: BorrowMut<hyper::Client<NC>>,
|
||||
A: oauth2::GetToken {
|
||||
YouTube::new(client, authenticator)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use hyper;
|
||||
use oauth2;
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
|
||||
#[test]
|
||||
fn instantiate() {
|
||||
let secret = <oauth2::ApplicationSecret as Default>::default();
|
||||
let auth = oauth2::Authenticator::new(
|
||||
&secret,
|
||||
oauth2::DefaultAuthenticatorDelegate,
|
||||
hyper::Client::new(),
|
||||
<oauth2::MemoryStorage as Default>::default(),
|
||||
None);
|
||||
let yt = YouTube::new(hyper::Client::new(), auth);
|
||||
|
||||
let v = yt.videos().insert("snippet", &Default::default());
|
||||
}
|
||||
|
||||
#[test] fn helper_test() {
|
||||
use std::default::Default;
|
||||
use oauth2::{Authenticator, DefaultAuthenticatorDelegate, ApplicationSecret, MemoryStorage};
|
||||
|
||||
let secret: ApplicationSecret = Default::default();
|
||||
let auth = Authenticator::new(&secret, DefaultAuthenticatorDelegate,
|
||||
hyper::Client::new(),
|
||||
<MemoryStorage as Default>::default(), None);
|
||||
let mut hub = YouTube::new(hyper::Client::new(), auth);
|
||||
let result = hub.channel_sections().insert()
|
||||
.delegate(&mut DefaultDelegate)
|
||||
.doit();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChannelSectionMethodsBuilder<'a, C, NC, A>
|
||||
where NC: 'a,
|
||||
C: 'a,
|
||||
A: 'a, {
|
||||
|
||||
hub: &'a YouTube<C, NC, A>,
|
||||
}
|
||||
|
||||
impl<'a, C, NC, A> ChannelSectionMethodsBuilder<'a, C, NC, A> {
|
||||
|
||||
/// Create a builder to help you perform the following task:
|
||||
///
|
||||
/// Adds a channelSection for the authenticated user's channel.
|
||||
pub fn insert(&self) -> ChannelSectionInsertMethodBuilder<'a, C, NC, A> {
|
||||
ChannelSectionInsertMethodBuilder {
|
||||
hub: self.hub,
|
||||
_delegate: Default::default(),
|
||||
_part: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChannelSectionInsertMethodBuilder<'a, C, NC, A>
|
||||
where NC: 'a,
|
||||
C: 'a,
|
||||
A: 'a, {
|
||||
|
||||
hub: &'a YouTube<C, NC, A>,
|
||||
_delegate: Option<&'a mut Delegate>,
|
||||
_part: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
impl<'a, C, NC, A> ChannelSectionInsertMethodBuilder<'a, C, NC, A> where NC: hyper::net::NetworkConnector, C: BorrowMut<hyper::Client<NC>> + 'a, A: oauth2::GetToken {
|
||||
|
||||
/// Perform the operation you have build so far.
|
||||
/// TODO: Build actual call
|
||||
pub fn doit(mut self) -> () {
|
||||
let mut params: Vec<(&str, String)> = Vec::with_capacity(1);
|
||||
if self._part.is_none() {
|
||||
self._part = Some("parts from request value".to_string());
|
||||
}
|
||||
if self._delegate.is_some() {
|
||||
self._delegate.as_mut().unwrap().connection_error(hyper::HttpError::HttpStatusError);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delegate(mut self, new_value: &'a mut Delegate) -> ChannelSectionInsertMethodBuilder<'a, C, NC, A> {
|
||||
self._delegate = Some(new_value);
|
||||
self
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub trait Delegate {
|
||||
|
||||
/// Called whenever there is an HttpError, usually if there are network problems.
|
||||
///
|
||||
/// Return retry information.
|
||||
fn connection_error(&mut self, hyper::HttpError) -> oauth2::Retry {
|
||||
oauth2::Retry::Abort
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DefaultDelegate;
|
||||
|
||||
impl Delegate for DefaultDelegate {}
|
||||
@@ -1,3 +0,0 @@
|
||||
pub use self::service::*;
|
||||
|
||||
mod service;
|
||||
@@ -1,126 +0,0 @@
|
||||
use std::cell::RefCell;
|
||||
use std::borrow::BorrowMut;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rustc_serialize;
|
||||
|
||||
use hyper;
|
||||
use oauth2;
|
||||
|
||||
use super::super::YouTube;
|
||||
|
||||
/// Reresents all aspects of a youtube video resource. May only be partially
|
||||
/// available
|
||||
#[derive(RustcEncodable, RustcDecodable, Default, Clone)]
|
||||
pub struct Video {
|
||||
pub snippet: Option<VideoSnippet>,
|
||||
pub recordingDetails: Option<VideoRecordingDetails>,
|
||||
pub status: Option<VideoStatus>,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(RustcEncodable, RustcDecodable, Default, Clone)]
|
||||
pub struct VideoSnippet {
|
||||
pub categoryId: String,
|
||||
pub description: String,
|
||||
pub tags: Vec<String>,
|
||||
pub title: String,
|
||||
|
||||
pub status: Option<VideoStatus>,
|
||||
pub recordingDetails: Option<VideoRecordingDetails>,
|
||||
}
|
||||
|
||||
impl Video {
|
||||
fn parts(&self) -> String {
|
||||
let mut res = String::new();
|
||||
if self.status.is_some() {
|
||||
res = res + "status,";
|
||||
}
|
||||
if self.recordingDetails.is_some() {
|
||||
res = res + "recordingDetails";
|
||||
}
|
||||
if self.snippet.is_some() {
|
||||
res = res + "snippet,";
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(RustcEncodable, RustcDecodable, Default, Clone)]
|
||||
pub struct VideoStatus {
|
||||
pub privacyStatus: String,
|
||||
pub embeddable: bool,
|
||||
pub license: String,
|
||||
pub publicStatsViewable: bool,
|
||||
pub publishAt: String,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(RustcEncodable, RustcDecodable, Default, Clone)]
|
||||
pub struct VideoRecordingDetails {
|
||||
locationDescription: String,
|
||||
recordingDate: String,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(RustcEncodable, RustcDecodable, Default, Clone)]
|
||||
pub struct GeoPoint {
|
||||
altitude: f64,
|
||||
latitude: f64,
|
||||
longitude: f64,
|
||||
}
|
||||
|
||||
/// The videos service - provides actual functionality through builders.
|
||||
pub struct Service<'a, C, NC, A>
|
||||
where NC: 'a,
|
||||
C: 'a,
|
||||
A: 'a, {
|
||||
|
||||
hub: &'a YouTube<C, NC, A>
|
||||
}
|
||||
|
||||
impl<'a, C, NC, A> Service<'a, C, NC, A>
|
||||
where NC: hyper::net::NetworkConnector,
|
||||
C: BorrowMut<hyper::Client<NC>> + 'a,
|
||||
A: oauth2::GetToken + 'a {
|
||||
|
||||
pub fn new(hub: &'a YouTube<C, NC, A>) -> Service<'a, C, NC, A> {
|
||||
Service { hub: hub }
|
||||
}
|
||||
|
||||
pub fn insert(&self, parts: &str, video: &Video) -> VideosInsertBuilder<'a, C, NC, A> {
|
||||
VideosInsertBuilder {
|
||||
hub: self.hub,
|
||||
video: video.clone(),
|
||||
parts: parts.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VideosInsertBuilder<'a, C, NC, A>
|
||||
where NC: 'a,
|
||||
C: 'a,
|
||||
A: 'a {
|
||||
|
||||
hub: &'a YouTube<C, NC, A>,
|
||||
video: Video,
|
||||
parts: String,
|
||||
}
|
||||
|
||||
|
||||
impl<'a, C, NC, A> VideosInsertBuilder<'a, C, NC, A>
|
||||
where NC: hyper::net::NetworkConnector,
|
||||
C: BorrowMut<hyper::Client<NC>> + 'a,
|
||||
A: oauth2::GetToken + 'a {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::default::Default;
|
||||
use super::*;
|
||||
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
#![feature(core,io)]
|
||||
#![allow(dead_code)]
|
||||
//! library with code shared by all generated implementations
|
||||
extern crate hyper;
|
||||
extern crate mime;
|
||||
extern crate "rustc-serialize" as rustc_serialize;
|
||||
extern crate "yup-oauth2" as oauth2;
|
||||
|
||||
@@ -9,4 +11,26 @@ mod cmn;
|
||||
|
||||
/// This module is for testing only, its code is used in mako templates
|
||||
#[cfg(test)]
|
||||
mod dev;
|
||||
mod tests {
|
||||
extern crate "yup-hyper-mock" as hyper_mock;
|
||||
use super::cmn::*;
|
||||
use self::hyper_mock::*;
|
||||
use std::io::Read;
|
||||
use std::default::Default;
|
||||
|
||||
|
||||
#[test]
|
||||
fn multi_part_reader() {
|
||||
let mut r1 = MockStream::with_input(b"foo");
|
||||
let mut r2 = MockStream::with_input(b"bar");
|
||||
let mut mpr: MultiPartReader = Default::default();
|
||||
|
||||
mpr.add_part(&mut r1, 50, &"application/json".parse().unwrap())
|
||||
.add_part(&mut r2, 25, &"application/plain".parse().unwrap());
|
||||
|
||||
let mut res = String::new();
|
||||
assert_eq!(mpr.read_to_string(&mut res), Ok(57));
|
||||
println!("{}", res);
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user