<commit_before>
extern crate gl;
extern crate glutin;
extern crate png;
extern crate cable_math;
extern crate rusttype;

mod color;
mod matrix_stack;
mod input;

pub mod texture;
#[macro_use]
pub mod shader;
pub mod buffer;
pub mod util;
pub mod framebuffer;
pub mod font;
pub mod ui;

pub use color::*;
pub use input::*;
pub use matrix_stack::*;
pub use util::graphics;

use cable_math::Vec2;
use std::time::{Instant, Duration};
use std::sync::mpsc;

/// The most generic result type possible. Used in top-level
pub type GameResult<T> = Result<T, Box<std::error::Error>>;

/// Creates a new window and runs the given game in this window. This function does
/// not return until the game exits.
///
/// # Example
/// ```rust,no_run
/// extern crate gondola;
///
/// use gondola::{Game, GameResult, GameState, MatrixStack};
///
/// fn main() {
///     gondola::run::<Pong>();
/// }
///
/// struct Pong {
///     // All data neded for game is defined here
/// }
///
/// impl Game for Pong {
///     fn setup(state: &mut GameState) -> GameResult<Pong> {
///         Ok(Pong {})
///     }
///
///     fn update(&mut self, delta: u32, state: &mut GameState) {
///         // All logic goes here.
///     }
///
///     fn draw(&mut self, state: &GameState, mut mat_stack: &mut MatrixStack) {
///         // All rendering goes here
///     }
/// }
/// ```
pub fn run<T: Game + Sized>() {
    // Create window
    let window = glutin::Window::new().unwrap();
    unsafe {
        window.make_current().unwrap();
        gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
    }
    window.set_title(T::name());

    // Set up game state
    let mut state = GameState::new();
    state.win_size = {
        let (width, height) = window.get_inner_size_pixels().unwrap();
        Vec2::new(width, height)
    };
    graphics::viewport(0, 0, state.win_size.x, state.win_size.y);

    let mut mat_stack = MatrixStack::new();

    // Set up game
    let mut game = match T::setup(&mut state) {
        Err(err) => {
            println!("Failed to launch game:\n{}", err);
            panic!();
        },
        Ok(game) => game,
    };

    let mut delta: u64 = 16;

    'main_loop:
    loop {
        let start_time = Instant::now();

        // Events
        for event in window.poll_events() {
            match event {
                glutin::Event::Closed => break 'main_loop,
                glutin::Event::Resized(..) => {
                    let (width, height) = window.get_inner_size_pixels().unwrap();
                    if width != 0 && height != 0 {
                        state.win_size = Vec2::new(width, height);
                        graphics::viewport(0, 0, state.win_size.x, state.win_size.y);
                        game.on_resize(&state);
                    }
                }
                other => {
                    // Send events to receivers, and remove those which are unable to receive.
                    state.event_sinks.retain(|sink| {
                        sink.send(other.clone()).is_ok()
                    });
                },
            }
        }

        // Logic and rendering
        game.update(delta as u32, &mut state);
        game.draw(&state, &mut mat_stack);
        window.swap_buffers().unwrap();
        graphics::print_errors();

        if state.exit {
            break 'main_loop;
        }

        // Timing
        let elapsed = start_time.elapsed();
        let target_delta = Duration::from_millis(state.target_delta as u64);
        if state.target_delta > 0 && elapsed < target_delta {
            std::thread::sleep(target_delta - elapsed); // This is not very precice :/
        }
        let delta_dur = start_time.elapsed();
        delta = delta_dur.as_secs()*1000 + (delta_dur.subsec_nanos() as u64)/1000000;
    }

    game.close();
}

/// General info about the currently running game. Passed as a parameter to
/// most [`Game`](trait.Game.html) methods.
pub struct GameState {
    /// The size of the window in which this game is running, in pixels.
    pub win_size: Vec2<u32>,
    /// If set to true the game will exit after rendering.
    pub exit: bool,
    /// The number of milliseconds per frame this game should aim to run at. Set to 16
    /// for 60 fps. If the main loop takes less time than this amount the game will
    /// sleep until a total of `target_delta` has ellapsed. If set to `0` the game will
    /// never sleep.
    pub target_delta: u32,

    event_sinks: Vec<mpsc::Sender<glutin::Event>>,
}

/// Used with [`gondola::run`](fn.run.html)
pub trait Game: Sized {
    /// Called before the main loop. Resources and initial state should be set up here.
    fn setup(state: &mut GameState) -> GameResult<Self>;
    /// Called once every frame, before drawing.
    fn update(&mut self, delta: u32, state: &mut GameState);
    /// Called once every frame, after updating.
    fn draw(&mut self, state: &GameState, mat_stack: &mut MatrixStack);

    /// Called whenever the game window is resized
    fn on_resize(&mut self, _state: &GameState) {}
    /// Called after the main game loop exists. This method is not called if the main
    /// loop `panic!`s.
    fn close(&mut self) {} // Most simple games dont need any special logic here

    fn name() -> &'static str { "Unnamed game (Override Game::name to change title)" }
}

impl GameState {
    fn new() -> GameState {
        GameState {
            win_size: Vec2::zero(),
            exit: false,
            target_delta: 15,

            event_sinks: Vec::new(),
        }
    }

    /// Generates a new receiver for glutin events. All events not consumed by the
    /// game itself will be received by this receiver. Note that all receiver receive
    /// all events, there is no notion of event consumption.
    pub fn gen_event_receiver(&mut self) -> mpsc::Receiver<glutin::Event> {
        let (sender, receiver) = mpsc::channel();
        self.event_sinks.push(sender);
        receiver
    }

    /// Creates a new input manager which can be used to access the state of input devices.
    pub fn gen_input_manager(&mut self) -> InputManager {
        let receiver = self.gen_event_receiver();
        InputManager::new(receiver)
    }
}

<commit_msg>Added some library documentation<commit_after>
//! A semi-safe, semi-stateless wrapper around OpenGL 3.3 Core. This library provides various
//! utilities to make using OpenGL 3.3 safer. It uses rust's type system to encode some information
//! which helps prevent common errors. This library is primarily intended to be used for games,
//! but you can also use it to create other graphics applications.
//!
//! Some points to get started:
//!
//!  - Use [`gondola::run`] to launch your game.
//!  - Use a [`VertexBuffer`] to do basic drawing.
//!  - Use [`GameState::gen_input_manager`] to get access to keyboard/mouse state.
//!
//! [`GameState::gen_input_manager`]: struct.GameState.html#method.gen_input_manager
//! [`VertexBuffer`]:                 buffer/struct.VertexBuffer.html
//! [`gondola::run`]:                 fn.run.html

extern crate gl;
extern crate glutin;
extern crate png;
extern crate cable_math;
extern crate rusttype;

mod color;
mod matrix_stack;
mod input;

pub mod texture;
#[macro_use]
pub mod shader;
pub mod buffer;
pub mod util;
pub mod framebuffer;
pub mod font;
pub mod ui;

pub use color::*;
pub use input::*;
pub use matrix_stack::*;
pub use util::graphics;

use cable_math::Vec2;
use std::time::{Instant, Duration};
use std::sync::mpsc;

/// The most generic result type possible. Used in top-level
pub type GameResult<T> = Result<T, Box<std::error::Error>>;

/// Creates a new window and runs the given game in this window. This function does
/// not return until the game exits.
///
/// # Example
/// ```rust,no_run
/// extern crate gondola;
///
/// use gondola::{Game, GameResult, GameState, MatrixStack};
///
/// fn main() {
///     gondola::run::<Pong>();
/// }
///
/// struct Pong {
///     // All data neded for game is defined here
/// }
///
/// impl Game for Pong {
///     fn setup(state: &mut GameState) -> GameResult<Pong> {
///         Ok(Pong {})
///     }
///
///     fn update(&mut self, delta: u32, state: &mut GameState) {
///         // All logic goes here.
///     }
///
///     fn draw(&mut self, state: &GameState, mut mat_stack: &mut MatrixStack) {
///         // All rendering goes here
///     }
/// }
/// ```
pub fn run<T: Game + Sized>() {
    // Create window
    let window = glutin::Window::new().unwrap();
    unsafe {
        window.make_current().unwrap();
        gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
    }
    window.set_title(T::name());

    // Set up game state
    let mut state = GameState::new();
    state.win_size = {
        let (width, height) = window.get_inner_size_pixels().unwrap();
        Vec2::new(width, height)
    };
    graphics::viewport(0, 0, state.win_size.x, state.win_size.y);

    let mut mat_stack = MatrixStack::new();

    // Set up game
    let mut game = match T::setup(&mut state) {
        Err(err) => {
            println!("Failed to launch game:\n{}", err);
            panic!();
        },
        Ok(game) => game,
    };

    let mut delta: u64 = 16;

    'main_loop:
    loop {
        let start_time = Instant::now();

        // Events
        for event in window.poll_events() {
            match event {
                glutin::Event::Closed => break 'main_loop,
                glutin::Event::Resized(..) => {
                    let (width, height) = window.get_inner_size_pixels().unwrap();
                    if width != 0 && height != 0 {
                        state.win_size = Vec2::new(width, height);
                        graphics::viewport(0, 0, state.win_size.x, state.win_size.y);
                        game.on_resize(&state);
                    }
                }
                other => {
                    // Send events to receivers, and remove those which are unable to receive.
                    state.event_sinks.retain(|sink| {
                        sink.send(other.clone()).is_ok()
                    });
                },
            }
        }

        // Logic and rendering
        game.update(delta as u32, &mut state);
        game.draw(&state, &mut mat_stack);
        window.swap_buffers().unwrap();
        graphics::print_errors();

        if state.exit {
            break 'main_loop;
        }

        // Timing
        let elapsed = start_time.elapsed();
        let target_delta = Duration::from_millis(state.target_delta as u64);
        if state.target_delta > 0 && elapsed < target_delta {
            std::thread::sleep(target_delta - elapsed); // This is not very precice :/
        }
        let delta_dur = start_time.elapsed();
        delta = delta_dur.as_secs()*1000 + (delta_dur.subsec_nanos() as u64)/1000000;
    }

    game.close();
}

/// General info about the currently running game. Passed as a parameter to
/// most [`Game`](trait.Game.html) methods.
pub struct GameState {
    /// The size of the window in which this game is running, in pixels.
    pub win_size: Vec2<u32>,
    /// If set to true the game will exit after rendering.
    pub exit: bool,
    /// The number of milliseconds per frame this game should aim to run at. Set to 16
    /// for 60 fps. If the main loop takes less time than this amount the game will
    /// sleep until a total of `target_delta` has ellapsed. If set to `0` the game will
    /// never sleep.
    pub target_delta: u32,

