<commit_before>use std::ptr;
use std::error;
use std::fmt;
use std::borrow::BorrowMut;
use std::marker::PhantomData;
use std::ffi::CString;
use gl;
use gl::types::*;
use types::GLError;
use context::{AContext, ContextOf};
use shader::Shader;
use uniform_data::{UniformData, UniformDatumType, UniformPrimitiveType};

pub struct Program {
    gl_id: GLuint
}

impl Program {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Program {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteProgram(self.gl_id);
        }
    }
}

unsafe fn _get_program_iv(program: &Program,
                          pname: GLenum,
                          params: *mut GLint)
{
    gl::GetProgramiv(program.gl_id(), pname, params);
    dbg_gl_sanity_check! {
        GLError::InvalidEnum => "`pname` is not an accepted value",
        GLError::InvalidValue => "`program` is not a value generated by OpenGL",
        GLError::InvalidOperation => "`program` does not refer to a program object",
        _ => "Unknown error"
    }
}

pub struct ProgramBuilder<'a, C>
    where C: AContext + 'a
{
    gl: &'a C,
    shaders: &'a [Shader]
}

impl<'a, C> ProgramBuilder<'a, C>
    where C: AContext
{
    pub fn new(gl: &'a C, shaders: &'a [Shader])
        -> Self
    {
        ProgramBuilder { gl: gl, shaders: shaders }
    }

    pub fn try_unwrap(self) -> Result<Program, GLError> {
        unsafe {
            let mut program = try! {
                self.gl.create_program().or_else(|_| {
                    let msg = "Error creating OpenGL program";
                    Err(GLError::Message(msg.to_owned()))
                })
            };

            for shader in self.shaders {
                self.gl.attach_shader(&mut program, shader);
            }

            try!(self.gl.link_program(&mut program));
            Ok(program)
        }
    }

    pub fn unwrap(self) -> Program {
        self.try_unwrap().unwrap()
    }
}

pub trait ContextProgramBuilderExt: AContext + Sized {
    fn build_program<'a>(&'a self, shaders: &'a [Shader])
        -> ProgramBuilder<'a, Self>;
}

impl<C: AContext> ContextProgramBuilderExt for C {
    fn build_program<'a>(&'a self, shaders: &'a [Shader])
        -> ProgramBuilder<'a, C>
    {
        ProgramBuilder::new(self, shaders)
    }
}

pub unsafe trait ContextProgramExt {
    unsafe fn create_program(&self) -> Result<Program, ()> {
        let id = gl::CreateProgram();
        if id > 0 {
            Ok(Program { gl_id: id })
        }
        else {
            Err(())
        }
    }

    fn attach_shader(&self, program: &mut Program, shader: &Shader) {
        unsafe {
            gl::AttachShader(program.gl_id(), shader.gl_id());
            dbg_gl_error! {
                GLError::InvalidValue => "One of either `program` or `shader` is not an OpenGL object",
                GLError::InvalidOperation => "`shader` is already attached to `program`, `shader` is not a shader object, or `program` is not a program object",
                _ => "Unknown error"
            }
        }
    }

    fn link_program(&self, program: &mut Program) -> Result<(), GLError> {
        let success = unsafe {
            gl::LinkProgram(program.gl_id());
            dbg_gl_error! {
                GLError::InvalidValue => "`program` is not a value from OpenGL",
                GLError::InvalidOperation => "`program` is not a program object",
                _ => "Unknown error"
            }

            let mut link_status : GLint = 0;
            _get_program_iv(program,
                            gl::LINK_STATUS,
                            &mut link_status as *mut GLint);

            link_status == gl::TRUE as GLint
        };

        if success {
            Ok(())
        }
        else {
            let msg = match self.get_program_info_log(&program) {
                Some(s) => { s },
                None => { String::from("[Unknown program error]") }
            };
            Err(GLError::Message(msg))
        }
    }

    fn get_program_info_log(&self, program: &Program) -> Option<String> {
        unsafe {
            let mut info_length : GLint = 0;
            _get_program_iv(program,
                            gl::INFO_LOG_LENGTH,
                            &mut info_length as *mut GLint);

            if info_length > 0 {
                let mut bytes = Vec::<u8>::with_capacity(info_length as usize);

                gl::GetProgramInfoLog(program.gl_id(),
                                      info_length,
                                      ptr::null_mut(),
                                      bytes.as_mut_ptr() as *mut GLchar);
                dbg_gl_sanity_check! {
                    GLError::InvalidValue => "`program` is not a value generated by OpenGL, or `maxLength` < 0",
                    GLError::InvalidOperation => "`program` is not a program object",
                    _ => "Unknown error"
                }
                bytes.set_len((info_length - 1) as usize);

                String::from_utf8(bytes).ok()
            }
            else {
                None
            }
        }
    }

    fn get_attrib_location<'a>(&self, program: &Program, name: &'a str)
        -> Result<ProgramAttrib, UnknownProgramAttrib<'a>>
    {
        let err = Err(UnknownProgramAttrib { name: name });

        let c_str = match CString::new(name) {
            Ok(s) => { s },
            Err(_) => { return err }
        };

        let str_ptr = c_str.as_ptr() as *const GLchar;
        unsafe {
            let index = gl::GetAttribLocation(program.gl_id(), str_ptr);
            dbg_gl_error! {
                GLError::InvalidOperation => "`program` has not been linked, `program` is not a program object, or `program` is not a value generated by OpenGL",
                _ => "Unknown error"
            }

            if index >= 0 {
                Ok(ProgramAttrib { gl_index: index as GLuint })
            }
            else {
                err
            }
        }
    }

    fn get_uniform_location<'a>(&self, program: &Program, name: &'a str)
        -> Result<ProgramUniform, UnknownProgramUniform<'a>>
    {
        let err = Err(UnknownProgramUniform { name: name });

        let c_str = match CString::new(name) {
            Ok(s) => { s },
            Err(_) => { return err; }
        };

        let str_ptr = c_str.as_ptr() as *const GLchar;
        unsafe {
            let index = gl::GetUniformLocation(program.gl_id(), str_ptr);
            dbg_gl_error! {
                GLError::InvalidValue => "`program` is not a value generated by OpenGL",
                GLError::InvalidOperation => "`program` is not a program object, or has not been successfully linked",
                _ => "Unknown error"
            }

            if index >= 0 {
                Ok(ProgramUniform { gl_index: index as GLuint })
            }
            else {
                err
            }
        }
    }

    fn set_uniform<T>(&self,
                      _gl_program: &ProgramBinding,
                      uniform: ProgramUniform,
                      val: T)
        where T: UniformData
    {
        let idx = uniform.gl_index as GLint;
        let count = val.uniform_elements() as GLsizei;
        let ptr = val.uniform_bytes().as_ptr();
        unsafe {
            match T::uniform_datum_type() {
                UniformDatumType::Vec1(p) => {
                    match p {
                        UniformPrimitiveType::Float => {
                            gl::Uniform1fv(idx, count, ptr as *const GLfloat);
                        },
                        UniformPrimitiveType::Int => {
                            gl::Uniform1iv(idx, count, ptr as *const GLint);
                        }
                    }
                },
                UniformDatumType::Vec2(p) => {
                    match p {
                        UniformPrimitiveType::Float => {
                            gl::Uniform2fv(idx, count, ptr as *const GLfloat);
                        },
                        UniformPrimitiveType::Int => {
                            gl::Uniform2iv(idx, count, ptr as *const GLint);
                        }
                    }
                },
                UniformDatumType::Vec3(p) => {
                    match p {
                        UniformPrimitiveType::Float => {
                            gl::Uniform3fv(idx, count, ptr as *const GLfloat);
                        },
                        UniformPrimitiveType::Int => {
                            gl::Uniform3iv(idx, count, ptr as *const GLint);
                        }
                    }
                },
                UniformDatumType::Vec4(p) => {
                    match p {
                        UniformPrimitiveType::Float => {
                            gl::Uniform4fv(idx, count, ptr as *const GLfloat);
                        },
                        UniformPrimitiveType::Int => {
                            gl::Uniform4iv(idx, count, ptr as *const GLint);
                        }
                    }
                },
                UniformDatumType::Matrix2x2 => {
                    gl::UniformMatrix2fv(idx,
                                         count,
                                         gl::FALSE,
                                         ptr as *const GLfloat);
                },
                UniformDatumType::Matrix3x3 => {
                    gl::UniformMatrix3fv(idx,
                                         count,
                                         gl::FALSE,
                                         ptr as *const GLfloat);
                },
                UniformDatumType::Matrix4x4 => {
                    gl::UniformMatrix4fv(idx,
                                         count,
                                         gl::FALSE,
                                         ptr as *const GLfloat);
                },
            }

            dbg_gl_error! {
                GLError::InvalidOperation => "Invalid uniform operation",
                GLError::InvalidValue => "`count` < 0 or `transpose` is not GL_FALSE",
                _ => "Unknown error"
            }
        }
    }
}

unsafe impl<B, F, P, R, T> ContextProgramExt for ContextOf<B, F, P, R, T> {

}

unsafe impl<'b, B, F, P, R, T> ContextProgramExt
    for &'b mut ContextOf<B, F, P, R, T>
{

}

pub trait ProgramContext: AContext {
    type Rest: AContext;

    fn use_program<'a>(self, program: &'a mut Program)
        -> (ProgramBinding<'a>, Self::Rest);
}

impl<B, F, P, R, T> ProgramContext for ContextOf<B, F, P, R, T>
    where P: BorrowMut<ProgramBinder>
{
    type Rest = ContextOf<B, F, (), R, T>;

    fn use_program<'a>(self, program: &'a mut Program)
        -> (ProgramBinding<'a>, Self::Rest)
    {
        let (mut program_binder, rest) = self.split_program();
        (program_binder.borrow_mut().bind(program), rest)
    }
}

impl<'b, B, F, P, R, T> ProgramContext for &'b mut ContextOf<B, F, P, R, T>
    where P: BorrowMut<ProgramBinder>
{
    type Rest = ContextOf<&'b mut B, &'b mut F, (), &'b mut R, &'b mut T>;

    fn use_program<'a>(self, program: &'a mut Program)
        -> (ProgramBinding<'a>, Self::Rest)
    {
        let gl = self.mut_into();
        let (program_binder, rest): (&mut P, _) = gl.split_program();
        (program_binder.borrow_mut().bind(program), rest)
    }
}

pub struct ProgramBinding<'a> {
    phantom: PhantomData<&'a mut Program>
}

pub struct ProgramBinder;
impl ProgramBinder {
    pub fn bind<'a>(&mut self, program: &'a mut Program) -> ProgramBinding<'a>
    {
        let binding = ProgramBinding { phantom: PhantomData };
        unsafe {
            gl::UseProgram(program.gl_id());
            dbg_gl_error! {
                GLError::InvalidValue => "`program` is neither 0 nor an object generated by OpenGL",
                GLError::InvalidOperation => "`program` is not a program object or `program` could not be made part of the current state",
                _ => "Unknown error"
            }
        }
        binding
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ProgramAttrib {
    pub gl_index: GLuint
}

#[derive(Debug, Clone, Copy)]
pub struct ProgramUniform {
    pub gl_index: GLuint
}

#[derive(Debug)]
pub struct UnknownProgramAttrib<'a> {
    name: &'a str
}

