<commit_before>use std::ops::Deref;
use std::sync::{
    atomic::{AtomicBool, Ordering},
    Arc,
};

use crate::backend::allocator::{Allocator, Buffer, Fourcc, Modifier};
use crate::utils::user_data::UserDataMap;

pub const SLOT_CAP: usize = 4;

/// Swapchain handling a fixed set of re-usable buffers e.g. for scan-out.
///
/// ## How am I supposed to use this?
///
/// To do proper buffer management, most compositors do so called double-buffering.
/// Which means you use two buffers, one that is currently presented (the front buffer)
/// and one that is currently rendered to (the back buffer). After each rendering operation
/// you swap the buffers around, the old front buffer becomes the new back buffer, while
/// the new front buffer is displayed to the user. This avoids showing the user rendering
/// artifacts doing rendering.
///
/// There are also reasons to do triple-buffering, e.g. if you swap operation takes a
/// unspecified amount of time. In that case you have one buffer, that is currently
/// displayed, one that is done drawing and about to be swapped in and another one,
/// which you can use to render currently.
///
/// Re-using and managing these buffers becomes increasingly complex the more buffers you
/// introduce, which is where `Swapchain` comes into play.
///
/// `Swapchain` allocates buffers for you and transparently re-created them, e.g. when resizing.
/// All you tell the swapchain is: *"Give me the next free buffer"* (by calling [`acquire`](Swapchain::acquire)).
/// You then hold on to the returned buffer during rendering and swapping and free it once it is displayed.
/// Efficient re-use of the buffers is done by the swapchain.
///
/// If you have associated resources for each buffer that can be reused (e.g. framebuffer `Handle`s for a `DrmDevice`),
/// you can store then in the `Slot`s userdata field. If a buffer is re-used, its userdata is preserved for the next time
/// it is returned by `acquire()`.
#[derive(Debug)]
pub struct Swapchain<A: Allocator<B>, B: Buffer> {
    /// Allocator used by the swapchain
    pub allocator: A,

    width: u32,
    height: u32,
    fourcc: Fourcc,
    modifiers: Vec<Modifier>,

    slots: [Arc<InternalSlot<B>>; SLOT_CAP],
}

/// Slot of a swapchain containing an allocated buffer and its userdata.
///
/// The buffer is marked for re-use once all copies are dropped.
/// Holding on to this struct will block the buffer in the swapchain.
#[derive(Debug)]
pub struct Slot<B: Buffer>(Arc<InternalSlot<B>>);

#[derive(Debug)]
struct InternalSlot<B: Buffer> {
    buffer: Option<B>,
    acquired: AtomicBool,
    userdata: UserDataMap,
}

impl<B: Buffer> Slot<B> {
    /// Retrieve userdata for this slot.
    pub fn userdata(&self) -> &UserDataMap {
        &self.0.userdata
    }
}

impl<B: Buffer> Default for InternalSlot<B> {
    fn default() -> Self {
        InternalSlot {
            buffer: None,
            acquired: AtomicBool::new(false),
            userdata: UserDataMap::new(),
        }
    }
}

impl<B: Buffer> Deref for Slot<B> {
    type Target = B;
    fn deref(&self) -> &B {
        Option::as_ref(&self.0.buffer).unwrap()
    }
}

impl<B: Buffer> Drop for Slot<B> {
    fn drop(&mut self) {
        self.0.acquired.store(false, Ordering::SeqCst);
    }
}

impl<A, B> Swapchain<A, B>
where
    A: Allocator<B>,
    B: Buffer,
{
    /// Create a new swapchain with the desired allocator, dimensions and pixel format for the created buffers.
    pub fn new(
        allocator: A,
        width: u32,
        height: u32,
        fourcc: Fourcc,
        modifiers: Vec<Modifier>,
    ) -> Swapchain<A, B> {
        Swapchain {
            allocator,
            width,
            height,
            fourcc,
            modifiers,
            slots: Default::default(),
        }
    }

    /// Acquire a new slot from the swapchain, if one is still free.
    ///
    /// The swapchain has an internal maximum of four re-usable buffers.
    /// This function returns the first free one.
    pub fn acquire(&mut self) -> Result<Option<Slot<B>>, A::Error> {
        if let Some(free_slot) = self
            .slots
            .iter_mut()
            .find(|s| !s.acquired.swap(true, Ordering::SeqCst))
        {
            if free_slot.buffer.is_none() {
                let mut free_slot =
                    Arc::get_mut(free_slot).expect("Acquired was false, but Arc is not unique?");
                free_slot.buffer = Some(self.allocator.create_buffer(
                    self.width,
                    self.height,
                    self.fourcc,
                    &self.modifiers,
                )?);
            }
            assert!(free_slot.buffer.is_some());
            return Ok(Some(Slot(free_slot.clone())));
        }

        // no free slots
        Ok(None)
    }

    /// Change the dimensions of newly returned buffers.
    ///
    /// Already obtained buffers are unaffected and will be cleaned up on drop.
    pub fn resize(&mut self, width: u32, height: u32) {
        if self.width == width && self.height == height {
            return;
        }

        self.width = width;
        self.height = height;
        self.slots = Default::default();
    }
}
<commit_msg>swapchain: Support buffer age<commit_after>use std::ops::Deref;
use std::sync::{
    atomic::{AtomicBool, AtomicU8, Ordering},
    Arc,
};

use crate::backend::allocator::{Allocator, Buffer, Fourcc, Modifier};
use crate::utils::user_data::UserDataMap;

pub const SLOT_CAP: usize = 4;

/// Swapchain handling a fixed set of re-usable buffers e.g. for scan-out.
///
/// ## How am I supposed to use this?
///
/// To do proper buffer management, most compositors do so called double-buffering.
/// Which means you use two buffers, one that is currently presented (the front buffer)
/// and one that is currently rendered to (the back buffer). After each rendering operation
/// you swap the buffers around, the old front buffer becomes the new back buffer, while
/// the new front buffer is displayed to the user. This avoids showing the user rendering
/// artifacts doing rendering.
///
/// There are also reasons to do triple-buffering, e.g. if you swap operation takes a
/// unspecified amount of time. In that case you have one buffer, that is currently
/// displayed, one that is done drawing and about to be swapped in and another one,
/// which you can use to render currently.
///
/// Re-using and managing these buffers becomes increasingly complex the more buffers you
/// introduce, which is where `Swapchain` comes into play.
///
/// `Swapchain` allocates buffers for you and transparently re-created them, e.g. when resizing.
/// All you tell the swapchain is: *"Give me the next free buffer"* (by calling [`acquire`](Swapchain::acquire)).
/// You then hold on to the returned buffer during rendering and swapping and free it once it is displayed.
/// Efficient re-use of the buffers is done by the swapchain.
///
/// If you have associated resources for each buffer that can be reused (e.g. framebuffer `Handle`s for a `DrmDevice`),
/// you can store then in the `Slot`s userdata field. If a buffer is re-used, its userdata is preserved for the next time
/// it is returned by `acquire()`.
#[derive(Debug)]
pub struct Swapchain<A: Allocator<B>, B: Buffer> {
    /// Allocator used by the swapchain
    pub allocator: A,

    width: u32,
    height: u32,
    fourcc: Fourcc,
    modifiers: Vec<Modifier>,

    slots: [Arc<InternalSlot<B>>; SLOT_CAP],
}

/// Slot of a swapchain containing an allocated buffer and its userdata.
///
/// The buffer is marked for re-use once all copies are dropped.
/// Holding on to this struct will block the buffer in the swapchain.
#[derive(Debug)]
pub struct Slot<B: Buffer>(Arc<InternalSlot<B>>);

#[derive(Debug)]
struct InternalSlot<B: Buffer> {
    buffer: Option<B>,
    acquired: AtomicBool,
    age: AtomicU8,
    userdata: UserDataMap,
}

impl<B: Buffer> Slot<B> {
    /// Retrieve userdata for this slot.
    pub fn userdata(&self) -> &UserDataMap {
        &self.0.userdata
    }

    /// Retrieve the age of the buffer
    pub fn age(&self) -> u8 {
        self.0.age.load(Ordering::SeqCst)
    }
}

impl<B: Buffer> Default for InternalSlot<B> {
    fn default() -> Self {
        InternalSlot {
            buffer: None,
            acquired: AtomicBool::new(false),
            age: AtomicU8::new(0),
            userdata: UserDataMap::new(),
        }
    }
}

impl<B: Buffer> Deref for Slot<B> {
    type Target = B;
    fn deref(&self) -> &B {
        Option::as_ref(&self.0.buffer).unwrap()
    }
}

impl<B: Buffer> Drop for Slot<B> {
    fn drop(&mut self) {
        self.0.acquired.store(false, Ordering::SeqCst);
    }
}

impl<A, B> Swapchain<A, B>
where
    A: Allocator<B>,
    B: Buffer,
{
    /// Create a new swapchain with the desired allocator, dimensions and pixel format for the created buffers.
    pub fn new(
        allocator: A,
        width: u32,
        height: u32,
        fourcc: Fourcc,
        modifiers: Vec<Modifier>,
    ) -> Swapchain<A, B> {
        Swapchain {
            allocator,
            width,
            height,
            fourcc,
            modifiers,
            slots: Default::default(),
        }
    }

    /// Acquire a new slot from the swapchain, if one is still free.
    ///
    /// The swapchain has an internal maximum of four re-usable buffers.
    /// This function returns the first free one.
    pub fn acquire(&mut self) -> Result<Option<Slot<B>>, A::Error> {
        if let Some(free_slot) = self
            .slots
            .iter_mut()
            .find(|s| !s.acquired.swap(true, Ordering::SeqCst))
        {
            if free_slot.buffer.is_none() {
                let mut free_slot =
                    Arc::get_mut(free_slot).expect("Acquired was false, but Arc is not unique?");
                free_slot.buffer = Some(self.allocator.create_buffer(
                    self.width,
                    self.height,
                    self.fourcc,
                    &self.modifiers,
                )?);
            }
            assert!(free_slot.buffer.is_some());
            return Ok(Some(Slot(free_slot.clone())));
        }

        // no free slots
        Ok(None)
    }

    /// Mark a given buffer as submitted.
    ///
    /// This might effect internal data (e.g. buffer age) and may only be called,
    /// if the buffer was actually used for display.
    /// Buffers can always just be safely discarded by dropping them, but not
    /// calling this function may affect performance characteristics
    /// (e.g. by not tracking the buffer age).
    pub fn submitted(&self, slot: Slot<B>) {
        // don't mess up the state, if the user submitted and old buffer, after e.g. a resize
        if !self.slots.iter().any(|other| Arc::ptr_eq(&slot.0, other)) {
            return;
        }

        slot.0.age.store(1, Ordering::SeqCst);
        for other_slot in &self.slots {
            if !Arc::ptr_eq(other_slot, &slot.0) {
                other_slot.age.fetch_add(1, Ordering::SeqCst);
            }
        }
    }

    /// Change the dimensions of newly returned buffers.
    ///
    /// Already obtained buffers are unaffected and will be cleaned up on drop.
    pub fn resize(&mut self, width: u32, height: u32) {
        if self.width == width && self.height == height {
            return;
        }

        self.width = width;
        self.height = height;
        self.slots = Default::default();
    }

    /// Remove all internally cached buffers to e.g. reset age values
    pub fn reset_buffers(&mut self) {
        self.slots = Default::default();
    }
}
<|endoftext|>


