<commit_before>use std::ptr;
use std::marker::PhantomData;
use std::ffi::CString;
use std::error;
use std::fmt;
use gl;
use gl::types::*;
use types::GLError;
use context::Context;
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> {
    gl: &'a Context,
    shaders: &'a [Shader]
}

impl<'a> ProgramBuilder<'a> {
    fn new(gl: &'a Context, 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()
    }
}

impl Context {
    pub fn build_program<'a>(&'a self, shaders: &'a [Shader])
        -> ProgramBuilder<'a>
    {
        ProgramBuilder::new(self, shaders)
    }

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

    pub 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"
            }
        }
    }

    pub 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))
        }
    }

    pub 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
            }
        }
    }

    pub fn get_attrib_location(&self, program: &Program, name: &str)
        -> Result<ProgramAttrib, ()>
    {
        let c_str = try!(CString::new(name).or(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(())
            }
        }
    }

    pub fn get_uniform_location(&self, program: &Program, name: &str)
        -> Result<ProgramUniform, ()>
    {
        let c_str = try!(CString::new(name).or(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(())
            }
        }
    }
}

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

impl<'a> ProgramBinding<'a> {
    pub fn set_uniform<T>(&self, 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"
            }
        }
    }
}

pub struct ProgramBinder;
impl ProgramBinder {
    pub fn bind(&mut self, program: &mut Program) -> ProgramBinding
    {
        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>Return an `UnknownProgramAttrib` from `get_attrib_location()` on error<commit_after>use std::ptr;
use std::marker::PhantomData;
use std::ffi::CString;
use std::error;
use std::fmt;
use gl;
use gl::types::*;
use types::GLError;
use context::Context;
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> {
    gl: &'a Context,
    shaders: &'a [Shader]
}

impl<'a> ProgramBuilder<'a> {
    fn new(gl: &'a Context, 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()
    }
}

impl Context {
    pub fn build_program<'a>(&'a self, shaders: &'a [Shader])
        -> ProgramBuilder<'a>
    {
        ProgramBuilder::new(self, shaders)
    }

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

    pub 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"
            }
        }
    }

    pub 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))
        }
    }

    pub 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
            }
        }
    }

    pub 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 c_str = try!(CString::new(name).or(Err(e)));
        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
            }
        }
    }

    pub fn get_uniform_location(&self, program: &Program, name: &str)
        -> Result<ProgramUniform, ()>
    {
        let c_str = try!(CString::new(name).or(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(())
            }
        }
    }
}

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

impl<'a> ProgramBinding<'a> {
    pub fn set_uniform<T>(&self, 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"
            }
        }
    }
}

pub struct ProgramBinder;
impl ProgramBinder {
    pub fn bind(&mut self, program: &mut Program) -> ProgramBinding
    {
        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>
use ffi;
use super::util::FFTW_MUTEX;
use super::{c32, c64};

use num_traits::Zero;
use std::slice::{from_raw_parts, from_raw_parts_mut};
use std::os::raw::c_void;
use std::ops::{Index, IndexMut};
use std::slice::{Iter, IterMut};

pub struct RawVec<T> {
    n: usize,
    data: *mut T,
}

pub trait AlignedAllocable {
    unsafe fn alloc(n: usize) -> *mut Self;
}

impl AlignedAllocable for f64 {
    unsafe fn alloc(n: usize) -> *mut Self { ffi::fftw_alloc_real(n) }
}

impl AlignedAllocable for f32 {
    unsafe fn alloc(n: usize) -> *mut Self { ffi::fftwf_alloc_real(n) }
}

impl AlignedAllocable for c64 {
    unsafe fn alloc(n: usize) -> *mut Self { ffi::fftw_alloc_complex(n) }
}

impl AlignedAllocable for c32 {
    unsafe fn alloc(n: usize) -> *mut Self { ffi::fftwf_alloc_complex(n) }
}

impl<T> RawVec<T> {
    pub fn as_slice(&self) -> &[T] { unsafe { from_raw_parts(self.data, self.n) } }
    pub fn as_mut_slice(&mut self) -> &mut [T] { unsafe { from_raw_parts_mut(self.data, self.n) } }

    pub fn as_ptr(&self) -> *const T { self.data }
    pub fn as_mut_ptr(&mut self) -> *mut T { self.data }

    pub fn len(&self) -> usize { self.n }

    pub fn iter<'a>(&'a self) -> Iter<'a, T> { self.as_slice().iter() }
    pub fn iter_mut<'a>(&'a mut self) -> IterMut<'a, T> { self.as_mut_slice().iter_mut() }
}

impl<T> Drop for RawVec<T> {
    fn drop(&mut self) {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        unsafe { ffi::fftw_free(self.data as *mut c_void) };
        drop(lock);
    }
}

impl<T> RawVec<T>
    where T: Zero + AlignedAllocable
{
    pub fn new(n: usize) -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let ptr = unsafe { T::alloc(n) };
        drop(lock);
        let mut vec = RawVec { n: n, data: ptr };
        for v in vec.iter_mut() {
            *v = T::zero();
        }
        vec
    }
}

impl<T> Index<usize> for RawVec<T> {
    type Output = T;
    fn index(&self, index: usize) -> &Self::Output { unsafe { &*self.data.offset(index) } }
}

impl<T> IndexMut<usize> for RawVec<T> {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        unsafe { &mut *self.data.offset(index) }
    }
}
<commit_msg>usize -> isize cast<commit_after>
use ffi;
use super::util::FFTW_MUTEX;
use super::{c32, c64};

use num_traits::Zero;
use std::slice::{from_raw_parts, from_raw_parts_mut};
use std::os::raw::c_void;
use std::ops::{Index, IndexMut};
use std::slice::{Iter, IterMut};

pub struct RawVec<T> {
    n: usize,
    data: *mut T,
}

pub trait AlignedAllocable {
    unsafe fn alloc(n: usize) -> *mut Self;
}

impl AlignedAllocable for f64 {
    unsafe fn alloc(n: usize) -> *mut Self { ffi::fftw_alloc_real(n) }
}

impl AlignedAllocable for f32 {
    unsafe fn alloc(n: usize) -> *mut Self { ffi::fftwf_alloc_real(n) }
}

impl AlignedAllocable for c64 {
    unsafe fn alloc(n: usize) -> *mut Self { ffi::fftw_alloc_complex(n) }
}

impl AlignedAllocable for c32 {
    unsafe fn alloc(n: usize) -> *mut Self { ffi::fftwf_alloc_complex(n) }
}

impl<T> RawVec<T> {
    pub fn as_slice(&self) -> &[T] { unsafe { from_raw_parts(self.data, self.n) } }
    pub fn as_mut_slice(&mut self) -> &mut [T] { unsafe { from_raw_parts_mut(self.data, self.n) } }

    pub fn as_ptr(&self) -> *const T { self.data }
    pub fn as_mut_ptr(&mut self) -> *mut T { self.data }

    pub fn len(&self) -> usize { self.n }

    pub fn iter<'a>(&'a self) -> Iter<'a, T> { self.as_slice().iter() }
    pub fn iter_mut<'a>(&'a mut self) -> IterMut<'a, T> { self.as_mut_slice().iter_mut() }
}

impl<T> Drop for RawVec<T> {
    fn drop(&mut self) {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        unsafe { ffi::fftw_free(self.data as *mut c_void) };
        drop(lock);
    }
}