impl<'a> fmt::Display for UnknownProgramAttrib<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Unknown program attribute: {:?}", self.name)
    }
}

impl<'a> error::Error for UnknownProgramAttrib<'a> {
    fn description(&self) -> &str {
        "The desired program attribute was not found"
    }
}

#[derive(Debug)]
pub struct UnknownProgramUniform<'a> {
    name: &'a str
}

impl<'a> fmt::Display for UnknownProgramUniform<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Unknown program uniform: {:?}", self.name)
    }
}

impl<'a> error::Error for UnknownProgramUniform<'a> {
    fn description(&self) -> &str {
        "The desired program uniform was not found"
    }
}
<commit_msg>Refactor `ProgramContext`<commit_after>use std::ptr;
use std::error;
use std::fmt;
use std::borrow::BorrowMut;
use std::marker::PhantomData;
use std::ffi::CString;
use gl;
use gl::types::*;
use types::GLError;
use context::{AContext, ContextOf};
use shader::Shader;
use uniform_data::{UniformData, UniformDatumType, UniformPrimitiveType};

pub struct Program {
    gl_id: GLuint
}

impl Program {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Program {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteProgram(self.gl_id);
        }
    }
}

unsafe fn _get_program_iv(program: &Program,
                          pname: GLenum,
                          params: *mut GLint)
{
    gl::GetProgramiv(program.gl_id(), pname, params);
    dbg_gl_sanity_check! {
        GLError::InvalidEnum => "`pname` is not an accepted value",
        GLError::InvalidValue => "`program` is not a value generated by OpenGL",
        GLError::InvalidOperation => "`program` does not refer to a program object",
        _ => "Unknown error"
    }
}

pub struct ProgramBuilder<'a, C>
    where C: AContext + 'a
{
    gl: &'a C,
    shaders: &'a [Shader]
}

impl<'a, C> ProgramBuilder<'a, C>
    where C: AContext
{
    pub fn new(gl: &'a C, shaders: &'a [Shader])
        -> Self
    {
        ProgramBuilder { gl: gl, shaders: shaders }
    }

    pub fn try_unwrap(self) -> Result<Program, GLError> {
        unsafe {
            let mut program = try! {
                self.gl.create_program().or_else(|_| {
                    let msg = "Error creating OpenGL program";
                    Err(GLError::Message(msg.to_owned()))
                })
            };

            for shader in self.shaders {
                self.gl.attach_shader(&mut program, shader);
            }

            try!(self.gl.link_program(&mut program));
            Ok(program)
        }
    }

    pub fn unwrap(self) -> Program {
        self.try_unwrap().unwrap()
    }
}

pub trait ContextProgramBuilderExt: AContext + Sized {
    fn build_program<'a>(&'a self, shaders: &'a [Shader])
        -> ProgramBuilder<'a, Self>;
}

impl<C: AContext> ContextProgramBuilderExt for C {
    fn build_program<'a>(&'a self, shaders: &'a [Shader])
        -> ProgramBuilder<'a, C>
    {
        ProgramBuilder::new(self, shaders)
    }
}

pub unsafe trait ContextProgramExt {
    unsafe fn create_program(&self) -> Result<Program, ()> {
        let id = gl::CreateProgram();
        if id > 0 {
            Ok(Program { gl_id: id })
        }
        else {
            Err(())
        }
    }

    fn attach_shader(&self, program: &mut Program, shader: &Shader) {
        unsafe {
            gl::AttachShader(program.gl_id(), shader.gl_id());
            dbg_gl_error! {
                GLError::InvalidValue => "One of either `program` or `shader` is not an OpenGL object",
                GLError::InvalidOperation => "`shader` is already attached to `program`, `shader` is not a shader object, or `program` is not a program object",
                _ => "Unknown error"
            }
        }
    }

    fn link_program(&self, program: &mut Program) -> Result<(), GLError> {
        let success = unsafe {
            gl::LinkProgram(program.gl_id());
            dbg_gl_error! {
                GLError::InvalidValue => "`program` is not a value from OpenGL",
                GLError::InvalidOperation => "`program` is not a program object",
                _ => "Unknown error"
            }

            let mut link_status : GLint = 0;
            _get_program_iv(program,
                            gl::LINK_STATUS,
                            &mut link_status as *mut GLint);

            link_status == gl::TRUE as GLint
        };

        if success {
            Ok(())
        }
        else {
            let msg = match self.get_program_info_log(&program) {
                Some(s) => { s },
                None => { String::from("[Unknown program error]") }
            };
            Err(GLError::Message(msg))
        }
    }

    fn get_program_info_log(&self, program: &Program) -> Option<String> {
        unsafe {
            let mut info_length : GLint = 0;
            _get_program_iv(program,
                            gl::INFO_LOG_LENGTH,
                            &mut info_length as *mut GLint);

            if info_length > 0 {
                let mut bytes = Vec::<u8>::with_capacity(info_length as usize);

                gl::GetProgramInfoLog(program.gl_id(),
                                      info_length,
                                      ptr::null_mut(),
                                      bytes.as_mut_ptr() as *mut GLchar);
                dbg_gl_sanity_check! {
                    GLError::InvalidValue => "`program` is not a value generated by OpenGL, or `maxLength` < 0",
                    GLError::InvalidOperation => "`program` is not a program object",
                    _ => "Unknown error"
                }
                bytes.set_len((info_length - 1) as usize);

                String::from_utf8(bytes).ok()
            }
            else {
                None
            }
        }
    }

    fn get_attrib_location<'a>(&self, program: &Program, name: &'a str)
        -> Result<ProgramAttrib, UnknownProgramAttrib<'a>>
    {
        let err = Err(UnknownProgramAttrib { name: name });

        let c_str = match CString::new(name) {
            Ok(s) => { s },
            Err(_) => { return err }
        };

        let str_ptr = c_str.as_ptr() as *const GLchar;
        unsafe {
            let index = gl::GetAttribLocation(program.gl_id(), str_ptr);
            dbg_gl_error! {
                GLError::InvalidOperation => "`program` has not been linked, `program` is not a program object, or `program` is not a value generated by OpenGL",
                _ => "Unknown error"
            }

            if index >= 0 {
                Ok(ProgramAttrib { gl_index: index as GLuint })
            }
            else {
                err
            }
        }
    }

    fn get_uniform_location<'a>(&self, program: &Program, name: &'a str)
        -> Result<ProgramUniform, UnknownProgramUniform<'a>>
    {
        let err = Err(UnknownProgramUniform { name: name });

        let c_str = match CString::new(name) {
            Ok(s) => { s },
            Err(_) => { return err; }
        };

        let str_ptr = c_str.as_ptr() as *const GLchar;
        unsafe {
            let index = gl::GetUniformLocation(program.gl_id(), str_ptr);
            dbg_gl_error! {
                GLError::InvalidValue => "`program` is not a value generated by OpenGL",
                GLError::InvalidOperation => "`program` is not a program object, or has not been successfully linked",
                _ => "Unknown error"
            }

            if index >= 0 {
                Ok(ProgramUniform { gl_index: index as GLuint })
            }
            else {
                err
            }
        }
    }

    fn set_uniform<T>(&self,
                      _gl_program: &ProgramBinding,
                      uniform: ProgramUniform,
                      val: T)
        where T: UniformData
    {
        let idx = uniform.gl_index as GLint;
        let count = val.uniform_elements() as GLsizei;
        let ptr = val.uniform_bytes().as_ptr();
        unsafe {
            match T::uniform_datum_type() {
                UniformDatumType::Vec1(p) => {
                    match p {
                        UniformPrimitiveType::Float => {
                            gl::Uniform1fv(idx, count, ptr as *const GLfloat);
                        },
                        UniformPrimitiveType::Int => {
                            gl::Uniform1iv(idx, count, ptr as *const GLint);
                        }
                    }
                },
                UniformDatumType::Vec2(p) => {
                    match p {
                        UniformPrimitiveType::Float => {
                            gl::Uniform2fv(idx, count, ptr as *const GLfloat);
                        },
                        UniformPrimitiveType::Int => {
                            gl::Uniform2iv(idx, count, ptr as *const GLint);
                        }
                    }
                },
                UniformDatumType::Vec3(p) => {
                    match p {
                        UniformPrimitiveType::Float => {
                            gl::Uniform3fv(idx, count, ptr as *const GLfloat);
                        },
                        UniformPrimitiveType::Int => {
                            gl::Uniform3iv(idx, count, ptr as *const GLint);
                        }
                    }
                },
                UniformDatumType::Vec4(p) => {
                    match p {
                        UniformPrimitiveType::Float => {
                            gl::Uniform4fv(idx, count, ptr as *const GLfloat);
                        },
                        UniformPrimitiveType::Int => {
                            gl::Uniform4iv(idx, count, ptr as *const GLint);
                        }
                    }
                },
                UniformDatumType::Matrix2x2 => {
                    gl::UniformMatrix2fv(idx,
                                         count,
                                         gl::FALSE,
                                         ptr as *const GLfloat);
                },
                UniformDatumType::Matrix3x3 => {
                    gl::UniformMatrix3fv(idx,
                                         count,
                                         gl::FALSE,
                                         ptr as *const GLfloat);
                },
                UniformDatumType::Matrix4x4 => {
                    gl::UniformMatrix4fv(idx,
                                         count,
                                         gl::FALSE,
                                         ptr as *const GLfloat);
                },
            }

            dbg_gl_error! {
                GLError::InvalidOperation => "Invalid uniform operation",
                GLError::InvalidValue => "`count` < 0 or `transpose` is not GL_FALSE",
                _ => "Unknown error"
            }
        }
    }
}

unsafe impl<B, F, P, R, T> ContextProgramExt for ContextOf<B, F, P, R, T> {

}

unsafe impl<'a, B, F, P, R, T> ContextProgramExt
    for &'a mut ContextOf<B, F, P, R, T>
{

}

pub trait ProgramContext: AContext {
    type Binder: BorrowMut<ProgramBinder>;
    type Rest: AContext;

    fn split_program(self) -> (Self::Binder, Self::Rest);

    fn use_program<'a>(self, program: &'a mut Program)
        -> (ProgramBinding<'a>, Self::Rest)
        where Self: Sized
    {
        let (mut binder, rest) = self.split_program();
        (binder.borrow_mut().bind(program), rest)
    }
}

impl<B, F, P, R, T> ProgramContext for ContextOf<B, F, P, R, T>
    where P: BorrowMut<ProgramBinder>
{
    type Binder = P;
    type Rest = ContextOf<B, F, (), R, T>;

    fn split_program(self) -> (Self::Binder, Self::Rest) {
        self.split_program()
    }
}