    event_sinks: Vec<mpsc::Sender<glutin::Event>>,
}

/// Used with [`gondola::run`](fn.run.html)
pub trait Game: Sized {
    /// Called before the main loop. Resources and initial state should be set up here.
    fn setup(state: &mut GameState) -> GameResult<Self>;
    /// Called once every frame, before drawing.
    fn update(&mut self, delta: u32, state: &mut GameState);
    /// Called once every frame, after updating.
    fn draw(&mut self, state: &GameState, mat_stack: &mut MatrixStack);

    /// Called whenever the game window is resized
    fn on_resize(&mut self, _state: &GameState) {}
    /// Called after the main game loop exists. This method is not called if the main
    /// loop `panic!`s.
    fn close(&mut self) {} // Most simple games dont need any special logic here

    fn name() -> &'static str { "Unnamed game (Override Game::name to change title)" }
}

impl GameState {
    fn new() -> GameState {
        GameState {
            win_size: Vec2::zero(),
            exit: false,
            target_delta: 15,

            event_sinks: Vec::new(),
        }
    }

    /// Generates a new receiver for glutin events. All events not consumed by the
    /// game itself will be received by this receiver. Note that all receiver receive
    /// all events, there is no notion of event consumption.
    pub fn gen_event_receiver(&mut self) -> mpsc::Receiver<glutin::Event> {
        let (sender, receiver) = mpsc::channel();
        self.event_sinks.push(sender);
        receiver
    }

    /// Creates a new input manager which can be used to access the state of input devices.
    pub fn gen_input_manager(&mut self) -> InputManager {
        let receiver = self.gen_event_receiver();
        InputManager::new(receiver)
    }
}

<|endoftext|>


<commit_before>//! A library providing a generic connection pool.
#![feature(unsafe_destructor, phase)]
#![warn(missing_doc)]
#![doc(html_root_url="http://www.rust-ci.org/sfackler/r2d2/doc")]

#[phase(plugin, link)]
extern crate log;

use std::comm;
use std::collections::{Deque, RingBuf};
use std::sync::{Arc, Mutex};
use std::fmt;

pub use config::Config;

mod config;

/// A trait which provides database-specific functionality.
pub trait PoolManager<C, E>: Send+Sync {
    /// Attempts to create a new connection.
    fn connect(&self) -> Result<C, E>;

    /// Determines if the connection is still connected to the database.
    ///
    /// A standard implementation would check if a simple query like `SELECT 1`
    /// succeeds.
    fn is_valid(&self, conn: &C) -> bool;
}

/// A trait which handles errors reported by the `PoolManager`.
pub trait ErrorHandler<E>: Send+Sync {
    /// Handles a connection error.
    fn handle_error(&self, error: E);
}

/// An `ErrorHandler` which does nothing.
pub struct NoopErrorHandler<E>;

impl<E> ErrorHandler<E> for NoopErrorHandler<E> {
    fn handle_error(&self, _: E) {}
}

/// An `ErrorHandler` which logs at the error level.
pub struct LoggingErrorHandler<E>;

impl<E> ErrorHandler<E> for LoggingErrorHandler<E> where E: fmt::Show {
    fn handle_error(&self, error: E) {
        error!("Error opening connection: {}", error);
    }
}

/// An error type returned if pool creation fails.
#[deriving(PartialEq, Eq)]
pub enum NewPoolError<E> {
    /// The provided pool configuration was invalid.
    InvalidConfig(&'static str),
    /// The manager returned an error when creating a connection.
    ConnectionError(E),
}

impl<E> fmt::Show for NewPoolError<E> where E: fmt::Show {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InvalidConfig(ref error) => write!(f, "Invalid config: {}", error),
            ConnectionError(ref error) => write!(f, "Unable to create connections: {}", error),
        }
    }
}

enum Command<C> {
    TestConnection(C),
}

struct PoolInternals<C, E> {
    conns: RingBuf<C>,
    failed_conns: RingBuf<E>,
    num_conns: uint,
}

struct InnerPool<C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    config: Config,
    manager: M,
    error_handler: H,
    internals: Mutex<PoolInternals<C, E>>,
}

/// A generic connection pool.
pub struct Pool<C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    helper_chan: Mutex<Sender<Command<C>>>,
    inner: Arc<InnerPool<C, E, M, H>>
}

impl<C, E, M, H> Pool<C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    /// Creates a new connection pool.
    pub fn new(config: Config, manager: M, error_handler: H)
               -> Result<Pool<C, E, M, H>, NewPoolError<E>> {
        match config.validate() {
            Ok(()) => {}
            Err(err) => return Err(InvalidConfig(err))
        }

        let mut internals = PoolInternals {
            conns: RingBuf::new(),
            failed_conns: RingBuf::new(),
            num_conns: config.pool_size,
        };

        for _ in range(0, config.pool_size) {
            match manager.connect() {
                Ok(conn) => internals.conns.push(conn),
                Err(err) => return Err(ConnectionError(err)),
            }
        }

        let inner = Arc::new(InnerPool {
            config: config,
            manager: manager,
            error_handler: error_handler,
            internals: Mutex::new(internals),
        });

        let (sender, receiver) = comm::channel();
        // FIXME :(
        let receiver = Arc::new(Mutex::new(receiver));

        for _ in range(0, config.helper_tasks) {
            let inner = inner.clone();
            let receiver = receiver.clone();
            spawn(proc() helper_task(receiver, inner));
        }

        Ok(Pool {
            helper_chan: Mutex::new(sender),
            inner: inner,
        })
    }

    /// Retrieves a connection from the pool.
    pub fn get<'a>(&'a self) -> Result<PooledConnection<'a, C, E, M, H>, E> {
        let mut internals = self.inner.internals.lock();

        loop {
            match internals.conns.pop_front() {
                Some(conn) => {
                    if self.inner.config.test_on_check_out {
                        drop(internals);
                        let valid = self.inner.manager.is_valid(&conn);

                        if !valid {
                            internals = self.inner.internals.lock();
                            internals.num_conns -= 1;
                            continue;
                        }
                    }

                    return Ok(PooledConnection {
                        pool: self,
                        conn: Some(conn),
                    })
                }
                None => {
                    match internals.failed_conns.pop_front() {
                        Some(err) => return Err(err),
                        None => {}
                    }

                    internals.cond.wait();
                }
            }
        }
    }

    fn put_back(&self, conn: C) {
        let mut internals = self.inner.internals.lock();
        internals.conns.push(conn);
        internals.cond.signal();
    }
}

fn helper_task<C, E, M, H>(receiver: Arc<Mutex<Receiver<Command<C>>>>,
                           inner: Arc<InnerPool<C, E, M, H>>)
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    loop {
        let mut receiver = receiver.lock();
        let res = receiver.recv_opt();
        drop(receiver);

        match res {
            Ok(TestConnection(conn)) => test_connection(&*inner, conn),
            Err(()) => break,
        }
    }
}

fn test_connection<C, E, M, H>(inner: &InnerPool<C, E, M, H>, conn: C)
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    let is_valid = inner.manager.is_valid(&conn);
    let mut internals = inner.internals.lock();
    if is_valid {
        internals.conns.push(conn);
    } else {
        internals.num_conns -= 1;
    }
}

/// A smart pointer wrapping an underlying connection.
pub struct PooledConnection<'a, C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    pool: &'a Pool<C, E, M, H>,
    conn: Option<C>,
}

#[unsafe_destructor]
impl<'a, C, E, M, H> Drop for PooledConnection<'a, C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    fn drop(&mut self) {
        self.pool.put_back(self.conn.take().unwrap());
    }
}

impl<'a, C, E, M, H> Deref<C> for PooledConnection<'a, C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    fn deref(&self) -> &C {
        self.conn.as_ref().unwrap()
    }
}
<commit_msg>Some work towards moving to the new error model<commit_after>//! A library providing a generic connection pool.
#![feature(unsafe_destructor, phase)]
#![warn(missing_doc)]
#![doc(html_root_url="http://www.rust-ci.org/sfackler/r2d2/doc")]

#[phase(plugin, link)]
extern crate log;

use std::comm;
use std::collections::{Deque, RingBuf};
use std::sync::{Arc, Mutex};
use std::fmt;

pub use config::Config;

mod config;

/// A trait which provides database-specific functionality.
pub trait PoolManager<C, E>: Send+Sync {
    /// Attempts to create a new connection.
    fn connect(&self) -> Result<C, E>;

    /// Determines if the connection is still connected to the database.
    ///
    /// A standard implementation would check if a simple query like `SELECT 1`
    /// succeeds.
    fn is_valid(&self, conn: &C) -> bool;
}

/// A trait which handles errors reported by the `PoolManager`.
pub trait ErrorHandler<E>: Send+Sync {
    /// Handles a connection error.
    fn handle_error(&self, error: E);
}

/// An `ErrorHandler` which does nothing.
pub struct NoopErrorHandler<E>;

impl<E> ErrorHandler<E> for NoopErrorHandler<E> {
    fn handle_error(&self, _: E) {}
}

/// An `ErrorHandler` which logs at the error level.
pub struct LoggingErrorHandler<E>;

impl<E> ErrorHandler<E> for LoggingErrorHandler<E> where E: fmt::Show {
    fn handle_error(&self, error: E) {
        error!("Error opening connection: {}", error);
    }
}

/// An error type returned if pool creation fails.
#[deriving(PartialEq, Eq)]
pub enum NewPoolError<E> {
    /// The provided pool configuration was invalid.
    InvalidConfig(&'static str),
    /// The manager returned an error when creating a connection.
    ConnectionError(E),
}

impl<E> fmt::Show for NewPoolError<E> where E: fmt::Show {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InvalidConfig(ref error) => write!(f, "Invalid config: {}", error),
            ConnectionError(ref error) => write!(f, "Unable to create connections: {}", error),
        }
    }
}

enum Command<C> {
    AddConnection,
    TestConnection(C),
}

struct PoolInternals<C, E> {
    conns: RingBuf<C>,
    num_conns: uint,
}

struct InnerPool<C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    config: Config,
    manager: M,
    error_handler: H,
    internals: Mutex<PoolInternals<C, E>>,
}

/// A generic connection pool.
pub struct Pool<C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    helper_chan: Mutex<Sender<Command<C>>>,
    inner: Arc<InnerPool<C, E, M, H>>
}