<commit_before>//! Tests!
extern crate fern;
extern crate log;
extern crate tempdir;

use std::io::prelude::*;
use std::{fs, io};

use log::Level::*;

mod support;

use support::manual_log;

#[cfg(all(not(windows), feature = "reopen-03"))]
#[test]
fn test_basic_logging_reopen_logging() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test.log");

    {
        // Create a basic logger configuration
        let (_max_level, logger) = fern::Dispatch::new()
            .format(|out, msg, record| out.finish(format_args!("[{}] {}", record.level(), msg)))
            .level(log::LevelFilter::Info)
            .chain(io::stdout())
            .chain(fern::log_reopen(&log_file, None).expect("Failed to open log file"))
            .into_log();

        let l = &*logger;
        manual_log(l, Trace, "SHOULD NOT DISPLAY");
        manual_log(l, Debug, "SHOULD NOT DISPLAY");
        manual_log(l, Info, "Test information message");
        manual_log(l, Warn, "Test warning message");
        manual_log(l, Error, "Test error message");

        // ensure all File objects are dropped and OS buffers are flushed.
        log::logger().flush();

        {
            let result = {
                let mut log_read = fs::File::open(&temp_log_dir.path().join("test.log")).unwrap();
                let mut buf = String::new();
                log_read.read_to_string(&mut buf).unwrap();
                buf
            };
            assert!(
                !result.contains("SHOULD NOT DISPLAY"),
                "expected result not including \"SHOULD_NOT_DISPLAY\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[INFO] Test information message"),
                "expected result including \"[INFO] Test information message\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[WARN] Test warning message"),
                "expected result including \"[WARN] Test warning message\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[ERROR] Test error message"),
                "expected result to not include \"[ERROR] Test error message\", found:\n```\n{}\n```\n",
                result
            );
        }
    } // ensure logger is dropped before temp dir

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

#[cfg(all(not(windows), feature = "reopen-03"))]
#[test]
fn test_custom_line_separators() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test_custom_line_sep.log");

    {
        // Create a basic logger configuration
        let (_max_level, logger) = fern::Dispatch::new()
            // default format is just the message if not specified
            // default log level is 'trace' if not specified (logs all messages)
            // output to the log file with the "\r\n" line separator.
            .chain(fern::Output::reopen(fern::log_reopen(&log_file, None).expect("Failed to open log file"), "\r\n"))
            .into_log();

        let l = &*logger;
        manual_log(l, Info, "message1");
        manual_log(l, Info, "message2");

        // ensure all File objects are dropped and OS buffers are flushed.
        logger.flush();

        {
            let result = {
                let mut log_read =
                    fs::File::open(&temp_log_dir.path().join("test_custom_line_sep.log")).unwrap();
                let mut buf = String::new();
                log_read.read_to_string(&mut buf).unwrap();
                buf
            };
            assert_eq!(&result, "message1\r\nmessage2\r\n");
        }
    } // ensure logger is dropped before temp dir

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

<commit_msg>Remove empty line<commit_after>//! Tests!
extern crate fern;
extern crate log;
extern crate tempdir;

use std::io::prelude::*;
use std::{fs, io};
use log::Level::*;

mod support;

use support::manual_log;

#[cfg(all(not(windows), feature = "reopen-03"))]
#[test]
fn test_basic_logging_reopen_logging() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test.log");

    {
        // Create a basic logger configuration
        let (_max_level, logger) = fern::Dispatch::new()
            .format(|out, msg, record| out.finish(format_args!("[{}] {}", record.level(), msg)))
            .level(log::LevelFilter::Info)
            .chain(io::stdout())
            .chain(fern::log_reopen(&log_file, None).expect("Failed to open log file"))
            .into_log();

        let l = &*logger;
        manual_log(l, Trace, "SHOULD NOT DISPLAY");
        manual_log(l, Debug, "SHOULD NOT DISPLAY");
        manual_log(l, Info, "Test information message");
        manual_log(l, Warn, "Test warning message");
        manual_log(l, Error, "Test error message");

        // ensure all File objects are dropped and OS buffers are flushed.
        log::logger().flush();

        {
            let result = {
                let mut log_read = fs::File::open(&temp_log_dir.path().join("test.log")).unwrap();
                let mut buf = String::new();
                log_read.read_to_string(&mut buf).unwrap();
                buf
            };
            assert!(
                !result.contains("SHOULD NOT DISPLAY"),
                "expected result not including \"SHOULD_NOT_DISPLAY\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[INFO] Test information message"),
                "expected result including \"[INFO] Test information message\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[WARN] Test warning message"),
                "expected result including \"[WARN] Test warning message\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[ERROR] Test error message"),
                "expected result to not include \"[ERROR] Test error message\", found:\n```\n{}\n```\n",
                result
            );
        }
    } // ensure logger is dropped before temp dir

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

#[cfg(all(not(windows), feature = "reopen-03"))]
#[test]
fn test_custom_line_separators() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test_custom_line_sep.log");

    {
        // Create a basic logger configuration
        let (_max_level, logger) = fern::Dispatch::new()
            // default format is just the message if not specified
            // default log level is 'trace' if not specified (logs all messages)
            // output to the log file with the "\r\n" line separator.
            .chain(fern::Output::reopen(fern::log_reopen(&log_file, None).expect("Failed to open log file"), "\r\n"))
            .into_log();

        let l = &*logger;
        manual_log(l, Info, "message1");
        manual_log(l, Info, "message2");

        // ensure all File objects are dropped and OS buffers are flushed.
        logger.flush();

        {
            let result = {
                let mut log_read =
                    fs::File::open(&temp_log_dir.path().join("test_custom_line_sep.log")).unwrap();
                let mut buf = String::new();
                log_read.read_to_string(&mut buf).unwrap();
                buf
            };
            assert_eq!(&result, "message1\r\nmessage2\r\n");
        }
    } // ensure logger is dropped before temp dir

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

<|endoftext|>


<commit_before><commit_msg>Compare geometries rectangles with Rect::approx_eq()<commit_after><|endoftext|>


<commit_before>use gfx;
use gfx_phase;

mod pipe;

pub use self::pipe::{order, Pipeline};

pub type Phase<R> = gfx_phase::CachedPhase<R, ::Material<R>, ::view::Info<f32>, Technique<R>>;

#[derive(Clone)]
#[shader_param]
pub struct Params<R: gfx::Resources> {
    #[name = "u_Transform"]
    pub mvp: [[f32; 4]; 4],
    #[name = "u_NormalRotation"]
    pub normal: [[f32; 3]; 3],
    #[name = "u_Color"]
    pub color: [f32; 4],
    #[name = "t_Diffuse"]
    pub texture: gfx::shade::TextureParam<R>,
}

const PHONG_VS: &'static [u8] = include_bytes!("../../gpu/phong.glslv");
const PHONG_FS: &'static [u8] = include_bytes!("../../gpu/phong.glslf");

pub struct Technique<R: gfx::Resources> {
    program: gfx::ProgramHandle<R>,
    state_additive: gfx::DrawState,
    state_alpha: gfx::DrawState,
    state_opaque: gfx::DrawState,
    state_multiply: gfx::DrawState,
    default_texture_param: gfx::shade::TextureParam<R>,
}

impl<R: gfx::Resources> Technique<R> {
    pub fn from_program(program: gfx::ProgramHandle<R>, tex_param: gfx::shade::TextureParam<R>)
                        -> Technique<R> {
        let state = gfx::DrawState::new().depth(
            gfx::state::Comparison::LessEqual,
            true
        );
        Technique {
            program: program,
            state_additive: state.clone().blend(gfx::BlendPreset::Additive),
            state_alpha: state.clone().blend(gfx::BlendPreset::Alpha),
            state_multiply: state.clone().blend(gfx::BlendPreset::Multiplicative),
            state_opaque: state,
            default_texture_param: tex_param,
        }
    }

    pub fn new<F: gfx::Factory<R>>(factory: &mut F, tex_param: gfx::shade::TextureParam<R>)
               -> Result<Technique<R>, gfx::ProgramError> {
        use gfx::traits::FactoryExt;
        factory.link_program(PHONG_VS, PHONG_FS)
               .map(|p| Technique::from_program(p, tex_param))
    }
}

pub type Kernel = Option<gfx::BlendPreset>;

impl<R: gfx::Resources> gfx_phase::Technique<R, ::Material<R>, ::view::Info<f32>> for Technique<R> {
    type Kernel = Kernel;
    type Params = Params<R>;

    fn test(&self, _mesh: &gfx::Mesh<R>, mat: &::Material<R>) -> Option<Kernel> {
        Some(mat.blend)
    }

    fn compile<'a>(&'a self, kernel: Kernel, _space: ::view::Info<f32>)
                   -> gfx_phase::TechResult<'a, R, Params<R>> {
        (   &self.program,
            Params {
                mvp: [[0.0; 4]; 4],
                normal: [[0.0; 3]; 3],
                color: [0.0; 4],
                texture: self.default_texture_param.clone(),
            },
            None,
            match kernel {
                Some(gfx::BlendPreset::Additive) => &self.state_additive,
                Some(gfx::BlendPreset::Alpha) => &self.state_alpha,
                Some(gfx::BlendPreset::Multiplicative) => &self.state_multiply,
                None => &self.state_opaque,
            }
        )
    }

    fn fix_params(&self, mat: &::Material<R>, space: &::view::Info<f32>, params: &mut Params<R>) {
        use cgmath::FixedArray;
        params.mvp = *space.mx_vertex.as_fixed();
        params.normal = *space.mx_normal.as_fixed();
        params.color = mat.color;
        params.texture = mat.texture.clone();
    }
}
<commit_msg>Updated to the latest draw_state<commit_after>use gfx;
use gfx_phase;

mod pipe;

pub use self::pipe::{order, Pipeline};

pub type Phase<R> = gfx_phase::CachedPhase<R, ::Material<R>, ::view::Info<f32>, Technique<R>>;