impl<'a, B, F, P, R, T> ProgramContext for &'a mut ContextOf<B, F, P, R, T>
    where &'a mut P: BorrowMut<ProgramBinder>
{
    type Binder = &'a mut P;
    type Rest = ContextOf<&'a mut B, &'a mut F, (), &'a mut R, &'a mut T>;

    fn split_program(self) -> (Self::Binder, Self::Rest) {
        let gl = self.borrowed_mut();
        gl.split_program()
    }
}

pub struct ProgramBinding<'a> {
    phantom: PhantomData<&'a mut Program>
}

pub struct ProgramBinder;
impl ProgramBinder {
    pub fn bind<'a>(&mut self, program: &'a mut Program) -> ProgramBinding<'a>
    {
        let binding = ProgramBinding { phantom: PhantomData };
        unsafe {
            gl::UseProgram(program.gl_id());
            dbg_gl_error! {
                GLError::InvalidValue => "`program` is neither 0 nor an object generated by OpenGL",
                GLError::InvalidOperation => "`program` is not a program object or `program` could not be made part of the current state",
                _ => "Unknown error"
            }
        }
        binding
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ProgramAttrib {
    pub gl_index: GLuint
}

#[derive(Debug, Clone, Copy)]
pub struct ProgramUniform {
    pub gl_index: GLuint
}

#[derive(Debug)]
pub struct UnknownProgramAttrib<'a> {
    name: &'a str
}

impl<'a> fmt::Display for UnknownProgramAttrib<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Unknown program attribute: {:?}", self.name)
    }
}

impl<'a> error::Error for UnknownProgramAttrib<'a> {
    fn description(&self) -> &str {
        "The desired program attribute was not found"
    }
}

#[derive(Debug)]
pub struct UnknownProgramUniform<'a> {
    name: &'a str
}

impl<'a> fmt::Display for UnknownProgramUniform<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Unknown program uniform: {:?}", self.name)
    }
}

impl<'a> error::Error for UnknownProgramUniform<'a> {
    fn description(&self) -> &str {
        "The desired program uniform was not found"
    }
}
<|endoftext|>


<commit_before>/// Worker contains all workers that do the expensive job in background.

use std::sync::Arc;
use std::thread::{self, JoinHandle, Builder};
use std::io;
use std::fmt::Display;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::{self, Sender, Receiver};
use std::result;

use util::SlowTimer;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Stopped
        IoError(e: io::Error) {
            from()
            display("{}", e)
        }
    }
}

impl<T> From<mpsc::SendError<T>> for Error {
    fn from(_: mpsc::SendError<T>) -> Error {
        // do we need to return the failed data
        Error::Stopped
    }
}

pub type Result<T> = result::Result<T, Error>;

pub trait Runnable<T: Display> {
    fn run(&mut self, t: T);
}

pub trait BatchRunnable<T: Display> {
    /// run a batch of tasks.
    ///
    /// Please note that ts will be clear after invoking this method.
    fn run_batch(&mut self, ts: &mut Vec<T>);
}

impl<T: Display, R: Runnable<T>> BatchRunnable<T> for R {
    fn run_batch(&mut self, ts: &mut Vec<T>) {
        for t in ts.drain(..) {
            let task_str = format!("{}", t);
            let timer = SlowTimer::new();
            self.run(t);
            slow_log!(timer,
                      "task {} takes {:?} to finish.",
                      task_str,
                      timer.elapsed());
        }
    }
}

/// Scheduler provides interface to schedule task to underlying workers.
pub struct Scheduler<T> {
    counter: Arc<AtomicUsize>,
    sender: Sender<Option<T>>,
}

impl<T: Display> Scheduler<T> {
    fn new(counter: AtomicUsize, sender: Sender<Option<T>>) -> Scheduler<T> {
        Scheduler {
            counter: Arc::new(counter),
            sender: sender,
        }
    }

    /// Schedule a task to run.
    ///
    /// If the worker is stopped, an error will return.
    pub fn schedule(&self, task: T) -> Result<()> {
        debug!("scheduling task {}", task);
        try!(self.sender.send(Some(task)));
        self.counter.fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    /// Check if underlying worker can't handle task immediately.
    pub fn is_busy(&self) -> bool {
        self.counter.load(Ordering::SeqCst) > 0
    }
}

impl<T: Display> Clone for Scheduler<T> {
    fn clone(&self) -> Scheduler<T> {
        Scheduler {
            counter: self.counter.clone(),
            sender: self.sender.clone(),
        }
    }
}

/// A worker that can schedule time consuming tasks.
pub struct Worker<T: Display> {
    name: String,
    scheduler: Scheduler<T>,
    receiver: Option<Receiver<Option<T>>>,
    handle: Option<JoinHandle<()>>,
}

fn poll<R, T>(mut runner: R, rx: Receiver<Option<T>>, counter: Arc<AtomicUsize>, batch_size: usize)
    where R: BatchRunnable<T> + Send + 'static,
          T: Display + Send + 'static
{
    let mut keep_going = true;
    let mut buffer = Vec::with_capacity(batch_size);
    while keep_going {
        let t = rx.recv();
        match t {
            Ok(Some(t)) => buffer.push(t),
            _ => return,
        }
        while buffer.len() < batch_size {
            match rx.try_recv() {
                Ok(None) => {
                    keep_going = false;
                    break;
                }
                Ok(Some(t)) => buffer.push(t),
                _ => break,
            }
        }
        counter.fetch_sub(buffer.len(), Ordering::SeqCst);
        runner.run_batch(&mut buffer);
        buffer.clear();
    }
}

impl<T: Display + Send + 'static> Worker<T> {
    /// Create a worker.
    pub fn new<S: Into<String>>(name: S) -> Worker<T> {
        let (tx, rx) = mpsc::channel();
        Worker {
            name: name.into(),
            scheduler: Scheduler::new(AtomicUsize::new(0), tx),
            receiver: Some(rx),
            handle: None,
        }
    }

    /// Start the worker.
    pub fn start<R: Runnable<T> + Send + 'static>(&mut self, runner: R) -> Result<()> {
        self.start_batch(runner, 1)
    }

    pub fn start_batch<R>(&mut self, runner: R, batch_size: usize) -> Result<()>
        where R: BatchRunnable<T> + Send + 'static
    {
        info!("starting working thread: {}", self.name);
        if self.receiver.is_none() {
            warn!("worker {} has been started.", self.name);
            return Ok(());
        }

        let rx = self.receiver.take().unwrap();
        let counter = self.scheduler.counter.clone();
        let h = try!(Builder::new()
            .name(self.name.clone())
            .spawn(move || poll(runner, rx, counter, batch_size)));
        self.handle = Some(h);
        Ok(())
    }

    /// Get a scheduler to schedule task.
    pub fn scheduler(&self) -> Scheduler<T> {
        self.scheduler.clone()
    }

    /// Schedule a task to run.
    ///
    /// If the worker is stopped, an error will return.
    pub fn schedule(&self, task: T) -> Result<()> {
        self.scheduler.schedule(task)
    }

    /// Check if underlying worker can't handle task immediately.
    pub fn is_busy(&self) -> bool {
        self.handle.is_none() || self.scheduler.is_busy()
    }

    /// Stop the worker thread.
    pub fn stop(&mut self) -> thread::Result<()> {
        let handler = match self.handle.take() {
            Some(h) => h,
            None => return Ok(()),
        };

        // close sender explicitly so the background thread will exit.
        info!("stoping {}", self.name);
        if let Err(e) = self.scheduler.sender.send(None) {
            warn!("failed to stop worker thread: {:?}", e);
        }
        handler.join()
    }
}

#[cfg(test)]
mod test {
    use std::thread;
    use std::sync::Arc;
    use std::sync::atomic::*;
    use std::cmp;
    use std::time::Duration;

    use super::*;

    struct CountRunner {
        count: Arc<AtomicUsize>,
    }

    impl Runnable<u64> for CountRunner {
        fn run(&mut self, step: u64) {
            self.count.fetch_add(step as usize, Ordering::SeqCst);
            thread::sleep(Duration::from_millis(10));
        }
    }

    struct BatchRunner {
        count: Arc<AtomicUsize>,
    }

    impl BatchRunnable<u64> for BatchRunner {
        fn run_batch(&mut self, ms: &mut Vec<u64>) {
            let total = ms.iter().fold(0, |l, &r| l + r);
            self.count.fetch_add(total as usize, Ordering::SeqCst);
            let max_sleep = ms.iter().fold(0, |l, &r| cmp::max(l, r));
            thread::sleep(Duration::from_millis(max_sleep));
        }
    }

    #[test]
    fn test_worker() {
        let mut worker = Worker::new("test-worker");
        let count = Arc::new(AtomicUsize::new(0));
        worker.start(CountRunner { count: count.clone() }).unwrap();
        assert!(!worker.is_busy());
        worker.schedule(50).unwrap();
        worker.schedule(50).unwrap();
        worker.schedule(50).unwrap();
        assert!(worker.is_busy());
        thread::sleep(Duration::from_millis(60));
        assert!(!worker.is_busy());
        worker.stop().unwrap();
        // now worker can't handle any task
        assert!(worker.is_busy());
        assert_eq!(count.load(Ordering::SeqCst), 150);
    }

    #[test]
    fn test_threaded() {
        let mut worker = Worker::new("test-worker-threaded");
        let count = Arc::new(AtomicUsize::new(0));
        worker.start(CountRunner { count: count.clone() }).unwrap();
        let scheduler = worker.scheduler();
        thread::spawn(move || {
            scheduler.schedule(100).unwrap();
            scheduler.schedule(100).unwrap();
        });
        for _ in 1..1000 {
            if worker.is_busy() {
                break;
            }
            thread::sleep(Duration::from_millis(1));
        }
        worker.stop().unwrap();
        assert_eq!(count.load(Ordering::SeqCst), 200);
    }

    #[test]
    fn test_batch() {
        let mut worker = Worker::new("test-worker-batch");
        let count = Arc::new(AtomicUsize::new(0));
        worker.start_batch(BatchRunner { count: count.clone() }, 10).unwrap();
        for _ in 0..20 {
            worker.schedule(50).unwrap();
        }
        worker.stop().unwrap();
        assert_eq!(count.load(Ordering::SeqCst), 20 * 50);
    }
}
<commit_msg>make worker test more robust (#631)<commit_after>/// Worker contains all workers that do the expensive job in background.

use std::sync::Arc;
use std::thread::{self, JoinHandle, Builder};
use std::io;
use std::fmt::Display;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::{self, Sender, Receiver};
use std::result;

use util::SlowTimer;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Stopped
        IoError(e: io::Error) {
            from()
            display("{}", e)
        }
    }
}

impl<T> From<mpsc::SendError<T>> for Error {
    fn from(_: mpsc::SendError<T>) -> Error {
        // do we need to return the failed data
        Error::Stopped
    }
}

pub type Result<T> = result::Result<T, Error>;

pub trait Runnable<T: Display> {
    fn run(&mut self, t: T);
}