impl<C, E, M, H> Pool<C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    /// Creates a new connection pool.
    pub fn new(config: Config, manager: M, error_handler: H)
               -> Result<Pool<C, E, M, H>, NewPoolError<E>> {
        match config.validate() {
            Ok(()) => {}
            Err(err) => return Err(InvalidConfig(err))
        }

        let mut internals = PoolInternals {
            conns: RingBuf::new(),
            num_conns: config.pool_size,
        };

        for _ in range(0, config.pool_size) {
            match manager.connect() {
                Ok(conn) => internals.conns.push(conn),
                Err(err) => return Err(ConnectionError(err)),
            }
        }

        let inner = Arc::new(InnerPool {
            config: config,
            manager: manager,
            error_handler: error_handler,
            internals: Mutex::new(internals),
        });

        let (sender, receiver) = comm::channel();
        // FIXME :(
        let receiver = Arc::new(Mutex::new(receiver));

        for _ in range(0, config.helper_tasks) {
            let inner = inner.clone();
            let receiver = receiver.clone();
            spawn(proc() helper_task(receiver, inner));
        }

        Ok(Pool {
            helper_chan: Mutex::new(sender),
            inner: inner,
        })
    }

    /// Retrieves a connection from the pool.
    pub fn get<'a>(&'a self) -> Result<PooledConnection<'a, C, E, M, H>, E> {
        let mut internals = self.inner.internals.lock();

        loop {
            match internals.conns.pop_front() {
                Some(conn) => {
                    if self.inner.config.test_on_check_out {
                        drop(internals);
                        let valid = self.inner.manager.is_valid(&conn);

                        if !valid {
                            internals = self.inner.internals.lock();
                            internals.num_conns -= 1;
                            continue;
                        }
                    }

                    return Ok(PooledConnection {
                        pool: self,
                        conn: Some(conn),
                    })
                }
                None => internals.cond.wait(),
            }
        }
    }

    fn put_back(&self, conn: C) {
        let mut internals = self.inner.internals.lock();
        internals.conns.push(conn);
        internals.cond.signal();
    }
}

fn helper_task<C, E, M, H>(receiver: Arc<Mutex<Receiver<Command<C>>>>,
                           inner: Arc<InnerPool<C, E, M, H>>)
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    loop {
        let mut receiver = receiver.lock();
        let res = receiver.recv_opt();
        drop(receiver);

        match res {
            Ok(AddConnection) => add_connection(&*inner),
            Ok(TestConnection(conn)) => test_connection(&*inner, conn),
            Err(()) => break,
        }
    }
}

fn add_connection<C, E, M, H>(inner: &InnerPool<C, E, M, H>)
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    match inner.manager.connect() {
        Ok(conn) => {
            let mut internals = inner.internals.lock();
            internals.conns.push(conn);
            internals.num_conns += 1;
            internals.cond.signal();
        }
        Err(err) => inner.error_handler.handle_error(err),
    }
}

fn test_connection<C, E, M, H>(inner: &InnerPool<C, E, M, H>, conn: C)
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    let is_valid = inner.manager.is_valid(&conn);
    let mut internals = inner.internals.lock();
    if is_valid {
        internals.conns.push(conn);
    } else {
        internals.num_conns -= 1;
    }
}

/// A smart pointer wrapping an underlying connection.
pub struct PooledConnection<'a, C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    pool: &'a Pool<C, E, M, H>,
    conn: Option<C>,
}

#[unsafe_destructor]
impl<'a, C, E, M, H> Drop for PooledConnection<'a, C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    fn drop(&mut self) {
        self.pool.put_back(self.conn.take().unwrap());
    }
}

impl<'a, C, E, M, H> Deref<C> for PooledConnection<'a, C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    fn deref(&self) -> &C {
        self.conn.as_ref().unwrap()
    }
}
<|endoftext|>


<commit_before>#![doc(html_root_url = "http://mmitteregger.github.io/rust-twitch-client")]
#![warn(missing_docs)]
#![cfg_attr(test, deny(missing_docs))]
#![cfg_attr(test, deny(warnings))]

//! # Overview
//!
//! Rust Twitch Client is a library for the [Twitch REST API](https://github.com/justintv/Twitch-API) written in Rust!
//!
//! It uses [hyper](https://github.com/hyperium/hyper) as http client
//! and [serde](https://github.com/serde-rs/serde) for the serialization and deserialization of the REST entities.
//!
//! # Examples
//!
//! ```
//! use twitch_client::*;
//!
//! fn main() {
//!     let twitch_client = TwitchClient::new().unwrap();
//!
//!     match twitch_client.top_games(TopGamesParams::default()) {
//!         Ok(top_games) => {
//!             println!("Total games: {}", top_games.total());
//!             println!("---");
//!             for game_info in top_games.top() {
//!                 println!("Game: {}, Viewers: {}", game_info.game().name(), game_info.viewers());
//!             }
//!             println!("---");
//!         },
//!         Err(err) => println!("Failed to retrieve top games: {}", err),
//!     }
//! }
//! ```

#[macro_use] extern crate hyper;
extern crate hyper_native_tls;
extern crate native_tls;
extern crate url;
extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate serde_json;

pub mod model;
pub mod error;
mod http;
pub mod param;

pub use param::*;
use http::TwitchHttpClient;
use error::Result;

/// Readonly client for the [Twitch API](https://github.com/justintv/twitch-api).
///
/// Currently Twitch API version 3 is used.
///
/// By using the Twitch Client you agree to follow the
/// [Twitch API Terms of Service](https://www.twitch.tv/user/legal?page=api_terms_of_service)
/// and the [Twitch Terms of Service](https://www.twitch.tv/p/api_terms_of_service).
/// This library is in no way affiliated with, authorized, maintained, sponsored
/// or endorsed by Twitch or any of its affiliates or subsidiaries
///
/// # Examples
///
/// ```
/// use twitch_client::*;
///
/// let twitch_client = TwitchClient::new().unwrap();
///
/// match twitch_client.top_games(TopGamesParams::default()) {
///     Ok(top_games) => println!("Total games: {}", top_games.total()),
///     Err(err) => println!("Failed to retrieve top games: {}", err),
/// }
/// ```
pub struct TwitchClient {
    http_client: TwitchHttpClient,
}

impl TwitchClient {

    /// Constructs a new instance without client id and with a default hyper client.
    ///
    /// It is highly recommended to specify a client id to avoid being rate limited by Twitch
    /// with the `with_client_id` method.
    pub fn new() -> Result<TwitchClient> {
        let http_client = try!(TwitchHttpClient::new());

        let twitch_client = TwitchClient {
            http_client: http_client,
        };
        Ok(twitch_client)
    }

    /// Sets the Twitch client id.
    ///
    /// See [https://github.com/justintv/twitch-api#rate-limits](https://github.com/justintv/twitch-api#rate-limits)
    /// for more information.
    pub fn with_client_id(mut self, client_id: &str) -> TwitchClient {
        self.http_client.set_client_id(client_id);
        self
    }

    /// Sets a custom configured hyper client.
    ///
    /// See [hyper::client::Client](http://hyper.rs/hyper/hyper/client/struct.Client.html)
    /// for more information.
    pub fn with_hyper_client(mut self, hyper_client: hyper::Client) -> TwitchClient {
        self.http_client.set_hyper_client(hyper_client);
        self
    }
}

impl TwitchClient {

    /// Get games by number of viewers.
    ///
    /// Returns a list of games objects sorted by number of current viewers on Twitch, most popular first.
    pub fn top_games(&self, params: TopGamesParams) -> Result<model::game::TopGames> {
        let response = try!(self.http_client.get_content_with_params("/games/top", params));
        let top_games: model::game::TopGames = try!(serde_json::from_str(&response));
        Ok(top_games)
    }

    /// Get list of ingests.
    ///
    /// Returns a list of ingest objects.
    pub fn ingests(&self) -> Result<model::ingest::Ingests> {
        let response = try!(self.http_client.get_content("/ingests"));
        let ingests: model::ingest::Ingests = try!(serde_json::from_str(&response));
        Ok(ingests)
    }

    /// Get top level links object and authorization status.
    ///
    /// Basic information about the API and authentication status.
    /// If you are authenticated, the response includes the status of your token and links to other related resources.
    pub fn basic_info(&self) -> Result<model::root::BasicInfo> {
        let response = try!(self.http_client.get_content("/"));
        let basic_info: model::root::BasicInfo = try!(serde_json::from_str(&response));
        Ok(basic_info)
    }

    /// Get stream object.
    ///
    /// Returns a stream object if live.
    pub fn stream(&self, channel: &str) -> Result<model::stream::ChannelStream> {
        let url = format!("/streams/{}", channel);
        let response = try!(self.http_client.get_content(&url));
        let channel_stream: model::stream::ChannelStream = try!(serde_json::from_str(&response));
        Ok(channel_stream)
    }

    /// Get stream object.
    ///
    /// Returns a list of stream objects that are queried by a number of parameters
    /// sorted by number of viewers descending.
    pub fn streams(&self, params: StreamsParams) -> Result<model::stream::Streams> {
        let response = try!(self.http_client.get_content_with_params("/streams", params));
        let streams: model::stream::Streams = try!(serde_json::from_str(&response));
        Ok(streams)
    }

    /// Get a list of featured streams.
    ///
    /// Returns a list of featured (promoted) stream objects.
    pub fn featured_streams(&self, params: FeaturedStreamsParams) -> Result<model::stream::FeaturedStreams> {
        let response = try!(self.http_client.get_content_with_params("/streams/featured", params));
        let featured_streams: model::stream::FeaturedStreams = try!(serde_json::from_str(&response));
        Ok(featured_streams)
    }

    /// Get a summary of streams.
    ///
    /// Returns a summary of current streams.
    pub fn streams_summary(&self, params: StreamsSummaryParams) -> Result<model::stream::StreamsSummary> {
        let response = try!(self.http_client.get_content_with_params("/streams/summary", params));
        let streams_summary: model::stream::StreamsSummary = try!(serde_json::from_str(&response));
        Ok(streams_summary)
    }