#[derive(Clone)]
#[shader_param]
pub struct Params<R: gfx::Resources> {
    #[name = "u_Transform"]
    pub mvp: [[f32; 4]; 4],
    #[name = "u_NormalRotation"]
    pub normal: [[f32; 3]; 3],
    #[name = "u_Color"]
    pub color: [f32; 4],
    #[name = "t_Diffuse"]
    pub texture: gfx::shade::TextureParam<R>,
}

const PHONG_VS: &'static [u8] = include_bytes!("../../gpu/phong.glslv");
const PHONG_FS: &'static [u8] = include_bytes!("../../gpu/phong.glslf");

pub struct Technique<R: gfx::Resources> {
    program: gfx::ProgramHandle<R>,
    state_add: gfx::DrawState,
    state_alpha: gfx::DrawState,
    state_opaque: gfx::DrawState,
    state_multiply: gfx::DrawState,
    default_texture_param: gfx::shade::TextureParam<R>,
}

impl<R: gfx::Resources> Technique<R> {
    pub fn from_program(program: gfx::ProgramHandle<R>, tex_param: gfx::shade::TextureParam<R>)
                        -> Technique<R> {
        let state = gfx::DrawState::new().depth(
            gfx::state::Comparison::LessEqual,
            true
        );
        Technique {
            program: program,
            state_add: state.clone().blend(gfx::BlendPreset::Add),
            state_alpha: state.clone().blend(gfx::BlendPreset::Alpha),
            state_multiply: state.clone().blend(gfx::BlendPreset::Multiply),
            state_opaque: state,
            default_texture_param: tex_param,
        }
    }

    pub fn new<F: gfx::Factory<R>>(factory: &mut F, tex_param: gfx::shade::TextureParam<R>)
               -> Result<Technique<R>, gfx::ProgramError> {
        use gfx::traits::FactoryExt;
        factory.link_program(PHONG_VS, PHONG_FS)
               .map(|p| Technique::from_program(p, tex_param))
    }
}

pub type Kernel = Option<gfx::BlendPreset>;

impl<R: gfx::Resources> gfx_phase::Technique<R, ::Material<R>, ::view::Info<f32>> for Technique<R> {
    type Kernel = Kernel;
    type Params = Params<R>;

    fn test(&self, _mesh: &gfx::Mesh<R>, mat: &::Material<R>) -> Option<Kernel> {
        match mat.blend {
            Some(gfx::BlendPreset::Invert) => None,
            other => Some(other)
        }
    }

    fn compile<'a>(&'a self, kernel: Kernel, _space: ::view::Info<f32>)
                   -> gfx_phase::TechResult<'a, R, Params<R>> {
        (   &self.program,
            Params {
                mvp: [[0.0; 4]; 4],
                normal: [[0.0; 3]; 3],
                color: [0.0; 4],
                texture: self.default_texture_param.clone(),
            },
            None,
            match kernel {
                Some(gfx::BlendPreset::Add) => &self.state_add,
                Some(gfx::BlendPreset::Alpha) => &self.state_alpha,
                Some(gfx::BlendPreset::Multiply) => &self.state_multiply,
                _ => &self.state_opaque,
            }
        )
    }

    fn fix_params(&self, mat: &::Material<R>, space: &::view::Info<f32>, params: &mut Params<R>) {
        use cgmath::FixedArray;
        params.mvp = *space.mx_vertex.as_fixed();
        params.normal = *space.mx_normal.as_fixed();
        params.color = mat.color;
        params.texture = mat.texture.clone();
    }
}
<|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/.
*/

use rusqlite::Row;

use crate::catalog::CatalogVersion;
use crate::fromdb::FromDb;
use crate::lrobject::{LrId, LrObject};
use crate::lron;
use crate::{AspectRatio, Point, Rect};

/// Some misc properties of the image specific to Lr
#[derive(Default, Debug)]
pub struct Properties {
    /// Where the loupe is focused
    loupe_focus: Option<Point>,
    /// Aspect ration of the crop
    crop_aspect_ratio: Option<AspectRatio>,
    /// Default crop
    default_crop: Option<Rect>,
}

impl Properties {
    #[allow(clippy::unnecessary_unwrap)]
    fn loupe_focus(value: &[lron::Object]) -> Option<Point> {
        use crate::lron::{Object, Value};

        let mut x: Option<f64> = None;
        let mut y: Option<f64> = None;
        let mut is_point = false;
        value.iter().for_each(|o| {
            if let Object::Pair(p) = o {
                match p.key.as_str() {
                    "_ag_className" => is_point = p.value == Value::Str("AgPoint".to_owned()),
                    "y" => y = p.value.to_number(),
                    "x" => x = p.value.to_number(),
                    _ => {}
                }
            }
        });
        // This triggers clippy::unnecessary_unwrap
        if is_point && x.is_some() && y.is_some() {
            Some(Point {
                x: x.unwrap(),
                y: y.unwrap(),
            })
        } else {
            None
        }
    }

    #[allow(clippy::unnecessary_unwrap)]
    fn properties(value: &[lron::Object]) -> Self {
        use crate::lron::{Object, Value};

        let mut props = Properties::default();
        let mut crop_aspect_h: Option<i32> = None;
        let mut crop_aspect_w: Option<i32> = None;

        let mut top: Option<f64> = None;
        let mut bottom: Option<f64> = None;
        let mut left: Option<f64> = None;
        let mut right: Option<f64> = None;
        value.iter().for_each(|o| {
            if let Object::Pair(p) = o {
                match p.key.as_str() {
                    "loupeFocusPoint" => {
                        if let Value::Dict(ref v) = p.value {
                            props.loupe_focus = Self::loupe_focus(v);
                        }
                    }
                    "cropAspectH" => {
                        if let Value::Int(i) = p.value {
                            crop_aspect_h = Some(i);
                        }
                    }
                    "cropAspectW" => {
                        if let Value::Int(i) = p.value {
                            crop_aspect_w = Some(i);
                        }
                    }
                    "defaultCropBottom" => {
                        bottom = p.value.to_number();
                    }
                    "defaultCropLeft" => {
                        left = p.value.to_number();
                    }
                    "defaultCropRight" => {
                        right = p.value.to_number();
                    }
                    "defaultCropTop" => {
                        top = p.value.to_number();
                    }
                    _ => {}
                }
            }
        });

        // This triggers clippy::unnecessary_unwrap
        if crop_aspect_h.is_some() && crop_aspect_w.is_some() {
            props.crop_aspect_ratio = Some(AspectRatio {
                width: crop_aspect_w.unwrap(),
                height: crop_aspect_h.unwrap(),
            });
        }
        // This triggers clippy::unnecessary_unwrap
        if top.is_some() && bottom.is_some() && left.is_some() && right.is_some() {
            props.default_crop = Some(Rect {
                top: top.unwrap(),
                bottom: bottom.unwrap(),
                left: left.unwrap(),
                right: right.unwrap(),
            });
        }
        props
    }
}

impl From<lron::Object> for Properties {
    fn from(object: lron::Object) -> Self {
        use crate::lron::{Object, Value};

        match object {
            Object::Pair(ref s) => {
                if &s.key == "properties" {
                    match s.value {
                        Value::Dict(ref dict) => Self::properties(dict),
                        _ => Properties::default(),
                    }
                } else {
                    Properties::default()
                }
            }
            _ => Properties::default(),
        }
    }
}

/// An image in the `Catalog`. Requires a `LibraryFile` backing it
pub struct Image {
    id: LrId,
    uuid: String,
    /// If this a copy, id of the `Image` it is a copy of
    pub master_image: Option<LrId>,
    /// Name of copy.
    pub copy_name: Option<String>,
    /// Star rating
    pub rating: Option<i64>,
    /// Backing `LibraryFile` id.
    pub root_file: LrId,
    /// File format
    pub file_format: String,
    /// Pick. -1, 0, 1
    pub pick: i64,
    /// Orientation string (set Lr format documentation)
    /// Convert to EXIF orientation with `self.exif_orientation()`.
    pub orientation: Option<String>,
    /// Capture date.
    pub capture_time: String,
    /// XMP block as stored in the database. If len() == 0,
    /// there is no XMP.
    pub xmp: String,
    /// XMP is embedded: whether the XMP packet in the file
    /// like a JPEG, or in a sidecar like in a RAW (non DNG)
    /// file, regardless of `xmp`.
    pub xmp_embedded: bool,
    /// The external XMP (ie not in the database) is different.
    pub xmp_external_dirty: bool,
    /// Misc properties from the Adobe_imageProperties table
    pub properties: Option<Properties>,
}

impl Image {
    /// Return the Exif value for the image orientation
    /// No orientation = 0.
    /// Error = -1 or unknown value
    /// Otherwise the Exif value for `orientation`
    pub fn exif_orientation(&self) -> i32 {
        self.orientation.as_ref().map_or(0, |s| match s.as_ref() {
            "AB" => 1,
            "DA" => 8,
            "BC" => 6,
            "CD" => 3,
            _ => -1,
        })
    }
}

impl LrObject for Image {
    fn id(&self) -> LrId {
        self.id
    }
    fn uuid(&self) -> &str {
        &self.uuid
    }
}

impl FromDb for Image {
    fn read_from(_version: CatalogVersion, row: &Row) -> crate::Result<Self> {
        let properties = row
            .get::<&str, String>("propertiesString")
            .ok()
            .and_then(|v| lron::Object::from_string(&v).ok())
            .map(Properties::from);
        Ok(Image {
            id: row.get("id_local")?,
            uuid: row.get("id_global")?,
            master_image: row.get("masterImage").ok(),
            rating: row.get("rating").ok(),
            root_file: row.get("rootFile")?,
            file_format: row.get("fileFormat")?,
            pick: row.get("pick")?,
            orientation: row.get("orientation").ok(),
            capture_time: row.get("captureTime")?,
            copy_name: row.get("copyName").ok(),
            xmp: row.get("xmp")?,
            xmp_embedded: row.get("embeddedXmp")?,
            xmp_external_dirty: row.get("externalXmpIsDirty")?,
            properties,
        })
    }
    fn read_db_tables(_version: CatalogVersion) -> &'static str {
        "Adobe_images as img,Adobe_AdditionalMetadata as meta,Adobe_imageProperties as props"
    }
    fn read_db_columns(_version: CatalogVersion) -> &'static str {
        "img.id_local,img.id_global,img.masterImage,img.rating,img.rootFile,img.fileFormat,cast(img.pick as integer) as pick,img.orientation,img.captureTime,img.copyName,meta.xmp,meta.embeddedXmp,meta.externalXmpIsDirty,props.propertiesString"
    }
    fn read_join_where(_version: CatalogVersion) -> &'static str {
        "meta.image = img.id_local and props.image = img.id_local"
    }
}