pub trait BatchRunnable<T: Display> {
    /// run a batch of tasks.
    ///
    /// Please note that ts will be clear after invoking this method.
    fn run_batch(&mut self, ts: &mut Vec<T>);
}

impl<T: Display, R: Runnable<T>> BatchRunnable<T> for R {
    fn run_batch(&mut self, ts: &mut Vec<T>) {
        for t in ts.drain(..) {
            let task_str = format!("{}", t);
            let timer = SlowTimer::new();
            self.run(t);
            slow_log!(timer,
                      "task {} takes {:?} to finish.",
                      task_str,
                      timer.elapsed());
        }
    }
}

/// Scheduler provides interface to schedule task to underlying workers.
pub struct Scheduler<T> {
    counter: Arc<AtomicUsize>,
    sender: Sender<Option<T>>,
}

impl<T: Display> Scheduler<T> {
    fn new(counter: AtomicUsize, sender: Sender<Option<T>>) -> Scheduler<T> {
        Scheduler {
            counter: Arc::new(counter),
            sender: sender,
        }
    }

    /// Schedule a task to run.
    ///
    /// If the worker is stopped, an error will return.
    pub fn schedule(&self, task: T) -> Result<()> {
        debug!("scheduling task {}", task);
        try!(self.sender.send(Some(task)));
        self.counter.fetch_add(1, Ordering::SeqCst);
        Ok(())
    }

    /// Check if underlying worker can't handle task immediately.
    pub fn is_busy(&self) -> bool {
        self.counter.load(Ordering::SeqCst) > 0
    }
}

impl<T: Display> Clone for Scheduler<T> {
    fn clone(&self) -> Scheduler<T> {
        Scheduler {
            counter: self.counter.clone(),
            sender: self.sender.clone(),
        }
    }
}

/// A worker that can schedule time consuming tasks.
pub struct Worker<T: Display> {
    name: String,
    scheduler: Scheduler<T>,
    receiver: Option<Receiver<Option<T>>>,
    handle: Option<JoinHandle<()>>,
}

fn poll<R, T>(mut runner: R, rx: Receiver<Option<T>>, counter: Arc<AtomicUsize>, batch_size: usize)
    where R: BatchRunnable<T> + Send + 'static,
          T: Display + Send + 'static
{
    let mut keep_going = true;
    let mut buffer = Vec::with_capacity(batch_size);
    while keep_going {
        let t = rx.recv();
        match t {
            Ok(Some(t)) => buffer.push(t),
            _ => return,
        }
        while buffer.len() < batch_size {
            match rx.try_recv() {
                Ok(None) => {
                    keep_going = false;
                    break;
                }
                Ok(Some(t)) => buffer.push(t),
                _ => break,
            }
        }
        counter.fetch_sub(buffer.len(), Ordering::SeqCst);
        runner.run_batch(&mut buffer);
        buffer.clear();
    }
}

impl<T: Display + Send + 'static> Worker<T> {
    /// Create a worker.
    pub fn new<S: Into<String>>(name: S) -> Worker<T> {
        let (tx, rx) = mpsc::channel();
        Worker {
            name: name.into(),
            scheduler: Scheduler::new(AtomicUsize::new(0), tx),
            receiver: Some(rx),
            handle: None,
        }
    }

    /// Start the worker.
    pub fn start<R: Runnable<T> + Send + 'static>(&mut self, runner: R) -> Result<()> {
        self.start_batch(runner, 1)
    }

    pub fn start_batch<R>(&mut self, runner: R, batch_size: usize) -> Result<()>
        where R: BatchRunnable<T> + Send + 'static
    {
        info!("starting working thread: {}", self.name);
        if self.receiver.is_none() {
            warn!("worker {} has been started.", self.name);
            return Ok(());
        }

        let rx = self.receiver.take().unwrap();
        let counter = self.scheduler.counter.clone();
        let h = try!(Builder::new()
            .name(self.name.clone())
            .spawn(move || poll(runner, rx, counter, batch_size)));
        self.handle = Some(h);
        Ok(())
    }

    /// Get a scheduler to schedule task.
    pub fn scheduler(&self) -> Scheduler<T> {
        self.scheduler.clone()
    }

    /// Schedule a task to run.
    ///
    /// If the worker is stopped, an error will return.
    pub fn schedule(&self, task: T) -> Result<()> {
        self.scheduler.schedule(task)
    }

    /// Check if underlying worker can't handle task immediately.
    pub fn is_busy(&self) -> bool {
        self.handle.is_none() || self.scheduler.is_busy()
    }

    /// Stop the worker thread.
    pub fn stop(&mut self) -> thread::Result<()> {
        let handler = match self.handle.take() {
            Some(h) => h,
            None => return Ok(()),
        };

        // close sender explicitly so the background thread will exit.
        info!("stoping {}", self.name);
        if let Err(e) = self.scheduler.sender.send(None) {
            warn!("failed to stop worker thread: {:?}", e);
        }
        handler.join()
    }
}

#[cfg(test)]
mod test {
    use std::thread;
    use std::sync::Arc;
    use std::sync::atomic::*;
    use std::cmp;
    use std::time::Duration;

    use super::*;

    struct CountRunner {
        count: Arc<AtomicUsize>,
    }

    impl Runnable<u64> for CountRunner {
        fn run(&mut self, step: u64) {
            self.count.fetch_add(step as usize, Ordering::SeqCst);
            thread::sleep(Duration::from_millis(10));
        }
    }

    struct BatchRunner {
        count: Arc<AtomicUsize>,
    }

    impl BatchRunnable<u64> for BatchRunner {
        fn run_batch(&mut self, ms: &mut Vec<u64>) {
            let total = ms.iter().fold(0, |l, &r| l + r);
            self.count.fetch_add(total as usize, Ordering::SeqCst);
            let max_sleep = ms.iter().fold(0, |l, &r| cmp::max(l, r));
            thread::sleep(Duration::from_millis(max_sleep));
        }
    }

    #[test]
    fn test_worker() {
        let mut worker = Worker::new("test-worker");
        let count = Arc::new(AtomicUsize::new(0));
        worker.start(CountRunner { count: count.clone() }).unwrap();
        assert!(!worker.is_busy());
        worker.schedule(50).unwrap();
        worker.schedule(50).unwrap();
        worker.schedule(50).unwrap();
        assert!(worker.is_busy());
        for _ in 0..100 {
            if !worker.is_busy() {
                break;
            }
            thread::sleep(Duration::from_millis(10));
        }
        assert!(!worker.is_busy());
        assert_eq!(count.load(Ordering::SeqCst), 150);
        worker.stop().unwrap();
        // now worker can't handle any task
        assert!(worker.is_busy());
    }

    #[test]
    fn test_threaded() {
        let mut worker = Worker::new("test-worker-threaded");
        let count = Arc::new(AtomicUsize::new(0));
        worker.start(CountRunner { count: count.clone() }).unwrap();
        let scheduler = worker.scheduler();
        thread::spawn(move || {
            scheduler.schedule(100).unwrap();
            scheduler.schedule(100).unwrap();
        });
        for _ in 1..1000 {
            if worker.is_busy() {
                break;
            }
            thread::sleep(Duration::from_millis(1));
        }
        worker.stop().unwrap();
        assert_eq!(count.load(Ordering::SeqCst), 200);
    }

    #[test]
    fn test_batch() {
        let mut worker = Worker::new("test-worker-batch");
        let count = Arc::new(AtomicUsize::new(0));
        worker.start_batch(BatchRunner { count: count.clone() }, 10).unwrap();
        for _ in 0..20 {
            worker.schedule(50).unwrap();
        }
        worker.stop().unwrap();
        assert_eq!(count.load(Ordering::SeqCst), 20 * 50);
    }
}
<|endoftext|>


<commit_before>use postgres;
use uuid::Uuid;
use std::fmt;
use std::collections::BTreeMap;
use chrono::{NaiveDateTime, Utc};

use youtube;
use soundcloud;
use spotify;
use apple_music;
use error::Error;
use super::{conn, Model};
use model::provider::Provider;
use model::enclosure::Enclosure;
use youtube::HasThumbnail;

static PROPS: [&'static str; 9]  = ["id",
                                    "provider",
                                    "identifier",
                                    "url",
                                    "name",
                                    "thumbnail_url",
                                    "artwork_url",
                                    "created_at",
                                    "updated_at"];

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Artist {
    pub id:            Uuid,
    pub provider:      Provider,
    pub identifier:    String,
    pub url:           String,
    pub name:          String,
    pub thumbnail_url: Option<String>,
    pub artwork_url:   Option<String>,
    pub created_at:    NaiveDateTime,
    pub updated_at:    NaiveDateTime,
}

impl fmt::Display for Artist {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{}", self.provider, self.identifier)
    }
}

impl<'a> Model<'a> for Artist {
    fn table_name() -> String { "artists".to_string() }
    fn props_str(prefix: &str) -> String {
        PROPS
            .iter()
            .map(|&p| format!("{}{}", prefix, p))
            .collect::<Vec<String>>().join(",")
    }
    fn row_to_item(row: postgres::rows::Row) -> Artist {
        Artist {
            id:            row.get(0),
            provider:      Provider::new(row.get(1)),
            identifier:    row.get(2),
            url:           row.get(3),
            name:          row.get(4),
            thumbnail_url: row.get(5),
            artwork_url:   row.get(6),
            created_at:    row.get(7),
            updated_at:    row.get(8),
        }
    }

    fn create(&self) -> Result<Artist, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO artists (provider, identifier, url, name)
                                 VALUES ($1, $2, $3, $4) RETURNING id"));
        let rows = try!(stmt.query(&[&self.provider.to_string(), &self.identifier, &self.url, &self.name]));
        let mut artist = self.clone();
        for row in rows.iter() {
            artist.id = row.get(0);
        }
        Ok(artist)
    }

    fn save(&mut self) -> Result<(), Error> {
        self.updated_at = Utc::now().naive_utc();
        let conn = conn().unwrap();
        let stmt = conn.prepare("UPDATE artists SET
                                 provider      = $2,
                                 identifier    = $3,
                                 url           = $4,
                                 name          = $5,
                                 thumbnail_url = $6,
                                 artwork_url   = $7,
                                 created_at    = $8,
                                 updated_at    = $9
                                 WHERE id = $1").unwrap();
        let result = stmt.query(&[&self.id,
                                  &self.provider.to_string(),
                                  &self.identifier,
                                  &self.url,
                                  &self.name,
                                  &self.thumbnail_url,
                                  &self.artwork_url,
                                  &self.created_at,
                                  &self.updated_at,
        ]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected),
        }
    }
}

impl<'a> Enclosure<'a> for Artist {
    fn new(provider: Provider, identifier: String) -> Artist {
        Artist {
            id:            Uuid::new_v4(),
            provider:      provider,
            identifier:    identifier,
            url:           "".to_string(),
            name:          "".to_string(),
            thumbnail_url: None,
            artwork_url:   None,
            created_at:    Utc::now().naive_utc(),
            updated_at:    Utc::now().naive_utc(),
        }
    }