    /// Get channel object.
    ///
    /// Returns a channel object.
    pub fn channel(&self, channel: &str) -> Result<model::channel::Channel> {
        let url = format!("/channels/{}", channel);
        let response = try!(self.http_client.get_content(&url));
        let channel: model::channel::Channel = try!(serde_json::from_str(&response));
        Ok(channel)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs::File;
    use std::io::Read;
    use serde_json;

    #[test]
    fn test_top_games_with_default_params() {
        let client = create_test_twitch_client();
        let top_games = client.top_games(TopGamesParams::default()).unwrap();
        assert!(top_games.total() > 0, "top_games.total() = {} > 0", top_games.total());
        assert_eq!(top_games.top().len(), 10);
    }

    #[test]
    fn test_top_games_with_custom_params() {
        let client = create_test_twitch_client();
        let params = TopGamesParams::new()
                .with_offset(0)
                .with_limit(2);
        let top_games = client.top_games(params).unwrap();
        assert!(top_games.total() > 0, "top_games.total() = {} > 0", top_games.total());
        assert_eq!(top_games.top().len(), 2);
    }

    #[test]
    fn test_ingests() {
        let client = create_test_twitch_client();
        let ingests = client.ingests().unwrap();
        assert!(ingests.ingests().len() > 0, "ingests.ingests().len() = {} > 0", ingests.ingests().len());
    }

    #[test]
    fn test_basic_info() {
        let client = create_test_twitch_client();
        let basic_info = client.basic_info().unwrap();
        let token = basic_info.token();
        assert!(!token.valid(), "expecting invalid token for unauthenticated access");
        assert!(token.user_name().is_none(), "expecting no user name for unauthenticated access");
        assert!(token.authorization().is_none(), "expecting no auth info for unauthenticated access");
    }

    #[test]
    fn test_stream() {
        let client = create_test_twitch_client();
        let channel_stream = client.stream("test_channel").unwrap();
        assert!(channel_stream.stream().is_none(), "expecting test channel stream to be offline");
    }

    #[test]
    fn test_streams_with_default_params() {
        let client = create_test_twitch_client();
        let streams = client.streams(StreamsParams::default()).unwrap();
        assert!(streams.total() > 0, "streams.total() = {} > 0", streams.total());
    }

    #[test]
    fn test_streams_with_custom_params() {
        let client = create_test_twitch_client();
        let params = StreamsParams::new()
                .with_offset(0)
                .with_limit(2)
                .with_stream_type(StreamType::Live);
        let streams = client.streams(params).unwrap();
        assert!(streams.total() > 0, "streams.total() = {} > 0", streams.total());
        assert_eq!(streams.streams().len(), 2);
    }

    #[test]
    fn test_featured_streams_with_default_params() {
        let client = create_test_twitch_client();
        let featured_streams = client.featured_streams(FeaturedStreamsParams::default()).unwrap();
        assert!(featured_streams.featured().len() > 0, "featured_streams.featured().len() = {} > 0", featured_streams.featured().len());
    }

    #[test]
    fn test_featured_streams_with_custom_params() {
        let client = create_test_twitch_client();
        let params = FeaturedStreamsParams::new()
                .with_offset(0)
                .with_limit(2);
        let featured_streams = client.featured_streams(params).unwrap();
        assert_eq!(featured_streams.featured().len(), 2);
    }

    #[test]
    fn test_streams_summary_with_default_params() {
        let client = create_test_twitch_client();
        let streams_summary = client.streams_summary(StreamsSummaryParams::default()).unwrap();
        assert!(streams_summary.channels() > 0, "streams_summary.channels() = {} > 0", streams_summary.channels());
        assert!(streams_summary.viewers() > 0, "streams_summary.viewers() = {} > 0", streams_summary.viewers());
    }

    #[test]
    fn test_streams_summary_with_custom_params() {
        let client = create_test_twitch_client();
        let params = StreamsSummaryParams::new()
                .with_game("Overwatch");
        client.streams_summary(params).unwrap();
    }

    #[test]
    fn test_channel() {
        let client = create_test_twitch_client();
        let channel = client.channel("test_channel").unwrap();
        assert_eq!(channel.name(), "test_channel");
        assert!(channel.url().find("test_channel").is_some(), "channel.url should contain \"test_channel\"");
        assert!(channel.views() > 0, "channel.views() = {} > 0", channel.views());
        assert!(channel.followers() > 0, "channel.followers() = {} > 0", channel.followers());
    }

    fn create_test_twitch_client() -> TwitchClient {
        let auth = read_auth();
        let mut twitch_client = TwitchClient::new().unwrap();

        match auth {
            Some(auth) => {
                twitch_client = twitch_client.with_client_id(&auth.client_id);
            },
            None => {},
        }

        twitch_client
    }

    fn read_auth() -> Option<Auth> {
        let mut auth_file = match File::open("twitch_auth.json") {
            Ok(file) => file,
            Err(_) => return None,
        };
        let mut auth_string = String::new();
        auth_file.read_to_string(&mut auth_string).unwrap();
        let auth: Auth = serde_json::from_str(&auth_string).unwrap();
        Some(auth)
    }

    #[derive(Deserialize, Debug)]
    struct Auth {
        name: String,
        client_id: String,
        redirect_uri: Option<String>,
        client_secret: Option<String>,
    }
}
<commit_msg>Add with_client_id to examples and require twitch_auth.json file for tests<commit_after>#![doc(html_root_url = "http://mmitteregger.github.io/rust-twitch-client")]
#![warn(missing_docs)]
#![cfg_attr(test, deny(missing_docs))]
#![cfg_attr(test, deny(warnings))]

//! # Overview
//!
//! Rust Twitch Client is a library for the [Twitch REST API](https://github.com/justintv/Twitch-API) written in Rust!
//!
//! It uses [hyper](https://github.com/hyperium/hyper) as http client
//! and [serde](https://github.com/serde-rs/serde) for the serialization and deserialization of the REST entities.
//!
//! # Examples
//!
//! ```
//! use twitch_client::*;
//!
//! fn main() {
//!     let twitch_client = TwitchClient::new().unwrap().with_client_id("<INSERT_YOU_CLIENT_ID_HERE>");
//!
//!     match twitch_client.top_games(TopGamesParams::default()) {
//!         Ok(top_games) => {
//!             println!("Total games: {}", top_games.total());
//!             println!("---");
//!             for game_info in top_games.top() {
//!                 println!("Game: {}, Viewers: {}", game_info.game().name(), game_info.viewers());
//!             }
//!             println!("---");
//!         },
//!         Err(err) => println!("Failed to retrieve top games: {}", err),
//!     }
//! }
//! ```

#[macro_use] extern crate hyper;
extern crate hyper_native_tls;
extern crate native_tls;
extern crate url;
extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate serde_json;

pub mod model;
pub mod error;
mod http;
pub mod param;

pub use param::*;
use http::TwitchHttpClient;
use error::Result;

/// Readonly client for the [Twitch API](https://github.com/justintv/twitch-api).
///
/// Currently Twitch API version 3 is used.
///
/// By using the Twitch Client you agree to follow the
/// [Twitch API Terms of Service](https://www.twitch.tv/user/legal?page=api_terms_of_service)
/// and the [Twitch Terms of Service](https://www.twitch.tv/p/api_terms_of_service).
/// This library is in no way affiliated with, authorized, maintained, sponsored
/// or endorsed by Twitch or any of its affiliates or subsidiaries
///
/// # Examples
///
/// ```
/// use twitch_client::*;
///
/// let twitch_client = TwitchClient::new().unwrap().with_client_id("<INSERT_YOU_CLIENT_ID_HERE>");
///
/// match twitch_client.top_games(TopGamesParams::default()) {
///     Ok(top_games) => println!("Total games: {}", top_games.total()),
///     Err(err) => println!("Failed to retrieve top games: {}", err),
/// }
/// ```
pub struct TwitchClient {
    http_client: TwitchHttpClient,
}

impl TwitchClient {

    /// Constructs a new instance without client id and with a default hyper client.
    ///
    /// It is highly recommended to specify a client id to avoid being rate limited by Twitch
    /// with the `with_client_id` method.
    pub fn new() -> Result<TwitchClient> {
        let http_client = try!(TwitchHttpClient::new());

        let twitch_client = TwitchClient {
            http_client: http_client,
        };
        Ok(twitch_client)
    }

    /// Sets the Twitch client id.
    ///
    /// See [https://github.com/justintv/twitch-api#rate-limits](https://github.com/justintv/twitch-api#rate-limits)
    /// for more information.
    pub fn with_client_id(mut self, client_id: &str) -> TwitchClient {
        self.http_client.set_client_id(client_id);
        self
    }

    /// Sets a custom configured hyper client.
    ///
    /// See [hyper::client::Client](http://hyper.rs/hyper/hyper/client/struct.Client.html)
    /// for more information.
    pub fn with_hyper_client(mut self, hyper_client: hyper::Client) -> TwitchClient {
        self.http_client.set_hyper_client(hyper_client);
        self
    }
}

impl TwitchClient {

    /// Get games by number of viewers.
    ///
    /// Returns a list of games objects sorted by number of current viewers on Twitch, most popular first.
    pub fn top_games(&self, params: TopGamesParams) -> Result<model::game::TopGames> {
        let response = try!(self.http_client.get_content_with_params("/games/top", params));
        let top_games: model::game::TopGames = try!(serde_json::from_str(&response));
        Ok(top_games)
    }

    /// Get list of ingests.
    ///
    /// Returns a list of ingest objects.
    pub fn ingests(&self) -> Result<model::ingest::Ingests> {
        let response = try!(self.http_client.get_content("/ingests"));
        let ingests: model::ingest::Ingests = try!(serde_json::from_str(&response));
        Ok(ingests)
    }

    /// Get top level links object and authorization status.
    ///
    /// Basic information about the API and authentication status.
    /// If you are authenticated, the response includes the status of your token and links to other related resources.
    pub fn basic_info(&self) -> Result<model::root::BasicInfo> {
        let response = try!(self.http_client.get_content("/"));
        let basic_info: model::root::BasicInfo = try!(serde_json::from_str(&response));
        Ok(basic_info)
    }

    /// Get stream object.
    ///
    /// Returns a stream object if live.
    pub fn stream(&self, channel: &str) -> Result<model::stream::ChannelStream> {
        let url = format!("/streams/{}", channel);
        let response = try!(self.http_client.get_content(&url));
        let channel_stream: model::stream::ChannelStream = try!(serde_json::from_str(&response));
        Ok(channel_stream)
    }

    /// Get stream object.
    ///
    /// Returns a list of stream objects that are queried by a number of parameters
    /// sorted by number of viewers descending.
    pub fn streams(&self, params: StreamsParams) -> Result<model::stream::Streams> {
        let response = try!(self.http_client.get_content_with_params("/streams", params));
        let streams: model::stream::Streams = try!(serde_json::from_str(&response));
        Ok(streams)
    }

    /// Get a list of featured streams.
    ///
    /// Returns a list of featured (promoted) stream objects.
    pub fn featured_streams(&self, params: FeaturedStreamsParams) -> Result<model::stream::FeaturedStreams> {
        let response = try!(self.http_client.get_content_with_params("/streams/featured", params));
        let featured_streams: model::stream::FeaturedStreams = try!(serde_json::from_str(&response));
        Ok(featured_streams)
    }