#[cfg(test)]
mod tests {
    use super::Image;
    use super::Properties;
    use crate::lron;

    #[test]
    fn test_exif_orientation() {
        let mut image = Image {
            id: 1,
            uuid: String::new(),
            master_image: None,
            rating: None,
            root_file: 2,
            file_format: String::from("RAW"),
            pick: 0,
            orientation: None,
            capture_time: String::new(),
            copy_name: None,
            xmp: String::new(),
            xmp_embedded: false,
            xmp_external_dirty: false,
            properties: None,
        };

        assert_eq!(image.exif_orientation(), 0);
        image.orientation = Some(String::from("ZZ"));
        assert_eq!(image.exif_orientation(), -1);

        image.orientation = Some(String::from("AB"));
        assert_eq!(image.exif_orientation(), 1);
        image.orientation = Some(String::from("DA"));
        assert_eq!(image.exif_orientation(), 8);
        image.orientation = Some(String::from("BC"));
        assert_eq!(image.exif_orientation(), 6);
        image.orientation = Some(String::from("CD"));
        assert_eq!(image.exif_orientation(), 3);
    }

    #[test]
    fn test_properties_loading() {
        const LRON1: &str = "properties = { \
	cropAspectH = 9, \
	cropAspectW = 16, \
	defaultCropBottom = 0.92105263157895, \
	defaultCropLeft = 0, \
	defaultCropRight = 1, \
	defaultCropTop = 0.078947368421053, \
	loupeFocusPoint = { \
		_ag_className = \"AgPoint\", \
		x = 0.6377015605549, \
		y = 0.70538265910057, \
	}, \
        }";

        let object = lron::Object::from_string(LRON1);

        assert!(object.is_ok());
        let object = object.unwrap();
        let properties = Properties::from(object);

        assert!(properties.loupe_focus.is_some());
        if let Some(ref loupe_focus) = properties.loupe_focus {
            assert_eq!(loupe_focus.x, 0.6377015605549);
            assert_eq!(loupe_focus.y, 0.70538265910057);
        }

        assert!(properties.crop_aspect_ratio.is_some());
        if let Some(ref ar) = properties.crop_aspect_ratio {
            assert_eq!(ar.height, 9);
            assert_eq!(ar.width, 16);
        }

        assert!(properties.default_crop.is_some());
        if let Some(ref crop) = properties.default_crop {
            assert_eq!(crop.top, 0.078947368421053);
            assert_eq!(crop.bottom, 0.92105263157895);
            assert_eq!(crop.left, 0.0);
            assert_eq!(crop.right, 1.0);
        }
    }
}
<commit_msg>Use row.get<usize>() instead of get<&str>()<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/.
*/

use rusqlite::Row;

use crate::catalog::CatalogVersion;
use crate::fromdb::FromDb;
use crate::lrobject::{LrId, LrObject};
use crate::lron;
use crate::{AspectRatio, Point, Rect};

/// Some misc properties of the image specific to Lr
#[derive(Default, Debug)]
pub struct Properties {
    /// Where the loupe is focused
    loupe_focus: Option<Point>,
    /// Aspect ration of the crop
    crop_aspect_ratio: Option<AspectRatio>,
    /// Default crop
    default_crop: Option<Rect>,
}

impl Properties {
    #[allow(clippy::unnecessary_unwrap)]
    fn loupe_focus(value: &[lron::Object]) -> Option<Point> {
        use crate::lron::{Object, Value};

        let mut x: Option<f64> = None;
        let mut y: Option<f64> = None;
        let mut is_point = false;
        value.iter().for_each(|o| {
            if let Object::Pair(p) = o {
                match p.key.as_str() {
                    "_ag_className" => is_point = p.value == Value::Str("AgPoint".to_owned()),
                    "y" => y = p.value.to_number(),
                    "x" => x = p.value.to_number(),
                    _ => {}
                }
            }
        });
        // This triggers clippy::unnecessary_unwrap
        if is_point && x.is_some() && y.is_some() {
            Some(Point {
                x: x.unwrap(),
                y: y.unwrap(),
            })
        } else {
            None
        }
    }

    #[allow(clippy::unnecessary_unwrap)]
    fn properties(value: &[lron::Object]) -> Self {
        use crate::lron::{Object, Value};

        let mut props = Properties::default();
        let mut crop_aspect_h: Option<i32> = None;
        let mut crop_aspect_w: Option<i32> = None;

        let mut top: Option<f64> = None;
        let mut bottom: Option<f64> = None;
        let mut left: Option<f64> = None;
        let mut right: Option<f64> = None;
        value.iter().for_each(|o| {
            if let Object::Pair(p) = o {
                match p.key.as_str() {
                    "loupeFocusPoint" => {
                        if let Value::Dict(ref v) = p.value {
                            props.loupe_focus = Self::loupe_focus(v);
                        }
                    }
                    "cropAspectH" => {
                        if let Value::Int(i) = p.value {
                            crop_aspect_h = Some(i);
                        }
                    }
                    "cropAspectW" => {
                        if let Value::Int(i) = p.value {
                            crop_aspect_w = Some(i);
                        }
                    }
                    "defaultCropBottom" => {
                        bottom = p.value.to_number();
                    }
                    "defaultCropLeft" => {
                        left = p.value.to_number();
                    }
                    "defaultCropRight" => {
                        right = p.value.to_number();
                    }
                    "defaultCropTop" => {
                        top = p.value.to_number();
                    }
                    _ => {}
                }
            }
        });

        // This triggers clippy::unnecessary_unwrap
        if crop_aspect_h.is_some() && crop_aspect_w.is_some() {
            props.crop_aspect_ratio = Some(AspectRatio {
                width: crop_aspect_w.unwrap(),
                height: crop_aspect_h.unwrap(),
            });
        }
        // This triggers clippy::unnecessary_unwrap
        if top.is_some() && bottom.is_some() && left.is_some() && right.is_some() {
            props.default_crop = Some(Rect {
                top: top.unwrap(),
                bottom: bottom.unwrap(),
                left: left.unwrap(),
                right: right.unwrap(),
            });
        }
        props
    }
}

impl From<lron::Object> for Properties {
    fn from(object: lron::Object) -> Self {
        use crate::lron::{Object, Value};

        match object {
            Object::Pair(ref s) => {
                if &s.key == "properties" {
                    match s.value {
                        Value::Dict(ref dict) => Self::properties(dict),
                        _ => Properties::default(),
                    }
                } else {
                    Properties::default()
                }
            }
            _ => Properties::default(),
        }
    }
}

/// An image in the `Catalog`. Requires a `LibraryFile` backing it
pub struct Image {
    id: LrId,
    uuid: String,
    /// If this a copy, id of the `Image` it is a copy of
    pub master_image: Option<LrId>,
    /// Name of copy.
    pub copy_name: Option<String>,
    /// Star rating
    pub rating: Option<i64>,
    /// Backing `LibraryFile` id.
    pub root_file: LrId,
    /// File format
    pub file_format: String,
    /// Pick. -1, 0, 1
    pub pick: i64,
    /// Orientation string (set Lr format documentation)
    /// Convert to EXIF orientation with `self.exif_orientation()`.
    pub orientation: Option<String>,
    /// Capture date.
    pub capture_time: String,
    /// XMP block as stored in the database. If len() == 0,
    /// there is no XMP.
    pub xmp: String,
    /// XMP is embedded: whether the XMP packet in the file
    /// like a JPEG, or in a sidecar like in a RAW (non DNG)
    /// file, regardless of `xmp`.
    pub xmp_embedded: bool,
    /// The external XMP (ie not in the database) is different.
    pub xmp_external_dirty: bool,
    /// Misc properties from the Adobe_imageProperties table
    pub properties: Option<Properties>,
}

impl Image {
    /// Return the Exif value for the image orientation
    /// No orientation = 0.
    /// Error = -1 or unknown value
    /// Otherwise the Exif value for `orientation`
    pub fn exif_orientation(&self) -> i32 {
        self.orientation.as_ref().map_or(0, |s| match s.as_ref() {
            "AB" => 1,
            "DA" => 8,
            "BC" => 6,
            "CD" => 3,
            _ => -1,
        })
    }
}

impl LrObject for Image {
    fn id(&self) -> LrId {
        self.id
    }
    fn uuid(&self) -> &str {
        &self.uuid
    }
}

impl FromDb for Image {
    fn read_from(_version: CatalogVersion, row: &Row) -> crate::Result<Self> {
        let properties = row
            .get::<usize, String>(13)
            .ok()
            .and_then(|v| lron::Object::from_string(&v).ok())
            .map(Properties::from);
        Ok(Image {
            id: row.get(0)?,
            uuid: row.get(1)?,
            master_image: row.get(2).ok(),
            rating: row.get(3).ok(),
            root_file: row.get(4)?,
            file_format: row.get(5)?,
            pick: row.get(6)?,
            orientation: row.get(7).ok(),
            capture_time: row.get(8)?,
            copy_name: row.get(9).ok(),
            xmp: row.get(10)?,
            xmp_embedded: row.get(11)?,
            xmp_external_dirty: row.get(12)?,
            properties,
        })
    }
    fn read_db_tables(_version: CatalogVersion) -> &'static str {
        "Adobe_images as img,Adobe_AdditionalMetadata as meta,Adobe_imageProperties as props"
    }
    fn read_db_columns(_version: CatalogVersion) -> &'static str {
        "img.id_local,img.id_global,img.masterImage,img.rating,img.rootFile,img.fileFormat,cast(img.pick as integer) as pick,img.orientation,img.captureTime,img.copyName,meta.xmp,meta.embeddedXmp,meta.externalXmpIsDirty,props.propertiesString"
    }
    fn read_join_where(_version: CatalogVersion) -> &'static str {
        "meta.image = img.id_local and props.image = img.id_local"
    }
}

#[cfg(test)]
mod tests {
    use super::Image;
    use super::Properties;
    use crate::lron;

    #[test]
    fn test_exif_orientation() {
        let mut image = Image {
            id: 1,
            uuid: String::new(),
            master_image: None,
            rating: None,
            root_file: 2,
            file_format: String::from("RAW"),
            pick: 0,
            orientation: None,
            capture_time: String::new(),
            copy_name: None,
            xmp: String::new(),
            xmp_embedded: false,
            xmp_external_dirty: false,
            properties: None,
        };

        assert_eq!(image.exif_orientation(), 0);
        image.orientation = Some(String::from("ZZ"));
        assert_eq!(image.exif_orientation(), -1);

        image.orientation = Some(String::from("AB"));
        assert_eq!(image.exif_orientation(), 1);
        image.orientation = Some(String::from("DA"));
        assert_eq!(image.exif_orientation(), 8);
        image.orientation = Some(String::from("BC"));
        assert_eq!(image.exif_orientation(), 6);
        image.orientation = Some(String::from("CD"));
        assert_eq!(image.exif_orientation(), 3);
    }