    fn set_url(&mut self, url: String) -> &mut Artist {
        self.url = url;
        self
    }

    fn set_owner_id(&mut self, _owner_id: Option<String>) -> &mut Artist {
        self
    }

    fn fetch_props(&mut self) -> Result<(), Error> {
        match self.provider {
            Provider::YouTube => match youtube::fetch_channel(&self.identifier) {
                Ok(artist) => self.update_with_yt_channel(&artist),
                Err(_)     => self,
            },
            Provider::AppleMusic => {
                let country = apple_music::country(&self.url);
                match apple_music::fetch_artist(&country, &self.identifier) {
                    Ok(artist) => self.update_with_am_artist(&artist),
                    Err(_)     => self,
                }
            },
            Provider::Spotify => match spotify::fetch_artist(&self.identifier) {
                Ok(artist) => self.update_with_sp_artist(&artist),
                Err(_)     => self,
            },
            _ => self,
        };
        Ok(())
    }

    fn find_by_entry_id(_entry_id: Uuid) -> Vec<Artist> {
        vec![]
    }
}

impl Artist {
    fn find_by(provider: &Provider, identifier: &str) -> Result<Artist, Error> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM {}
                     WHERE provider = $1 AND identifier = $2
                     ORDER BY updated_at DESC",
                     Artist::props_str(""), Artist::table_name())).unwrap();
        let rows = stmt.query(&[&(*provider).to_string(), &identifier]).unwrap();
        let items = Artist::rows_to_items(rows);
        if items.len() > 0 {
            return Ok(items[0].clone());
        }
        return Err(Error::NotFound)
    }
    pub fn find_or_create(provider: Provider, identifier: String) -> Result<Artist, Error> {
        return match Artist::find_by(&provider, &identifier) {
            Ok(item) => Ok(item),
            Err(_)   => Artist::new(provider, identifier).create()
        }
    }

    pub fn find_by_provider(provider: &Provider) -> Vec<Artist> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM artists WHERE artists.provider = $1
                        ORDER BY artists.created_at DESC",
                     Artist::props_str(""))).unwrap();
        let rows = stmt.query(&[&(*provider).to_string()]).unwrap();
        Artist::rows_to_items(rows)
    }

    pub fn from_yt_channel(channel_id: &str, channel_title: &str) -> Artist {
        let mut artist = Artist::new(Provider::YouTube, channel_id.to_string());
        artist.name = channel_title.to_string();
        artist.clone()
    }
    pub fn from_sp_artist(artist: &spotify::Artist) -> Artist {
        Artist::new(Provider::Spotify, (*artist).id.to_string())
            .update_with_sp_artist(artist)
            .clone()
    }

    pub fn from_sc_user(playlist: &soundcloud::User) -> Artist {
        Artist::new(Provider::SoundCloud, (*playlist).id.to_string())
            .update_with_sc_user(playlist)
            .clone()
    }

    pub fn from_am_artist(artist: &str) -> Artist {
        let mut artist = Artist::new(Provider::AppleMusic, artist.to_string());
        artist.name = artist.to_string();
        artist.clone()
    }

    pub fn update_with_sp_artist(&mut self, artist: &spotify::Artist) -> &mut Artist {
        self.provider       = Provider::Spotify;
        self.identifier     = artist.id.to_string();
        self.url            = artist.uri.clone();
        self.name           = artist.name.clone();
        if let Some(ref images) = artist.images {
            if images.len() > 0 {
                self.artwork_url   = Some(images[0].url.clone());
                self.thumbnail_url = Some(images[0].url.clone());
            }
            if images.len() > 1 {
                self.thumbnail_url = Some(images[1].url.clone());
            }
        }
        self
    }

    pub fn update_with_am_artist(&mut self, artist: &apple_music::Artist) -> &mut Artist {
        self.provider      = Provider::AppleMusic;
        self.identifier    = artist.id.to_string();
        self.url           = artist.attributes.url.to_string();
        self.name          = artist.attributes.name.to_string();
        if let Some(album) = artist.clone().relationships
            .and_then(|r| r.albums.data.first().map(|a| a.clone())) {
                self.thumbnail_url = Some(album.attributes.artwork.get_thumbnail_url());
                self.artwork_url   = Some(album.attributes.artwork.get_artwork_url());
            }
        self
    }

    pub fn update_with_sc_user(&mut self, user: &soundcloud::User) -> &mut Artist {
        self.provider      = Provider::SoundCloud;
        self.identifier    = user.id.to_string();
        self.url           = user.permalink.to_string();
        self.name          = user.username.to_string();
        self.thumbnail_url = Some(user.avatar_url.clone());
        self.artwork_url   = Some(user.avatar_url.clone());
        self
    }

    pub fn update_with_yt_channel(&mut self, channel: &youtube::Channel) -> &mut Artist {
        let s              = &channel.snippet;
        self.provider      = Provider::YouTube;
        self.identifier    = channel.id.to_string();
        self.url           = format!("https://www.youtube.com/channel/{}", channel.id);
        self.name          = s.title.to_string();
        self.thumbnail_url = s.get_thumbnail_url();
        self.artwork_url   = s.get_artwork_url();
        self
    }
}

#[cfg(test)]
mod test {
    use model::Model;
    use model::enclosure::Enclosure;
    use super::Artist;
    use Provider;
    #[test]
    fn test_new() {
        let artist = Artist::new(Provider::Spotify, "4tZwfgrHOc3mvqYlEYSvVi".to_string());
        assert_eq!(artist.provider, Provider::Spotify);
        assert_eq!(&artist.identifier, "4tZwfgrHOc3mvqYlEYSvVi");
    }
    #[test]
    fn test_find_of_create() {
        let identifier = "4tZwfgrHOc3mvqYlEYSvVi".to_string();
        let result     = Artist::find_or_create(Provider::Spotify, identifier);
        assert!(result.is_ok());
    }
    #[test]
    fn test_save() {
        let id = "test_save";
        let mut artist = Artist::find_or_create(Provider::Spotify, id.to_string()).unwrap();
        artist.name   = "name".to_string();
        let result    = artist.save();
        assert!(result.is_ok());
        let artist = Artist::find_or_create(Provider::Spotify, id.to_string()).unwrap();
        assert_eq!(&artist.name, "name");
    }
}
<commit_msg>Implement Artist::find_all<commit_after>use postgres;
use uuid::Uuid;
use std::fmt;
use std::collections::BTreeMap;
use chrono::{NaiveDateTime, Utc};

use youtube;
use soundcloud;
use spotify;
use apple_music;
use error::Error;
use super::{conn, Model};
use model::provider::Provider;
use model::enclosure::Enclosure;
use youtube::HasThumbnail;

static PROPS: [&'static str; 9]  = ["id",
                                    "provider",
                                    "identifier",
                                    "url",
                                    "name",
                                    "thumbnail_url",
                                    "artwork_url",
                                    "created_at",
                                    "updated_at"];

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Artist {
    pub id:            Uuid,
    pub provider:      Provider,
    pub identifier:    String,
    pub url:           String,
    pub name:          String,
    pub thumbnail_url: Option<String>,
    pub artwork_url:   Option<String>,
    pub created_at:    NaiveDateTime,
    pub updated_at:    NaiveDateTime,
}

impl fmt::Display for Artist {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{}", self.provider, self.identifier)
    }
}

impl<'a> Model<'a> for Artist {
    fn table_name() -> String { "artists".to_string() }
    fn props_str(prefix: &str) -> String {
        PROPS
            .iter()
            .map(|&p| format!("{}{}", prefix, p))
            .collect::<Vec<String>>().join(",")
    }
    fn row_to_item(row: postgres::rows::Row) -> Artist {
        Artist {
            id:            row.get(0),
            provider:      Provider::new(row.get(1)),
            identifier:    row.get(2),
            url:           row.get(3),
            name:          row.get(4),
            thumbnail_url: row.get(5),
            artwork_url:   row.get(6),
            created_at:    row.get(7),
            updated_at:    row.get(8),
        }
    }

    fn create(&self) -> Result<Artist, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO artists (provider, identifier, url, name)
                                 VALUES ($1, $2, $3, $4) RETURNING id"));
        let rows = try!(stmt.query(&[&self.provider.to_string(), &self.identifier, &self.url, &self.name]));
        let mut artist = self.clone();
        for row in rows.iter() {
            artist.id = row.get(0);
        }
        Ok(artist)
    }

    fn save(&mut self) -> Result<(), Error> {
        self.updated_at = Utc::now().naive_utc();
        let conn = conn().unwrap();
        let stmt = conn.prepare("UPDATE artists SET
                                 provider      = $2,
                                 identifier    = $3,
                                 url           = $4,
                                 name          = $5,
                                 thumbnail_url = $6,
                                 artwork_url   = $7,
                                 created_at    = $8,
                                 updated_at    = $9
                                 WHERE id = $1").unwrap();
        let result = stmt.query(&[&self.id,
                                  &self.provider.to_string(),
                                  &self.identifier,
                                  &self.url,
                                  &self.name,
                                  &self.thumbnail_url,
                                  &self.artwork_url,
                                  &self.created_at,
                                  &self.updated_at,
        ]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected),
        }
    }
}

impl<'a> Enclosure<'a> for Artist {
    fn new(provider: Provider, identifier: String) -> Artist {
        Artist {
            id:            Uuid::new_v4(),
            provider:      provider,
            identifier:    identifier,
            url:           "".to_string(),
            name:          "".to_string(),
            thumbnail_url: None,
            artwork_url:   None,
            created_at:    Utc::now().naive_utc(),
            updated_at:    Utc::now().naive_utc(),
        }
    }

    fn set_url(&mut self, url: String) -> &mut Artist {
        self.url = url;
        self
    }

    fn set_owner_id(&mut self, _owner_id: Option<String>) -> &mut Artist {
        self
    }

    fn fetch_props(&mut self) -> Result<(), Error> {
        match self.provider {
            Provider::YouTube => match youtube::fetch_channel(&self.identifier) {
                Ok(artist) => self.update_with_yt_channel(&artist),
                Err(_)     => self,
            },
            Provider::AppleMusic => {
                let country = apple_music::country(&self.url);
                match apple_music::fetch_artist(&country, &self.identifier) {
                    Ok(artist) => self.update_with_am_artist(&artist),
                    Err(_)     => self,
                }
            },
            Provider::Spotify => match spotify::fetch_artist(&self.identifier) {
                Ok(artist) => self.update_with_sp_artist(&artist),
                Err(_)     => self,
            },
            _ => self,
        };
        Ok(())
    }

    fn find_by_entry_id(_entry_id: Uuid) -> Vec<Artist> {
        vec![]
    }
}

