From 71c827b3067131a150bfd4a3503a61b836ec39b5 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 18 Mar 2015 17:53:39 +0100 Subject: [PATCH] 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. --- Cargo.toml | 1 + src/mako/Cargo.toml.mako | 3 - src/mako/lib/mbuild.mako | 10 +- src/rust/cmn.rs | 37 +++++++- src/rust/dev/common.rs | 28 ------ src/rust/dev/mod.rs | 168 --------------------------------- src/rust/dev/videos/mod.rs | 3 - src/rust/dev/videos/service.rs | 126 ------------------------- src/rust/lib.rs | 26 ++++- 9 files changed, 65 insertions(+), 337 deletions(-) delete mode 100644 src/rust/dev/common.rs delete mode 100644 src/rust/dev/mod.rs delete mode 100644 src/rust/dev/videos/mod.rs delete mode 100644 src/rust/dev/videos/service.rs diff --git a/Cargo.toml b/Cargo.toml index 4a5418ba795..5066b656628 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ path = "src/rust/lib.rs" [dependencies] hyper = "*" +mime = "*" rustc-serialize = "*" yup-oauth2 = "*" diff --git a/src/mako/Cargo.toml.mako b/src/mako/Cargo.toml.mako index 75fae29399e..c21704d78b3 100644 --- a/src/mako/Cargo.toml.mako +++ b/src/mako/Cargo.toml.mako @@ -21,6 +21,3 @@ mime = "*" url = "*" rustc-serialize = "*" yup-oauth2 = "*" - -[dev-dependencies] -yup-hyper-mock = "*" diff --git a/src/mako/lib/mbuild.mako b/src/mako/lib/mbuild.mako index 54fc64ce38e..70dd23cfb0d 100644 --- a/src/mako/lib/mbuild.mako +++ b/src/mako/lib/mbuild.mako @@ -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; diff --git a/src/rust/cmn.rs b/src/rust/cmn.rs index cbc4a86c911..4d2158139a0 100644 --- a/src/rust/cmn.rs +++ b/src/rust/cmn.rs @@ -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 { - 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::::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) + } + } } } \ No newline at end of file diff --git a/src/rust/dev/common.rs b/src/rust/dev/common.rs deleted file mode 100644 index 13ad359ae3d..00000000000 --- a/src/rust/dev/common.rs +++ /dev/null @@ -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", - } - } -} \ No newline at end of file diff --git a/src/rust/dev/mod.rs b/src/rust/dev/mod.rs deleted file mode 100644 index 5fb19193cb5..00000000000 --- a/src/rust/dev/mod.rs +++ /dev/null @@ -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 { - client: RefCell, - auth: RefCell, - _m: PhantomData -} - -impl<'a, C, NC, A> YouTube - where NC: hyper::net::NetworkConnector, - C: BorrowMut> + 'a, - A: oauth2::GetToken { - - pub fn new(client: C, authenticator: A) -> YouTube { - 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(client: C, authenticator: A) -> YouTube - where NC: hyper::net::NetworkConnector, - C: BorrowMut>, - 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 = ::default(); - let auth = oauth2::Authenticator::new( - &secret, - oauth2::DefaultAuthenticatorDelegate, - hyper::Client::new(), - ::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(), - ::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, -} - -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, - _delegate: Option<&'a mut Delegate>, - _part: Option, -} - - -impl<'a, C, NC, A> ChannelSectionInsertMethodBuilder<'a, C, NC, A> where NC: hyper::net::NetworkConnector, C: BorrowMut> + '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 {} diff --git a/src/rust/dev/videos/mod.rs b/src/rust/dev/videos/mod.rs deleted file mode 100644 index 86790a92ca4..00000000000 --- a/src/rust/dev/videos/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub use self::service::*; - -mod service; \ No newline at end of file diff --git a/src/rust/dev/videos/service.rs b/src/rust/dev/videos/service.rs deleted file mode 100644 index 176e6b70860..00000000000 --- a/src/rust/dev/videos/service.rs +++ /dev/null @@ -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, - pub recordingDetails: Option, - pub status: Option, -} - -#[allow(non_snake_case)] -#[derive(RustcEncodable, RustcDecodable, Default, Clone)] -pub struct VideoSnippet { - pub categoryId: String, - pub description: String, - pub tags: Vec, - pub title: String, - - pub status: Option, - pub recordingDetails: Option, -} - -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 -} - -impl<'a, C, NC, A> Service<'a, C, NC, A> - where NC: hyper::net::NetworkConnector, - C: BorrowMut> + 'a, - A: oauth2::GetToken + 'a { - - pub fn new(hub: &'a YouTube) -> 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, - video: Video, - parts: String, -} - - -impl<'a, C, NC, A> VideosInsertBuilder<'a, C, NC, A> - where NC: hyper::net::NetworkConnector, - C: BorrowMut> + 'a, - A: oauth2::GetToken + 'a { - -} - - - -#[cfg(test)] -mod tests { - use std::default::Default; - use super::*; - -} \ No newline at end of file diff --git a/src/rust/lib.rs b/src/rust/lib.rs index 37d32a636f2..a0ed54e18cb 100644 --- a/src/rust/lib.rs +++ b/src/rust/lib.rs @@ -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; \ No newline at end of file +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); + } +} \ No newline at end of file