    #[test]
    fn test_properties_loading() {
        const LRON1: &str = "properties = { \
	cropAspectH = 9, \
	cropAspectW = 16, \
	defaultCropBottom = 0.92105263157895, \
	defaultCropLeft = 0, \
	defaultCropRight = 1, \
	defaultCropTop = 0.078947368421053, \
	loupeFocusPoint = { \
		_ag_className = \"AgPoint\", \
		x = 0.6377015605549, \
		y = 0.70538265910057, \
	}, \
        }";

        let object = lron::Object::from_string(LRON1);

        assert!(object.is_ok());
        let object = object.unwrap();
        let properties = Properties::from(object);

        assert!(properties.loupe_focus.is_some());
        if let Some(ref loupe_focus) = properties.loupe_focus {
            assert_eq!(loupe_focus.x, 0.6377015605549);
            assert_eq!(loupe_focus.y, 0.70538265910057);
        }

        assert!(properties.crop_aspect_ratio.is_some());
        if let Some(ref ar) = properties.crop_aspect_ratio {
            assert_eq!(ar.height, 9);
            assert_eq!(ar.width, 16);
        }

        assert!(properties.default_crop.is_some());
        if let Some(ref crop) = properties.default_crop {
            assert_eq!(crop.top, 0.078947368421053);
            assert_eq!(crop.bottom, 0.92105263157895);
            assert_eq!(crop.left, 0.0);
            assert_eq!(crop.right, 1.0);
        }
    }
}
<|endoftext|>


<commit_before>/// Maximum number of channels supported in the FLAC format.
pub const MAX_CHANNELS: usize = 8;

/// Audio frame that contains one sample for each channel.
pub struct Frame {
  pub header: Header,
  pub footer: Footer,
}

/// Channel assignment order.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChannelAssignment {
  Independent = 0,
  LeftSide    = 1,
  RightSide   = 2,
  MiddleSide  = 3,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NumberType {
  Frame(u32),
  Sample(u64),
}

/// Information regarding the current audio frame.
#[derive(Debug, PartialEq, Eq)]
pub struct Header {
  pub block_size: u32,
  pub sample_rate: u32,
  pub channels: u8,
  pub channel_assignment: ChannelAssignment,
  pub bits_per_sample: usize,
  pub number: NumberType,
  pub crc: u8,
}

/// End of the audio frame.
///
/// Contains a value that represents the CRC-16 of everything inside the
/// frame before the footer.
#[derive(Debug, PartialEq, Eq)]
pub struct Footer(pub u16);
<commit_msg>Add documentation for Frame fields<commit_after>/// Maximum number of channels supported in the FLAC format.
pub const MAX_CHANNELS: usize = 8;

/// Audio frame that contains one sample for each channel.
pub struct Frame {
  /// Information regarding the current audio frame.
  pub header: Header,
  /// CRC-16 of all frame bytes before this footer.
  pub footer: Footer,
}

/// Channel assignment order.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChannelAssignment {
  Independent = 0,
  LeftSide    = 1,
  RightSide   = 2,
  MiddleSide  = 3,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NumberType {
  Frame(u32),
  Sample(u64),
}

/// Information regarding the current audio frame.
#[derive(Debug, PartialEq, Eq)]
pub struct Header {
  pub block_size: u32,
  pub sample_rate: u32,
  pub channels: u8,
  pub channel_assignment: ChannelAssignment,
  pub bits_per_sample: usize,
  pub number: NumberType,
  pub crc: u8,
}

/// End of the audio frame.
///
/// Contains a value that represents the CRC-16 of everything inside the
/// frame before the footer.
#[derive(Debug, PartialEq, Eq)]
pub struct Footer(pub u16);
<|endoftext|>


<commit_before>use std::net::TcpStream;
use openssl::ssl::{SslContext, SslStream};
use std::io::{Error, ErrorKind, Read, Result, Write};
use regex::Regex;

enum IMAPStreamTypes {
	Basic(TcpStream),
	Ssl(SslStream<TcpStream>)
}

pub struct IMAPStream {
	stream: IMAPStreamTypes,
	pub host: &'static str,
	pub port: u16,
	tag: u32,
	tag_prefix: &'static str
}

pub struct IMAPMailbox {
	pub flags: String,
	pub exists: u32,
	pub recent: u32,
	pub unseen: Option<u32>,
	pub permanent_flags: Option<String>,
	pub uid_next: Option<u32>,
	pub uid_validity: Option<u32>
}

impl IMAPStream {

	pub fn connect(host: &'static str, port: u16, ssl_context: Option<SslContext>) -> Result<IMAPStream> {
		let connect_string = format!("{}:{}", host, port);
		let tcp_stream = TcpStream::connect(&*connect_string).unwrap();
		let mut socket = match ssl_context {
			Some(context) => IMAPStream { stream: IMAPStreamTypes::Ssl(SslStream::new(&context, tcp_stream).unwrap()), host: host, port: port, tag: 1, tag_prefix: "a"},
			None => IMAPStream { stream: IMAPStreamTypes::Basic(tcp_stream), host: host, port: port, tag: 1, tag_prefix: "a"},
		};

		match socket.read_greeting() {
			Ok(_) => (),
			Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read greet response"))
		}

		Ok(socket)
	}

	//LOGIN
	pub fn login(&mut self, username: & str, password: & str) -> Result<()> {
		self.run_command_and_check_ok(&format!("LOGIN {} {}", username, password).to_string())
	}

	//SELECT
	pub fn select(&mut self, mailbox_name: &str) -> Result<IMAPMailbox> {
		match self.run_command(&format!("SELECT {}", mailbox_name).to_string()) {
			Ok(lines) => IMAPStream::parse_select_or_examine(lines),
			Err(e) => Err(e)
		}
	}

	fn parse_select_or_examine(lines: Vec<String>) -> Result<IMAPMailbox> {
		let exists_regex = match Regex::new(r"^\* (\d+) EXISTS\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let recent_regex = match Regex::new(r"^\* (\d+) RECENT\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let flags_regex = match Regex::new(r"^\* FLAGS (.+)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let unseen_regex = match Regex::new(r"^OK \[UNSEEN (\d+)\](.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let uid_validity_regex = match Regex::new(r"^OK \[UIDVALIDITY (\d+)\](.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let uid_next_regex = match Regex::new(r"^OK \[UIDNEXT (\d+)\](.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let permanent_flags_regex = match Regex::new(r"^OK \[PERMANENTFLAGS (.+)\]\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		//Check Ok
		match IMAPStream::parse_response_ok(lines.clone()) {
			Ok(_) => (),
			Err(e) => return Err(e)
		};

		let mut mailbox = IMAPMailbox{
			flags: "".to_string(),
			exists: 0,
			recent: 0,
			unseen: None,
			permanent_flags: None,
			uid_next: None,
			uid_validity: None
		};

		for line in lines.iter() {
			if exists_regex.is_match(line) {
				let cap = exists_regex.captures(line).unwrap();
				mailbox.exists = cap.at(1).unwrap().parse::<u32>().unwrap();
			} else if recent_regex.is_match(line) {
				let cap = recent_regex.captures(line).unwrap();
				mailbox.recent = cap.at(1).unwrap().parse::<u32>().unwrap();
			} else if flags_regex.is_match(line) {
				let cap = flags_regex.captures(line).unwrap();
				mailbox.flags = cap.at(1).unwrap().to_string();
			} else if unseen_regex.is_match(line) {
				let cap = unseen_regex.captures(line).unwrap();
				mailbox.unseen = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
			} else if uid_validity_regex.is_match(line) {
				let cap = uid_validity_regex.captures(line).unwrap();
				mailbox.uid_validity = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
			} else if uid_next_regex.is_match(line) {
				let cap = uid_next_regex.captures(line).unwrap();
				mailbox.uid_next = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
			} else if permanent_flags_regex.is_match(line) {
				let cap = permanent_flags_regex.captures(line).unwrap();
				mailbox.permanent_flags = Some(cap.at(1).unwrap().to_string());
			}
		}

		return Ok(mailbox);
	}

	//EXAMINE
	pub fn examine(&mut self, mailbox_name: &str) -> Result<IMAPMailbox> {
		match self.run_command(&format!("EXAMINE {}", mailbox_name).to_string()) {
			Ok(lines) => IMAPStream::parse_select_or_examine(lines),
			Err(e) => Err(e)
		}
	}

	//FETCH
	pub fn fetch(&mut self, sequence_set: &str, query: &str) -> Result<Vec<String>> {
		self.run_command(&format!("FETCH {} {}", sequence_set, query).to_string())
	}

	//NOOP
	pub fn noop(&mut self) -> Result<()> {
		self.run_command_and_check_ok("NOOP")
	}

	//LOGOUT
	pub fn logout(&mut self) -> Result<()> {
		self.run_command_and_check_ok("LOGOUT")
	}

	//CREATE
	pub fn create(&mut self, mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("CREATE {}", mailbox_name).to_string())
	}

	//DELETE
	pub fn delete(&mut self, mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("DELETE {}", mailbox_name).to_string())
	}

	//RENAME
	pub fn rename(&mut self, current_mailbox_name: &str, new_mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("RENAME {} {}", current_mailbox_name, new_mailbox_name).to_string())
	}

	//SUBSCRIBE
	pub fn subscribe(&mut self, mailbox: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("SUBSCRIBE {}", mailbox).to_string())
	}

	//UNSUBSCRIBE
	pub fn unsubscribe(&mut self, mailbox: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("UNSUBSCRIBE {}", mailbox).to_string())
	}

	//CAPABILITY
	pub fn capability(&mut self) -> Result<Vec<String>> {
		match self.run_command(&format!("CAPABILITY").to_string()) {
			Ok(lines) => IMAPStream::parse_capability(lines),
			Err(e) => Err(e)
		}
	}

	fn parse_capability(lines: Vec<String>) -> Result<Vec<String>> {
		let capability_regex = match Regex::new(r"^\* CAPABILITY (.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		//Check Ok
		match IMAPStream::parse_response_ok(lines.clone()) {
			Ok(_) => (),
			Err(e) => return Err(e)
		};

		for line in lines.iter() {
			if capability_regex.is_match(line) {
				let cap = capability_regex.captures(line).unwrap();
				let capabilities_str = cap.at(1).unwrap();
				return Ok(capabilities_str.split(' ').map(|x| x.to_string()).collect());
			}
		}

		Err(Error::new(ErrorKind::Other, "Error parsing capabilities response"))
	}

	//COPY
	pub fn copy(&mut self, sequence_set: &str, mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("COPY {} {}", sequence_set, mailbox_name).to_string())
	}

	fn run_command_and_check_ok(&mut self, command: &str) -> Result<()> {
		match self.run_command(command) {
			Ok(lines) => IMAPStream::parse_response_ok(lines),
			Err(e) => Err(e)
		}
	}

	fn run_command(&mut self, untagged_command: &str) -> Result<Vec<String>> {
		let command = self.create_command(untagged_command.to_string());

		match self.write_str(&*command) {
			Ok(_) => (),
			Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to write")),
		};

		let ret = match self.read_response() {
			Ok(lines) => Ok(lines),
			Err(_) => Err(Error::new(ErrorKind::Other, "Failed to read")),
		};

		self.tag += 1;

		return ret;
	}

	fn parse_response_ok(lines: Vec<String>) -> Result<()> {
		let ok_regex = match Regex::new(r"^([a-zA-Z0-9]+) ([a-zA-Z0-9]+)(.*)") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};
		let last_line = lines.last().unwrap();

		for cap in ok_regex.captures_iter(last_line) {
			let response_type = cap.at(2).unwrap_or("");
			if response_type == "OK" {
				return Ok(());
			}
		}

		return Err(Error::new(ErrorKind::Other, format!("Invalid Response: {}", last_line).to_string()));
	}