impl<T> RawVec<T>
    where T: Zero + AlignedAllocable
{
    pub fn new(n: usize) -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let ptr = unsafe { T::alloc(n) };
        drop(lock);
        let mut vec = RawVec { n: n, data: ptr };
        for v in vec.iter_mut() {
            *v = T::zero();
        }
        vec
    }
}

impl<T> Index<usize> for RawVec<T> {
    type Output = T;
    fn index(&self, index: usize) -> &Self::Output { unsafe { &*self.data.offset(index as isize) } }
}

impl<T> IndexMut<usize> for RawVec<T> {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        unsafe { &mut *self.data.offset(index as isize) }
    }
}
<|endoftext|>


<commit_before>use {Value, Response};
use parser::parse_response;
use error::RequestError;
use utils::escape_xml;

use hyper::client::{Client, Body};
use hyper::header::{ContentType, UserAgent};

use std::io::{self, Write};

/// A request to call a procedure.
pub struct Request<'a> {
    name: &'a str,
    args: Vec<Value>,
}

impl<'a> Request<'a> {
    /// Creates a new request to call a function named `name`.
    ///
    /// By default, no arguments are passed. Use the `arg` method to append arguments.
    pub fn new(name: &'a str) -> Self {
        Request {
            name: name,
            args: Vec::new(),
        }
    }

    /// Appends an argument to be passed to the current list of arguments.
    pub fn arg<T: Into<Value>>(mut self, value: T) -> Self {
        self.args.push(value.into());

        Request {
            name: self.name,
            args: self.args,
        }
    }

    /// Calls the method using the given `Client`.
    ///
    /// Returns a `RequestResult` indicating whether the request was sent and processed successfully
    /// (according to the rules of XML-RPC).
    pub fn call(&self, client: &Client, url: &str) -> RequestResult {
        use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};

        // First, build the body XML
        let mut body = Vec::new();
        self.write_as_xml(&mut body)?;

        // Send XML-RPC request
        let mut response = client.post(url)
            .header(UserAgent("Rust xmlrpc".to_string()))
            .header(ContentType(Mime(TopLevel::Text, SubLevel::Xml, vec![(Attr::Charset, Value::Utf8)])))
            .body(Body::BufBody(&body, body.len()))
            .send()?;

        // FIXME Check that the response headers are correct