impl Artist {
    pub fn find_all() -> Vec<Artist> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM artists ORDER BY artists.created_at DESC",
                     Artist::props_str(""))).unwrap();
        let rows = stmt.query(&[]).unwrap();
        Artist::rows_to_items(rows)
    }
    fn find_by(provider: &Provider, identifier: &str) -> Result<Artist, Error> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM {}
                     WHERE provider = $1 AND identifier = $2
                     ORDER BY updated_at DESC",
                     Artist::props_str(""), Artist::table_name())).unwrap();
        let rows = stmt.query(&[&(*provider).to_string(), &identifier]).unwrap();
        let items = Artist::rows_to_items(rows);
        if items.len() > 0 {
            return Ok(items[0].clone());
        }
        return Err(Error::NotFound)
    }
    pub fn find_or_create(provider: Provider, identifier: String) -> Result<Artist, Error> {
        return match Artist::find_by(&provider, &identifier) {
            Ok(item) => Ok(item),
            Err(_)   => Artist::new(provider, identifier).create()
        }
    }

    pub fn find_by_provider(provider: &Provider) -> Vec<Artist> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM artists WHERE artists.provider = $1
                        ORDER BY artists.created_at DESC",
                     Artist::props_str(""))).unwrap();
        let rows = stmt.query(&[&(*provider).to_string()]).unwrap();
        Artist::rows_to_items(rows)
    }

    pub fn from_yt_channel(channel_id: &str, channel_title: &str) -> Artist {
        let mut artist = Artist::new(Provider::YouTube, channel_id.to_string());
        artist.name = channel_title.to_string();
        artist.clone()
    }
    pub fn from_sp_artist(artist: &spotify::Artist) -> Artist {
        Artist::new(Provider::Spotify, (*artist).id.to_string())
            .update_with_sp_artist(artist)
            .clone()
    }

    pub fn from_sc_user(playlist: &soundcloud::User) -> Artist {
        Artist::new(Provider::SoundCloud, (*playlist).id.to_string())
            .update_with_sc_user(playlist)
            .clone()
    }

    pub fn from_am_artist(artist: &str) -> Artist {
        let mut artist = Artist::new(Provider::AppleMusic, artist.to_string());
        artist.name = artist.to_string();
        artist.clone()
    }

    pub fn update_with_sp_artist(&mut self, artist: &spotify::Artist) -> &mut Artist {
        self.provider       = Provider::Spotify;
        self.identifier     = artist.id.to_string();
        self.url            = artist.uri.clone();
        self.name           = artist.name.clone();
        if let Some(ref images) = artist.images {
            if images.len() > 0 {
                self.artwork_url   = Some(images[0].url.clone());
                self.thumbnail_url = Some(images[0].url.clone());
            }
            if images.len() > 1 {
                self.thumbnail_url = Some(images[1].url.clone());
            }
        }
        self
    }

    pub fn update_with_am_artist(&mut self, artist: &apple_music::Artist) -> &mut Artist {
        self.provider      = Provider::AppleMusic;
        self.identifier    = artist.id.to_string();
        self.url           = artist.attributes.url.to_string();
        self.name          = artist.attributes.name.to_string();
        if let Some(album) = artist.clone().relationships
            .and_then(|r| r.albums.data.first().map(|a| a.clone())) {
                self.thumbnail_url = Some(album.attributes.artwork.get_thumbnail_url());
                self.artwork_url   = Some(album.attributes.artwork.get_artwork_url());
            }
        self
    }

    pub fn update_with_sc_user(&mut self, user: &soundcloud::User) -> &mut Artist {
        self.provider      = Provider::SoundCloud;
        self.identifier    = user.id.to_string();
        self.url           = user.permalink.to_string();
        self.name          = user.username.to_string();
        self.thumbnail_url = Some(user.avatar_url.clone());
        self.artwork_url   = Some(user.avatar_url.clone());
        self
    }

    pub fn update_with_yt_channel(&mut self, channel: &youtube::Channel) -> &mut Artist {
        let s              = &channel.snippet;
        self.provider      = Provider::YouTube;
        self.identifier    = channel.id.to_string();
        self.url           = format!("https://www.youtube.com/channel/{}", channel.id);
        self.name          = s.title.to_string();
        self.thumbnail_url = s.get_thumbnail_url();
        self.artwork_url   = s.get_artwork_url();
        self
    }
}

#[cfg(test)]
mod test {
    use model::Model;
    use model::enclosure::Enclosure;
    use super::Artist;
    use Provider;
    #[test]
    fn test_new() {
        let artist = Artist::new(Provider::Spotify, "4tZwfgrHOc3mvqYlEYSvVi".to_string());
        assert_eq!(artist.provider, Provider::Spotify);
        assert_eq!(&artist.identifier, "4tZwfgrHOc3mvqYlEYSvVi");
    }
    #[test]
    fn test_find_of_create() {
        let identifier = "4tZwfgrHOc3mvqYlEYSvVi".to_string();
        let result     = Artist::find_or_create(Provider::Spotify, identifier);
        assert!(result.is_ok());
    }
    #[test]
    fn test_save() {
        let id = "test_save";
        let mut artist = Artist::find_or_create(Provider::Spotify, id.to_string()).unwrap();
        artist.name   = "name".to_string();
        let result    = artist.save();
        assert!(result.is_ok());
        let artist = Artist::find_or_create(Provider::Spotify, id.to_string()).unwrap();
        assert_eq!(&artist.name, "name");
    }
}
<|endoftext|>


<commit_before>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! Types and traits for working with the [Matrix](https://matrix.org) protocol.
//!
//! This crate re-exports things from all of the other ruma crates so you don't
//! have to manually keep all the versions in sync.
//!
//! Which crates are re-exported can be configured through cargo features.
//!
//! > ⚠ Some details might be missing because rustdoc has trouble with re-exports so you may need
//!   to refer to other crates' documentations.
//!
//! # API features
//!
//! Depending on which parts of Matrix are relevant to you, activate the following features:
//!
//! * `appservice-api` -- Application Service API.
//! * `client-api` -- Client-Server API.
//! * `federation-api` -- Server-Server (Federation) API.
//! * `identity-service-api` -- Identity Service API.
//! * `push-gateway-api` -- Push Gateway API.
//!
//! These features have `client`- and `server`-optimized variants that are enabled respectively
//! with the `-c` and `-s` suffixes. For example:
//!   * `client-api-c` -- The Client-Server API optimized for the client side.
//!   * `client-api-s` -- The Client-Server API optimized for the server side.
//!
//! # Compatibility feature
//!
//! * `compat` increases compatibility with other parts of the Matrix ecosystem, at the expense of
//! deviating from the specification.
//!
//! # Convenience features
//!
//! These features are only useful if you want to use a method that requires it:
//!
//! * `either`
//! * `rand`
//! * `markdown`
//!
//! # Unstable features
//!
//! By using these features, you opt out of all semver guarantees Ruma otherwise provides:
//!
//! * `unstable-exhaustive-types` -- Most types in Ruma are marked as non-exhaustive to avoid
//!   breaking changes when new fields are added in the specification. This feature compiles all
//!   types as exhaustive.
//! * `unstable-mscXXXX`, where `XXXX` is the MSC number -- Upcoming Matrix features that may be
//!   subject to change or removal.
//! * `unstable-pre-spec` -- Undocumented Matrix features that may be subject to change or removal.
//!
//! # Common features
//!
//! These submodules are usually activated by the API features when needed:
//!
//! * `api`
//! * `events`
//! * `signatures`
//!
//! # `ruma-client` features
//!
//! The `client` feature activates [`ruma::client`][client], and `client-ext-client-api` activates
//! `ruma-client`s `client-api` feature. All other `client-*` features activate the same feature
//! without the `client-` prefix on `ruma-client`. See the crate's documentation for the effect of
//! these features.
//!
//! If you are viewing this on `docs.rs`, you can have a look at the feature dependencies by
//! clicking **Feature flags** in the toolbar at the top.

#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

#[doc(inline)]
pub use ruma_common::serde;

#[cfg(feature = "client")]
#[doc(inline)]
pub use ruma_client as client;
#[cfg(feature = "events")]
#[doc(inline)]
pub use ruma_common::events;
#[cfg(feature = "signatures")]
#[doc(inline)]
pub use ruma_signatures as signatures;
#[cfg(feature = "state-res")]
#[doc(inline)]
pub use ruma_state_res as state_res;

/// (De)serializable types for various [Matrix APIs][apis] requests and responses and abstractions
/// for them.
///
/// [apis]: https://spec.matrix.org/v1.2/#matrix-apis
#[cfg(feature = "api")]
pub mod api {
    pub use ruma_common::api::*;

    #[cfg(feature = "ruma-appservice-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "appservice-api",
            feature = "appservice-api-c",
            feature = "appservice-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_appservice_api as appservice;
    #[cfg(feature = "ruma-client-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "client-api",
            feature = "client-api-c",
            feature = "client-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_client_api as client;
    #[cfg(feature = "ruma-federation-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "federation-api",
            feature = "federation-api-c",
            feature = "federation-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_federation_api as federation;
    #[cfg(feature = "ruma-identity-service-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "identity-service-api",
            feature = "identity-service-api-c",
            feature = "identity-service-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_identity_service_api as identity_service;
    #[cfg(feature = "ruma-push-gateway-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "push-gateway-api",
            feature = "push-gateway-api-c",
            feature = "push-gateway-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_push_gateway_api as push_gateway;
}

#[doc(no_inline)]
pub use assign::assign;
#[doc(no_inline)]
pub use js_int::{int, uint, Int, UInt};
#[cfg(feature = "client-ext-client-api")]
pub use ruma_client::Client;
pub use ruma_common::{
    authentication, device_id, device_key_id, directory, encryption, event_id, exports, matrix_uri,
    mxc_uri, power_levels, presence, push, receipt, room, room_alias_id, room_id, room_version_id,
    serde::Incoming, server_name, server_signing_key_id, thirdparty, to_device, user_id,
    ClientSecret, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, DeviceSignatures, DeviceSigningKeyId,
    EntitySignatures, EventEncryptionAlgorithm, EventId, IdParseError, KeyId, KeyName, MatrixToUri,
    MatrixUri, MilliSecondsSinceUnixEpoch, MxcUri, PrivOwnedStr, RoomAliasId, RoomId, RoomName,
    RoomOrAliasId, RoomVersionId, SecondsSinceUnixEpoch, ServerName, ServerSignatures,
    ServerSigningKeyId, SessionId, Signatures, SigningKeyAlgorithm, TransactionId, UserId,
};
<commit_msg>ruma: Re-export new owned ID types<commit_after>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! Types and traits for working with the [Matrix](https://matrix.org) protocol.
//!
//! This crate re-exports things from all of the other ruma crates so you don't
//! have to manually keep all the versions in sync.
//!
//! Which crates are re-exported can be configured through cargo features.
//!
//! > ⚠ Some details might be missing because rustdoc has trouble with re-exports so you may need
//!   to refer to other crates' documentations.
//!
//! # API features
//!
//! Depending on which parts of Matrix are relevant to you, activate the following features:
//!
//! * `appservice-api` -- Application Service API.
//! * `client-api` -- Client-Server API.
//! * `federation-api` -- Server-Server (Federation) API.
//! * `identity-service-api` -- Identity Service API.
//! * `push-gateway-api` -- Push Gateway API.
//!
//! These features have `client`- and `server`-optimized variants that are enabled respectively
//! with the `-c` and `-s` suffixes. For example:
//!   * `client-api-c` -- The Client-Server API optimized for the client side.
//!   * `client-api-s` -- The Client-Server API optimized for the server side.
//!
//! # Compatibility feature
//!
//! * `compat` increases compatibility with other parts of the Matrix ecosystem, at the expense of
//! deviating from the specification.
//!
//! # Convenience features
//!
//! These features are only useful if you want to use a method that requires it:
//!
//! * `either`
//! * `rand`
//! * `markdown`
//!
//! # Unstable features
//!
//! By using these features, you opt out of all semver guarantees Ruma otherwise provides:
//!
//! * `unstable-exhaustive-types` -- Most types in Ruma are marked as non-exhaustive to avoid
//!   breaking changes when new fields are added in the specification. This feature compiles all
//!   types as exhaustive.
//! * `unstable-mscXXXX`, where `XXXX` is the MSC number -- Upcoming Matrix features that may be
//!   subject to change or removal.
//! * `unstable-pre-spec` -- Undocumented Matrix features that may be subject to change or removal.
//!
//! # Common features
//!
//! These submodules are usually activated by the API features when needed:
//!
//! * `api`
//! * `events`
//! * `signatures`
//!
//! # `ruma-client` features
//!
//! The `client` feature activates [`ruma::client`][client], and `client-ext-client-api` activates
//! `ruma-client`s `client-api` feature. All other `client-*` features activate the same feature
//! without the `client-` prefix on `ruma-client`. See the crate's documentation for the effect of
//! these features.
//!
//! If you are viewing this on `docs.rs`, you can have a look at the feature dependencies by
//! clicking **Feature flags** in the toolbar at the top.

#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

#[doc(inline)]
pub use ruma_common::serde;

#[cfg(feature = "client")]
#[doc(inline)]
pub use ruma_client as client;
#[cfg(feature = "events")]
#[doc(inline)]
pub use ruma_common::events;
#[cfg(feature = "signatures")]
#[doc(inline)]
pub use ruma_signatures as signatures;
#[cfg(feature = "state-res")]
#[doc(inline)]
pub use ruma_state_res as state_res;

/// (De)serializable types for various [Matrix APIs][apis] requests and responses and abstractions
/// for them.
///
/// [apis]: https://spec.matrix.org/v1.2/#matrix-apis
#[cfg(feature = "api")]
pub mod api {
    pub use ruma_common::api::*;

    #[cfg(feature = "ruma-appservice-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "appservice-api",
            feature = "appservice-api-c",
            feature = "appservice-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_appservice_api as appservice;
    #[cfg(feature = "ruma-client-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "client-api",
            feature = "client-api-c",
            feature = "client-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_client_api as client;
    #[cfg(feature = "ruma-federation-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "federation-api",
            feature = "federation-api-c",
            feature = "federation-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_federation_api as federation;
    #[cfg(feature = "ruma-identity-service-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "identity-service-api",
            feature = "identity-service-api-c",
            feature = "identity-service-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_identity_service_api as identity_service;
    #[cfg(feature = "ruma-push-gateway-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "push-gateway-api",
            feature = "push-gateway-api-c",
            feature = "push-gateway-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_push_gateway_api as push_gateway;
}

#[doc(no_inline)]
pub use assign::assign;
#[doc(no_inline)]
pub use js_int::{int, uint, Int, UInt};
#[cfg(feature = "client-ext-client-api")]
pub use ruma_client::Client;
pub use ruma_common::{
    authentication, device_id, device_key_id, directory, encryption, event_id, exports, matrix_uri,
    mxc_uri, power_levels, presence, push, receipt, room, room_alias_id, room_id, room_version_id,
    serde::Incoming, server_name, server_signing_key_id, thirdparty, to_device, user_id,
    ClientSecret, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, DeviceSignatures, DeviceSigningKeyId,
    EntitySignatures, EventEncryptionAlgorithm, EventId, IdParseError, KeyId, KeyName, MatrixToUri,
    MatrixUri, MilliSecondsSinceUnixEpoch, MxcUri, OwnedClientSecret, OwnedDeviceId,
    OwnedDeviceKeyId, OwnedDeviceSigningKeyId, OwnedEventId, OwnedKeyId, OwnedKeyName, OwnedMxcUri,
    OwnedRoomAliasId, OwnedRoomId, OwnedRoomName, OwnedRoomOrAliasId, OwnedServerName,
    OwnedServerSigningKeyId, OwnedSessionId, OwnedSigningKeyId, OwnedTransactionId, OwnedUserId,
    PrivOwnedStr, RoomAliasId, RoomId, RoomName, RoomOrAliasId, RoomVersionId,
    SecondsSinceUnixEpoch, ServerName, ServerSignatures, ServerSigningKeyId, SessionId, Signatures,
    SigningKeyAlgorithm, TransactionId, UserId,
};
<|endoftext|>


<commit_before>use frame::ChannelAssignment;

pub fn decode_left_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let left = buffer[i];
    let side = buffer[i + block_size];

    // right channel
    buffer[i + block_size] = left - side;
  }
}