	fn write_str(&mut self, s: &str) -> Result<()> {
		match self.stream {
			IMAPStreamTypes::Ssl(ref mut stream) => stream.write_fmt(format_args!("{}", s)),
			IMAPStreamTypes::Basic(ref mut stream) => stream.write_fmt(format_args!("{}", s)),
		}
	}

	fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
		match self.stream {
			IMAPStreamTypes::Ssl(ref mut stream) => stream.read(buf),
			IMAPStreamTypes::Basic(ref mut stream) => stream.read(buf),
		}
	}

	fn read_response(&mut self) -> Result<Vec<String>> {
		//Carriage return
		let cr = 0x0d;
		//Line Feed
		let lf = 0x0a;
		let mut found_tag_line = false;
		let start_str = format!("a{} ", self.tag);
		let mut lines: Vec<String> = Vec::new();

		while !found_tag_line {
			let mut line_buffer: Vec<u8> = Vec::new();
			while line_buffer.len() < 2 || (line_buffer[line_buffer.len()-1] != lf && line_buffer[line_buffer.len()-2] != cr) {
					let byte_buffer: &mut [u8] = &mut [0];
					match self.read(byte_buffer) {
						Ok(_) => {},
						Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read the response")),
					}
					line_buffer.push(byte_buffer[0]);
			}

			let line = String::from_utf8(line_buffer).unwrap();

			lines.push(line.clone());

			if (&*line).starts_with(&*start_str) {
				found_tag_line = true;
			}
		}

		Ok(lines)
	}

	fn read_greeting(&mut self) -> Result<()> {
		//Carriage return
		let cr = 0x0d;
		//Line Feed
		let lf = 0x0a;

		let mut line_buffer: Vec<u8> = Vec::new();
		while line_buffer.len() < 2 || (line_buffer[line_buffer.len()-1] != lf && line_buffer[line_buffer.len()-2] != cr) {
				let byte_buffer: &mut [u8] = &mut [0];
				match self.read(byte_buffer) {
					Ok(_) => {},
					Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read the response")),
				}
				line_buffer.push(byte_buffer[0]);
		}

		Ok(())
	}

	fn create_command(&mut self, command: String) -> String {
		let command = format!("{}{} {}\r\n", self.tag_prefix, self.tag, command);
		return command;
	}
}
<commit_msg>Allow connect to take both &str and String<commit_after>use std::net::TcpStream;
use openssl::ssl::{SslContext, SslStream};
use std::io::{Error, ErrorKind, Read, Result, Write};
use regex::Regex;

enum IMAPStreamTypes {
	Basic(TcpStream),
	Ssl(SslStream<TcpStream>)
}

pub struct IMAPStream {
	stream: IMAPStreamTypes,
	pub host: String,
	pub port: u16,
	tag: u32,
	tag_prefix: &'static str
}

pub struct IMAPMailbox {
	pub flags: String,
	pub exists: u32,
	pub recent: u32,
	pub unseen: Option<u32>,
	pub permanent_flags: Option<String>,
	pub uid_next: Option<u32>,
	pub uid_validity: Option<u32>
}

impl IMAPStream {

	pub fn connect<S: Into<String>>(host: S, port: u16, ssl_context: Option<SslContext>) -> Result<IMAPStream> {
                let host_string = host.into();
		let connect_string = format!("{}:{}", host_string, port);
		let tcp_stream = TcpStream::connect(&*connect_string).unwrap();
		let mut socket = match ssl_context {
			Some(context) => IMAPStream { stream: IMAPStreamTypes::Ssl(SslStream::new(&context, tcp_stream).unwrap()), host: host_string, port: port, tag: 1, tag_prefix: "a"},
			None => IMAPStream { stream: IMAPStreamTypes::Basic(tcp_stream), host: host_string, port: port, tag: 1, tag_prefix: "a"},
		};

		match socket.read_greeting() {
			Ok(_) => (),
			Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read greet response"))
		}

		Ok(socket)
	}

	//LOGIN
	pub fn login(&mut self, username: & str, password: & str) -> Result<()> {
		self.run_command_and_check_ok(&format!("LOGIN {} {}", username, password).to_string())
	}

	//SELECT
	pub fn select(&mut self, mailbox_name: &str) -> Result<IMAPMailbox> {
		match self.run_command(&format!("SELECT {}", mailbox_name).to_string()) {
			Ok(lines) => IMAPStream::parse_select_or_examine(lines),
			Err(e) => Err(e)
		}
	}

	fn parse_select_or_examine(lines: Vec<String>) -> Result<IMAPMailbox> {
		let exists_regex = match Regex::new(r"^\* (\d+) EXISTS\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let recent_regex = match Regex::new(r"^\* (\d+) RECENT\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let flags_regex = match Regex::new(r"^\* FLAGS (.+)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let unseen_regex = match Regex::new(r"^OK \[UNSEEN (\d+)\](.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let uid_validity_regex = match Regex::new(r"^OK \[UIDVALIDITY (\d+)\](.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let uid_next_regex = match Regex::new(r"^OK \[UIDNEXT (\d+)\](.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let permanent_flags_regex = match Regex::new(r"^OK \[PERMANENTFLAGS (.+)\]\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		//Check Ok
		match IMAPStream::parse_response_ok(lines.clone()) {
			Ok(_) => (),
			Err(e) => return Err(e)
		};

		let mut mailbox = IMAPMailbox{
			flags: "".to_string(),
			exists: 0,
			recent: 0,
			unseen: None,
			permanent_flags: None,
			uid_next: None,
			uid_validity: None
		};

		for line in lines.iter() {
			if exists_regex.is_match(line) {
				let cap = exists_regex.captures(line).unwrap();
				mailbox.exists = cap.at(1).unwrap().parse::<u32>().unwrap();
			} else if recent_regex.is_match(line) {
				let cap = recent_regex.captures(line).unwrap();
				mailbox.recent = cap.at(1).unwrap().parse::<u32>().unwrap();
			} else if flags_regex.is_match(line) {
				let cap = flags_regex.captures(line).unwrap();
				mailbox.flags = cap.at(1).unwrap().to_string();
			} else if unseen_regex.is_match(line) {
				let cap = unseen_regex.captures(line).unwrap();
				mailbox.unseen = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
			} else if uid_validity_regex.is_match(line) {
				let cap = uid_validity_regex.captures(line).unwrap();
				mailbox.uid_validity = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
			} else if uid_next_regex.is_match(line) {
				let cap = uid_next_regex.captures(line).unwrap();
				mailbox.uid_next = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
			} else if permanent_flags_regex.is_match(line) {
				let cap = permanent_flags_regex.captures(line).unwrap();
				mailbox.permanent_flags = Some(cap.at(1).unwrap().to_string());
			}
		}

		return Ok(mailbox);
	}

	//EXAMINE
	pub fn examine(&mut self, mailbox_name: &str) -> Result<IMAPMailbox> {
		match self.run_command(&format!("EXAMINE {}", mailbox_name).to_string()) {
			Ok(lines) => IMAPStream::parse_select_or_examine(lines),
			Err(e) => Err(e)
		}
	}

	//FETCH
	pub fn fetch(&mut self, sequence_set: &str, query: &str) -> Result<Vec<String>> {
		self.run_command(&format!("FETCH {} {}", sequence_set, query).to_string())
	}

	//NOOP
	pub fn noop(&mut self) -> Result<()> {
		self.run_command_and_check_ok("NOOP")
	}

	//LOGOUT
	pub fn logout(&mut self) -> Result<()> {
		self.run_command_and_check_ok("LOGOUT")
	}

	//CREATE
	pub fn create(&mut self, mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("CREATE {}", mailbox_name).to_string())
	}

	//DELETE
	pub fn delete(&mut self, mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("DELETE {}", mailbox_name).to_string())
	}

	//RENAME
	pub fn rename(&mut self, current_mailbox_name: &str, new_mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("RENAME {} {}", current_mailbox_name, new_mailbox_name).to_string())
	}

	//SUBSCRIBE
	pub fn subscribe(&mut self, mailbox: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("SUBSCRIBE {}", mailbox).to_string())
	}

	//UNSUBSCRIBE
	pub fn unsubscribe(&mut self, mailbox: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("UNSUBSCRIBE {}", mailbox).to_string())
	}

	//CAPABILITY
	pub fn capability(&mut self) -> Result<Vec<String>> {
		match self.run_command(&format!("CAPABILITY").to_string()) {
			Ok(lines) => IMAPStream::parse_capability(lines),
			Err(e) => Err(e)
		}
	}

	fn parse_capability(lines: Vec<String>) -> Result<Vec<String>> {
		let capability_regex = match Regex::new(r"^\* CAPABILITY (.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		//Check Ok
		match IMAPStream::parse_response_ok(lines.clone()) {
			Ok(_) => (),
			Err(e) => return Err(e)
		};

		for line in lines.iter() {
			if capability_regex.is_match(line) {
				let cap = capability_regex.captures(line).unwrap();
				let capabilities_str = cap.at(1).unwrap();
				return Ok(capabilities_str.split(' ').map(|x| x.to_string()).collect());
			}
		}

		Err(Error::new(ErrorKind::Other, "Error parsing capabilities response"))
	}

	//COPY
	pub fn copy(&mut self, sequence_set: &str, mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("COPY {} {}", sequence_set, mailbox_name).to_string())
	}

	fn run_command_and_check_ok(&mut self, command: &str) -> Result<()> {
		match self.run_command(command) {
			Ok(lines) => IMAPStream::parse_response_ok(lines),
			Err(e) => Err(e)
		}
	}

	fn run_command(&mut self, untagged_command: &str) -> Result<Vec<String>> {
		let command = self.create_command(untagged_command.to_string());

		match self.write_str(&*command) {
			Ok(_) => (),
			Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to write")),
		};

		let ret = match self.read_response() {
			Ok(lines) => Ok(lines),
			Err(_) => Err(Error::new(ErrorKind::Other, "Failed to read")),
		};

		self.tag += 1;

		return ret;
	}

	fn parse_response_ok(lines: Vec<String>) -> Result<()> {
		let ok_regex = match Regex::new(r"^([a-zA-Z0-9]+) ([a-zA-Z0-9]+)(.*)") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};
		let last_line = lines.last().unwrap();

		for cap in ok_regex.captures_iter(last_line) {
			let response_type = cap.at(2).unwrap_or("");
			if response_type == "OK" {
				return Ok(());
			}
		}

		return Err(Error::new(ErrorKind::Other, format!("Invalid Response: {}", last_line).to_string()));
	}