    /// Get a summary of streams.
    ///
    /// Returns a summary of current streams.
    pub fn streams_summary(&self, params: StreamsSummaryParams) -> Result<model::stream::StreamsSummary> {
        let response = try!(self.http_client.get_content_with_params("/streams/summary", params));
        let streams_summary: model::stream::StreamsSummary = try!(serde_json::from_str(&response));
        Ok(streams_summary)
    }

    /// Get channel object.
    ///
    /// Returns a channel object.
    pub fn channel(&self, channel: &str) -> Result<model::channel::Channel> {
        let url = format!("/channels/{}", channel);
        let response = try!(self.http_client.get_content(&url));
        let channel: model::channel::Channel = try!(serde_json::from_str(&response));
        Ok(channel)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs::File;
    use std::io::Read;
    use serde_json;

    #[test]
    fn test_top_games_with_default_params() {
        let client = create_test_twitch_client();
        let top_games = client.top_games(TopGamesParams::default()).unwrap();
        assert!(top_games.total() > 0, "top_games.total() = {} > 0", top_games.total());
        assert_eq!(top_games.top().len(), 10);
    }

    #[test]
    fn test_top_games_with_custom_params() {
        let client = create_test_twitch_client();
        let params = TopGamesParams::new()
                .with_offset(0)
                .with_limit(2);
        let top_games = client.top_games(params).unwrap();
        assert!(top_games.total() > 0, "top_games.total() = {} > 0", top_games.total());
        assert_eq!(top_games.top().len(), 2);
    }

    #[test]
    fn test_ingests() {
        let client = create_test_twitch_client();
        let ingests = client.ingests().unwrap();
        assert!(ingests.ingests().len() > 0, "ingests.ingests().len() = {} > 0", ingests.ingests().len());
    }

    #[test]
    fn test_basic_info() {
        let client = create_test_twitch_client();
        let basic_info = client.basic_info().unwrap();
        let token = basic_info.token();
        assert!(!token.valid(), "expecting invalid token for unauthenticated access");
        assert!(token.user_name().is_none(), "expecting no user name for unauthenticated access");
        assert!(token.authorization().is_none(), "expecting no auth info for unauthenticated access");
    }

    #[test]
    fn test_stream() {
        let client = create_test_twitch_client();
        let channel_stream = client.stream("test_channel").unwrap();
        assert!(channel_stream.stream().is_none(), "expecting test channel stream to be offline");
    }

    #[test]
    fn test_streams_with_default_params() {
        let client = create_test_twitch_client();
        let streams = client.streams(StreamsParams::default()).unwrap();
        assert!(streams.total() > 0, "streams.total() = {} > 0", streams.total());
    }

    #[test]
    fn test_streams_with_custom_params() {
        let client = create_test_twitch_client();
        let params = StreamsParams::new()
                .with_offset(0)
                .with_limit(2)
                .with_stream_type(StreamType::Live);
        let streams = client.streams(params).unwrap();
        assert!(streams.total() > 0, "streams.total() = {} > 0", streams.total());
        assert_eq!(streams.streams().len(), 2);
    }

    #[test]
    fn test_featured_streams_with_default_params() {
        let client = create_test_twitch_client();
        let featured_streams = client.featured_streams(FeaturedStreamsParams::default()).unwrap();
        assert!(featured_streams.featured().len() > 0, "featured_streams.featured().len() = {} > 0", featured_streams.featured().len());
    }

    #[test]
    fn test_featured_streams_with_custom_params() {
        let client = create_test_twitch_client();
        let params = FeaturedStreamsParams::new()
                .with_offset(0)
                .with_limit(2);
        let featured_streams = client.featured_streams(params).unwrap();
        assert_eq!(featured_streams.featured().len(), 2);
    }

    #[test]
    fn test_streams_summary_with_default_params() {
        let client = create_test_twitch_client();
        let streams_summary = client.streams_summary(StreamsSummaryParams::default()).unwrap();
        assert!(streams_summary.channels() > 0, "streams_summary.channels() = {} > 0", streams_summary.channels());
        assert!(streams_summary.viewers() > 0, "streams_summary.viewers() = {} > 0", streams_summary.viewers());
    }

    #[test]
    fn test_streams_summary_with_custom_params() {
        let client = create_test_twitch_client();
        let params = StreamsSummaryParams::new()
                .with_game("Overwatch");
        client.streams_summary(params).unwrap();
    }

    #[test]
    fn test_channel() {
        let client = create_test_twitch_client();
        let channel = client.channel("test_channel").unwrap();
        assert_eq!(channel.name(), "test_channel");
        assert!(channel.url().find("test_channel").is_some(), "channel.url should contain \"test_channel\"");
        assert!(channel.views() > 0, "channel.views() = {} > 0", channel.views());
        assert!(channel.followers() > 0, "channel.followers() = {} > 0", channel.followers());
    }

    fn create_test_twitch_client() -> TwitchClient {
        let auth = read_auth();
        TwitchClient::new().unwrap().with_client_id(&auth.client_id)
    }

    fn read_auth() -> Auth {
        let mut auth_file = match File::open("twitch_auth.json") {
            Ok(file) => file,
            Err(_) => {
                panic!("File twitch_auth.json required at the crate root directory \
                        for the Twitch Client-ID. Have a look at twitch_auth_template.json");
            },
        };
        let mut auth_string = String::new();
        auth_file.read_to_string(&mut auth_string).unwrap();
        let auth: Auth = serde_json::from_str(&auth_string).unwrap();
        auth
    }

    #[derive(Deserialize, Debug)]
    struct Auth {
        name: String,
        client_id: String,
        redirect_uri: Option<String>,
        client_secret: Option<String>,
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;

extern crate num_traits;
#[macro_use]
extern crate num_derive;

extern crate libindy_sys as ffi;

#[macro_use]
mod macros;

pub mod anoncreds;
pub mod blob_storage;
pub mod crypto;
pub mod did;
pub mod ledger;
pub mod payments;
pub mod pairwise;
pub mod pool;
pub mod wallet;
pub mod utils;

use std::sync::mpsc;

pub type IndyHandle = i32;

#[derive(Debug, PartialEq, Copy, Clone, FromPrimitive, ToPrimitive)]
#[repr(i32)]
#[allow(dead_code)]
pub enum ErrorCode
{
    Success = 0,

    // Common errors

    // Caller passed invalid value as param 1 (null, invalid json and etc..)
    CommonInvalidParam1 = 100,

    // Caller passed invalid value as param 2 (null, invalid json and etc..)
    CommonInvalidParam2 = 101,

    // Caller passed invalid value as param 3 (null, invalid json and etc..)
    CommonInvalidParam3 = 102,

    // Caller passed invalid value as param 4 (null, invalid json and etc..)
    CommonInvalidParam4 = 103,

    // Caller passed invalid value as param 5 (null, invalid json and etc..)
    CommonInvalidParam5 = 104,

    // Caller passed invalid value as param 6 (null, invalid json and etc..)
    CommonInvalidParam6 = 105,

    // Caller passed invalid value as param 7 (null, invalid json and etc..)
    CommonInvalidParam7 = 106,

    // Caller passed invalid value as param 8 (null, invalid json and etc..)
    CommonInvalidParam8 = 107,

    // Caller passed invalid value as param 9 (null, invalid json and etc..)
    CommonInvalidParam9 = 108,

    // Caller passed invalid value as param 10 (null, invalid json and etc..)
    CommonInvalidParam10 = 109,

    // Caller passed invalid value as param 11 (null, invalid json and etc..)
    CommonInvalidParam11 = 110,

    // Caller passed invalid value as param 11 (null, invalid json and etc..)
    CommonInvalidParam12 = 111,

    // Invalid library state was detected in runtime. It signals library bug
    CommonInvalidState = 112,

    // Object (json, config, key, credential and etc...) passed by library caller has invalid structure
    CommonInvalidStructure = 113,

    // IO Error
    CommonIOError = 114,

    // Caller passed invalid value as param 13 (null, invalid json and etc..)
    CommonInvalidParam13 = 115,

    // Caller passed invalid value as param 14 (null, invalid json and etc..)
    CommonInvalidParam14 = 116,

    // Wallet errors
    // Caller passed invalid wallet handle
    WalletInvalidHandle = 200,

    // Unknown type of wallet was passed on create_wallet
    WalletUnknownTypeError = 201,

    // Attempt to register already existing wallet type
    WalletTypeAlreadyRegisteredError = 202,

    // Attempt to create wallet with name used for another exists wallet
    WalletAlreadyExistsError = 203,

    // Requested entity id isn't present in wallet
    WalletNotFoundError = 204,

    // Trying to use wallet with pool that has different name
    WalletIncompatiblePoolError = 205,

    // Trying to open wallet that was opened already
    WalletAlreadyOpenedError = 206,

    // Attempt to open encrypted wallet with invalid credentials
    WalletAccessFailed = 207,

    // Input provided to wallet operations is considered not valid
    WalletInputError = 208,

    // Decoding of wallet data during input/output failed
    WalletDecodingError = 209,

    // Storage error occurred during wallet operation
    WalletStorageError = 210,

    // Error during encryption-related operations
    WalletEncryptionError = 211,

    // Requested wallet item not found
    WalletItemNotFound = 212,

    // Returned if wallet's add_record operation is used with record name that already exists
    WalletItemAlreadyExists = 213,

    // Returned if provided wallet query is invalid
    WalletQueryError = 214,

    // Ledger errors
    // Trying to open pool ledger that wasn't created before
    PoolLedgerNotCreatedError = 300,

    // Caller passed invalid pool ledger handle
    PoolLedgerInvalidPoolHandle = 301,

    // Pool ledger terminated
    PoolLedgerTerminated = 302,

    // No concensus during ledger operation
    LedgerNoConsensusError = 303,

    // Attempt to parse invalid transaction response
    LedgerInvalidTransaction = 304,

    // Attempt to send transaction without the necessary privileges
    LedgerSecurityError = 305,

    // Attempt to create pool ledger config with name used for another existing pool
    PoolLedgerConfigAlreadyExistsError = 306,

    // Timeout for action
    PoolLedgerTimeout = 307,

    // Revocation registry is full and creation of new registry is necessary
    AnoncredsRevocationRegistryFullError = 400,

    AnoncredsInvalidUserRevocIndex = 401,

    AnoncredsAccumulatorIsFull = 402,

    AnoncredsNotIssuedError = 403,

    // Attempt to generate master secret with duplicated name
    AnoncredsMasterSecretDuplicateNameError = 404,

    AnoncredsProofRejected = 405,

    AnoncredsCredentialRevoked = 406,

    // Attempt to create credential definition with duplicated did schema pair
    AnoncredsCredDefAlreadyExistsError = 407,

    // Signus errors
    // Unknown format of DID entity keys
    UnknownCryptoTypeError = 500,

    // Attempt to create duplicate did
    DidAlreadyExistsError = 600,

    // Unknown payment method was given
    UnknownPaymentMethod = 700,

    //No method were scraped from inputs/outputs or more than one were scraped
    IncompatiblePaymentError = 701,

    // Insufficient funds on inputs
    PaymentInsufficientFundsError = 702,
}

impl ErrorCode {
    #[allow(non_snake_case)]
    #[allow(dead_code)]
    #[allow(unused)]
    pub fn description(&self) -> &'static str {
        match self {
            CommonInvalidParam1 => "Caller passed invalid value as param 1",
            CommonInvalidParam2 => "Caller passed invalid value as param 2",
            CommonInvalidParam3 => "Caller passed invalid value as param 3",
            CommonInvalidParam4 => "Caller passed invalid value as param 4",
            CommonInvalidParam5 => "Caller passed invalid value as param 5",
            CommonInvalidParam6 => "Caller passed invalid value as param 6",
            CommonInvalidParam7 => "Caller passed invalid value as param 7",
            CommonInvalidParam8 => "Caller passed invalid value as param 8",
            CommonInvalidParam9 => "Caller passed invalid value as param 9",
            CommonInvalidParam10 => "Caller passed invalid value as param 10",
            CommonInvalidParam11 => "Caller passed invalid value as param 11",
            CommonInvalidParam12 => "Caller passed invalid value as param 12",
            CommonInvalidParam13 => "Caller passed invalid value as param 13",
            CommonInvalidParam14 => "Caller passed invalid value as param 14",
            CommonInvalidState => "Invalid library state was detected in runtime. It signals library bug",
            CommonInvalidStructure => "Object (json, config, key, credential and etc...) passed by library caller has invalid structure",
            CommonIOError => "IO Error",
            WalletInvalidHandle => "Caller passed invalid wallet handle",
            WalletUnknownTypeError => "Caller passed invalid wallet handle",
            WalletTypeAlreadyRegisteredError => "Attempt to register already existing wallet type",
            WalletAlreadyExistsError => "Attempt to create wallet with name used for another exists wallet",
            WalletNotFoundError => "Requested entity id isn't present in wallet",
            WalletIncompatiblePoolError => "Trying to use wallet with pool that has different name",
            WalletAccessFailed => "Trying to open wallet encrypted wallet with invalid credentials",
            WalletAlreadyOpenedError => "Trying to open wallet that was opened already",
            WalletInputError => "Input provided to wallet operations is considered not valid",
            WalletDecodingError => "Decoding of wallet data during input/output failed",
            WalletStorageError => "Storage error occurred during wallet operation",
            WalletEncryptionError => "Error during encryption-related operations",
            WalletItemNotFound => "Requested wallet item not found",
            WalletItemAlreadyExists => "Returned if wallet's add_record operation is used with record name that already exists",
            WalletQueryError => "Returned if provided wallet query is invalid",
            PoolLedgerNotCreatedError => "Trying to open pool ledger that wasn't created before",
            PoolLedgerInvalidPoolHandle => "Caller passed invalid pool ledger handle",
            PoolLedgerTerminated => "Pool ledger terminated",
            LedgerNoConsensusError => "No concensus during ledger operation",
            LedgerInvalidTransaction => "Attempt to send unknown or incomplete transaction message",
            LedgerSecurityError => "Attempt to send transaction without the necessary privileges",
            PoolLedgerConfigAlreadyExistsError => "Attempt to create pool ledger config with name used for another existing pool",
            AnoncredsRevocationRegistryFullError => "Revocation registry is full and creation of new registry is necessary",
            AnoncredsInvalidUserRevocIndex => "Invalid user revocation index",
            AnoncredsAccumulatorIsFull => "Revocation accumulator is full",
            AnoncredsNotIssuedError => "Not issued",
            AnoncredsMasterSecretDuplicateNameError => "Attempt to generate master secret with duplicated name",
            AnoncredsProofRejected => "Proof rejected",
            AnoncredsCredentialRevoked => "Credential revoked",
            AnoncredsCredDefAlreadyExistsError => "Credential definition already exists",
            UnknownCryptoTypeError => "Unknown format of DID entity keys",
            DidAlreadyExistsError => "Did already exists",
            UnknownPaymentMethod => "Unknown payment method was given",
            IncompatiblePaymentError => "Multiple different payment methods were specified",
            PaymentInsufficientFundsError => "Payment cannot be processed because there was insufficient funds"
        }
    }

    pub fn is_ok(&self) -> bool {
        *self == ErrorCode::Success
    }

    pub fn is_err(&self) -> bool {
        *self != ErrorCode::Success
    }

    pub fn try_err(&self) -> Result<(), ErrorCode> {
        if self.is_err() {
            return Err(*self)
        }
        Ok(())
    }
}

impl From<i32> for ErrorCode {
    fn from(i: i32) -> Self {
        let conversion = num_traits::FromPrimitive::from_i32(i);
        if conversion.is_some() {
            conversion.unwrap()
        } else {
            panic!("Unable to convert from {}, unknown error code", i)
        }
    }
}

impl Into<i32> for ErrorCode {
    fn into(self) -> i32 {
        num_traits::ToPrimitive::to_i32(&self).unwrap()
    }
}

impl From<mpsc::RecvTimeoutError> for ErrorCode {
    fn from(err: mpsc::RecvTimeoutError) -> Self {
        match err {
            mpsc::RecvTimeoutError::Timeout => {
                warn!("Timed out waiting for libindy to call back");
                ErrorCode::CommonIOError
            },
            mpsc::RecvTimeoutError::Disconnected => {
                warn!("Channel to libindy was disconnected unexpectedly");
                ErrorCode::CommonIOError
            }
        }
    }
}

impl From<mpsc::RecvError> for ErrorCode {
    fn from(e: mpsc::RecvError) -> Self {
        warn!("Channel returned an error - {:?}", e);
        ErrorCode::CommonIOError
    }
}
<commit_msg>new payment error code<commit_after>#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;

extern crate num_traits;
#[macro_use]
extern crate num_derive;

extern crate libindy_sys as ffi;

#[macro_use]
mod macros;

pub mod anoncreds;
pub mod blob_storage;
pub mod crypto;
pub mod did;
pub mod ledger;
pub mod payments;
pub mod pairwise;
pub mod pool;
pub mod wallet;
pub mod utils;

use std::sync::mpsc;

pub type IndyHandle = i32;

#[derive(Debug, PartialEq, Copy, Clone, FromPrimitive, ToPrimitive)]
#[repr(i32)]
#[allow(dead_code)]
pub enum ErrorCode
{
    Success = 0,

    // Common errors

    // Caller passed invalid value as param 1 (null, invalid json and etc..)
    CommonInvalidParam1 = 100,

    // Caller passed invalid value as param 2 (null, invalid json and etc..)
    CommonInvalidParam2 = 101,

    // Caller passed invalid value as param 3 (null, invalid json and etc..)
    CommonInvalidParam3 = 102,

    // Caller passed invalid value as param 4 (null, invalid json and etc..)
    CommonInvalidParam4 = 103,

    // Caller passed invalid value as param 5 (null, invalid json and etc..)
    CommonInvalidParam5 = 104,

    // Caller passed invalid value as param 6 (null, invalid json and etc..)
    CommonInvalidParam6 = 105,

    // Caller passed invalid value as param 7 (null, invalid json and etc..)
    CommonInvalidParam7 = 106,

    // Caller passed invalid value as param 8 (null, invalid json and etc..)
    CommonInvalidParam8 = 107,

    // Caller passed invalid value as param 9 (null, invalid json and etc..)
    CommonInvalidParam9 = 108,

    // Caller passed invalid value as param 10 (null, invalid json and etc..)
    CommonInvalidParam10 = 109,

    // Caller passed invalid value as param 11 (null, invalid json and etc..)
    CommonInvalidParam11 = 110,

    // Caller passed invalid value as param 11 (null, invalid json and etc..)
    CommonInvalidParam12 = 111,

    // Invalid library state was detected in runtime. It signals library bug
    CommonInvalidState = 112,

    // Object (json, config, key, credential and etc...) passed by library caller has invalid structure
    CommonInvalidStructure = 113,

    // IO Error
    CommonIOError = 114,

    // Caller passed invalid value as param 13 (null, invalid json and etc..)
    CommonInvalidParam13 = 115,

    // Caller passed invalid value as param 14 (null, invalid json and etc..)
    CommonInvalidParam14 = 116,

    // Wallet errors
    // Caller passed invalid wallet handle
    WalletInvalidHandle = 200,

    // Unknown type of wallet was passed on create_wallet
    WalletUnknownTypeError = 201,

    // Attempt to register already existing wallet type
    WalletTypeAlreadyRegisteredError = 202,

    // Attempt to create wallet with name used for another exists wallet
    WalletAlreadyExistsError = 203,

    // Requested entity id isn't present in wallet
    WalletNotFoundError = 204,

    // Trying to use wallet with pool that has different name
    WalletIncompatiblePoolError = 205,

    // Trying to open wallet that was opened already
    WalletAlreadyOpenedError = 206,

    // Attempt to open encrypted wallet with invalid credentials
    WalletAccessFailed = 207,

    // Input provided to wallet operations is considered not valid
    WalletInputError = 208,

    // Decoding of wallet data during input/output failed
    WalletDecodingError = 209,

    // Storage error occurred during wallet operation
    WalletStorageError = 210,

    // Error during encryption-related operations
    WalletEncryptionError = 211,

    // Requested wallet item not found
    WalletItemNotFound = 212,

    // Returned if wallet's add_record operation is used with record name that already exists
    WalletItemAlreadyExists = 213,

    // Returned if provided wallet query is invalid
    WalletQueryError = 214,

    // Ledger errors
    // Trying to open pool ledger that wasn't created before
    PoolLedgerNotCreatedError = 300,

    // Caller passed invalid pool ledger handle
    PoolLedgerInvalidPoolHandle = 301,

    // Pool ledger terminated
    PoolLedgerTerminated = 302,

    // No concensus during ledger operation
    LedgerNoConsensusError = 303,

    // Attempt to parse invalid transaction response
    LedgerInvalidTransaction = 304,

    // Attempt to send transaction without the necessary privileges
    LedgerSecurityError = 305,

    // Attempt to create pool ledger config with name used for another existing pool
    PoolLedgerConfigAlreadyExistsError = 306,

    // Timeout for action
    PoolLedgerTimeout = 307,

    // Revocation registry is full and creation of new registry is necessary
    AnoncredsRevocationRegistryFullError = 400,

    AnoncredsInvalidUserRevocIndex = 401,

    AnoncredsAccumulatorIsFull = 402,

    AnoncredsNotIssuedError = 403,

    // Attempt to generate master secret with duplicated name
    AnoncredsMasterSecretDuplicateNameError = 404,

    AnoncredsProofRejected = 405,

    AnoncredsCredentialRevoked = 406,

    // Attempt to create credential definition with duplicated did schema pair
    AnoncredsCredDefAlreadyExistsError = 407,

    // Signus errors
    // Unknown format of DID entity keys
    UnknownCryptoTypeError = 500,

    // Attempt to create duplicate did
    DidAlreadyExistsError = 600,

    // Unknown payment method was given
    UnknownPaymentMethod = 700,

    //No method were scraped from inputs/outputs or more than one were scraped
    IncompatiblePaymentError = 701,

    // Insufficient funds on inputs
    PaymentInsufficientFundsError = 702,

    // Extra funds on inputs
    PaymentExtraFundsError = 705
}

impl ErrorCode {
    #[allow(non_snake_case)]
    #[allow(dead_code)]
    #[allow(unused)]
    pub fn description(&self) -> &'static str {
        match self {
            CommonInvalidParam1 => "Caller passed invalid value as param 1",
            CommonInvalidParam2 => "Caller passed invalid value as param 2",
            CommonInvalidParam3 => "Caller passed invalid value as param 3",
            CommonInvalidParam4 => "Caller passed invalid value as param 4",
            CommonInvalidParam5 => "Caller passed invalid value as param 5",
            CommonInvalidParam6 => "Caller passed invalid value as param 6",
            CommonInvalidParam7 => "Caller passed invalid value as param 7",
            CommonInvalidParam8 => "Caller passed invalid value as param 8",
            CommonInvalidParam9 => "Caller passed invalid value as param 9",
            CommonInvalidParam10 => "Caller passed invalid value as param 10",
            CommonInvalidParam11 => "Caller passed invalid value as param 11",
            CommonInvalidParam12 => "Caller passed invalid value as param 12",
            CommonInvalidParam13 => "Caller passed invalid value as param 13",
            CommonInvalidParam14 => "Caller passed invalid value as param 14",
            CommonInvalidState => "Invalid library state was detected in runtime. It signals library bug",
            CommonInvalidStructure => "Object (json, config, key, credential and etc...) passed by library caller has invalid structure",
            CommonIOError => "IO Error",
            WalletInvalidHandle => "Caller passed invalid wallet handle",
            WalletUnknownTypeError => "Caller passed invalid wallet handle",
            WalletTypeAlreadyRegisteredError => "Attempt to register already existing wallet type",
            WalletAlreadyExistsError => "Attempt to create wallet with name used for another exists wallet",
            WalletNotFoundError => "Requested entity id isn't present in wallet",
            WalletIncompatiblePoolError => "Trying to use wallet with pool that has different name",
            WalletAccessFailed => "Trying to open wallet encrypted wallet with invalid credentials",
            WalletAlreadyOpenedError => "Trying to open wallet that was opened already",
            WalletInputError => "Input provided to wallet operations is considered not valid",
            WalletDecodingError => "Decoding of wallet data during input/output failed",
            WalletStorageError => "Storage error occurred during wallet operation",
            WalletEncryptionError => "Error during encryption-related operations",
            WalletItemNotFound => "Requested wallet item not found",
            WalletItemAlreadyExists => "Returned if wallet's add_record operation is used with record name that already exists",
            WalletQueryError => "Returned if provided wallet query is invalid",
            PoolLedgerNotCreatedError => "Trying to open pool ledger that wasn't created before",
            PoolLedgerInvalidPoolHandle => "Caller passed invalid pool ledger handle",
            PoolLedgerTerminated => "Pool ledger terminated",
            LedgerNoConsensusError => "No concensus during ledger operation",
            LedgerInvalidTransaction => "Attempt to send unknown or incomplete transaction message",
            LedgerSecurityError => "Attempt to send transaction without the necessary privileges",
            PoolLedgerConfigAlreadyExistsError => "Attempt to create pool ledger config with name used for another existing pool",
            AnoncredsRevocationRegistryFullError => "Revocation registry is full and creation of new registry is necessary",
            AnoncredsInvalidUserRevocIndex => "Invalid user revocation index",
            AnoncredsAccumulatorIsFull => "Revocation accumulator is full",
            AnoncredsNotIssuedError => "Not issued",
            AnoncredsMasterSecretDuplicateNameError => "Attempt to generate master secret with duplicated name",
            AnoncredsProofRejected => "Proof rejected",
            AnoncredsCredentialRevoked => "Credential revoked",
            AnoncredsCredDefAlreadyExistsError => "Credential definition already exists",
            UnknownCryptoTypeError => "Unknown format of DID entity keys",
            DidAlreadyExistsError => "Did already exists",
            UnknownPaymentMethod => "Unknown payment method was given",
            IncompatiblePaymentError => "Multiple different payment methods were specified",
            PaymentInsufficientFundsError => "Payment cannot be processed because there was insufficient funds",
            PaymentExtraFundsError => "Payment cannot be processed because there were more funds than required"
        }
    }

    pub fn is_ok(&self) -> bool {
        *self == ErrorCode::Success
    }

    pub fn is_err(&self) -> bool {
        *self != ErrorCode::Success
    }

    pub fn try_err(&self) -> Result<(), ErrorCode> {
        if self.is_err() {
            return Err(*self)
        }
        Ok(())
    }
}

impl From<i32> for ErrorCode {
    fn from(i: i32) -> Self {
        let conversion = num_traits::FromPrimitive::from_i32(i);
        if conversion.is_some() {
            conversion.unwrap()
        } else {
            panic!("Unable to convert from {}, unknown error code", i)
        }
    }
}

impl Into<i32> for ErrorCode {
    fn into(self) -> i32 {
        num_traits::ToPrimitive::to_i32(&self).unwrap()
    }
}

impl From<mpsc::RecvTimeoutError> for ErrorCode {
    fn from(err: mpsc::RecvTimeoutError) -> Self {
        match err {
            mpsc::RecvTimeoutError::Timeout => {
                warn!("Timed out waiting for libindy to call back");
                ErrorCode::CommonIOError
            },
            mpsc::RecvTimeoutError::Disconnected => {
                warn!("Channel to libindy was disconnected unexpectedly");
                ErrorCode::CommonIOError
            }
        }
    }
}

impl From<mpsc::RecvError> for ErrorCode {
    fn from(e: mpsc::RecvError) -> Self {
        warn!("Channel returned an error - {:?}", e);
        ErrorCode::CommonIOError
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate hyper_native_tls;
extern crate pbr;
extern crate regex;

use pbr::ProgressBar;
use std::{process,str};
use std::collections::HashMap;
use hyper::client::response::Response;
use hyper::Client;
use hyper::net::HttpsConnector;
use hyper_native_tls::NativeTlsClient;
use hyper::header::ContentLength;
use std::io::Read;
use std::io::prelude::*;
use std::fs::File;
use regex::Regex;

pub struct Info {
    pub video_url: String,
}

pub fn new(video_url: &str) -> Info {
    //Regex for youtube URLs.
    let url_regex = Regex::new(r"^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*").unwrap();
    let mut vid = "https://www.youtube.com/watch?v=DjMkfARvGE8";
    if url_regex.is_match(vid) {
        let vid_split = url_regex.captures(vid).unwrap();
        vid = vid_split.get(1).unwrap().as_str();
    }
    let url = format!("https://youtube.com/get_video_info?video_id={}", vid);
    download(&url);
    Info { video_url: video_url.to_string() }
}

fn download(url: &str) {
    let mut response = send_request(url);
    let mut response_str = String::new();
    response.read_to_string(&mut response_str).unwrap();
    let hq = parse_url(&response_str);

    if hq["status"] != "ok" {
        println!("Video not found!");
        process::exit(1);
    }

    // get video info
    let streams: Vec<&str> = hq["url_encoded_fmt_stream_map"]
        .split(',')
        .collect();

    // list of available qualities
    let mut qualities: HashMap<i32, (String, String)> = HashMap::new();
    for (i, url) in streams.iter().enumerate() {
        let quality = parse_url(url);
        let extension = quality["type"]
            .split('/')
            .nth(1)
            .unwrap()
            .split(';')
            .next()
            .unwrap();
        qualities.insert(i as i32,
                         (quality["url"].to_string(), extension.to_owned()));
        println!("{}- {} {}",
                 i,
                 quality["quality"],
                 quality["type"]);
    }
}

fn send_request(url: &str) -> Response {
    let ssl = NativeTlsClient::new().unwrap();
    let connector = HttpsConnector::new(ssl);
    let client = Client::with_connector(connector);
    client.get(url).send().unwrap_or_else(|e| {
        println!("Network request failed: {}", e);
        process::exit(1);
    })
}

fn parse_url(query: &str) -> HashMap<String, String> {
    let u = format!("{}{}", "http://e.com?", query);
    let parsed_url = hyper::Url::parse(&u).unwrap();
    parsed_url.query_pairs().into_owned().collect()
}
<commit_msg>Add more pub variables<commit_after>extern crate hyper;
extern crate hyper_native_tls;
extern crate pbr;
extern crate regex;

use pbr::ProgressBar;
use std::{process,str};
use std::collections::HashMap;
use hyper::client::response::Response;
use hyper::Client;
use hyper::net::HttpsConnector;
use hyper_native_tls::NativeTlsClient;
use hyper::header::ContentLength;
use std::io::Read;
use std::io::prelude::*;
use std::fs::File;
use regex::Regex;

pub struct Info {
    pub url: String,
    pub title: String,
    //pub rating: ,
    pub viewcount: u32,
    pub author: String,
    pub length: u32,
    pub duration: String,
    pub likes: u32,
    pub dislikes: u32,
    pub description: String,
    //pub streams: ,
    //pub audiostreams: ,
    //pub allstreams: ,
}

pub fn new(video_url: &str) -> Info {
    //Regex for youtube URLs.
    let url_regex = Regex::new(r"^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*").unwrap();
    let mut vid = "https://www.youtube.com/watch?v=DjMkfARvGE8";
    if url_regex.is_match(vid) {
        let vid_split = url_regex.captures(vid).unwrap();
        vid = vid_split.get(1).unwrap().as_str();
    }
    let url = format!("https://youtube.com/get_video_info?video_id={}", vid);
    download(&url);
    Info { video_url: video_url.to_string() }
}

fn download(url: &str) {
    let mut response = send_request(url);
    let mut response_str = String::new();
    response.read_to_string(&mut response_str).unwrap();
    let hq = parse_url(&response_str);

    if hq["status"] != "ok" {
        println!("Video not found!");
        process::exit(1);
    }

    // get video info
    let streams: Vec<&str> = hq["url_encoded_fmt_stream_map"]
        .split(',')
        .collect();

    // list of available qualities
    let mut qualities: HashMap<i32, (String, String)> = HashMap::new();
    for (i, url) in streams.iter().enumerate() {
        let quality = parse_url(url);
        let extension = quality["type"]
            .split('/')
            .nth(1)
            .unwrap()
            .split(';')
            .next()
            .unwrap();
        qualities.insert(i as i32,
                         (quality["url"].to_string(), extension.to_owned()));
        println!("{}- {} {}",
                 i,
                 quality["quality"],
                 quality["type"]);
    }
}

fn send_request(url: &str) -> Response {
    let ssl = NativeTlsClient::new().unwrap();
    let connector = HttpsConnector::new(ssl);
    let client = Client::with_connector(connector);
    client.get(url).send().unwrap_or_else(|e| {
        println!("Network request failed: {}", e);
        process::exit(1);
    })
}

fn parse_url(query: &str) -> HashMap<String, String> {
    let u = format!("{}{}", "http://e.com?", query);
    let parsed_url = hyper::Url::parse(&u).unwrap();
    parsed_url.query_pairs().into_owned().collect()
}
<|endoftext|>


<commit_before>

pub mod graph {
    use std::collections::HashMap;
    use std::ops::Deref;

    #[derive(Debug)]
    pub struct Graph {
        elements: i64,
        vertices: HashMap<i64, *mut Box<Vertex>>,
    }

    impl Graph {
        pub fn new() -> Box<Graph> {
            let vertices: HashMap<i64, *mut Box<Vertex>> = HashMap::new();
            Box::new(Graph{elements:0, vertices:vertices})
        }

        pub fn add_vertex(&mut self) -> VertexProxy {
            let new_id = self.elements + 1;
            self.elements += 1;

            let mut v = Vertex::new(new_id);

            let ptr: *mut Box<Vertex> = &mut v as *mut Box<Vertex>;

            self.vertices.insert(new_id, ptr);

            // return the proxy which knows it's own id
            VertexProxy{id:new_id, v: ptr}
        }
        pub fn get(vertex_id:i64)  {
        }
    }

    /*
    I'm not sure on this, but it feels reasonable.  a vertex can own it's out_edges.
    this should? make it easier to manage the memory?

    additionally, it should cut down on the number of "things" i have to traverse
    if a vertex has 10k edges (5k in and out 5k out) then doing something like

    g(v).outV()

    should be faster to iterate over

    the cost is a little code complexity but i think that won't be too bad
     */
    #[derive(Debug)]
    pub struct Vertex {
        pub id: i64,

        // pointers on both sides, yay
        pub out_edges: Vec<*mut Edge>,
        pub in_edges:  Vec<*mut Edge>,
    }

    impl Vertex {
        pub fn new(id: i64) -> Box<Vertex> {

            // in edges
            let in_edges : Vec<*mut Edge> = Vec::new();
            let out_edges: Vec<*mut Edge> = Vec::new();

            Box::new(Vertex{id:id,
                            out_edges: out_edges,
                            in_edges:in_edges})
        }

        pub fn add_out_edge(&mut self, edge: &mut Edge ) {
            let edge_p: *mut Edge = edge;
            self.out_edges.push(edge_p);
        }

        pub fn add_in_edge() {

        }

    }

    pub struct VertexProxy {
        pub id: i64,
        pub v: *mut Box<Vertex>,
    }

    //let i: u32 = 1;
    //// explicit cast
    //let p_imm: *const u32 = &i as *const u32;
    //let mut m: u32 = 2;
    //// implicit coercion
    //let p_mut: *mut u32 = &mut m;
    //
    //unsafe {
    //let ref_imm: &u32 = &*p_imm;
    //let ref_mut: &mut u32 = &mut *p_mut;
    //}

    impl VertexProxy {
        pub fn add_edge(&mut self, to_vertex: &mut VertexProxy) {
            let in_vertex: &mut Box<Vertex>;
            let out_vertex: &mut Box<Vertex>;

            unsafe {
                in_vertex =  &mut *(self.v);
                out_vertex = &mut *(to_vertex.v);
            }

            // create the edge
            let mut e = Edge{from_vertex: self.v, to_vertex: to_vertex.v};
            in_vertex.add_out_edge(&mut e);
        }
        pub fn query(self) {

        }
    }

    impl Deref for VertexProxy {
        type Target = Box<Vertex>;

        fn deref<'a>(&'a self) -> &'a Box<Vertex> {
            unsafe {
                &*(self.v)
            }
        }
    }