        // Read the response and parse it
        // FIXME `BufRead`?
        Ok(parse_response(&mut response)?)
    }

    /// Formats this `Request` as XML.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        write!(fmt, r#"<?xml version="1.0" encoding="utf-8"?>"#)?;
        write!(fmt, r#"<methodCall>"#)?;
        write!(fmt, r#"    <methodName>{}</methodName>"#, escape_xml(&self.name))?;
        write!(fmt, r#"    <params>"#)?;
        for value in &self.args {
            write!(fmt, r#"        <param>"#)?;
            value.format(fmt)?;
            write!(fmt, r#"        </param>"#)?;
        }
        write!(fmt, r#"    </params>"#)?;
        write!(fmt, r#"</methodCall>"#)?;
        Ok(())
    }
}

/// The result of executing a request.
///
/// When the request was executed without major errors (like an HTTP error or a malformed response),
/// this is `Ok`. The `Response` can still denote a `Fault` if the server returned a `<fault>`
/// response.
pub type RequestResult = Result<Response, RequestError>;

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

    #[test]
    fn escapes_method_names() {
        let mut output: Vec<u8> = Vec::new();
        let req = Request::new("x<&x");

        req.write_as_xml(&mut output).unwrap();
        assert!(
            str::from_utf8(&output)
            .unwrap()
            .contains("<methodName>x<&x</methodName>"));
    }
}
<commit_msg>Make it possible to change the hyper RequestBuilder<commit_after>use {Value, Response};
use parser::parse_response;
use error::RequestError;
use utils::escape_xml;

use hyper::client::{Client, Body, RequestBuilder};
use hyper::header::{ContentType, UserAgent};

use std::io::{self, Write};

/// A request to call a procedure.
pub struct Request<'a> {
    name: &'a str,
    args: Vec<Value>,
}

impl<'a> Request<'a> {
    /// Creates a new request to call a function named `name`.
    ///
    /// By default, no arguments are passed. Use the `arg` method to append arguments.
    pub fn new(name: &'a str) -> Self {
        Request {
            name: name,
            args: Vec::new(),
        }
    }

    /// Appends an argument to be passed to the current list of arguments.
    pub fn arg<T: Into<Value>>(mut self, value: T) -> Self {
        self.args.push(value.into());

        Request {
            name: self.name,
            args: self.args,
        }
    }

    /// Calls the method using the given `Client`.
    ///
    /// Returns a `RequestResult` indicating whether the request was sent and processed successfully
    /// (according to the rules of XML-RPC).
    pub fn call(&self, client: &Client, url: &str) -> RequestResult {
        self.call_with(client, url, |b| b)
    }

    /// Calls the method, giving the closure a chance to amend the hyper
    /// `RequestBuilder` before sending.
    pub fn call_with<F>(&self, client: &Client, url: &str, cb: F) -> RequestResult
        where F: Fn(RequestBuilder) -> RequestBuilder
    {
        use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};

        // First, build the body XML
        let mut body = Vec::new();
        self.write_as_xml(&mut body)?;

        // Send XML-RPC request
        let builder = client.post(url)
            .header(UserAgent("Rust xmlrpc".to_string()))
            .header(ContentType(Mime(TopLevel::Text, SubLevel::Xml,
                                     vec![(Attr::Charset, Value::Utf8)])))
            .body(Body::BufBody(&body, body.len()));
        let builder = cb(builder);
        let mut response = builder.send()?;

        // FIXME Check that the response headers are correct

        // Read the response and parse it
        // FIXME `BufRead`?
        Ok(parse_response(&mut response)?)
    }

    /// Formats this `Request` as XML.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        write!(fmt, r#"<?xml version="1.0" encoding="utf-8"?>"#)?;
        write!(fmt, r#"<methodCall>"#)?;
        write!(fmt, r#"    <methodName>{}</methodName>"#, escape_xml(&self.name))?;
        write!(fmt, r#"    <params>"#)?;
        for value in &self.args {
            write!(fmt, r#"        <param>"#)?;
            value.format(fmt)?;
            write!(fmt, r#"        </param>"#)?;
        }
        write!(fmt, r#"    </params>"#)?;
        write!(fmt, r#"</methodCall>"#)?;
        Ok(())
    }
}

/// The result of executing a request.
///
/// When the request was executed without major errors (like an HTTP error or a malformed response),
/// this is `Ok`. The `Response` can still denote a `Fault` if the server returned a `<fault>`
/// response.
pub type RequestResult = Result<Response, RequestError>;

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

    #[test]
    fn escapes_method_names() {
        let mut output: Vec<u8> = Vec::new();
        let req = Request::new("x<&x");

        req.write_as_xml(&mut output).unwrap();
        assert!(
            str::from_utf8(&output)
            .unwrap()
            .contains("<methodName>x<&x</methodName>"));
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Code to handle initial setup steps for a pool.
// Initial setup steps are steps that do not alter the environment.

use std::{
    collections::{HashMap, HashSet},
    fs::OpenOptions,
    path::{Path, PathBuf},
};

use chrono::{DateTime, Utc};
use libudev;
use serde_json;

use devicemapper::{devnode_to_devno, Device, Sectors};

use crate::{
    engine::{
        strat_engine::{
            backstore::{
                blockdev::StratBlockDev,
                device::DevOwnership,
                metadata::{device_identifiers, BDA},
            },
            device::blkdev_size,
            serde_structs::{BackstoreSave, BaseBlockDevSave, PoolSave},
            udev::{block_enumerator, decide_ownership, is_multipath_member, stratis_enumerator},
        },
        types::{BlockDevTier, DevUuid, PoolUuid},
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

/// Retrieve all Stratis block devices that should be made use of by the
/// Stratis engine. This excludes Stratis block devices that are multipath
/// members.
///
/// Includes a fallback path, which is used if no Stratis block devices are
/// found using the obvious udev property- and enumerator-based approach.
/// This fallback path is more expensive, because it must search all block
/// devices via udev, and may also attempt to directly read Stratis
/// metadata from the device.
fn get_stratis_block_devices() -> StratisResult<Vec<PathBuf>> {
    info!("Beginning initial search for Stratis block devices");
    let devices = {
        let context = libudev::Context::new()?;
        let mut enumerator = stratis_enumerator(&context)?;

        enumerator
            .scan_devices()?
            .filter(|dev| dev.is_initialized())
            .filter(|dev| !is_multipath_member(dev).unwrap_or(true))
            .filter_map(|i| i.devnode().map(|d| d.into()))
            .collect::<Vec<PathBuf>>()
    };

    if devices.is_empty() {
        // No Stratis devices have been found, possible reasons are:
        // 1. There are none
        // 2. There are some but libblkid version is less than 2.32, so
        // Stratis devices are not recognized by udev.
        // 3. There are many incomplete udev entries, because this code is
        // being run before the udev database is populated.
        //
        // Try again to find Stratis block devices, but this time enumerate
        // all block devices, not just all the ones that can be identified
        // as Stratis blockdevs by udev, and then scrutinize each one
        // using various methods.

        info!("Could not identify any Stratis devices by a udev search for devices with ID_FS_TYPE=\"stratis\"; using fallback search mechanism");

        let context = libudev::Context::new()?;
        let mut enumerator = block_enumerator(&context)?;
        Ok(enumerator
            .scan_devices()?
            .filter(|dev| dev.is_initialized())
            .filter_map(|dev| {
                dev.devnode().and_then(|devnode| {
                    decide_ownership(&dev)
                        .and_then(|decision| DevOwnership::from_udev_ownership(&decision, devnode))
                        .ok()
                        .and_then(|ownership| ownership.stratis_identifiers())
                        .map(|_| devnode.into())
                })
            })
            .collect())
    } else {
        Ok(devices)
    }
}

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> StratisResult<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let mut pool_map = HashMap::new();

    // FIXME: If get_stratis_block_devices() has to go to fallback mechanism,
    // it has already read the device identifiers for every device it returns.
    // It seems a waste to just read them all again in that case. Likely
    // get_stratis_block_devices() and this method should be consolidated in
    // some creative way, or get_stratis_block_devices() should be required
    // to return the device identifiers from the device as well as the devnode,
    // regardless of which mechanism was used.
    for devnode in get_stratis_block_devices()? {
        match devnode_to_devno(&devnode)? {
            None => continue,
            Some(devno) => {
                if let Some((pool_uuid, _)) =
                    device_identifiers(&mut OpenOptions::new().read(true).open(&devnode)?)?
                {
                    pool_map
                        .entry(pool_uuid)
                        .or_insert_with(HashMap::new)
                        .insert(Device::from(devno), devnode);
                }
            }
        }
    }
    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
#[allow(clippy::implicit_hasher)]
pub fn get_metadata(
    pool_uuid: PoolUuid,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<Option<(DateTime<Utc>, PoolSave)>> {
    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes.values() {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?;
        if let Some(bda) = bda {
            if bda.pool_uuid() == pool_uuid {
                bdas.push((devnode, bda));
            }
        }
    }

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = {
        match bdas
            .iter()
            .filter_map(|&(_, ref bda)| bda.last_update_time())
            .max()
        {
            Some(time) => time,
            None => return Ok(None),
        }
    };

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    bdas.iter()
        .filter_map(|&(devnode, ref bda)| {
            if bda.last_update_time() == Some(most_recent_time) {
                OpenOptions::new()
                    .read(true)
                    .open(devnode)
                    .ok()
                    .and_then(|mut f| bda.load_state(&mut f).unwrap_or(None))
                    .and_then(|data| serde_json::from_slice(&data).ok())
            } else {
                None
            }
        })
        .next()
        .ok_or_else(|| {
            StratisError::Engine(
                ErrorEnum::NotFound,
                "timestamp indicates data was written, but no data successfully read".into(),
            )
        })
        .map(|psave| Some((*most_recent_time, psave)))
}

/// Get all the blockdevs corresponding to this pool that can be obtained from
/// the given devices. Sort the blockdevs in the order in which they were
/// recorded in the metadata.
/// Returns an error if a BDA can not be read or can not be found on any
/// blockdev in devnodes.
/// Returns an error if the blockdevs obtained do not match the metadata.
/// Returns a tuple, of which the first are the data devs, and the second
/// are the devs that support the cache tier.
/// Precondition: Every device in devnodes has already been determined to
/// belong to the pool with the specified pool uuid.
#[allow(clippy::implicit_hasher)]
pub fn get_blockdevs(
    pool_uuid: PoolUuid,
    backstore_save: &BackstoreSave,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<(Vec<StratBlockDev>, Vec<StratBlockDev>)> {
    let recorded_data_map: HashMap<DevUuid, (usize, &BaseBlockDevSave)> = backstore_save
        .data_tier
        .blockdev
        .devs
        .iter()
        .enumerate()
        .map(|(i, bds)| (bds.uuid, (i, bds)))
        .collect();

    let recorded_cache_map: HashMap<DevUuid, (usize, &BaseBlockDevSave)> =
        match backstore_save.cache_tier {
            Some(ref cache_tier) => cache_tier
                .blockdev
                .devs
                .iter()
                .enumerate()
                .map(|(i, bds)| (bds.uuid, (i, bds)))
                .collect(),
            None => HashMap::new(),
        };

    let mut segment_table: HashMap<DevUuid, Vec<(Sectors, Sectors)>> = HashMap::new();
    for seg in &backstore_save.data_tier.blockdev.allocs[0] {
        segment_table
            .entry(seg.parent)
            .or_insert_with(Vec::default)
            .push((seg.start, seg.length))
    }

    if let Some(ref cache_tier) = backstore_save.cache_tier {
        for seg in cache_tier.blockdev.allocs.iter().flat_map(|i| i.iter()) {
            segment_table
                .entry(seg.parent)
                .or_insert_with(Vec::default)
                .push((seg.start, seg.length))
        }
    }

    // Construct a single StratBlockDev. Return the tier to which the
    // blockdev has been found to belong. Returns an error if the block
    // device has shrunk, no metadata can be found for the block device,
    // or it is impossible to set up the device because the recorded
    // allocation information is impossible.
    fn get_blockdev(
        device: Device,
        devnode: &Path,
        bda: BDA,
        data_map: &HashMap<DevUuid, (usize, &BaseBlockDevSave)>,
        cache_map: &HashMap<DevUuid, (usize, &BaseBlockDevSave)>,
        segment_table: &HashMap<DevUuid, Vec<(Sectors, Sectors)>>,
    ) -> StratisResult<(BlockDevTier, StratBlockDev)> {
        // Return an error if apparent size of Stratis block device appears to
        // have decreased since metadata was recorded or if size of block
        // device could not be obtained.
        blkdev_size(&OpenOptions::new().read(true).open(devnode)?).and_then(|actual_size| {
            let actual_size_sectors = actual_size.sectors();
            let recorded_size = bda.dev_size().sectors();
            if actual_size_sectors < recorded_size {
                let err_msg = format!(
                    "Stratis device with device number {}, devnode {}, pool UUID {} and device UUID {} had recorded size ({}), but actual size is less at ({})",
                    device,
                    devnode.display(),
                    bda.pool_uuid(),
                    bda.dev_uuid(),
                    recorded_size,
                    actual_size
                );
                Err(StratisError::Engine(ErrorEnum::Error, err_msg))
            } else {
                Ok(())
            }
        })?;

        let dev_uuid = bda.dev_uuid();

        // Locate the device in the metadata using its uuid. Return the device
        // metadata and whether it was a cache or a datadev.
        let (tier, &(_, bd_save)) = data_map
            .get(&dev_uuid)
            .map(|bd_save| (BlockDevTier::Data, bd_save))
            .or_else(|| {
                cache_map
                    .get(&dev_uuid)
                    .map(|bd_save| (BlockDevTier::Cache, bd_save))
            })
            .ok_or_else(|| {
                let err_msg = format!(
                        "Stratis device with device number {}, devnode {}, pool UUID {} and device UUID {} had no record in pool metadata",
                        device,
                        devnode.display(),
                        bda.pool_uuid(),
                        bda.dev_uuid()
                    );
                StratisError::Engine(ErrorEnum::NotFound, err_msg)
            })?;

        // This should always succeed since the actual size is at
        // least the recorded size, so all segments should be
        // available to be allocated. If this fails, the most likely
        // conclusion is metadata corruption.
        let segments = segment_table.get(&dev_uuid);
        Ok((
            tier,
            StratBlockDev::new(
                device,
                devnode.to_owned(),
                bda,
                segments.unwrap_or(&vec![]),
                bd_save.user_info.clone(),
                bd_save.hardware_info.clone(),
            )?,
        ))
    }

    let (mut datadevs, mut cachedevs): (Vec<StratBlockDev>, Vec<StratBlockDev>) = (vec![], vec![]);
    for (device, devnode) in devnodes {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?.ok_or_else(|| {
            StratisError::Engine(ErrorEnum::NotFound,
                                                 format!("Device {} with devnode {} was previously determined to belong to pool with uuid {} but no BDA was found",
                                                 device,
                                                 devnode.display(),
                                                 pool_uuid))
        })?;

        get_blockdev(
            *device,
            devnode,
            bda,
            &recorded_data_map,
            &recorded_cache_map,
            &segment_table,
        )
        .map(|(tier, blockdev)| {
            match tier {
                BlockDevTier::Data => &mut datadevs,
                BlockDevTier::Cache => &mut cachedevs,
            }
            .push(blockdev)
        })?;
    }

    // Verify that devices located are congruent with the metadata recorded
    // and generally consistent with expectations. If all seems correct,
    // sort the devices according to their order in the metadata.
    fn check_and_sort_devs(
        mut devs: Vec<StratBlockDev>,
        dev_map: &HashMap<DevUuid, (usize, &BaseBlockDevSave)>,
    ) -> StratisResult<Vec<StratBlockDev>> {
        let mut uuids = HashSet::new();
        let mut duplicate_uuids = Vec::new();
        for dev in &devs {
            let dev_uuid = dev.uuid();
            if !uuids.insert(dev_uuid) {
                duplicate_uuids.push(dev_uuid);
            }
        }

        if !duplicate_uuids.is_empty() {
            let err_msg = format!(
                "The following list of Stratis UUIDs were each claimed by more than one Stratis device: {}",
                duplicate_uuids.iter().map(|u| u.to_simple_ref().to_string()).collect::<Vec<_>>().join(", ")
            );
            return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg));
        }

        let recorded_uuids: HashSet<_> = dev_map.keys().cloned().collect();
        if uuids != recorded_uuids {
            let err_msg = format!(
                "UUIDs of devices found ({}) did not correspond with UUIDs specified in the metadata for this group of devices ({})",
                uuids.iter().map(|u| u.to_simple_ref().to_string()).collect::<Vec<_>>().join(", "),
                recorded_uuids.iter().map(|u| u.to_simple_ref().to_string()).collect::<Vec<_>>().join(", "),
            );
            return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg));
        }

        // Sort the devices according to their original location in the
        // metadata. Use a faster unstable sort, because the order of
        // devs before the sort is arbitrary and does not need to be
        // preserved.
        devs.sort_unstable_by_key(|dev| dev_map[&dev.uuid()].0);
        Ok(devs)
    }

    let datadevs = check_and_sort_devs(datadevs, &recorded_data_map)?;
    let cachedevs = check_and_sort_devs(cachedevs, &recorded_cache_map)?;

    Ok((datadevs, cachedevs))
}
<commit_msg>Remove some unnecessary implicit-hasher clippy allows<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Code to handle initial setup steps for a pool.
// Initial setup steps are steps that do not alter the environment.

use std::{
    collections::{HashMap, HashSet},
    fs::OpenOptions,
    path::{Path, PathBuf},
};

use chrono::{DateTime, Utc};
use libudev;
use serde_json;

use devicemapper::{devnode_to_devno, Device, Sectors};

use crate::{
    engine::{
        strat_engine::{
            backstore::{
                blockdev::StratBlockDev,
                device::DevOwnership,
                metadata::{device_identifiers, BDA},
            },
            device::blkdev_size,
            serde_structs::{BackstoreSave, BaseBlockDevSave, PoolSave},
            udev::{block_enumerator, decide_ownership, is_multipath_member, stratis_enumerator},
        },
        types::{BlockDevTier, DevUuid, PoolUuid},
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

/// Retrieve all Stratis block devices that should be made use of by the
/// Stratis engine. This excludes Stratis block devices that are multipath
/// members.
///
/// Includes a fallback path, which is used if no Stratis block devices are
/// found using the obvious udev property- and enumerator-based approach.
/// This fallback path is more expensive, because it must search all block
/// devices via udev, and may also attempt to directly read Stratis
/// metadata from the device.
fn get_stratis_block_devices() -> StratisResult<Vec<PathBuf>> {
    info!("Beginning initial search for Stratis block devices");
    let devices = {
        let context = libudev::Context::new()?;
        let mut enumerator = stratis_enumerator(&context)?;

        enumerator
            .scan_devices()?
            .filter(|dev| dev.is_initialized())
            .filter(|dev| !is_multipath_member(dev).unwrap_or(true))
            .filter_map(|i| i.devnode().map(|d| d.into()))
            .collect::<Vec<PathBuf>>()
    };

    if devices.is_empty() {
        // No Stratis devices have been found, possible reasons are:
        // 1. There are none
        // 2. There are some but libblkid version is less than 2.32, so
        // Stratis devices are not recognized by udev.
        // 3. There are many incomplete udev entries, because this code is
        // being run before the udev database is populated.
        //
        // Try again to find Stratis block devices, but this time enumerate
        // all block devices, not just all the ones that can be identified
        // as Stratis blockdevs by udev, and then scrutinize each one
        // using various methods.

        info!("Could not identify any Stratis devices by a udev search for devices with ID_FS_TYPE=\"stratis\"; using fallback search mechanism");

        let context = libudev::Context::new()?;
        let mut enumerator = block_enumerator(&context)?;
        Ok(enumerator
            .scan_devices()?
            .filter(|dev| dev.is_initialized())
            .filter_map(|dev| {
                dev.devnode().and_then(|devnode| {
                    decide_ownership(&dev)
                        .and_then(|decision| DevOwnership::from_udev_ownership(&decision, devnode))
                        .ok()
                        .and_then(|ownership| ownership.stratis_identifiers())
                        .map(|_| devnode.into())
                })
            })
            .collect())
    } else {
        Ok(devices)
    }
}

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> StratisResult<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let mut pool_map = HashMap::new();

    // FIXME: If get_stratis_block_devices() has to go to fallback mechanism,
    // it has already read the device identifiers for every device it returns.
    // It seems a waste to just read them all again in that case. Likely
    // get_stratis_block_devices() and this method should be consolidated in
    // some creative way, or get_stratis_block_devices() should be required
    // to return the device identifiers from the device as well as the devnode,
    // regardless of which mechanism was used.
    for devnode in get_stratis_block_devices()? {
        match devnode_to_devno(&devnode)? {
            None => continue,
            Some(devno) => {
                if let Some((pool_uuid, _)) =
                    device_identifiers(&mut OpenOptions::new().read(true).open(&devnode)?)?
                {
                    pool_map
                        .entry(pool_uuid)
                        .or_insert_with(HashMap::new)
                        .insert(Device::from(devno), devnode);
                }
            }
        }
    }
    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
pub fn get_metadata(
    pool_uuid: PoolUuid,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<Option<(DateTime<Utc>, PoolSave)>> {
    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes.values() {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?;
        if let Some(bda) = bda {
            if bda.pool_uuid() == pool_uuid {
                bdas.push((devnode, bda));
            }
        }
    }

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = {
        match bdas
            .iter()
            .filter_map(|&(_, ref bda)| bda.last_update_time())
            .max()
        {
            Some(time) => time,
            None => return Ok(None),
        }
    };

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    bdas.iter()
        .filter_map(|&(devnode, ref bda)| {
            if bda.last_update_time() == Some(most_recent_time) {
                OpenOptions::new()
                    .read(true)
                    .open(devnode)
                    .ok()
                    .and_then(|mut f| bda.load_state(&mut f).unwrap_or(None))
                    .and_then(|data| serde_json::from_slice(&data).ok())
            } else {
                None
            }
        })
        .next()
        .ok_or_else(|| {
            StratisError::Engine(
                ErrorEnum::NotFound,
                "timestamp indicates data was written, but no data successfully read".into(),
            )
        })
        .map(|psave| Some((*most_recent_time, psave)))
}

/// Get all the blockdevs corresponding to this pool that can be obtained from
/// the given devices. Sort the blockdevs in the order in which they were
/// recorded in the metadata.
/// Returns an error if a BDA can not be read or can not be found on any
/// blockdev in devnodes.
/// Returns an error if the blockdevs obtained do not match the metadata.
/// Returns a tuple, of which the first are the data devs, and the second
/// are the devs that support the cache tier.
/// Precondition: Every device in devnodes has already been determined to
/// belong to the pool with the specified pool uuid.
pub fn get_blockdevs(
    pool_uuid: PoolUuid,
    backstore_save: &BackstoreSave,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<(Vec<StratBlockDev>, Vec<StratBlockDev>)> {
    let recorded_data_map: HashMap<DevUuid, (usize, &BaseBlockDevSave)> = backstore_save
        .data_tier
        .blockdev
        .devs
        .iter()
        .enumerate()
        .map(|(i, bds)| (bds.uuid, (i, bds)))
        .collect();

    let recorded_cache_map: HashMap<DevUuid, (usize, &BaseBlockDevSave)> =
        match backstore_save.cache_tier {
            Some(ref cache_tier) => cache_tier
                .blockdev
                .devs
                .iter()
                .enumerate()
                .map(|(i, bds)| (bds.uuid, (i, bds)))
                .collect(),
            None => HashMap::new(),
        };

    let mut segment_table: HashMap<DevUuid, Vec<(Sectors, Sectors)>> = HashMap::new();
    for seg in &backstore_save.data_tier.blockdev.allocs[0] {
        segment_table
            .entry(seg.parent)
            .or_insert_with(Vec::default)
            .push((seg.start, seg.length))
    }

    if let Some(ref cache_tier) = backstore_save.cache_tier {
        for seg in cache_tier.blockdev.allocs.iter().flat_map(|i| i.iter()) {
            segment_table
                .entry(seg.parent)
                .or_insert_with(Vec::default)
                .push((seg.start, seg.length))
        }
    }

    // Construct a single StratBlockDev. Return the tier to which the
    // blockdev has been found to belong. Returns an error if the block
    // device has shrunk, no metadata can be found for the block device,
    // or it is impossible to set up the device because the recorded
    // allocation information is impossible.
    fn get_blockdev(
        device: Device,
        devnode: &Path,
        bda: BDA,
        data_map: &HashMap<DevUuid, (usize, &BaseBlockDevSave)>,
        cache_map: &HashMap<DevUuid, (usize, &BaseBlockDevSave)>,
        segment_table: &HashMap<DevUuid, Vec<(Sectors, Sectors)>>,
    ) -> StratisResult<(BlockDevTier, StratBlockDev)> {
        // Return an error if apparent size of Stratis block device appears to
        // have decreased since metadata was recorded or if size of block
        // device could not be obtained.
        blkdev_size(&OpenOptions::new().read(true).open(devnode)?).and_then(|actual_size| {
            let actual_size_sectors = actual_size.sectors();
            let recorded_size = bda.dev_size().sectors();
            if actual_size_sectors < recorded_size {
                let err_msg = format!(
                    "Stratis device with device number {}, devnode {}, pool UUID {} and device UUID {} had recorded size ({}), but actual size is less at ({})",
                    device,
                    devnode.display(),
                    bda.pool_uuid(),
                    bda.dev_uuid(),
                    recorded_size,
                    actual_size
                );
                Err(StratisError::Engine(ErrorEnum::Error, err_msg))
            } else {
                Ok(())
            }
        })?;

        let dev_uuid = bda.dev_uuid();

        // Locate the device in the metadata using its uuid. Return the device
        // metadata and whether it was a cache or a datadev.
        let (tier, &(_, bd_save)) = data_map
            .get(&dev_uuid)
            .map(|bd_save| (BlockDevTier::Data, bd_save))
            .or_else(|| {
                cache_map
                    .get(&dev_uuid)
                    .map(|bd_save| (BlockDevTier::Cache, bd_save))
            })
            .ok_or_else(|| {
                let err_msg = format!(
                        "Stratis device with device number {}, devnode {}, pool UUID {} and device UUID {} had no record in pool metadata",
                        device,
                        devnode.display(),
                        bda.pool_uuid(),
                        bda.dev_uuid()
                    );
                StratisError::Engine(ErrorEnum::NotFound, err_msg)
            })?;

        // This should always succeed since the actual size is at
        // least the recorded size, so all segments should be
        // available to be allocated. If this fails, the most likely
        // conclusion is metadata corruption.
        let segments = segment_table.get(&dev_uuid);
        Ok((
            tier,
            StratBlockDev::new(
                device,
                devnode.to_owned(),
                bda,
                segments.unwrap_or(&vec![]),
                bd_save.user_info.clone(),
                bd_save.hardware_info.clone(),
            )?,
        ))
    }

    let (mut datadevs, mut cachedevs): (Vec<StratBlockDev>, Vec<StratBlockDev>) = (vec![], vec![]);
    for (device, devnode) in devnodes {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?.ok_or_else(|| {
            StratisError::Engine(ErrorEnum::NotFound,
                                                 format!("Device {} with devnode {} was previously determined to belong to pool with uuid {} but no BDA was found",
                                                 device,
                                                 devnode.display(),
                                                 pool_uuid))
        })?;

        get_blockdev(
            *device,
            devnode,
            bda,
            &recorded_data_map,
            &recorded_cache_map,
            &segment_table,
        )
        .map(|(tier, blockdev)| {
            match tier {
                BlockDevTier::Data => &mut datadevs,
                BlockDevTier::Cache => &mut cachedevs,
            }
            .push(blockdev)
        })?;
    }

    // Verify that devices located are congruent with the metadata recorded
    // and generally consistent with expectations. If all seems correct,
    // sort the devices according to their order in the metadata.
    fn check_and_sort_devs(
        mut devs: Vec<StratBlockDev>,
        dev_map: &HashMap<DevUuid, (usize, &BaseBlockDevSave)>,
    ) -> StratisResult<Vec<StratBlockDev>> {
        let mut uuids = HashSet::new();
        let mut duplicate_uuids = Vec::new();
        for dev in &devs {
            let dev_uuid = dev.uuid();
            if !uuids.insert(dev_uuid) {
                duplicate_uuids.push(dev_uuid);
            }
        }

        if !duplicate_uuids.is_empty() {
            let err_msg = format!(
                "The following list of Stratis UUIDs were each claimed by more than one Stratis device: {}",
                duplicate_uuids.iter().map(|u| u.to_simple_ref().to_string()).collect::<Vec<_>>().join(", ")
            );
            return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg));
        }

        let recorded_uuids: HashSet<_> = dev_map.keys().cloned().collect();
        if uuids != recorded_uuids {
            let err_msg = format!(
                "UUIDs of devices found ({}) did not correspond with UUIDs specified in the metadata for this group of devices ({})",
                uuids.iter().map(|u| u.to_simple_ref().to_string()).collect::<Vec<_>>().join(", "),
                recorded_uuids.iter().map(|u| u.to_simple_ref().to_string()).collect::<Vec<_>>().join(", "),
            );
            return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg));
        }

        // Sort the devices according to their original location in the
        // metadata. Use a faster unstable sort, because the order of
        // devs before the sort is arbitrary and does not need to be
        // preserved.
        devs.sort_unstable_by_key(|dev| dev_map[&dev.uuid()].0);
        Ok(devs)
    }

    let datadevs = check_and_sort_devs(datadevs, &recorded_data_map)?;
    let cachedevs = check_and_sort_devs(cachedevs, &recorded_cache_map)?;

    Ok((datadevs, cachedevs))
}
<|endoftext|>


<commit_before>extern crate html5ever;
extern crate tendril;
extern crate regex;
extern crate hyper;
extern crate string_cache;
extern crate url;

use html5ever::rcdom::{Document, Doctype, Text, Comment, Element};
use html5ever::rcdom::{RcDom, Handle};
use html5ever::{parse_document, Attribute};
use tendril::stream::TendrilSink;
use std::default::Default;
use uuid::Uuid;

use regex::Regex;
use hyper::Client;
use hyper::header::Connection;
use hyper::header::ConnectionOption;

use Provider;
use Track;
use soundcloud;
use youtube;
use error::Error;

use url::percent_encoding::{percent_decode};

static YOUTUBE_EMBED:       &'static str = r"www.youtube.com/embed/([a-zA-Z0-9_-].+)";
static YOUTUBE_LIST:        &'static str = r"www.youtube.com/embed/videoseries\?list=([a-zA-Z0-9_-]+)";
static YOUTUBE_WATCH:       &'static str = r"www.youtube.com/watch\?v=([a-zA-Z0-9_-]+)";
static SOUNDCLOUD_TRACK:    &'static str = r"api.soundcloud.com/tracks/([a-zA-Z0-9_-]+)";
static SOUNDCLOUD_PLAYLIST: &'static str = r"api.soundcloud.com/playlists/([a-zA-Z0-9_-]+)";
static SOUNDCLOUD_USER:     &'static str = r"api.soundcloud.com/users/([a-zA-Z0-9_-]+)";

pub fn extract_tracks(url: &str) -> Result<Vec<Track>, Error> {
    let client = Client::new();
    let mut res = client.get(url)
        .header(Connection(vec![ConnectionOption::Close]))
        .send()
        .unwrap();
    if res.status.is_success() {
        let dom = parse_document(RcDom::default(), Default::default())
            .from_utf8()
            .read_from(&mut res)
            .unwrap();
        let mut tracks  = Vec::new();
        walk(0, dom.document, &mut tracks);
        Ok(tracks)
    } else {
        println!("Failed to get entry html {}: {}", res.status, url);
        Err(Error::NotFound)
    }
}

// This is not proper HTML serialization, of course.
fn walk(indent: usize, handle: Handle, tracks: &mut Vec<Track>) {
    let node = handle.borrow();
    match node.node {
        Document         => (),
        Doctype(_, _, _) => (),
        Text(_)          => (),
        Comment(_)       => (),
        Element(ref name, _, ref attrs) => {
            let tag_name = name.local.as_ref();
            let ts: Vec<Track> = extract_tracks_from_tag(tag_name, attrs);
            for track in ts.iter().cloned() {
                if !(tracks).iter().any(|t| track == *t) {
                    (*tracks).push(track)
                }
            }
        }
    }
    for child in node.children.iter() {
        walk(indent+4, child.clone(), tracks);
    }
}

fn attr(attr_name: &str, attrs: &Vec<Attribute>) -> Option<String> {
    for attr in attrs.iter() {
        if attr.name.local.as_ref() == attr_name {
            return Some(attr.value.to_string())
        }
    }
    None
}

fn extract_identifier(value: &str, regex_str: &str) -> Option<String> {
    match Regex::new(regex_str) {
        Ok(re) => match re.captures(value) {
            Some(cap) => match cap.at(1) {
                Some(str) => {
                    let strs: Vec<&str> = str.split('?').collect();
                    return Some(strs[0].to_string())
                },
                None => None
            },
            None => None
        },
        Err(_) => None
    }
}

pub fn extract_tracks_from_tag(tag_name: &str,
                               attrs: &Vec<Attribute>) -> Vec<Track> {
    if tag_name == "iframe" {
        match attr("src", attrs) {
            Some(ref src) => extract_tracks_from_url(src.to_string()),
            None => vec![]
        }
    } else if tag_name == "a" || tag_name == "link" {
        match attr("href", attrs) {
            Some(ref href) => extract_tracks_from_url(href.to_string()),
            None => vec![]
        }
    } else {
        vec![]
    }
}

fn extract_tracks_from_url(url: String) -> Vec<Track> {
    let decoded = percent_decode(url.as_bytes()).decode_utf8_lossy().into_owned();
    match extract_identifier(&decoded, YOUTUBE_WATCH) {
        Some(identifier) => {
            return vec![Track {
                        id: Uuid::new_v4(),
                  provider: Provider::YouTube,
                     title: "".to_string(),
                       url: url.to_string(),
                identifier: identifier
            }]
        },
        None => ()
    }
    match extract_identifier(&decoded, YOUTUBE_LIST) {
        Some(identifier) => {
            return match youtube::fetch_playlist(&identifier) {
                Ok(res) => res.items.iter()
                                    .map(|ref i| Track::from_yt_playlist_item(i))
                                    .collect::<Vec<_>>(),
                Err(_)       => vec![]
            }
        },
        None => ()
    }
    match extract_identifier(&decoded, YOUTUBE_EMBED) {
        Some(identifier) => {
            return vec![Track {
                        id: Uuid::new_v4(),
                  provider: Provider::YouTube,
                     title: "".to_string(),
                       url: url.to_string(),
                identifier: identifier
            }]
        },
        None => ()
    }
    match extract_identifier(&decoded, SOUNDCLOUD_TRACK) {
        Some(identifier) => {
            return vec![Track {
                        id: Uuid::new_v4(),
                  provider: Provider::SoundCloud,
                     title: "".to_string(),
                       url: url.to_string(),
                identifier: identifier
            }]
        },
        None => ()
    }
    match extract_identifier(&decoded, SOUNDCLOUD_PLAYLIST) {
        Some(identifier) => {
            return match soundcloud::fetch_playlist(&identifier) {
                Ok(playlist) => playlist.tracks
                                        .iter()
                                        .map(|ref t| Track::from_sc_track(t))
                                        .collect::<Vec<_>>(),
                Err(_)       => vec![]
            }
        },
        None => ()
    }
    match extract_identifier(&decoded, SOUNDCLOUD_USER) {
        Some(identifier) => {
            print!("user {}", identifier);
            return match soundcloud::fetch_user_tracks(&identifier) {
                Ok(tracks) => tracks.iter()
                                    .map(|ref t| Track::from_sc_track(t))
                                    .collect::<Vec<_>>(),
                Err(_)     => vec![]
            }
        },
        None => ()
    }
    return vec![]
}

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

    #[test]
    fn test_extract_identifier() {
        let soundcloud_src = "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/195425494/stream&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true";
        match extract_identifier(soundcloud_src, super::SOUNDCLOUD_TRACK) {
            Some(identifier) => assert_eq!(identifier, "195425494".to_string()),
            None             => assert!(false)
        }
        let youtube_embed = "https://www.youtube.com/embed/X8tOngmlES0?rel=0";
        match extract_identifier(youtube_embed, super::YOUTUBE_EMBED) {
            Some(identifier) => assert_eq!(identifier, "X8tOngmlES0".to_string()),
            None             => assert!(false)
        }

        let youtube_watch = "https://www.youtube.com/watch?v=oDuif301F-8";
        match extract_identifier(youtube_watch, super::YOUTUBE_WATCH) {
            Some(identifier) => assert_eq!(identifier, "oDuif301F-8".to_string()),
            None             => assert!(false)
        }

        let youtube_list = "https://www.youtube.com/embed/videoseries?list=PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX";
        match extract_identifier(youtube_list, super::YOUTUBE_LIST) {
            Some(identifier) => assert_eq!(identifier, "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX".to_string()),
            None             => assert!(false)
        }
    }
}
<commit_msg>Refactoring: Change method positions<commit_after>extern crate html5ever;
extern crate tendril;
extern crate regex;
extern crate hyper;
extern crate string_cache;
extern crate url;

use html5ever::rcdom::{Document, Doctype, Text, Comment, Element};
use html5ever::rcdom::{RcDom, Handle};
use html5ever::{parse_document, Attribute};
use tendril::stream::TendrilSink;
use std::default::Default;
use uuid::Uuid;

use regex::Regex;
use hyper::Client;
use hyper::header::Connection;
use hyper::header::ConnectionOption;

use Provider;
use Track;
use soundcloud;
use youtube;
use error::Error;

use url::percent_encoding::{percent_decode};

static YOUTUBE_EMBED:       &'static str = r"www.youtube.com/embed/([a-zA-Z0-9_-].+)";
static YOUTUBE_LIST:        &'static str = r"www.youtube.com/embed/videoseries\?list=([a-zA-Z0-9_-]+)";
static YOUTUBE_WATCH:       &'static str = r"www.youtube.com/watch\?v=([a-zA-Z0-9_-]+)";
static SOUNDCLOUD_TRACK:    &'static str = r"api.soundcloud.com/tracks/([a-zA-Z0-9_-]+)";
static SOUNDCLOUD_PLAYLIST: &'static str = r"api.soundcloud.com/playlists/([a-zA-Z0-9_-]+)";
static SOUNDCLOUD_USER:     &'static str = r"api.soundcloud.com/users/([a-zA-Z0-9_-]+)";

pub fn extract_tracks(url: &str) -> Result<Vec<Track>, Error> {
    let client = Client::new();
    let mut res = client.get(url)
        .header(Connection(vec![ConnectionOption::Close]))
        .send()
        .unwrap();
    if res.status.is_success() {
        let dom = parse_document(RcDom::default(), Default::default())
            .from_utf8()
            .read_from(&mut res)
            .unwrap();
        let mut tracks  = Vec::new();
        walk(0, dom.document, &mut tracks);
        Ok(tracks)
    } else {
        println!("Failed to get entry html {}: {}", res.status, url);
        Err(Error::NotFound)
    }
}

// This is not proper HTML serialization, of course.
fn walk(indent: usize, handle: Handle, tracks: &mut Vec<Track>) {
    let node = handle.borrow();
    match node.node {
        Document         => (),
        Doctype(_, _, _) => (),
        Text(_)          => (),
        Comment(_)       => (),
        Element(ref name, _, ref attrs) => {
            let tag_name = name.local.as_ref();
            let ts: Vec<Track> = extract_tracks_from_tag(tag_name, attrs);
            for track in ts.iter().cloned() {
                if !(tracks).iter().any(|t| track == *t) {
                    (*tracks).push(track)
                }
            }
        }
    }
    for child in node.children.iter() {
        walk(indent+4, child.clone(), tracks);
    }
}

fn attr(attr_name: &str, attrs: &Vec<Attribute>) -> Option<String> {
    for attr in attrs.iter() {
        if attr.name.local.as_ref() == attr_name {
            return Some(attr.value.to_string())
        }
    }
    None
}

pub fn extract_tracks_from_tag(tag_name: &str,
                               attrs: &Vec<Attribute>) -> Vec<Track> {
    if tag_name == "iframe" {
        match attr("src", attrs) {
            Some(ref src) => extract_tracks_from_url(src.to_string()),
            None => vec![]
        }
    } else if tag_name == "a" || tag_name == "link" {
        match attr("href", attrs) {
            Some(ref href) => extract_tracks_from_url(href.to_string()),
            None => vec![]
        }
    } else {
        vec![]
    }
}

fn extract_identifier(value: &str, regex_str: &str) -> Option<String> {
    match Regex::new(regex_str) {
        Ok(re) => match re.captures(value) {
            Some(cap) => match cap.at(1) {
                Some(str) => {
                    let strs: Vec<&str> = str.split('?').collect();
                    return Some(strs[0].to_string())
                },
                None => None
            },
            None => None
        },
        Err(_) => None
    }
}

fn extract_tracks_from_url(url: String) -> Vec<Track> {
    let decoded = percent_decode(url.as_bytes()).decode_utf8_lossy().into_owned();
    match extract_identifier(&decoded, YOUTUBE_WATCH) {
        Some(identifier) => {
            return vec![Track {
                        id: Uuid::new_v4(),
                  provider: Provider::YouTube,
                     title: "".to_string(),
                       url: url.to_string(),
                identifier: identifier
            }]
        },
        None => ()
    }
    match extract_identifier(&decoded, YOUTUBE_LIST) {
        Some(identifier) => {
            return match youtube::fetch_playlist(&identifier) {
                Ok(res) => res.items.iter()
                                    .map(|ref i| Track::from_yt_playlist_item(i))
                                    .collect::<Vec<_>>(),
                Err(_)       => vec![]
            }
        },
        None => ()
    }
    match extract_identifier(&decoded, YOUTUBE_EMBED) {
        Some(identifier) => {
            return vec![Track {
                        id: Uuid::new_v4(),
                  provider: Provider::YouTube,
                     title: "".to_string(),
                       url: url.to_string(),
                identifier: identifier
            }]
        },
        None => ()
    }
    match extract_identifier(&decoded, SOUNDCLOUD_TRACK) {
        Some(identifier) => {
            return vec![Track {
                        id: Uuid::new_v4(),
                  provider: Provider::SoundCloud,
                     title: "".to_string(),
                       url: url.to_string(),
                identifier: identifier
            }]
        },
        None => ()
    }
    match extract_identifier(&decoded, SOUNDCLOUD_PLAYLIST) {
        Some(identifier) => {
            return match soundcloud::fetch_playlist(&identifier) {
                Ok(playlist) => playlist.tracks
                                        .iter()
                                        .map(|ref t| Track::from_sc_track(t))
                                        .collect::<Vec<_>>(),
                Err(_)       => vec![]
            }
        },
        None => ()
    }
    match extract_identifier(&decoded, SOUNDCLOUD_USER) {
        Some(identifier) => {
            print!("user {}", identifier);
            return match soundcloud::fetch_user_tracks(&identifier) {
                Ok(tracks) => tracks.iter()
                                    .map(|ref t| Track::from_sc_track(t))
                                    .collect::<Vec<_>>(),
                Err(_)     => vec![]
            }
        },
        None => ()
    }
    return vec![]
}

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

    #[test]
    fn test_extract_identifier() {
        let soundcloud_src = "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/195425494/stream&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true";
        match extract_identifier(soundcloud_src, super::SOUNDCLOUD_TRACK) {
            Some(identifier) => assert_eq!(identifier, "195425494".to_string()),
            None             => assert!(false)
        }
        let youtube_embed = "https://www.youtube.com/embed/X8tOngmlES0?rel=0";
        match extract_identifier(youtube_embed, super::YOUTUBE_EMBED) {
            Some(identifier) => assert_eq!(identifier, "X8tOngmlES0".to_string()),
            None             => assert!(false)
        }

        let youtube_watch = "https://www.youtube.com/watch?v=oDuif301F-8";
        match extract_identifier(youtube_watch, super::YOUTUBE_WATCH) {
            Some(identifier) => assert_eq!(identifier, "oDuif301F-8".to_string()),
            None             => assert!(false)
        }

        let youtube_list = "https://www.youtube.com/embed/videoseries?list=PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX";
        match extract_identifier(youtube_list, super::YOUTUBE_LIST) {
            Some(identifier) => assert_eq!(identifier, "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX".to_string()),
            None             => assert!(false)
        }
    }
}
<|endoftext|>


<commit_before>#![allow(non_snake_case)]

use std::str::from_utf8;
use std::thread;
use std::time::Duration;

use rustc_serialize::json;
use curl::http;

pub const SESSION_TTL: &'static str = "15s";

#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct SessionCreate {
    Name: String,
    TTL: String
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct SessionID {
    ID: String
}

pub struct Session {
    endpoint: String
}

impl Session {
    pub fn new(address: &str) ->  Session {
        Session {
            endpoint: format!("http://{}/v1/session", address)
        }
    }

    pub fn create(&self, name: String) -> Option<String> {
        let url = format!("{}/create", self.endpoint);
        let session = SessionCreate {
            Name: name,
            TTL: self::SESSION_TTL.to_owned()
        };
        let json_str = json::encode(&session).unwrap();

        let resp = http::handle()
            .put(url, &json_str)
            .content_type("application/json")
            .exec().unwrap();
        if resp.get_code() != 200 {
            panic!("Consul: Error creating a session! Response: {}", resp);
        }
        let result = from_utf8(resp.get_body()).unwrap();
        let json_data = match json::Json::from_str(result) {
            Ok(value) => value,
            Err(err) => panic!("consul: Could not convert to json: {:?}. Err: {}", result, err)
        };
        super::get_string(&json_data, &["ID"])
    }

    pub fn renew(&self, session_id: &String) -> bool {
        for _ in 0..10 {
            let url = format!("{}/renew/{}", self.endpoint, session_id);
            let resp = http::handle()
                .put(url, "")
                .content_type("application/json")
                .exec().unwrap();
            if resp.get_code() != 200 {
                if resp.get_code() == 404 {
                    println!("Could not renew session: {}, returned HTTP code: {:?}. Returning false.", session_id, resp.get_code());
                    return false;
                }
                else {
                    println!("Could not renew session: {}, returned HTTP code: {:?}. Sleeping for 2 seconds", session_id, resp.get_code());
                    thread::sleep(Duration::from_millis(2000u64));
                }
            }
            else {
                return true;
            }
        }
        panic!("Could not renew session: {} after 10 tries.", session_id);
        false
    }

    pub fn end(&self, session_id: &String) {
        let url = format!("{}/destroy/{}", self.endpoint, session_id);
        let resp = http::handle()
            .put(url, "")
            .content_type("application/json")
            .exec().unwrap();
        if resp.get_code() != 200 {
            panic!("Cound not destroy session: {}", session_id);
        }

    }

}
<commit_msg>Return None if create_session fails<commit_after>#![allow(non_snake_case)]

use std::str::from_utf8;
use std::thread;
use std::time::Duration;

use rustc_serialize::json;
use curl::http;

pub const SESSION_TTL: &'static str = "15s";

#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct SessionCreate {
    Name: String,
    TTL: String
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct SessionID {
    ID: String
}

pub struct Session {
    endpoint: String
}

impl Session {
    pub fn new(address: &str) ->  Session {
        Session {
            endpoint: format!("http://{}/v1/session", address)
        }
    }

    pub fn create(&self, name: String) -> Option<String> {
        let url = format!("{}/create", self.endpoint);
        let session = SessionCreate {
            Name: name,
            TTL: self::SESSION_TTL.to_owned()
        };
        let json_str = json::encode(&session).unwrap();

        let resp = http::handle()
            .put(url, &json_str)
            .content_type("application/json")
            .exec().unwrap();
        if resp.get_code() != 200 {
            println!("Consul: Error creating a session! Response: {}", resp);
            return None;
        }
        let result = from_utf8(resp.get_body()).unwrap();
        let json_data = match json::Json::from_str(result) {
            Ok(value) => value,
            Err(err) => panic!("consul: Could not convert to json: {:?}. Err: {}", result, err)
        };
        super::get_string(&json_data, &["ID"])
    }

    pub fn renew(&self, session_id: &String) -> bool {
        for _ in 0..10 {
            let url = format!("{}/renew/{}", self.endpoint, session_id);
            let resp = http::handle()
                .put(url, "")
                .content_type("application/json")
                .exec().unwrap();
            if resp.get_code() != 200 {
                if resp.get_code() == 404 {
                    println!("Could not renew session: {}, returned HTTP code: {:?}. Returning false.", session_id, resp.get_code());
                    return false;
                }
                else {
                    println!("Could not renew session: {}, returned HTTP code: {:?}. Sleeping for 2 seconds", session_id, resp.get_code());
                    thread::sleep(Duration::from_millis(2000u64));
                }
            }
            else {
                return true;
            }
        }
        panic!("Could not renew session: {} after 10 tries.", session_id);
        false
    }

    pub fn end(&self, session_id: &String) {
        let url = format!("{}/destroy/{}", self.endpoint, session_id);
        let resp = http::handle()
            .put(url, "")
            .content_type("application/json")
            .exec().unwrap();
        if resp.get_code() != 200 {
            panic!("Cound not destroy session: {}", session_id);
        }

    }

}
<|endoftext|>