	fn write_str(&mut self, s: &str) -> Result<()> {
		match self.stream {
			IMAPStreamTypes::Ssl(ref mut stream) => stream.write_fmt(format_args!("{}", s)),
			IMAPStreamTypes::Basic(ref mut stream) => stream.write_fmt(format_args!("{}", s)),
		}
	}

	fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
		match self.stream {
			IMAPStreamTypes::Ssl(ref mut stream) => stream.read(buf),
			IMAPStreamTypes::Basic(ref mut stream) => stream.read(buf),
		}
	}

	fn read_response(&mut self) -> Result<Vec<String>> {
		//Carriage return
		let cr = 0x0d;
		//Line Feed
		let lf = 0x0a;
		let mut found_tag_line = false;
		let start_str = format!("a{} ", self.tag);
		let mut lines: Vec<String> = Vec::new();

		while !found_tag_line {
			let mut line_buffer: Vec<u8> = Vec::new();
			while line_buffer.len() < 2 || (line_buffer[line_buffer.len()-1] != lf && line_buffer[line_buffer.len()-2] != cr) {
					let byte_buffer: &mut [u8] = &mut [0];
					match self.read(byte_buffer) {
						Ok(_) => {},
						Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read the response")),
					}
					line_buffer.push(byte_buffer[0]);
			}

			let line = String::from_utf8(line_buffer).unwrap();

			lines.push(line.clone());

			if (&*line).starts_with(&*start_str) {
				found_tag_line = true;
			}
		}

		Ok(lines)
	}

	fn read_greeting(&mut self) -> Result<()> {
		//Carriage return
		let cr = 0x0d;
		//Line Feed
		let lf = 0x0a;

		let mut line_buffer: Vec<u8> = Vec::new();
		while line_buffer.len() < 2 || (line_buffer[line_buffer.len()-1] != lf && line_buffer[line_buffer.len()-2] != cr) {
				let byte_buffer: &mut [u8] = &mut [0];
				match self.read(byte_buffer) {
					Ok(_) => {},
					Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read the response")),
				}
				line_buffer.push(byte_buffer[0]);
		}

		Ok(())
	}

	fn create_command(&mut self, command: String) -> String {
		let command = format!("{}{} {}\r\n", self.tag_prefix, self.tag, command);
		return command;
	}
}
<|endoftext|>


<commit_before>use std::io;
use std::io::net::tcp;
use std::io::BufferedReader;
use std::io::util::LimitReader;
use common::{Header, Method};
use Request;
use url::Path;
use semver::Version;

/// A ClientConnection is an object that will store a socket to a client
/// and return Request objects.
pub struct ClientConnection {
    initial_socket: tcp::TcpStream,         // copy of the socket to be passed to request objects
	socket: BufferedReader<tcp::TcpStream>,
}

impl ClientConnection {
    pub fn new(socket: tcp::TcpStream) -> ClientConnection {
        ClientConnection {
            initial_socket: socket.clone(),
            socket: BufferedReader::new(socket),
        }
    }

    /// Generates an IoError with invalid input.
    /// This function is here because it is incredibly annoying to create this error.
    fn gen_invalid_input(desc: &'static str) -> io::IoError {
        io::IoError {
            kind: io::InvalidInput,
            desc: desc,
            detail: None
        }
    }

    /// Parses a "HTTP/1.1" string.
    fn parse_http_version(version: &str) -> io::IoResult<Version> {
        let elems = version.splitn('/', 2).map(|e| e.to_string()).collect::<Vec<String>>();
        if elems.len() != 2 {
            return Err(ClientConnection::gen_invalid_input("Wrong HTTP version format"))
        }

        let elems = elems.get(1).as_slice().splitn('.', 2)
            .map(|e| e.to_string()).collect::<Vec<String>>();
        if elems.len() != 2 {
            return Err(ClientConnection::gen_invalid_input("Wrong HTTP version format"))
        }

        match (from_str(elems.get(0).as_slice()), from_str(elems.get(1).as_slice())) {
            (Some(major), Some(minor)) =>
                Ok(Version {
                    major: major,
                    minor: minor,
                    patch: 0,
                    pre: Vec::new(),
                    build: Vec::new()
                }),
            _ => Err(ClientConnection::gen_invalid_input("Wrong HTTP version format"))
        }
    }

    /// Parses the first line of the request.
    /// eg. GET / HTTP/1.1
    fn parse_first_line(line: &str) -> io::IoResult<(Method, Path, Version)> {
        let mut words = line.words();

        let method = words.next();
        let path = words.next();
        let version = words.next();

        let (method, path, version) = match (method, path, version) {
            (Some(m), Some(p), Some(v)) => (m, p, v),
            _ => return Err(ClientConnection::gen_invalid_input("Missing element in first line"))
        };

        let method = match from_str(method) {
            Some(method) => method,
            None => return Err(ClientConnection::gen_invalid_input("Could not parse method"))
        };

        let path = match Path::parse(path) {
            Ok(p) => p,
            Err(_) => return Err(ClientConnection::gen_invalid_input("Wrong requested URL"))
        };

        let version = try!(ClientConnection::parse_http_version(version));

        Ok((method, path, version))
    }

    /// Parses a header line.
    /// eg. Host: example.com
    fn parse_header(line: &str) -> io::IoResult<Header> {
        let elems = line.splitn(':', 2).map(|e| e.to_string()).collect::<Vec<String>>();

        if elems.len() <= 1 {
            return Err(ClientConnection::gen_invalid_input(
                "Wrong header format (no ':')"))
        }
        if elems.get(1).as_slice().chars().next() != Some(' ') {
            return Err(ClientConnection::gen_invalid_input(
                "Wrong header format (missing space after ':')"))
        }

        let field = match from_str(elems.get(0).as_slice()) {
            None => return Err(ClientConnection::gen_invalid_input("Could not parse header")),
            Some(f) => f
        };

        Ok(Header {
            field: field,
            value: elems.get(1).as_slice().slice_from(1).to_string()
        })
    }

    /// Reads a request from the stream.
    /// Blocks until the header has been read.
    fn read(&mut self) -> io::IoResult<Request> {
        let mut lines = self.socket.lines();

        // reading the request line
        let (method, path, version) =
            try!(ClientConnection::parse_first_line(
                match lines.next() {
                    Some(line) => try!(line),
                    None => return Err(ClientConnection::gen_invalid_input(
                                "Missing first line of request"))
                }.as_slice().trim()
            ));

        // getting all headers
        let headers = {
            let mut headers = Vec::new();
            loop {
                match lines.next() {
                    Some(line) => {
                        let line = try!(line);
                        if line.as_slice().trim().len() == 0 { break };
                        headers.push(
                            try!(ClientConnection::parse_header(line.as_slice().trim()))
                        )
                    },
                    None => break
                }
            }
            headers
        };

        // finding length of body
        let body_length = headers.iter()
            .find(|h| h.field.equiv(&"Content-Length"))
            .and_then(|h| from_str::<uint>(h.value.as_slice()))
            .unwrap_or(0u);

        // building the request
        Ok(Request {
            read_socket: LimitReader::new(
                        BufferedReader::new(self.initial_socket.clone()), body_length
                    ),
            write_socket: self.initial_socket.clone(),
            method: method,
            path: path,
            http_version: version,
            headers: headers,
            body_length: body_length,
        })
    }
}

impl Iterator<Request> for ClientConnection {
    /// Blocks until the next Request is available.
    /// Returns None when the connection to the client has been closed.
    fn next(&mut self) -> Option<Request> {
        // TODO: send back message to client
        loop {
            return match self.read() {
                Err(error) => None,
                Ok(rq) => Some(rq)
            }
        }
    }
}
<commit_msg>Minor vocabulary fix<commit_after>use std::io;
use std::io::net::tcp;
use std::io::BufferedReader;
use std::io::util::LimitReader;
use common::{Header, Method};
use Request;
use url::Path;
use semver::Version;

/// A ClientConnection is an object that will store a socket to a client
/// and return Request objects.
pub struct ClientConnection {
    initial_socket: tcp::TcpStream,         // copy of the socket to be passed to request objects
	socket: BufferedReader<tcp::TcpStream>,
}

impl ClientConnection {
    pub fn new(socket: tcp::TcpStream) -> ClientConnection {
        ClientConnection {
            initial_socket: socket.clone(),
            socket: BufferedReader::new(socket),
        }
    }

    /// Generates an IoError with invalid input.
    /// This function is here because it is incredibly annoying to create this error.
    fn gen_invalid_input(desc: &'static str) -> io::IoError {
        io::IoError {
            kind: io::InvalidInput,
            desc: desc,
            detail: None
        }
    }

    /// Parses a "HTTP/1.1" string.
    fn parse_http_version(version: &str) -> io::IoResult<Version> {
        let elems = version.splitn('/', 2).map(|e| e.to_string()).collect::<Vec<String>>();
        if elems.len() != 2 {
            return Err(ClientConnection::gen_invalid_input("Wrong HTTP version format"))
        }

        let elems = elems.get(1).as_slice().splitn('.', 2)
            .map(|e| e.to_string()).collect::<Vec<String>>();
        if elems.len() != 2 {
            return Err(ClientConnection::gen_invalid_input("Wrong HTTP version format"))
        }

        match (from_str(elems.get(0).as_slice()), from_str(elems.get(1).as_slice())) {
            (Some(major), Some(minor)) =>
                Ok(Version {
                    major: major,
                    minor: minor,
                    patch: 0,
                    pre: Vec::new(),
                    build: Vec::new()
                }),
            _ => Err(ClientConnection::gen_invalid_input("Wrong HTTP version format"))
        }
    }