    #[derive(Debug)]
    pub struct Edge {
        from_vertex: *const Box<Vertex>,
        to_vertex: *const Box<Vertex>
    }
}

mod tests {
    use graph::{Graph, Vertex};

    #[test]
    fn test_unsafe_vertex() {
        let v = Vertex::new(1);

        let out_edges = &v.out_edges;
        let in_edges = &v.in_edges;
    }

    #[test]
    fn test_deref() {
        let mut g = Graph::new();
        let v1 = g.add_vertex();
        let vertex = &*v1;
        assert!(vertex.id == 1);
    }

    #[test]
    fn test_new_vertex() {
        let mut g = Graph::new();
        let v1 = g.add_vertex();

        assert_eq!(v1.id, 1);

        unsafe {
            let vertex = &*v1;
            assert_eq!((*v1).id, 1);
        }

        let v2 = g.add_vertex();
        assert!(v2.id == 2);
    }

    #[test]
    fn test_add_edge() {
        let mut g = Graph::new();
        let mut v1 = g.add_vertex();
        let mut v2 = g.add_vertex();

        //v1.add_edge(&mut v2);
    }
}
<commit_msg>transmute the boxed vertex to a vertex pointer<commit_after>

pub mod graph {
    use std::collections::HashMap;
    use std::ops::Deref;
    use std::mem;