fn decode_right_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let side  = buffer[i];
    let right = buffer[i + block_size];

    // left channel
    buffer[i] = side + right;
  }
}

fn decode_middle_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let mut middle = buffer[i];
    let side       = buffer[i + block_size];

    middle = (middle << 1) | (side & 1);

    // left and right channel
    buffer[i]              = (middle + side) >> 1;
    buffer[i + block_size] = (middle - side) >> 1;
  }
}

pub fn decode(channel_assignment: ChannelAssignment, buffer: &mut [i32]) {
  match channel_assignment {
    ChannelAssignment::Independent => return,
    ChannelAssignment::LeftSide    => decode_left_side(buffer),
    ChannelAssignment::RightSide   => decode_right_side(buffer),
    ChannelAssignment::MiddleSide  => decode_middle_side(buffer),
  }
}
<commit_msg>Add test for `decode_left_side`<commit_after>use frame::ChannelAssignment;

pub fn decode_left_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let left = buffer[i];
    let side = buffer[i + block_size];

    // right channel
    buffer[i + block_size] = left - side;
  }
}

fn decode_right_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let side  = buffer[i];
    let right = buffer[i + block_size];

    // left channel
    buffer[i] = side + right;
  }
}

fn decode_middle_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let mut middle = buffer[i];
    let side       = buffer[i + block_size];

    middle = (middle << 1) | (side & 1);

    // left and right channel
    buffer[i]              = (middle + side) >> 1;
    buffer[i + block_size] = (middle - side) >> 1;
  }
}

pub fn decode(channel_assignment: ChannelAssignment, buffer: &mut [i32]) {
  match channel_assignment {
    ChannelAssignment::Independent => return,
    ChannelAssignment::LeftSide    => decode_left_side(buffer),
    ChannelAssignment::RightSide   => decode_right_side(buffer),
    ChannelAssignment::MiddleSide  => decode_middle_side(buffer),
  }
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn test_decode_left_side() {
    let mut channels = [ 2, 5, 83, 113, 127, -63, -45, -15
                       , 7, 38, 142, 238, 0, -152, -52, -18
                       ];
    let result       = [-5, -33, -59, -125, 127, 89, 7, 3];

    decode_left_side(&mut channels);

    assert_eq!(&channels[8..16], &result);
  }
}
<|endoftext|>


<commit_before>use neb::ram::schema::Field;
use neb::ram::cell::MAX_CELL_SIZE;
use neb::ram::types::{TypeId, Id, id_io, u32_io, key_hash};
use neb::client::transaction::{Transaction, TxnError};

use graph::vertex::Vertex;

pub const NEXT_KEY: &'static str = "_next";
pub const LIST_KEY: &'static str = "_list";

lazy_static! {
    pub static ref ID_LINKED_LIST: Vec<Field> = vec![
            Field::new(&String::from(NEXT_KEY), TypeId::Id as u32, true, false, None),
            Field::new(&String::from(LIST_KEY), TypeId::Id as u32, false, true, None)
          ];
    pub static ref LIST_CAPACITY: usize =
        ((MAX_CELL_SIZE - u32_io::size(0) - id_io::size(0)) / id_io::size(0));
    pub static ref NEXT_KEY_ID: u64 = key_hash(&String::from(NEXT_KEY));
}

pub struct IdList<'a> {
    txn: &'a mut Transaction,
    id: Id,
    field_id: u64
}

impl<'a> IdList <'a> {
    pub fn from_txn_vertex(txn: &'a mut Transaction, id: &Id, field_id: u64) -> IdList<'a> {
        IdList {
            txn: txn,
            id: *id,
            field_id: field_id
        }
    }
    pub fn into_iter(self) -> IdListIntoIterator<'a> {
        IdListIntoIterator {
            list: self,
            index: 0
        }
    }
}

pub struct IdListIntoIterator<'a> {
    list: IdList<'a>,
    index: usize,
}

impl <'a>Iterator for IdListIntoIterator<'a> {

    type Item = Id;

    fn next(&mut self) -> Option<Self::Item> {
        unimplemented!()
    }

    fn count(self) -> usize where Self: Sized {
        unimplemented!()
    }

    fn last(self) -> Option<Self::Item> where Self: Sized {
        unimplemented!()
    }

    fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
        unimplemented!()
    }
}

<commit_msg>id list iterators<commit_after>use neb::ram::schema::Field;
use neb::ram::cell::{MAX_CELL_SIZE, Cell};
use neb::ram::types::{TypeId, Id, Map, Value, id_io, u32_io, key_hash};
use neb::client::transaction::{Transaction, TxnError};

use graph::vertex::Vertex;

pub const NEXT_KEY: &'static str = "_next";
pub const LIST_KEY: &'static str = "_list";

lazy_static! {
    pub static ref ID_LINKED_LIST: Vec<Field> = vec![
            Field::new(&String::from(NEXT_KEY), TypeId::Id as u32, true, false, None),
            Field::new(&String::from(LIST_KEY), TypeId::Id as u32, false, true, None)
          ];
    pub static ref LIST_CAPACITY: usize =
        ((MAX_CELL_SIZE - u32_io::size(0) - id_io::size(0)) / id_io::size(0));
    pub static ref NEXT_KEY_ID: u64 = key_hash(&String::from(NEXT_KEY));
    pub static ref LIST_KEY_ID: u64 = key_hash(&String::from(LIST_KEY));
}

pub struct IdList<'a> {
    txn: &'a mut Transaction,
    id: Id,
    field_id: u64
}

impl<'a> IdList <'a> {
    pub fn from_txn_vertex(txn: &'a mut Transaction, id: &Id, field_id: u64) -> IdList<'a> {
        IdList {
            txn: txn,
            id: *id,
            field_id: field_id,
        }
    }
}

pub struct IdListSegmentIntoIterator<'a> {
    txn: &'a mut Transaction,
    next: Id
}