    /// Parses the request line of the request.
    /// eg. GET / HTTP/1.1
    fn parse_request_line(line: &str) -> io::IoResult<(Method, Path, Version)> {
        let mut words = line.words();

        let method = words.next();
        let path = words.next();
        let version = words.next();

        let (method, path, version) = match (method, path, version) {
            (Some(m), Some(p), Some(v)) => (m, p, v),
            _ => return Err(ClientConnection::gen_invalid_input("Missing element in request line"))
        };

        let method = match from_str(method) {
            Some(method) => method,
            None => return Err(ClientConnection::gen_invalid_input("Could not parse method"))
        };

        let path = match Path::parse(path) {
            Ok(p) => p,
            Err(_) => return Err(ClientConnection::gen_invalid_input("Wrong requested URL"))
        };

        let version = try!(ClientConnection::parse_http_version(version));

        Ok((method, path, version))
    }

    /// Parses a header line.
    /// eg. Host: example.com
    fn parse_header(line: &str) -> io::IoResult<Header> {
        let elems = line.splitn(':', 2).map(|e| e.to_string()).collect::<Vec<String>>();

        if elems.len() <= 1 {
            return Err(ClientConnection::gen_invalid_input(
                "Wrong header format (no ':')"))
        }
        if elems.get(1).as_slice().chars().next() != Some(' ') {
            return Err(ClientConnection::gen_invalid_input(
                "Wrong header format (missing space after ':')"))
        }

        let field = match from_str(elems.get(0).as_slice()) {
            None => return Err(ClientConnection::gen_invalid_input("Could not parse header")),
            Some(f) => f
        };

        Ok(Header {
            field: field,
            value: elems.get(1).as_slice().slice_from(1).to_string()
        })
    }

    /// Reads a request from the stream.
    /// Blocks until the header has been read.
    fn read(&mut self) -> io::IoResult<Request> {
        let mut lines = self.socket.lines();

        // reading the request line
        let (method, path, version) =
            try!(ClientConnection::parse_request_line(
                match lines.next() {
                    Some(line) => try!(line),
                    None => return Err(ClientConnection::gen_invalid_input(
                                "Missing request line"))
                }.as_slice().trim()
            ));

        // getting all headers
        let headers = {
            let mut headers = Vec::new();
            loop {
                match lines.next() {
                    Some(line) => {
                        let line = try!(line);
                        if line.as_slice().trim().len() == 0 { break };
                        headers.push(
                            try!(ClientConnection::parse_header(line.as_slice().trim()))
                        )
                    },
                    None => break
                }
            }
            headers
        };

        // finding length of body
        let body_length = headers.iter()
            .find(|h| h.field.equiv(&"Content-Length"))
            .and_then(|h| from_str::<uint>(h.value.as_slice()))
            .unwrap_or(0u);

        // building the request
        Ok(Request {
            read_socket: LimitReader::new(
                        BufferedReader::new(self.initial_socket.clone()), body_length
                    ),
            write_socket: self.initial_socket.clone(),
            method: method,
            path: path,
            http_version: version,
            headers: headers,
            body_length: body_length,
        })
    }
}

impl Iterator<Request> for ClientConnection {
    /// Blocks until the next Request is available.
    /// Returns None when the connection to the client has been closed.
    fn next(&mut self) -> Option<Request> {
        // TODO: send back message to client
        loop {
            return match self.read() {
                Err(error) => None,
                Ok(rq) => Some(rq)
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::error::Error as StdError;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::{Path, PathBuf};

use base::ast;
use vm::vm::VM;
use super::{filename_to_module, load_script};
use base::macros::{Macro, Error as MacroError};
use base::types::TcIdent;

quick_error! {
    /// Error type for the import macro
    #[derive(Debug)]
    pub enum Error {
        /// The importer found a cyclic dependency when loading files
        CyclicDependency(module: String) {
            description("Cyclic dependency")
            display("Module '{}' occurs in a cyclic dependency", module)
        }
        /// Generic message error
        String(message: &'static str) {
            description(message)
            display("{}", message)
        }
        /// The importer could not load the imported file
        IO(err: io::Error) {
            description(err.description())
            display("{}", err)
            from()
        }
    }
}

/// Macro which rewrites occurances of `import "filename"` to a load of that file if it is not
/// already loaded and then a global access to the loaded module
pub struct Import {
    visited: RefCell<Vec<String>>,
    paths: RefCell<Vec<PathBuf>>,
}

impl Import {
    /// Creates a new import macro
    pub fn new() -> Import {
        Import {
            visited: RefCell::new(Vec::new()),
            paths: RefCell::new(vec![PathBuf::from(".")]),
        }
    }

    /// Adds a path to the list of paths which the importer uses to find files
    pub fn add_path<P: Into<PathBuf>>(&self, path: P) {
        self.paths.borrow_mut().push(path.into());
    }
}

impl<'a> Macro<VM<'a>> for Import {
    fn expand(&self,
              vm: &VM<'a>,
              arguments: &mut [ast::LExpr<TcIdent>])
              -> Result<ast::LExpr<TcIdent>, MacroError> {
        if arguments.len() != 1 {
            return Err(Error::String("Expected import to get 1 argument").into());
        }
        match *arguments[0] {
            ast::Expr::Literal(ast::LiteralEnum::String(ref filename)) => {
                let modulename = filename_to_module(filename);
                let path = Path::new(&filename[..]);
                // Only load the script if it is not already loaded
                let name = vm.symbol(&*modulename);
                debug!("Import '{}' {:?}", modulename, self.visited);
                if !vm.global_exists(&modulename) {
                    if self.visited.borrow().iter().any(|m| **m == **filename) {
                        return Err(Error::CyclicDependency(filename.clone()).into());
                    }
                    self.visited.borrow_mut().push(filename.clone());
                    let file = self.paths
                                   .borrow()
                                   .iter()
                                   .filter_map(|p| {
                                       let mut base = p.clone();
                                       base.push(path);
                                       match File::open(&base) {
                                           Ok(file) => Some(file),
                                           Err(_) => None,
                                       }
                                   })
                                   .next();
                    let mut file = try!(file.ok_or_else(|| Error::String("Could not find file")));
                    let mut buffer = String::new();
                    try!(file.read_to_string(&mut buffer));
                    try!(load_script(vm, &modulename, &buffer));
                    self.visited.borrow_mut().pop();
                }
                // FIXME Does not handle shadowing
                Ok(ast::located(arguments[0].location,
                                ast::Expr::Identifier(TcIdent::new(name))))
            }
            _ => return Err(Error::String("Expected a string literal to import").into()),
        }
    }
}
<commit_msg>feat: Include the standard library files into the embed_lang crate<commit_after>use std::cell::RefCell;
use std::error::Error as StdError;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::{Path, PathBuf};

use base::ast;
use vm::vm::VM;
use super::{filename_to_module, load_script};
use base::macros::{Macro, Error as MacroError};
use base::types::TcIdent;

quick_error! {
    /// Error type for the import macro
    #[derive(Debug)]
    pub enum Error {
        /// The importer found a cyclic dependency when loading files
        CyclicDependency(module: String) {
            description("Cyclic dependency")
            display("Module '{}' occurs in a cyclic dependency", module)
        }
        /// Generic message error
        String(message: &'static str) {
            description(message)
            display("{}", message)
        }
        /// The importer could not load the imported file
        IO(err: io::Error) {
            description(err.description())
            display("{}", err)
            from()
        }
    }
}

macro_rules! std_libs {
    ($($file: expr),*) => {
        [$((concat!("std/", $file, ".hs"), include_str!(concat!("../std/", $file, ".hs")))),*]
    }
}
// Include the standard library distribution in the binary
static STD_LIBS: [(&'static str, &'static str); 7] = std_libs!("prelude", "map", "repl", "string", "state", "test", "writer");

/// Macro which rewrites occurances of `import "filename"` to a load of that file if it is not
/// already loaded and then a global access to the loaded module
pub struct Import {
    visited: RefCell<Vec<String>>,
    paths: RefCell<Vec<PathBuf>>,
}

impl Import {
    /// Creates a new import macro
    pub fn new() -> Import {
        Import {
            visited: RefCell::new(Vec::new()),
            paths: RefCell::new(vec![PathBuf::from(".")]),
        }
    }

    /// Adds a path to the list of paths which the importer uses to find files
    pub fn add_path<P: Into<PathBuf>>(&self, path: P) {
        self.paths.borrow_mut().push(path.into());
    }
}

impl<'a> Macro<VM<'a>> for Import {
    fn expand(&self,
              vm: &VM<'a>,
              arguments: &mut [ast::LExpr<TcIdent>])
              -> Result<ast::LExpr<TcIdent>, MacroError> {
        if arguments.len() != 1 {
            return Err(Error::String("Expected import to get 1 argument").into());
        }
        match *arguments[0] {
            ast::Expr::Literal(ast::LiteralEnum::String(ref filename)) => {
                let modulename = filename_to_module(filename);
                let path = Path::new(&filename[..]);
                // Only load the script if it is not already loaded
                let name = vm.symbol(&*modulename);
                debug!("Import '{}' {:?}", modulename, self.visited);
                if !vm.global_exists(&modulename) {
                    if self.visited.borrow().iter().any(|m| **m == **filename) {
                        return Err(Error::CyclicDependency(filename.clone()).into());
                    }
                    self.visited.borrow_mut().push(filename.clone());
                    let mut buffer = String::new();
                    let file_contents = match STD_LIBS.iter().find(|tup| tup.0 == filename) {
                        Some(tup) => tup.1,
                        None => {
                            let file = self.paths
                                           .borrow()
                                           .iter()
                                           .filter_map(|p| {
                                               let mut base = p.clone();
                                               base.push(path);
                                               match File::open(&base) {
                                                   Ok(file) => Some(file),
                                                   Err(_) => None,
                                               }
                                           })
                                           .next();
                            let mut file = try!(file.ok_or_else(|| Error::String("Could not find file")));
                            try!(file.read_to_string(&mut buffer));
                            &*buffer
                        }
                    };
                    try!(load_script(vm, &modulename, file_contents));
                    self.visited.borrow_mut().pop();
                }
                // FIXME Does not handle shadowing
                Ok(ast::located(arguments[0].location,
                                ast::Expr::Identifier(TcIdent::new(name))))
            }
            _ => return Err(Error::String("Expected a string literal to import").into()),
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>add test_multiple3<commit_after><|endoftext|>


<commit_before><commit_msg>chore: fix a comment typo (#1633)<commit_after><|endoftext|>