    #[derive(Debug)]
    pub struct Graph {
        elements: i64,
        vertices: HashMap<i64, *mut Vertex>,
    }

    impl Graph {
        pub fn new() -> Box<Graph> {
            let vertices: HashMap<i64, *mut Vertex> = HashMap::new();
            Box::new(Graph{elements:0, vertices:vertices})
        }

        pub fn add_vertex(&mut self) -> VertexProxy {
            let new_id = self.elements + 1;
            self.elements += 1;

            let mut v = Vertex::new(new_id);

            // let v: &[u8] = unsafe { mem::transmute("L") };
            //let ptr: *mut Box<Vertex> = &mut v as *mut Box<Vertex>;
            let ptr: *mut Vertex = unsafe { mem::transmute(v) };

            self.vertices.insert(new_id, ptr);

            // return the proxy which knows it's own id
            VertexProxy{id:new_id, v: ptr}
        }
        pub fn get(vertex_id:i64)  {
        }
    }

    /*
    I'm not sure on this, but it feels reasonable.  a vertex can own it's out_edges.
    this should? make it easier to manage the memory?

    additionally, it should cut down on the number of "things" i have to traverse
    if a vertex has 10k edges (5k in and out 5k out) then doing something like

    g(v).outV()

    should be faster to iterate over

    the cost is a little code complexity but i think that won't be too bad
     */
    #[derive(Debug)]
    pub struct Vertex {
        pub id: i64,