impl <'a>IdListSegmentIntoIterator<'a> {
    pub fn new(txn: &'a mut Transaction, head_id: Id) -> IdListSegmentIntoIterator<'a> {
        IdListSegmentIntoIterator {
            txn: txn,
            next: head_id
        }
    }
}

impl <'a> Iterator for IdListSegmentIntoIterator<'a> {

    type Item = Cell;

    fn next(&mut self) -> Option<Self::Item> {
        let mut id_set = false;
        if !self.next.is_unit_id() {
            match self.txn.read(&self.next) {
                Ok(Some(cell)) => {
                    if let Value::Map(ref map) = cell.data {
                        if let &Value::Id(ref id) = map.get_by_key_id(*NEXT_KEY_ID) {
                            self.next = *id;
                            id_set = true;
                        }
                    }
                    if id_set {
                        return Some(cell)
                    }
                },
                _ => {}
            }
        }
        None
    }
}

pub struct IdListIterator<'a> {
    segments: IdListSegmentIntoIterator<'a>,
    current_seg: Option<Cell>,
    current_pos: u32
}

impl <'a> IdListIterator <'a> {
    pub fn next_seg(&mut self) {
        self.current_seg = self.segments.next();
        self.current_pos = 0;
    }
}

impl <'a> Iterator for IdListIterator<'a> {
    type Item = Id;

    fn next(&mut self) -> Option<Self::Item> {
        let mut need_next_seg = false;
        if let Some(ref cell) = self.current_seg {
            let pos = self.current_pos;
            self.current_pos += 1;
            if let &Value::Map(ref map) = &cell.data {
                if let &Value::Array(ref list) = map.get_by_key_id(*LIST_KEY_ID) {
                    if let Some(&Value::Id(id)) = list.get(pos as usize) {
                        return Some(id);
                    } else {
                        need_next_seg = true
                    }
                }
            }
        };
        if need_next_seg {
            self.next_seg();
            self.next()
        } else {
            None
        }
    }

    fn last(self) -> Option<Self::Item> where Self: Sized {
        if let Some(last_seg) = self.segments.last() {
            if let &Value::Map(ref map) = &last_seg.data {
                if let &Value::Array(ref list) = map.get_by_key_id(*LIST_KEY_ID) {
                    if let Some(&Value::Id(id)) = list.last() {
                        return Some(id);
                    }
                }
            }
        }
        return None;
    }
    fn count(self) -> usize where Self: Sized {
        let mut count = 0;
        for seg in self.segments {
            if let &Value::Map(ref map) = &seg.data {
                if let &Value::Array(ref list) = map.get_by_key_id(*LIST_KEY_ID) {
                    count += list.len();
                }
            }
        }
        return count;
    }
}

<|endoftext|>


<commit_before>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use ffi;
use python::{Python, ToPythonPointer, PythonObject};
use conversion::ToPyObject;
use objects::{PyObject, PyList};
use err::{self, PyResult, PyErr};
use std::collections::HashMap;
use std::hash::Hash;
use std::cmp::Eq;

/// Represents a Python `dict`.
pub struct PyDict<'p>(PyObject<'p>);

pyobject_newtype!(PyDict, PyDict_Check, PyDict_Type);

impl <'p> PyDict<'p> {
    /// Creates a new empty dictionary.
    ///
    /// May panic when running out of memory.
    pub fn new(py: Python<'p>) -> PyDict<'p> {
        unsafe {
            err::cast_from_owned_ptr_or_panic(py, ffi::PyDict_New())
        }
    }

    /// Return a new dictionary that contains the same key-value pairs as self.
    /// Corresponds to `dict(self)` in Python.
    pub fn copy(&self) -> PyResult<'p, PyDict<'p>> {
        let py = self.python();
        unsafe {
            err::result_cast_from_owned_ptr(py, ffi::PyDict_Copy(self.as_ptr()))
        }
    }

    /// Empty an existing dictionary of all key-value pairs.
    #[inline]
    pub fn clear(&self) {
        unsafe { ffi::PyDict_Clear(self.as_ptr()) }
    }

    /// Return the number of items in the dictionary.
    /// This is equivalent to len(p) on a dictionary.
    #[inline]
    pub fn len(&self) -> usize {
        unsafe { ffi::PyDict_Size(self.as_ptr()) as usize }
    }

    /// Determine if the dictionary contains the specified key.
    /// This is equivalent to the Python expression `key in self`.
    pub fn contains<K>(&self, key: K) -> PyResult<'p, bool> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            match ffi::PyDict_Contains(self.as_ptr(), key) {
                1 => Ok(true),
                0 => Ok(false),
                _ => Err(PyErr::fetch(py))
            }
        })
    }

    /// Gets an item from the dictionary.
    /// Returns None if the item is not present, or if an error occurs.
    pub fn get_item<K>(&self, key: K) -> Option<PyObject<'p>> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            PyObject::from_borrowed_ptr_opt(py,
                ffi::PyDict_GetItem(self.as_ptr(), key))
        })
    }

    /// Sets an item value.
    /// This is equivalent to the Python expression `self[key] = value`.
    pub fn set_item<K, V>(&self, key: K, value: V) -> PyResult<'p, ()> where K: ToPyObject<'p>, V: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, move |key|
            value.with_borrowed_ptr(py, |value| unsafe {
                err::error_on_minusone(py,
                    ffi::PyDict_SetItem(self.as_ptr(), key, value))
            }))
    }

    /// Deletes an item.
    /// This is equivalent to the Python expression `del self[key]`.
    pub fn del_item<K>(&self, key: K) -> PyResult<'p, ()> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            err::error_on_minusone(py,
                ffi::PyDict_DelItem(self.as_ptr(), key))
        })
    }

    // List of dict items.
    // This is equivalent to the `dict.items()` method.
    pub fn items(&self) -> PyList {
        let py = self.python();
        unsafe {
            err::cast_from_owned_ptr_or_panic(py, ffi::PyDict_Items(self.as_ptr()))
        }
    }
}

// TODO: use macros to make implementations for different maps
impl <'p, K, V> ToPyObject<'p> for HashMap<K, V> where K: Hash+Eq+ToPyObject<'p>, V: ToPyObject<'p> {
    type ObjectType = PyDict<'p>;

    fn to_py_object(&self, py: Python<'p>) -> PyDict<'p> {
        let dict = PyDict::new(py);
        for (key, value) in self.iter() {
            dict.set_item(key, value).unwrap();
        };
        dict
    }
}
<commit_msg>Change PyDict::items() to return `Vec<(PyObject, PyObject)>` instead of `PyList`.<commit_after>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use ffi;
use python::{Python, ToPythonPointer, PythonObject};
use conversion::ToPyObject;
use objects::{PyObject, PyList};
use err::{self, PyResult, PyErr};
use std::{mem, collections, hash, cmp};

/// Represents a Python `dict`.
pub struct PyDict<'p>(PyObject<'p>);

pyobject_newtype!(PyDict, PyDict_Check, PyDict_Type);

impl <'p> PyDict<'p> {
    /// Creates a new empty dictionary.
    ///
    /// May panic when running out of memory.
    pub fn new(py: Python<'p>) -> PyDict<'p> {
        unsafe {
            err::cast_from_owned_ptr_or_panic(py, ffi::PyDict_New())
        }
    }

    /// Return a new dictionary that contains the same key-value pairs as self.
    /// Corresponds to `dict(self)` in Python.
    pub fn copy(&self) -> PyResult<'p, PyDict<'p>> {
        let py = self.python();
        unsafe {
            err::result_cast_from_owned_ptr(py, ffi::PyDict_Copy(self.as_ptr()))
        }
    }

    /// Empty an existing dictionary of all key-value pairs.
    #[inline]
    pub fn clear(&self) {
        unsafe { ffi::PyDict_Clear(self.as_ptr()) }
    }

    /// Return the number of items in the dictionary.
    /// This is equivalent to len(p) on a dictionary.
    #[inline]
    pub fn len(&self) -> usize {
        unsafe { ffi::PyDict_Size(self.as_ptr()) as usize }
    }

    /// Determine if the dictionary contains the specified key.
    /// This is equivalent to the Python expression `key in self`.
    pub fn contains<K>(&self, key: K) -> PyResult<'p, bool> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            match ffi::PyDict_Contains(self.as_ptr(), key) {
                1 => Ok(true),
                0 => Ok(false),
                _ => Err(PyErr::fetch(py))
            }
        })
    }

    /// Gets an item from the dictionary.
    /// Returns None if the item is not present, or if an error occurs.
    pub fn get_item<K>(&self, key: K) -> Option<PyObject<'p>> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            PyObject::from_borrowed_ptr_opt(py,
                ffi::PyDict_GetItem(self.as_ptr(), key))
        })
    }

    /// Sets an item value.
    /// This is equivalent to the Python expression `self[key] = value`.
    pub fn set_item<K, V>(&self, key: K, value: V) -> PyResult<'p, ()> where K: ToPyObject<'p>, V: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, move |key|
            value.with_borrowed_ptr(py, |value| unsafe {
                err::error_on_minusone(py,
                    ffi::PyDict_SetItem(self.as_ptr(), key, value))
            }))
    }

    /// Deletes an item.
    /// This is equivalent to the Python expression `del self[key]`.
    pub fn del_item<K>(&self, key: K) -> PyResult<'p, ()> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            err::error_on_minusone(py,
                ffi::PyDict_DelItem(self.as_ptr(), key))
        })
    }

    // List of dict items.
    // This is equivalent to the python expression `list(dict.items())`.
    pub fn items_list(&self) -> PyList<'p> {
        let py = self.python();
        unsafe {
            err::cast_from_owned_ptr_or_panic(py, ffi::PyDict_Items(self.as_ptr()))
        }
    }

    /// Returns the list of (key,value) pairs in this dictionary.
    pub fn items(&self) -> Vec<(PyObject<'p>, PyObject<'p>)> {
        // Note that we don't provide an iterator because
        // PyDict_Next() is unsafe to use when the dictionary might be changed
        // by other python code.
        let py = self.python();
        let mut vec = Vec::with_capacity(self.len());
        unsafe {
            let mut pos = 0;
            let mut key: *mut ffi::PyObject = mem::uninitialized();
            let mut value: *mut ffi::PyObject = mem::uninitialized();
            while ffi::PyDict_Next(self.as_ptr(), &mut pos, &mut key, &mut value) != 0 {
                vec.push((PyObject::from_borrowed_ptr(py, key),
                          PyObject::from_borrowed_ptr(py, value)));
            }
        }
        vec
    }
}

impl <'p, K, V> ToPyObject<'p> for collections::HashMap<K, V>
    where K: hash::Hash+cmp::Eq+ToPyObject<'p>,
          V: ToPyObject<'p>
{
    type ObjectType = PyDict<'p>;

    fn to_py_object(&self, py: Python<'p>) -> PyDict<'p> {
        let dict = PyDict::new(py);
        for (key, value) in self.iter() {
            dict.set_item(key, value).unwrap();
        };
        dict
    }
}

<|endoftext|>