        // pointers on both sides, yay
        pub out_edges: Vec<*mut Edge>,
        pub in_edges:  Vec<*mut Edge>,
    }

    impl Vertex {
        pub fn new(id: i64) -> Box<Vertex> {

            // in edges
            let in_edges : Vec<*mut Edge> = Vec::new();
            let out_edges: Vec<*mut Edge> = Vec::new();

            Box::new(Vertex{id:id,
                            out_edges: out_edges,
                            in_edges:in_edges})
        }

        pub fn add_out_edge(&mut self, edge: &mut Edge ) {
            let edge_p: *mut Edge = edge;
            self.out_edges.push(edge_p);
        }

        pub fn add_in_edge() {

        }

    }

    pub struct VertexProxy {
        pub id: i64,
        pub v: *mut Vertex,
    }

    //let i: u32 = 1;
    //// explicit cast
    //let p_imm: *const u32 = &i as *const u32;
    //let mut m: u32 = 2;
    //// implicit coercion
    //let p_mut: *mut u32 = &mut m;
    //
    //unsafe {
    //let ref_imm: &u32 = &*p_imm;
    //let ref_mut: &mut u32 = &mut *p_mut;
    //}

    impl VertexProxy {
        pub fn add_edge(&mut self, to_vertex: &mut VertexProxy) {
            let in_vertex: &mut Vertex;
            let out_vertex: &mut Vertex;

            unsafe {
                in_vertex =  &mut *(self.v);
                out_vertex = &mut *(to_vertex.v);
            }

            // create the edge
            let mut e = Edge{from_vertex: self.v, to_vertex: to_vertex.v};
            in_vertex.add_out_edge(&mut e);
        }
        pub fn query(self) {

        }
    }

    impl Deref for VertexProxy {
        type Target = Vertex;

        fn deref<'a>(&'a self) -> &'a Vertex {
            unsafe {
                &*(self.v)
            }
        }
    }

    #[derive(Debug)]
    pub struct Edge {
        from_vertex: *const Vertex,
        to_vertex: *const Vertex
    }
}

mod tests {
    use graph::{Graph, Vertex};

    #[test]
    fn test_unsafe_vertex() {
        let v = Vertex::new(1);

        let out_edges = &v.out_edges;
        let in_edges = &v.in_edges;
    }

    #[test]
    fn test_deref() {
        let mut g = Graph::new();
        let v1 = g.add_vertex();
        let vertex = &*v1;
        assert!(vertex.id == 1);
    }

    #[test]
    fn test_new_vertex() {
        let mut g = Graph::new();
        let v1 = g.add_vertex();

        assert_eq!(v1.id, 1);

        unsafe {
            let vertex = &*v1;
            assert_eq!((*v1).id, 1);
        }

        let v2 = g.add_vertex();
        assert!(v2.id == 2);
    }

    #[test]
    fn test_add_edge() {
        let mut g = Graph::new();
        let mut v1 = g.add_vertex();
        let mut v2 = g.add_vertex();

        v1.add_edge(&mut v2);
    }
}
<|endoftext|>