<commit_before>use std::mem;
use std::ptr;
use std::ops::Deref;
use std::ops::DerefMut;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use std::sync::TryLockError;

use memory::ChunkProperties;
use memory::Content;
use memory::CpuAccessible;
use memory::CpuWriteAccessible;
use memory::MemorySource;
use memory::MemorySourceChunk;
use memory::DeviceMemory;
use memory::MappedDeviceMemory;
use memory::ChunkRange;
use sync::Fence;
use sync::Semaphore;

use device::Device;
use device::Queue;

use OomError;
use VulkanObject;
use VulkanPointers;
use vk;

/// Dummy marker whose strategy is to allocate a new chunk of memory for each allocation.
///
/// The memory will not be accessible since it is not necessarily in host-visible memory.
///
/// This is good for large buffers, but inefficient is you use a lot of small buffers.
///
/// The memory is locked globally. That means that it doesn't matter whether you access the buffer
/// for reading or writing (like a `Mutex`).
#[derive(Debug, Copy, Clone)]
pub struct DeviceLocal;

unsafe impl MemorySource for DeviceLocal {
    type Chunk = DeviceLocalChunk;

    #[inline]
    fn is_sparse(&self) -> bool {
        false
    }

    #[inline]
    fn allocate(self, device: &Arc<Device>, size: usize, alignment: usize, memory_type_bits: u32)
                -> Result<DeviceLocalChunk, OomError>
    {
        let mem_ty = device.physical_device().memory_types()
                           .skip(memory_type_bits.trailing_zeros() as usize).next().unwrap();
        let mem = try!(DeviceMemory::alloc(device, &mem_ty, size));

        // note: alignment doesn't need to be checked because allocating memory is guaranteed to
        //       fulfill any alignment requirement

        Ok(DeviceLocalChunk {
            mem: mem,
            semaphore: Mutex::new(None),
        })
    }
}

/// A chunk allocated from a `DeviceLocal`.
pub struct DeviceLocalChunk {
    mem: DeviceMemory,
    semaphore: Mutex<Option<Arc<Semaphore>>>,
}

unsafe impl MemorySourceChunk for DeviceLocalChunk {
    #[inline]
    unsafe fn gpu_access(&self, _write: bool, _range: ChunkRange, _: &Arc<Queue>,
                         _: Option<Arc<Fence>>, mut semaphore: Option<Arc<Semaphore>>)
                         -> Option<Arc<Semaphore>>
    {
        assert!(semaphore.is_some());

        let mut self_semaphore = self.semaphore.lock().unwrap();
        mem::swap(&mut *self_semaphore, &mut semaphore);

        semaphore
    }

    #[inline]
    fn requires_fence(&self) -> bool {
        false
    }

    #[inline]
    fn properties(&self) -> ChunkProperties {
        ChunkProperties::Regular {
            memory: &self.mem,
            offset: 0,
            size: self.mem.size(),
        }
    }

    #[inline]
    fn may_alias(&self) -> bool {
        false
    }
}

/// Dummy marker whose strategy is to allocate a new chunk of memory for each allocation.
///
/// Guaranteed to allocate from a host-visible memory type.
///
/// This is good for large buffers, but inefficient is you use a lot of small buffers.
///
/// The memory is locked globally. That means that it doesn't matter whether you access the buffer
/// for reading or writing (like a `Mutex`).
#[derive(Debug, Copy, Clone)]
pub struct HostVisible;

unsafe impl MemorySource for HostVisible {
    type Chunk = HostVisibleChunk;

    #[inline]
    fn is_sparse(&self) -> bool {
        false
    }

    #[inline]
    fn allocate(self, device: &Arc<Device>, size: usize, alignment: usize, memory_type_bits: u32)
                -> Result<HostVisibleChunk, OomError>
    {
        let mem_ty = device.physical_device().memory_types()
                           .filter(|t| (memory_type_bits & (1 << t.id())) != 0)
                           .filter(|t| t.is_host_visible())
                           .next().unwrap();
        let mem = try!(DeviceMemory::alloc_and_map(device, &mem_ty, size));

        // note: alignment doesn't need to be checked because allocating memory is guaranteed to
        //       fulfill any alignment requirement

        Ok(HostVisibleChunk {
            mem: mem,
            coherent: mem_ty.is_host_coherent(),
            lock: Mutex::new((None, None)),
        })
    }
}

/// A chunk allocated from a `HostVisible`.
pub struct HostVisibleChunk {
    mem: MappedDeviceMemory,
    coherent: bool,
    lock: Mutex<(Option<Arc<Semaphore>>, Option<Arc<Fence>>)>,
}

unsafe impl MemorySourceChunk for HostVisibleChunk {
    #[inline]
    unsafe fn gpu_access(&self, _write: bool, _range: ChunkRange, _: &Arc<Queue>,
                         fence: Option<Arc<Fence>>, mut semaphore: Option<Arc<Semaphore>>)
                         -> Option<Arc<Semaphore>>
    {
        assert!(fence.is_some());
        assert!(semaphore.is_some());

        let mut self_lock = self.lock.lock().unwrap();
        mem::swap(&mut self_lock.0, &mut semaphore);
        self_lock.1 = fence;

        semaphore
    }

    #[inline]
    fn properties(&self) -> ChunkProperties {
        ChunkProperties::Regular {
            memory: &self.mem.memory(),
            offset: 0,
            size: self.mem.memory().size(),
        }
    }

    #[inline]
    fn may_alias(&self) -> bool {
        false
    }
}

unsafe impl<'a, T: ?Sized + 'a> CpuAccessible<'a, T> for HostVisibleChunk
    where T: Content
{
    type Read = GpuAccess<'a, T>;

    #[inline]
    fn read(&'a self, timeout_ns: u64) -> GpuAccess<'a, T> {
        self.write(timeout_ns)
    }

    #[inline]
    fn try_read(&'a self) -> Option<GpuAccess<'a, T>> {
        self.try_write()
    }
}

unsafe impl<'a, T: ?Sized + 'a> CpuWriteAccessible<'a, T> for HostVisibleChunk
    where T: Content
{
    type Write = GpuAccess<'a, T>;

    #[inline]
    fn write(&'a self, timeout_ns: u64) -> GpuAccess<'a, T> {
        let vk = self.mem.memory().device().pointers();
        let pointer = T::ref_from_ptr(self.mem.mapping_pointer(), self.mem.memory().size()).unwrap();       // TODO: error

        let mut lock = self.lock.lock().unwrap();
        if let Some(ref fence) = lock.1 {
            fence.wait(timeout_ns).unwrap();        // FIXME: error
        }
        lock.1 = None;

        if !self.coherent {
            let range = vk::MappedMemoryRange {
                sType: vk::STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
                pNext: ptr::null(),
                memory: self.mem.memory().internal_object(),
                offset: 0,
                size: vk::WHOLE_SIZE,
            };

            // TODO: check result?
            unsafe {
                vk.InvalidateMappedMemoryRanges(self.mem.memory().device().internal_object(),
                                                1, &range);
            }
        }

        GpuAccess {
            mem: &self.mem,
            coherent: self.coherent,
            guard: lock,
            pointer: pointer,
        }
    }

    #[inline]
    fn try_write(&'a self) -> Option<GpuAccess<'a, T>> {
        let vk = self.mem.memory().device().pointers();
        let pointer = T::ref_from_ptr(self.mem.mapping_pointer(), self.mem.memory().size()).unwrap();       // TODO: error

        let mut lock = match self.lock.try_lock() {
            Ok(l) => l,
            Err(TryLockError::Poisoned(_)) => panic!(),
            Err(TryLockError::WouldBlock) => return None,
        };

        if let Some(ref fence) = lock.1 {
            if fence.ready() != Ok(true) {      // TODO: we ignore ready()'s error here?
                return None;
            }
        }

        lock.1 = None;

        if !self.coherent {
            let range = vk::MappedMemoryRange {
                sType: vk::STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
                pNext: ptr::null(),
                memory: self.mem.memory().internal_object(),
                offset: 0,
                size: vk::WHOLE_SIZE,
            };

            // TODO: check result?
            unsafe {
                vk.InvalidateMappedMemoryRanges(self.mem.memory().device().internal_object(),
                                                1, &range);
            }
        }

        Some(GpuAccess {
            mem: &self.mem,
            coherent: self.coherent,
            guard: lock,
            pointer: pointer,
        })
    }
}

/// Object that can be used to read or write the content of a `HostVisibleChunk`.
///
/// Note that this object holds a mutex guard on the chunk. If another thread tries to access
/// this memory's content or tries to submit a GPU command that uses this memory, it will block.
pub struct GpuAccess<'a, T: ?Sized + 'a> {
    mem: &'a MappedDeviceMemory,
    pointer: *mut T,
    guard: MutexGuard<'a, (Option<Arc<Semaphore>>, Option<Arc<Fence>>)>,
    coherent: bool,
}

impl<'a, T: ?Sized + 'a> Deref for GpuAccess<'a, T> {
    type Target = T;

    #[inline]
    fn deref(&self) -> &T {
        unsafe { &*self.pointer }
    }
}

impl<'a, T: ?Sized + 'a> DerefMut for GpuAccess<'a, T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.pointer }
    }
}

impl<'a, T: ?Sized + 'a> Drop for GpuAccess<'a, T> {
    #[inline]
    fn drop(&mut self) {
        if !self.coherent {
            let vk = self.mem.memory().device().pointers();

            let range = vk::MappedMemoryRange {
                sType: vk::STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
                pNext: ptr::null(),
                memory: self.mem.memory().internal_object(),
                offset: 0,
                size: vk::WHOLE_SIZE,
            };

            // TODO: check result?
            unsafe {
                vk.FlushMappedMemoryRanges(self.mem.memory().device().internal_object(),
                                           1, &range);
            }
        }
    }
}
<commit_msg>DeviceLocal now prefers device-local memory types (as its name tells)<commit_after>use std::mem;
use std::ptr;
use std::ops::Deref;
use std::ops::DerefMut;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use std::sync::TryLockError;

use memory::ChunkProperties;
use memory::Content;
use memory::CpuAccessible;
use memory::CpuWriteAccessible;
use memory::MemorySource;
use memory::MemorySourceChunk;
use memory::DeviceMemory;
use memory::MappedDeviceMemory;
use memory::ChunkRange;
use sync::Fence;
use sync::Semaphore;

use device::Device;
use device::Queue;

use OomError;
use VulkanObject;
use VulkanPointers;
use vk;

/// Dummy marker whose strategy is to allocate a new chunk of memory for each allocation.
///
/// The memory will not be accessible since it is not necessarily in host-visible memory.
///
/// This is good for large buffers, but inefficient is you use a lot of small buffers.
///
/// The memory is locked globally. That means that it doesn't matter whether you access the buffer
/// for reading or writing (like a `Mutex`).
#[derive(Debug, Copy, Clone)]
pub struct DeviceLocal;

unsafe impl MemorySource for DeviceLocal {
    type Chunk = DeviceLocalChunk;

    #[inline]
    fn is_sparse(&self) -> bool {
        false
    }

    #[inline]
    fn allocate(self, device: &Arc<Device>, size: usize, alignment: usize, memory_type_bits: u32)
                -> Result<DeviceLocalChunk, OomError>
    {
        // We try to find a device-local memory type, but fall back to any memory type if we don't
        // find any.

        let device_local = device.physical_device().memory_types()
                                 .filter(|t| (memory_type_bits & (1 << t.id())) != 0)
                                 .filter(|t| t.is_device_local());

        let any = device.physical_device().memory_types()
                        .filter(|t| (memory_type_bits & (1 << t.id())) != 0);

        let mem_ty = device_local.chain(any).next().expect("could not find any memory type");

        let mem = try!(DeviceMemory::alloc(device, &mem_ty, size));

        // note: alignment doesn't need to be checked because allocating memory is guaranteed to
        //       fulfill any alignment requirement

        Ok(DeviceLocalChunk {
            mem: mem,
            semaphore: Mutex::new(None),
        })
    }
}

/// A chunk allocated from a `DeviceLocal`.
pub struct DeviceLocalChunk {
    mem: DeviceMemory,
    semaphore: Mutex<Option<Arc<Semaphore>>>,
}

unsafe impl MemorySourceChunk for DeviceLocalChunk {
    #[inline]
    unsafe fn gpu_access(&self, _write: bool, _range: ChunkRange, _: &Arc<Queue>,
                         _: Option<Arc<Fence>>, mut semaphore: Option<Arc<Semaphore>>)
                         -> Option<Arc<Semaphore>>
    {
        assert!(semaphore.is_some());

        let mut self_semaphore = self.semaphore.lock().unwrap();
        mem::swap(&mut *self_semaphore, &mut semaphore);

        semaphore
    }

    #[inline]
    fn requires_fence(&self) -> bool {
        false
    }

    #[inline]
    fn properties(&self) -> ChunkProperties {
        ChunkProperties::Regular {
            memory: &self.mem,
            offset: 0,
            size: self.mem.size(),
        }
    }

    #[inline]
    fn may_alias(&self) -> bool {
        false
    }
}

/// Dummy marker whose strategy is to allocate a new chunk of memory for each allocation.
///
/// Guaranteed to allocate from a host-visible memory type.
///
/// This is good for large buffers, but inefficient is you use a lot of small buffers.
///
/// The memory is locked globally. That means that it doesn't matter whether you access the buffer
/// for reading or writing (like a `Mutex`).
#[derive(Debug, Copy, Clone)]
pub struct HostVisible;

unsafe impl MemorySource for HostVisible {
    type Chunk = HostVisibleChunk;

    #[inline]
    fn is_sparse(&self) -> bool {
        false
    }

    #[inline]
    fn allocate(self, device: &Arc<Device>, size: usize, alignment: usize, memory_type_bits: u32)
                -> Result<HostVisibleChunk, OomError>
    {
        let mem_ty = device.physical_device().memory_types()
                           .filter(|t| (memory_type_bits & (1 << t.id())) != 0)
                           .filter(|t| t.is_host_visible())
                           .next().unwrap();
        let mem = try!(DeviceMemory::alloc_and_map(device, &mem_ty, size));

        // note: alignment doesn't need to be checked because allocating memory is guaranteed to
        //       fulfill any alignment requirement

        Ok(HostVisibleChunk {
            mem: mem,
            coherent: mem_ty.is_host_coherent(),
            lock: Mutex::new((None, None)),
        })
    }
}

/// A chunk allocated from a `HostVisible`.
pub struct HostVisibleChunk {
    mem: MappedDeviceMemory,
    coherent: bool,
    lock: Mutex<(Option<Arc<Semaphore>>, Option<Arc<Fence>>)>,
}

unsafe impl MemorySourceChunk for HostVisibleChunk {
    #[inline]
    unsafe fn gpu_access(&self, _write: bool, _range: ChunkRange, _: &Arc<Queue>,
                         fence: Option<Arc<Fence>>, mut semaphore: Option<Arc<Semaphore>>)
                         -> Option<Arc<Semaphore>>
    {
        assert!(fence.is_some());
        assert!(semaphore.is_some());

        let mut self_lock = self.lock.lock().unwrap();
        mem::swap(&mut self_lock.0, &mut semaphore);
        self_lock.1 = fence;

        semaphore
    }

    #[inline]
    fn properties(&self) -> ChunkProperties {
        ChunkProperties::Regular {
            memory: &self.mem.memory(),
            offset: 0,
            size: self.mem.memory().size(),
        }
    }

    #[inline]
    fn may_alias(&self) -> bool {
        false
    }
}

unsafe impl<'a, T: ?Sized + 'a> CpuAccessible<'a, T> for HostVisibleChunk
    where T: Content
{
    type Read = GpuAccess<'a, T>;

    #[inline]
    fn read(&'a self, timeout_ns: u64) -> GpuAccess<'a, T> {
        self.write(timeout_ns)
    }

    #[inline]
    fn try_read(&'a self) -> Option<GpuAccess<'a, T>> {
        self.try_write()
    }
}

unsafe impl<'a, T: ?Sized + 'a> CpuWriteAccessible<'a, T> for HostVisibleChunk
    where T: Content
{
    type Write = GpuAccess<'a, T>;

    #[inline]
    fn write(&'a self, timeout_ns: u64) -> GpuAccess<'a, T> {
        let vk = self.mem.memory().device().pointers();
        let pointer = T::ref_from_ptr(self.mem.mapping_pointer(), self.mem.memory().size()).unwrap();       // TODO: error

        let mut lock = self.lock.lock().unwrap();
        if let Some(ref fence) = lock.1 {
            fence.wait(timeout_ns).unwrap();        // FIXME: error
        }
        lock.1 = None;

        if !self.coherent {
            let range = vk::MappedMemoryRange {
                sType: vk::STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
                pNext: ptr::null(),
                memory: self.mem.memory().internal_object(),
                offset: 0,
                size: vk::WHOLE_SIZE,
            };

            // TODO: check result?
            unsafe {
                vk.InvalidateMappedMemoryRanges(self.mem.memory().device().internal_object(),
                                                1, &range);
            }
        }

        GpuAccess {
            mem: &self.mem,
            coherent: self.coherent,
            guard: lock,
            pointer: pointer,
        }
    }

    #[inline]
    fn try_write(&'a self) -> Option<GpuAccess<'a, T>> {
        let vk = self.mem.memory().device().pointers();
        let pointer = T::ref_from_ptr(self.mem.mapping_pointer(), self.mem.memory().size()).unwrap();       // TODO: error

        let mut lock = match self.lock.try_lock() {
            Ok(l) => l,
            Err(TryLockError::Poisoned(_)) => panic!(),
            Err(TryLockError::WouldBlock) => return None,
        };

        if let Some(ref fence) = lock.1 {
            if fence.ready() != Ok(true) {      // TODO: we ignore ready()'s error here?
                return None;
            }
        }

        lock.1 = None;

        if !self.coherent {
            let range = vk::MappedMemoryRange {
                sType: vk::STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
                pNext: ptr::null(),
                memory: self.mem.memory().internal_object(),
                offset: 0,
                size: vk::WHOLE_SIZE,
            };

            // TODO: check result?
            unsafe {
                vk.InvalidateMappedMemoryRanges(self.mem.memory().device().internal_object(),
                                                1, &range);
            }
        }

        Some(GpuAccess {
            mem: &self.mem,
            coherent: self.coherent,
            guard: lock,
            pointer: pointer,
        })
    }
}

/// Object that can be used to read or write the content of a `HostVisibleChunk`.
///
/// Note that this object holds a mutex guard on the chunk. If another thread tries to access
/// this memory's content or tries to submit a GPU command that uses this memory, it will block.
pub struct GpuAccess<'a, T: ?Sized + 'a> {
    mem: &'a MappedDeviceMemory,
    pointer: *mut T,
    guard: MutexGuard<'a, (Option<Arc<Semaphore>>, Option<Arc<Fence>>)>,
    coherent: bool,
}

impl<'a, T: ?Sized + 'a> Deref for GpuAccess<'a, T> {
    type Target = T;

    #[inline]
    fn deref(&self) -> &T {
        unsafe { &*self.pointer }
    }
}

impl<'a, T: ?Sized + 'a> DerefMut for GpuAccess<'a, T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.pointer }
    }
}

impl<'a, T: ?Sized + 'a> Drop for GpuAccess<'a, T> {
    #[inline]
    fn drop(&mut self) {
        if !self.coherent {
            let vk = self.mem.memory().device().pointers();

            let range = vk::MappedMemoryRange {
                sType: vk::STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
                pNext: ptr::null(),
                memory: self.mem.memory().internal_object(),
                offset: 0,
                size: vk::WHOLE_SIZE,
            };

            // TODO: check result?
            unsafe {
                vk.FlushMappedMemoryRanges(self.mem.memory().device().internal_object(),
                                           1, &range);
            }
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>removed header<commit_after><|endoftext|>


<commit_before>
macro_rules! impl_cons_iter(
    () => ();

    ($A:ident, $($B:ident,)*) => (
        impl_cons_iter!($($B,)*);
        #[allow(non_snake_case)]
        impl<X, Iter, $($B),*> Iterator for ConsTuples<Iter, (($($B,)*), X)>
            where Iter: Iterator<Item = (($($B,)*), X)>,
        {
            type Item = ($($B,)* X, );
            fn next(&mut self) -> Option<Self::Item> {
                self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, ))
            }

            fn size_hint(&self) -> (usize, Option<usize>) {
                self.iter.size_hint()
            }
        }

        #[allow(non_snake_case)]
        impl<X, Iter, $($B),*> DoubleEndedIterator for ConsTuples<Iter, (($($B,)*), X)>
            where Iter: DoubleEndedIterator<Item = (($($B,)*), X)>,
        {
            fn next_back(&mut self) -> Option<Self::Item> {
                self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, ))
            }
        }

    );
);

impl_cons_iter!(A, B, C, D, E, F, G, H,);

/// A helper iterator that maps an iterator of tuples like
/// `((A, B), C)` to an iterator of `(A, B, C)`.
///
/// Used by the `iproduct!()` macro.
pub struct ConsTuples<I, J>
    where I: Iterator<Item=J>,
{
    iter: I,
}

impl<I, J> Clone for ConsTuples<I, J>
    where I: Clone + Iterator<Item=J>,
{
    fn clone(&self) -> Self {
        ConsTuples {
            iter: self.iter.clone(),
        }
    }
}

impl<I, J> ConsTuples<I, J>
    where I: Iterator<Item=J>
{
    /// Create a new `ConsTuples`.
    #[doc(hidden)]
    pub fn new(iter: I) -> Self {
        ConsTuples { iter: iter }
    }
}
<commit_msg>cons_tuples: Implement (t, x) consing where t is a 2- to 7-tuple.<commit_after>
macro_rules! impl_cons_iter(
    ($_A:ident, $_B:ident, ) => (); // stop

    ($A:ident, $($B:ident,)*) => (
        impl_cons_iter!($($B,)*);
        #[allow(non_snake_case)]
        impl<X, Iter, $($B),*> Iterator for ConsTuples<Iter, (($($B,)*), X)>
            where Iter: Iterator<Item = (($($B,)*), X)>,
        {
            type Item = ($($B,)* X, );
            fn next(&mut self) -> Option<Self::Item> {
                self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, ))
            }

            fn size_hint(&self) -> (usize, Option<usize>) {
                self.iter.size_hint()
            }
        }

        #[allow(non_snake_case)]
        impl<X, Iter, $($B),*> DoubleEndedIterator for ConsTuples<Iter, (($($B,)*), X)>
            where Iter: DoubleEndedIterator<Item = (($($B,)*), X)>,
        {
            fn next_back(&mut self) -> Option<Self::Item> {
                self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, ))
            }
        }

    );
);

impl_cons_iter!(A, B, C, D, E, F, G, H,);

/// A helper iterator that maps an iterator of tuples like
/// `((A, B), C)` to an iterator of `(A, B, C)`.
///
/// Used by the `iproduct!()` macro.
pub struct ConsTuples<I, J>
    where I: Iterator<Item=J>,
{
    iter: I,
}

impl<I, J> Clone for ConsTuples<I, J>
    where I: Clone + Iterator<Item=J>,
{
    fn clone(&self) -> Self {
        ConsTuples {
            iter: self.iter.clone(),
        }
    }
}

impl<I, J> ConsTuples<I, J>
    where I: Iterator<Item=J>
{
    /// Create a new `ConsTuples`.
    #[doc(hidden)]
    pub fn new(iter: I) -> Self {
        ConsTuples { iter: iter }
    }
}
<|endoftext|>


<commit_before>use glm;

pub fn wrap_rects(original: glm::IVec4, max: glm::UVec2) -> [Option<glm::IVec4>; 4] {
    let max = glm::to_ivec2(max);
    let left = (original.x + max.x) % max.x;
    let right = left + original.z;
    let top = (original.y + max.y) % max.y;
    let bottom = top + original.w;
    let original = glm::ivec4(left, top, original.z, original.w);

    let side = if left < 0 {
        Some(glm::ivec4(left + max.x, top, original.z, original.w))
    } else if right > max.x {
        Some(glm::ivec4((right % max.x) - original.z, top, original.z, original.w))
    } else {
        None
    };

    let vert = if top < 0 {
        Some(glm::ivec4(left, top + max.y, original.z, original.w))
    } else if bottom > max.y {
        Some(glm::ivec4(left, bottom % max.y - original.w, original.z, original.w))
    } else {
        None
    };

    let side_vert = match (side, vert) {
        (Some(side_center), Some(vert_center)) => {
            Some(glm::ivec4(side_center.x, vert_center.y, original.z, original.w))
        }
        _ => None,
    };

    [Some(original), side, vert, side_vert]
}

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

    #[test]
    fn no_wrapping() {
        let max_coords = glm::uvec2(40, 30);
        let rect = glm::ivec4(25, 15, 10, 7);
        let rects: Vec<_> = wrap_rects(rect, max_coords).iter().filter_map(|&x| x).collect();
        assert_eq!(rects.len(), 1);
        assert_eq!(rect, rects[0]);
    }

    #[test]
    fn wrapping_left() {
        let max_coords = glm::uvec2(40, 30);
        let rect = glm::ivec4(-2, 16, 10, 8);
        let rects: Vec<_> = wrap_rects(rect, max_coords).iter().filter_map(|&x| x).collect();
        assert_eq!(rects.len(), 2);
        assert_eq!(rect, rects[1]);
        assert_eq!(glm::ivec4(38, 16, 10, 8), rects[0]);
    }

    #[test]
    fn wrapping_right() {
        let max_coords = glm::uvec2(40, 30);
        let rect = glm::ivec4(34, 16, 10, 13);
        let rects: Vec<_> = wrap_rects(rect, max_coords).iter().filter_map(|&x| x).collect();
        assert_eq!(rects.len(), 2);
        assert_eq!(rect, rects[0]);
        assert_eq!(glm::ivec4(-6, 16, 10, 13), rects[1]);
    }

    #[test]
    fn wrapping_bottom() {
        let max_coords = glm::uvec2(40, 30);
        let rect = glm::ivec4(27, 21, 6, 10);
        let rects: Vec<_> = wrap_rects(rect, max_coords).iter().filter_map(|&x| x).collect();
        assert_eq!(rects.len(), 2);
        assert_eq!(rect, rects[0]);
        assert_eq!(glm::ivec4(27, -9, 6, 10), rects[1]);
    }

    #[test]
    fn wrapping_top() {
        let max_coords = glm::uvec2(40, 30);
        let rect = glm::ivec4(27, -3, 6, 10);
        let rects: Vec<_> = wrap_rects(rect, max_coords).iter().filter_map(|&x| x).collect();
        assert_eq!(rects.len(), 2);
        assert_eq!(rect, rects[1]);
        assert_eq!(glm::ivec4(27, 27, 6, 10), rects[0]);
    }

    #[test]
    fn wrapping_corner() {
        let max_coords = glm::uvec2(40, 30);
        let rect = glm::ivec4(-2, -3, 10, 10);
        let rects: Vec<_> = wrap_rects(rect, max_coords).iter().filter_map(|&x| x).collect();
        assert_eq!(rects.len(), 4);
        assert_eq!(rect, rects[3]);
        assert_eq!(glm::ivec4(38, -3, 10, 10), rects[2]);
        assert_eq!(glm::ivec4(-2, 27, 10, 10), rects[1]);
        assert_eq!(glm::ivec4(38, 27, 10, 10), rects[0]);
    }
}
<commit_msg>split window_wrapper into functions<commit_after>use glm;

pub fn wrap_rects(original: glm::IVec4, max: glm::UVec2) -> [Option<glm::IVec4>; 4] {
    let max = glm::to_ivec2(max);
    let left = (original.x + max.x) % max.x;
    let top = (original.y + max.y) % max.y;
    let original = glm::ivec4(left, top, original.z, original.w);

    let side = side_wrap(left, top, original, max);
    let vert = vert_wrap(left, top, original, max);
    let side_vert = match (side, vert) {
        (Some(side_center), Some(vert_center)) => {
            Some(glm::ivec4(side_center.x, vert_center.y, original.z, original.w))
        }
        _ => None,
    };

    [Some(original), side, vert, side_vert]
}

fn side_wrap(left: i32, top: i32, original: glm::IVec4, max: glm::IVec2) -> Option<glm::IVec4> {
    let right = left + original.z;
    if left < 0 {
        Some(glm::ivec4(left + max.x, top, original.z, original.w))
    } else if right > max.x {
        Some(glm::ivec4((right % max.x) - original.z, top, original.z, original.w))
    } else {
        None
    }
}

fn vert_wrap(left: i32, top: i32, original: glm::IVec4, max: glm::IVec2) -> Option<glm::IVec4> {
    let bottom = top + original.w;
    if top < 0 {
        Some(glm::ivec4(left, top + max.y, original.z, original.w))
    } else if bottom > max.y {
        Some(glm::ivec4(left, bottom % max.y - original.w, original.z, original.w))
    } else {
        None
    }
}

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

    #[test]
    fn no_wrapping() {
        let max_coords = glm::uvec2(40, 30);
        let rect = glm::ivec4(25, 15, 10, 7);
        let rects: Vec<_> = wrap_rects(rect, max_coords).iter().filter_map(|&x| x).collect();
        assert_eq!(rects.len(), 1);
        assert_eq!(rect, rects[0]);
    }

    #[test]
    fn wrapping_left() {
        let max_coords = glm::uvec2(40, 30);
        let rect = glm::ivec4(-2, 16, 10, 8);
        let rects: Vec<_> = wrap_rects(rect, max_coords).iter().filter_map(|&x| x).collect();
        assert_eq!(rects.len(), 2);
        assert_eq!(rect, rects[1]);
        assert_eq!(glm::ivec4(38, 16, 10, 8), rects[0]);
    }

    #[test]
    fn wrapping_right() {
        let max_coords = glm::uvec2(40, 30);
        let rect = glm::ivec4(34, 16, 10, 13);
        let rects: Vec<_> = wrap_rects(rect, max_coords).iter().filter_map(|&x| x).collect();
        assert_eq!(rects.len(), 2);
        assert_eq!(rect, rects[0]);
        assert_eq!(glm::ivec4(-6, 16, 10, 13), rects[1]);
    }

    #[test]
    fn wrapping_bottom() {
        let max_coords = glm::uvec2(40, 30);
        let rect = glm::ivec4(27, 21, 6, 10);
        let rects: Vec<_> = wrap_rects(rect, max_coords).iter().filter_map(|&x| x).collect();
        assert_eq!(rects.len(), 2);
        assert_eq!(rect, rects[0]);
        assert_eq!(glm::ivec4(27, -9, 6, 10), rects[1]);
    }

    #[test]
    fn wrapping_top() {
        let max_coords = glm::uvec2(40, 30);
        let rect = glm::ivec4(27, -3, 6, 10);
        let rects: Vec<_> = wrap_rects(rect, max_coords).iter().filter_map(|&x| x).collect();
        assert_eq!(rects.len(), 2);
        assert_eq!(rect, rects[1]);
        assert_eq!(glm::ivec4(27, 27, 6, 10), rects[0]);
    }

    #[test]
    fn wrapping_corner() {
        let max_coords = glm::uvec2(40, 30);
        let rect = glm::ivec4(-2, -3, 10, 10);
        let rects: Vec<_> = wrap_rects(rect, max_coords).iter().filter_map(|&x| x).collect();
        assert_eq!(rects.len(), 4);
        assert_eq!(rect, rects[3]);
        assert_eq!(glm::ivec4(38, -3, 10, 10), rects[2]);
        assert_eq!(glm::ivec4(-2, 27, 10, 10), rects[1]);
        assert_eq!(glm::ivec4(38, 27, 10, 10), rects[0]);
    }
}
<|endoftext|>


<commit_before>use std::env::*;
use std::env;
use std::fs;
use std::fs::File;
use std::path::Path;
use std::error::Error;
use std::io::prelude::*;
use std::io::BufReader;
use std::ascii::AsciiExt;
use std::collections::HashMap;
use hyper::Client;
use hyper::header::Connection;
use error::*;
use regex::Regex;

extern crate rustc_serialize;
use self::rustc_serialize::json::*;

extern crate chrono;
use self::chrono::*;

#[derive(Clone, Debug)]
pub struct AWSCredentials {
    key: String,
    secret: String,
    token: Option<String>,
    expires_at: DateTime<UTC>
}

impl AWSCredentials {
    pub fn new<K, S>(key:K, secret:S, token:Option<String>, expires_at:DateTime<UTC>) -> AWSCredentials where K:Into<String>, S:Into<String> {
        AWSCredentials {
            key: key.into(),
            secret: secret.into(),
            token: token,
            expires_at: expires_at,
        }
    }

    pub fn get_aws_access_key_id(&self) -> &str {
    	&self.key
    }

    pub fn get_aws_secret_key(&self) -> &str {
    	&self.secret
    }

    pub fn get_expires_at(&self) -> &DateTime<UTC> {
        &self.expires_at
    }

    pub fn get_token(&self) -> &Option<String> {
        &self.token
    }

    fn credentials_are_expired(&self) -> bool {
        //println!("Seeing if creds of {:?} are expired compared to {:?}", self.expires_at, UTC::now() + Duration::seconds(20));
        // This is a rough hack to hopefully avoid someone requesting creds then sitting on them
        // before issuing the request:
        if self.expires_at < UTC::now() + Duration::seconds(20) {
            return true;
        }
        return false;
    }
}

pub trait AWSCredentialsProvider {
    fn new() -> Self;
	fn get_credentials(&mut self) -> Result<&AWSCredentials, &str>;
}

pub struct EnvironmentCredentialsProvider {
    credentials: Option<AWSCredentials>
}

impl AWSCredentialsProvider for EnvironmentCredentialsProvider {
    fn new() -> EnvironmentCredentialsProvider {
        EnvironmentCredentialsProvider { credentials: None }
    }

	fn get_credentials(&mut self) -> Result<&AWSCredentials, &str> {
        if self.credentials.is_none() || self.credentials.as_ref().unwrap().credentials_are_expired() {
           self.credentials = Some(try!(get_credentials_from_environment()));
        }
        Ok(self.credentials.as_ref().unwrap())
	}
}

fn get_credentials_from_environment<'a>() -> Result<AWSCredentials, &'a str> {
    let env_key = match var("AWS_ACCESS_KEY_ID") {
        Ok(val) => val,
        Err(_) => return Err("No AWS_ACCESS_KEY_ID in environment")
    };
    let env_secret = match var("AWS_SECRET_ACCESS_KEY") {
        Ok(val) => val,
        Err(_) => return Err("No AWS_SECRET_ACCESS_KEY in environment")
    };

    if env_key.is_empty() || env_secret.is_empty() {
        return Err("Couldn't find either AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY or both in environment.");
    }

    Ok(AWSCredentials::new(env_key, env_secret, None, in_ten_minutes()))
}

pub struct FileCredentialsProvider {
    credentials: Option<AWSCredentials>
}

impl AWSCredentialsProvider for FileCredentialsProvider {
    fn new() -> FileCredentialsProvider {
        FileCredentialsProvider { credentials: None }
    }

    fn get_credentials(&mut self) -> Result<&AWSCredentials, &str> {
        if self.credentials.is_none() || self.credentials.as_ref().unwrap().credentials_are_expired() {
            // Default credentials file location:
            // ~/.aws/credentials (Linux/Mac)
            // %USERPROFILE%\.aws\credentials  (Windows)
            let mut profile_location;
            match env::home_dir() {
                Some(ref p) => profile_location = p.display().to_string() + "/.aws/credentials",
                None => return Err("Couldn't get your home dir.")
            }

            match parse_credentials_file(&profile_location) {
                Ok(mut profiles) => {
                    let default_profile = profiles.remove("default");
                    if default_profile.is_none() {
                        return Err("default profile not found");
                    }
                    self.credentials = default_profile;
                },
                Err(_) => { return Err("Parse error"); }
            };
       }
       Ok(self.credentials.as_ref().unwrap())
   }
}

fn parse_credentials_file(file_with_path: &str) -> Result<HashMap<String, AWSCredentials>, AWSError> {
    let path = Path::new(&file_with_path);
    let display = path.display();

    match fs::metadata(&path) {
        Err(_) => return Err(AWSError::new("Couldn't stat credentials file.")),
        Ok(metadata) => {
            if !metadata.is_file() {
                return Err(AWSError::new("Couldn't open file."));
            }
        }
    };

    let file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
        Ok(opened_file) => opened_file,
    };

    let profile_regex = Regex::new(r"^\[([^\]]+)\]$").unwrap();
    let mut profiles: HashMap<String, AWSCredentials> = HashMap::new();
    let mut access_key: Option<String> = None;
    let mut secret_key: Option<String> = None;
    let mut profile_name: Option<String> = None;

    let file_lines = BufReader::new(&file);
    for line in file_lines.lines() {

        let unwrapped_line : String = line.unwrap();

        // skip comments
        if unwrapped_line.starts_with('#') {
            continue;
        }

        // handle the opening of named profile blocks
        if profile_regex.is_match(&unwrapped_line) {

            if profile_name.is_some() && access_key.is_some() && secret_key.is_some() {
                let creds = AWSCredentials::new(access_key.unwrap(), secret_key.unwrap(), None, in_ten_minutes());
                profiles.insert(profile_name.unwrap(), creds);
            }

            access_key = None;
            secret_key = None;

            let caps = profile_regex.captures(&unwrapped_line).unwrap();
            profile_name = Some(caps.at(1).unwrap().to_string());
            continue;
        }

        // otherwise look for key=value pairs we care about
        let lower_case_line = unwrapped_line.to_ascii_lowercase().to_string();

        if lower_case_line.contains("aws_access_key_id") {
            if access_key.is_none() {
                let v: Vec<&str> = unwrapped_line.split("=").collect();
                if v.len() > 0 {
                    access_key = Some(v[1].trim_matches(' ').to_string());
                }
            }
        } else if lower_case_line.contains("aws_secret_access_key") {
            if secret_key.is_none() {
                let v: Vec<&str> = unwrapped_line.split("=").collect();
                if v.len() > 0 {
                    secret_key = Some(v[1].trim_matches(' ').to_string());
                }
            }
        }

        // we could potentially explode here to indicate that the file is invalid

    }

    if profile_name.is_some() && access_key.is_some() && secret_key.is_some() {
        let creds = AWSCredentials::new(access_key.unwrap(), secret_key.unwrap(), None, in_ten_minutes());
        profiles.insert(profile_name.unwrap(), creds);
    }

    if profiles.is_empty() {
        return Err(AWSError::new("No credentials found."));
    }

    Ok(profiles)
}

pub struct IAMRoleCredentialsProvider {
    credentials: Option<AWSCredentials>
}

impl AWSCredentialsProvider for IAMRoleCredentialsProvider {
    fn new() -> IAMRoleCredentialsProvider {
        IAMRoleCredentialsProvider { credentials: None }
    }

    fn get_credentials(&mut self) -> Result<&AWSCredentials, &str> {
        if self.credentials.is_none() || self.credentials.as_ref().unwrap().credentials_are_expired() {
            // TODO: backoff and retry on failure.

            //println!("Calling IAM metadata");
            // for "real" use: http://169.254.169.254/latest/meta-data/iam/security-credentials/
            let mut address : String = "http://169.254.169.254/latest/meta-data/iam/security-credentials".to_string();
            let client = Client::new();
            let mut response;
            match client.get(&address)
                .header(Connection::close()).send() {
                    Err(_) => return Err("Couldn't connect to metadata service"), // add Why?
                    Ok(received_response) => response = received_response
                };

            let mut body = String::new();
            match response.read_to_string(&mut body) {
                Err(_) => return Err("Didn't get a parsable response body from metadata service"),
                Ok(_) => (),
            };

            address.push_str("/");
            address.push_str(&body);
            body = String::new();
            match client.get(&address)
                .header(Connection::close()).send() {
                    Err(_) => return Err("Didn't get a parseable response body from instance role details"),
                    Ok(received_response) => response = received_response
                };

            match response.read_to_string(&mut body) {
                Err(_) => return Err("Had issues with reading iam role response: {}"),
                Ok(_) => (),
            };

            let json_object : Json;
            match Json::from_str(&body) {
                Err(_) => return Err("Couldn't parse metadata response body."),
                Ok(val) => json_object = val
            };

            let mut access_key;
            match json_object.find("AccessKeyId") {
                None => return Err("Couldn't find AccessKeyId in response."),
                Some(val) => access_key = val.to_string().replace("\"", "")
            };

            let mut secret_key;
            match json_object.find("SecretAccessKey") {
                None => return Err("Couldn't find SecretAccessKey in response."),
                Some(val) => secret_key = val.to_string().replace("\"", "")
            };

            let mut expiration;
            match json_object.find("Expiration") {
                None => return Err("Couldn't find Expiration in response."),
                Some(val) => expiration = val.to_string().replace("\"", "")
            };

            let mut expiration_time;
            match expiration.parse::<DateTime<UTC>>() {
                Err(why) => panic!("Kabloey on parse: {}", why),
                Ok(val) => expiration_time = val
            };

            let mut token_from_response;
            match json_object.find("Token") {
                None => return Err("Couldn't find Token in response."),
                Some(val) => token_from_response = val.to_string().replace("\"", "")
            };

            self.credentials = Some(AWSCredentials::new(access_key, secret_key, Some(token_from_response.to_string()), expiration_time));
        }

		Ok(&self.credentials.as_ref().unwrap())
	}
}

#[derive(Debug, Clone)]
pub struct DefaultAWSCredentialsProviderChain {
    credentials: Option<AWSCredentials>
}

// Chain the providers:
impl DefaultAWSCredentialsProviderChain {
    pub fn new() -> DefaultAWSCredentialsProviderChain {
        DefaultAWSCredentialsProviderChain { credentials: None }
    }

    pub fn get_credentials(&mut self) -> Result<&AWSCredentials, AWSError> {
        if self.credentials.is_none() || self.credentials.as_ref().unwrap().credentials_are_expired() {
            // fetch creds in order: env, file, IAM

            if let Ok(creds) = EnvironmentCredentialsProvider::new().get_credentials() {
                //println!("Found creds in env");
                self.credentials = Some(creds.clone());
            } else if let Ok(creds) = FileCredentialsProvider::new().get_credentials() {
                //println!("Found creds in file");
                self.credentials = Some(creds.clone());
            } else if let Ok(creds) = IAMRoleCredentialsProvider::new().get_credentials() {
                //println!("Found creds via iam");
                self.credentials = Some(creds.clone());
            } else {
               panic!("Couldn't find AWS credentials in environment, default credential file location or IAM role.")
            }
        }
        Ok(self.credentials.as_ref().unwrap())
    }
}

fn in_ten_minutes() -> DateTime<UTC> {
    UTC::now() + Duration::seconds(600)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::BufReader;
    use std::fs::File;
    use error::*;
    use regex::*;

    #[test]
    fn parse_credentials_file_default_profile() {
        let result = super::parse_credentials_file("tests/sample-data/default_profile_credentials");
        assert!(result.is_ok());

        let profiles = result.ok().unwrap();
        assert_eq!(profiles.len(), 1);

        let default_profile = profiles.get("default").unwrap();
        assert_eq!(default_profile.get_aws_access_key_id(), "foo");
        assert_eq!(default_profile.get_aws_secret_key(), "bar");
    }

    #[test]
    fn parse_credentials_file_multiple_profiles() {
        let result = super::parse_credentials_file("tests/sample-data/multiple_profile_credentials");
        assert!(result.is_ok());

        let profiles = result.ok().unwrap();
        assert_eq!(profiles.len(), 2);

        let foo_profile = profiles.get("foo").unwrap();
        assert_eq!(foo_profile.get_aws_access_key_id(), "foo_access_key");
        assert_eq!(foo_profile.get_aws_secret_key(), "foo_secret_key");

        let bar_profile = profiles.get("bar").unwrap();
        assert_eq!(bar_profile.get_aws_access_key_id(), "bar_access_key");
        assert_eq!(bar_profile.get_aws_secret_key(), "bar_secret_key");

    }

    #[test]
    fn existing_file_no_credentials() {
        let result = super::parse_credentials_file("tests/sample-data/no_credentials");
        assert_eq!(result.err(), Some(AWSError::new("No credentials found.")))
    }

    #[test]
    fn parse_credentials_bad_path() {
        let result = super::parse_credentials_file("/bad/file/path");
        assert_eq!(result.err(), Some(AWSError::new("Couldn't stat credentials file.")));
    }

    #[test]
    fn parse_credentials_directory_path() {
        let result = super::parse_credentials_file("tests/");
        assert_eq!(result.err(), Some(AWSError::new("Couldn't open file.")));
    }

}

<commit_msg>ProfileCredentialsProvider implemented as superset of FileCredentialsProvider with identical default behavior<commit_after>use std::env::*;
use std::env;
use std::fs;
use std::fs::File;
use std::path::Path;
use std::error::Error;
use std::io::prelude::*;
use std::io::BufReader;
use std::ascii::AsciiExt;
use std::collections::HashMap;
use hyper::Client;
use hyper::header::Connection;
use error::*;
use regex::Regex;

extern crate rustc_serialize;
use self::rustc_serialize::json::*;

extern crate chrono;
use self::chrono::*;

#[derive(Clone, Debug)]
pub struct AWSCredentials {
    key: String,
    secret: String,
    token: Option<String>,
    expires_at: DateTime<UTC>
}

impl AWSCredentials {
    pub fn new<K, S>(key:K, secret:S, token:Option<String>, expires_at:DateTime<UTC>) -> AWSCredentials where K:Into<String>, S:Into<String> {
        AWSCredentials {
            key: key.into(),
            secret: secret.into(),
            token: token,
            expires_at: expires_at,
        }
    }

    pub fn get_aws_access_key_id(&self) -> &str {
    	&self.key
    }

    pub fn get_aws_secret_key(&self) -> &str {
    	&self.secret
    }

    pub fn get_expires_at(&self) -> &DateTime<UTC> {
        &self.expires_at
    }

    pub fn get_token(&self) -> &Option<String> {
        &self.token
    }

    fn credentials_are_expired(&self) -> bool {
        //println!("Seeing if creds of {:?} are expired compared to {:?}", self.expires_at, UTC::now() + Duration::seconds(20));
        // This is a rough hack to hopefully avoid someone requesting creds then sitting on them
        // before issuing the request:
        if self.expires_at < UTC::now() + Duration::seconds(20) {
            return true;
        }
        return false;
    }
}

pub trait AWSCredentialsProvider {
    fn new() -> Self;
	fn get_credentials(&mut self) -> Result<&AWSCredentials, &str>;
}

pub struct EnvironmentCredentialsProvider {
    credentials: Option<AWSCredentials>
}

impl AWSCredentialsProvider for EnvironmentCredentialsProvider {
    fn new() -> EnvironmentCredentialsProvider {
        EnvironmentCredentialsProvider { credentials: None }
    }

	fn get_credentials(&mut self) -> Result<&AWSCredentials, &str> {
        if self.credentials.is_none() || self.credentials.as_ref().unwrap().credentials_are_expired() {
           self.credentials = Some(try!(get_credentials_from_environment()));
        }
        Ok(self.credentials.as_ref().unwrap())
	}
}

fn get_credentials_from_environment<'a>() -> Result<AWSCredentials, &'a str> {
    let env_key = match var("AWS_ACCESS_KEY_ID") {
        Ok(val) => val,
        Err(_) => return Err("No AWS_ACCESS_KEY_ID in environment")
    };
    let env_secret = match var("AWS_SECRET_ACCESS_KEY") {
        Ok(val) => val,
        Err(_) => return Err("No AWS_SECRET_ACCESS_KEY in environment")
    };

    if env_key.is_empty() || env_secret.is_empty() {
        return Err("Couldn't find either AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY or both in environment.");
    }

    Ok(AWSCredentials::new(env_key, env_secret, None, in_ten_minutes()))
}

pub struct ProfileCredentialsProvider {
    profile: String,
    file_name: String,
    credentials: Option<AWSCredentials>
}

impl ProfileCredentialsProvider {
    pub fn with_configuration(profile: &str, file_name: &str) -> ProfileCredentialsProvider {
        ProfileCredentialsProvider { credentials: None, profile: profile.to_string(), file_name: file_name.to_string() }
    }
}

impl AWSCredentialsProvider for ProfileCredentialsProvider {
    fn new() -> ProfileCredentialsProvider {
        // Default credentials file location:
        // ~/.aws/credentials (Linux/Mac)
        // %USERPROFILE%\.aws\credentials  (Windows)
        let mut profile_location;
        match env::home_dir() {
            Some(ref p) => profile_location = p.display().to_string() + "/.aws/credentials",
            None => panic!("Couldn't get your home dir.")
        }

        ProfileCredentialsProvider { credentials: None, profile: "default".to_string(), file_name: profile_location }
    }

    fn get_credentials(&mut self) -> Result<&AWSCredentials, &str> {
        if self.credentials.is_none() || self.credentials.as_ref().unwrap().credentials_are_expired() {
            match parse_credentials_file(&self.file_name) {
                Ok(mut profiles) => {
                    let default_profile = profiles.remove(&self.profile);
                    if default_profile.is_none() {
                        return Err("profile not found");
                    }
                    self.credentials = default_profile;
                },
                Err(_) => { return Err("Parse error"); }
            };
       }
       Ok(self.credentials.as_ref().unwrap())
   }
}

fn parse_credentials_file(file_with_path: &str) -> Result<HashMap<String, AWSCredentials>, AWSError> {
    let path = Path::new(&file_with_path);
    let display = path.display();

    match fs::metadata(&path) {
        Err(_) => return Err(AWSError::new("Couldn't stat credentials file.")),
        Ok(metadata) => {
            if !metadata.is_file() {
                return Err(AWSError::new("Couldn't open file."));
            }
        }
    };

    let file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
        Ok(opened_file) => opened_file,
    };

    let profile_regex = Regex::new(r"^\[([^\]]+)\]$").unwrap();
    let mut profiles: HashMap<String, AWSCredentials> = HashMap::new();
    let mut access_key: Option<String> = None;
    let mut secret_key: Option<String> = None;
    let mut profile_name: Option<String> = None;

    let file_lines = BufReader::new(&file);
    for line in file_lines.lines() {

        let unwrapped_line : String = line.unwrap();

        // skip comments
        if unwrapped_line.starts_with('#') {
            continue;
        }

        // handle the opening of named profile blocks
        if profile_regex.is_match(&unwrapped_line) {

            if profile_name.is_some() && access_key.is_some() && secret_key.is_some() {
                let creds = AWSCredentials::new(access_key.unwrap(), secret_key.unwrap(), None, in_ten_minutes());
                profiles.insert(profile_name.unwrap(), creds);
            }

            access_key = None;
            secret_key = None;

            let caps = profile_regex.captures(&unwrapped_line).unwrap();
            profile_name = Some(caps.at(1).unwrap().to_string());
            continue;
        }

        // otherwise look for key=value pairs we care about
        let lower_case_line = unwrapped_line.to_ascii_lowercase().to_string();

        if lower_case_line.contains("aws_access_key_id") {
            if access_key.is_none() {
                let v: Vec<&str> = unwrapped_line.split("=").collect();
                if v.len() > 0 {
                    access_key = Some(v[1].trim_matches(' ').to_string());
                }
            }
        } else if lower_case_line.contains("aws_secret_access_key") {
            if secret_key.is_none() {
                let v: Vec<&str> = unwrapped_line.split("=").collect();
                if v.len() > 0 {
                    secret_key = Some(v[1].trim_matches(' ').to_string());
                }
            }
        }

        // we could potentially explode here to indicate that the file is invalid

    }

    if profile_name.is_some() && access_key.is_some() && secret_key.is_some() {
        let creds = AWSCredentials::new(access_key.unwrap(), secret_key.unwrap(), None, in_ten_minutes());
        profiles.insert(profile_name.unwrap(), creds);
    }

    if profiles.is_empty() {
        return Err(AWSError::new("No credentials found."));
    }

    Ok(profiles)
}

pub struct IAMRoleCredentialsProvider {
    credentials: Option<AWSCredentials>
}

impl AWSCredentialsProvider for IAMRoleCredentialsProvider {
    fn new() -> IAMRoleCredentialsProvider {
        IAMRoleCredentialsProvider { credentials: None }
    }

    fn get_credentials(&mut self) -> Result<&AWSCredentials, &str> {
        if self.credentials.is_none() || self.credentials.as_ref().unwrap().credentials_are_expired() {
            // TODO: backoff and retry on failure.

            //println!("Calling IAM metadata");
            // for "real" use: http://169.254.169.254/latest/meta-data/iam/security-credentials/
            let mut address : String = "http://169.254.169.254/latest/meta-data/iam/security-credentials".to_string();
            let client = Client::new();
            let mut response;
            match client.get(&address)
                .header(Connection::close()).send() {
                    Err(_) => return Err("Couldn't connect to metadata service"), // add Why?
                    Ok(received_response) => response = received_response
                };

            let mut body = String::new();
            match response.read_to_string(&mut body) {
                Err(_) => return Err("Didn't get a parsable response body from metadata service"),
                Ok(_) => (),
            };

            address.push_str("/");
            address.push_str(&body);
            body = String::new();
            match client.get(&address)
                .header(Connection::close()).send() {
                    Err(_) => return Err("Didn't get a parseable response body from instance role details"),
                    Ok(received_response) => response = received_response
                };

            match response.read_to_string(&mut body) {
                Err(_) => return Err("Had issues with reading iam role response: {}"),
                Ok(_) => (),
            };

            let json_object : Json;
            match Json::from_str(&body) {
                Err(_) => return Err("Couldn't parse metadata response body."),
                Ok(val) => json_object = val
            };

            let mut access_key;
            match json_object.find("AccessKeyId") {
                None => return Err("Couldn't find AccessKeyId in response."),
                Some(val) => access_key = val.to_string().replace("\"", "")
            };

            let mut secret_key;
            match json_object.find("SecretAccessKey") {
                None => return Err("Couldn't find SecretAccessKey in response."),
                Some(val) => secret_key = val.to_string().replace("\"", "")
            };

            let mut expiration;
            match json_object.find("Expiration") {
                None => return Err("Couldn't find Expiration in response."),
                Some(val) => expiration = val.to_string().replace("\"", "")
            };

            let mut expiration_time;
            match expiration.parse::<DateTime<UTC>>() {
                Err(why) => panic!("Kabloey on parse: {}", why),
                Ok(val) => expiration_time = val
            };

            let mut token_from_response;
            match json_object.find("Token") {
                None => return Err("Couldn't find Token in response."),
                Some(val) => token_from_response = val.to_string().replace("\"", "")
            };

            self.credentials = Some(AWSCredentials::new(access_key, secret_key, Some(token_from_response.to_string()), expiration_time));
        }

		Ok(&self.credentials.as_ref().unwrap())
	}
}

#[derive(Debug, Clone)]
pub struct DefaultAWSCredentialsProviderChain {
    credentials: Option<AWSCredentials>
}

// Chain the providers:
impl DefaultAWSCredentialsProviderChain {
    pub fn new() -> DefaultAWSCredentialsProviderChain {
        DefaultAWSCredentialsProviderChain { credentials: None }
    }

    pub fn get_credentials(&mut self) -> Result<&AWSCredentials, AWSError> {
        if self.credentials.is_none() || self.credentials.as_ref().unwrap().credentials_are_expired() {
            // fetch creds in order: env, file, IAM

            if let Ok(creds) = EnvironmentCredentialsProvider::new().get_credentials() {
                //println!("Found creds in env");
                self.credentials = Some(creds.clone());
            } else if let Ok(creds) = ProfileCredentialsProvider::new().get_credentials() {
                //println!("Found creds in file");
                self.credentials = Some(creds.clone());
            } else if let Ok(creds) = IAMRoleCredentialsProvider::new().get_credentials() {
                //println!("Found creds via iam");
                self.credentials = Some(creds.clone());
            } else {
               panic!("Couldn't find AWS credentials in environment, default credential file location or IAM role.")
            }
        }
        Ok(self.credentials.as_ref().unwrap())
    }
}

fn in_ten_minutes() -> DateTime<UTC> {
    UTC::now() + Duration::seconds(600)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::BufReader;
    use std::fs::File;
    use error::*;
    use regex::*;

    #[test]
    fn parse_credentials_file_default_profile() {
        let result = super::parse_credentials_file("tests/sample-data/default_profile_credentials");
        assert!(result.is_ok());

        let profiles = result.ok().unwrap();
        assert_eq!(profiles.len(), 1);

        let default_profile = profiles.get("default").unwrap();
        assert_eq!(default_profile.get_aws_access_key_id(), "foo");
        assert_eq!(default_profile.get_aws_secret_key(), "bar");
    }

    #[test]
    fn parse_credentials_file_multiple_profiles() {
        let result = super::parse_credentials_file("tests/sample-data/multiple_profile_credentials");
        assert!(result.is_ok());

        let profiles = result.ok().unwrap();
        assert_eq!(profiles.len(), 2);

        let foo_profile = profiles.get("foo").unwrap();
        assert_eq!(foo_profile.get_aws_access_key_id(), "foo_access_key");
        assert_eq!(foo_profile.get_aws_secret_key(), "foo_secret_key");

        let bar_profile = profiles.get("bar").unwrap();
        assert_eq!(bar_profile.get_aws_access_key_id(), "bar_access_key");
        assert_eq!(bar_profile.get_aws_secret_key(), "bar_secret_key");

    }

    #[test]
    fn profile_credentials_provider_happy_path() {
        let mut provider = ProfileCredentialsProvider::with_configuration("foo","tests/sample-data/multiple_profile_credentials");
        let result = provider.get_credentials();

        assert!(result.is_ok());

        let creds = result.ok().unwrap();
        assert_eq!(creds.get_aws_access_key_id(), "foo_access_key");
        assert_eq!(creds.get_aws_secret_key(), "foo_secret_key");
     }

    #[test]
    fn profile_credentials_provider_bad_profile() {
        let mut provider = ProfileCredentialsProvider::with_configuration("not_a_profile","tests/sample-data/multiple_profile_credentials");
        let result = provider.get_credentials();

        assert!(result.is_err());
        assert_eq!(result.err(), Some("profile not found"));
     }

    #[test]
    fn existing_file_no_credentials() {
        let result = super::parse_credentials_file("tests/sample-data/no_credentials");
        assert_eq!(result.err(), Some(AWSError::new("No credentials found.")))
    }

    #[test]
    fn parse_credentials_bad_path() {
        let result = super::parse_credentials_file("/bad/file/path");
        assert_eq!(result.err(), Some(AWSError::new("Couldn't stat credentials file.")));
    }

    #[test]
    fn parse_credentials_directory_path() {
        let result = super::parse_credentials_file("tests/");
        assert_eq!(result.err(), Some(AWSError::new("Couldn't open file.")));
    }

}

<|endoftext|>


<commit_before>#![feature(test)]
extern crate pleco;
extern crate test;
extern crate rand;

use self::pleco::tools::tt::*;
use self::pleco::core::piece_move::BitMove;
use self::test::{black_box, Bencher};
use pleco::tools::prng::PRNG;

#[bench]
fn tt_bench_single_thread_insert_empty(b: &mut Bencher) {
    let tt = TranspositionTable::new_num_entries(400_000);
    let mut prng = PRNG::init(1120246457);
    b.iter(|| {
        let key = prng.rand();
        let (_found, entry) = tt.probe(key);
        entry.place(key, BitMove::new(0x555), 3, 4, 3, NodeBound::Exact);
    })
}

#[bench]
fn tt_bench_single_thread_insert_full(b: &mut Bencher) {
    let tt = TranspositionTable::new_num_entries(400_000);
    let mut prng = PRNG::init(2500123475);

    for x in 0..1_600_000 {
        let key = prng.rand();
        let (_found, entry) = tt.probe(key);
        entry.place(key, BitMove::new(0x555), 3, 4, 3, NodeBound::Exact);
        entry.depth = x as u8;
    }

    b.iter(|| {
        let key = prng.rand();
        let (_found, entry) = tt.probe(key);
        entry.place(key, BitMove::new(0x555), 3, 4, key as u8, NodeBound::Exact);
    })
}

#[bench]
fn tt_bench_single_thread_lookup_sparse(b: &mut Bencher) {
    let seed: u64 = 7736583456;
    tt_single_thread_lookup(b,200_000, 20_000, seed);
}

#[bench]
fn tt_bench_single_thread_lookup_dense(b: &mut Bencher) {
    let seed: u64 = 80474222;
    tt_single_thread_lookup(b,200_000, 500_000, seed);
}

#[inline]
fn tt_single_thread_lookup(b: &mut Bencher, num_entries: usize, placements: u64, seed: u64) {
    let tt = TranspositionTable::new_num_entries(num_entries);
    let mut prng = PRNG::init(seed);

    for x in 0..placements {
        let key = prng.rand();
        let (_found, entry) = tt.probe(key);
        entry.place(key, BitMove::new(0x555), 3, 4, 3, NodeBound::Exact);
        entry.depth = x as u8;
    }

    b.iter(|| {
        let key = prng.rand();
        let (_found, _entry) = black_box(tt.probe(key));
    });
}<commit_msg>More fixes<commit_after>#![feature(test)]
extern crate pleco;
extern crate test;
extern crate rand;

use self::pleco::tools::tt::*;
use self::pleco::core::piece_move::BitMove;
use self::test::{black_box, Bencher};
use pleco::tools::prng::PRNG;

#[bench]
fn tt_bench_single_thread_insert_empty(b: &mut Bencher) {
    let tt = TranspositionTable::new_num_entries(400_000);
    let mut prng = PRNG::init(1120246457);
    b.iter(|| {
        let key = prng.rand();
        let (_found, entry) = tt.probe(key);
        entry.place(key, BitMove::new(0x555), 3, 4, 3, NodeBound::Exact, tt.time_age());
    })
}

#[bench]
fn tt_bench_single_thread_insert_full(b: &mut Bencher) {
    let tt = TranspositionTable::new_num_entries(400_000);
    let mut prng = PRNG::init(2500123475);

    for x in 0..1_600_000 {
        let key = prng.rand();
        let (_found, entry) = tt.probe(key);
        entry.place(key, BitMove::new(0x555), 3, 4, 3, NodeBound::Exact, tt.time_age());
        entry.depth = x as i8;
    }

    b.iter(|| {
        let key = prng.rand();
        let (_found, entry) = tt.probe(key);
        entry.place(key, BitMove::new(0x555), 3, 4, key as i8, NodeBound::Exact, tt.time_age());
    })
}

#[bench]
fn tt_bench_single_thread_lookup_sparse(b: &mut Bencher) {
    let seed: u64 = 7736583456;
    tt_single_thread_lookup(b,200_000, 20_000, seed);
}

#[bench]
fn tt_bench_single_thread_lookup_dense(b: &mut Bencher) {
    let seed: u64 = 80474222;
    tt_single_thread_lookup(b,200_000, 500_000, seed);
}

#[inline]
fn tt_single_thread_lookup(b: &mut Bencher, num_entries: usize, placements: u64, seed: u64) {
    let tt = TranspositionTable::new_num_entries(num_entries);
    let mut prng = PRNG::init(seed);

    for x in 0..placements {
        let key = prng.rand();
        let (_found, entry) = tt.probe(key);
        entry.place(key, BitMove::new(0x555), 3, 4, 3, NodeBound::Exact, tt.time_age());
        entry.depth = x as i8;
    }

    b.iter(|| {
        let key = prng.rand();
        let (_found, _entry) = black_box(tt.probe(key));
    });
}<|endoftext|>


<commit_before>extern crate string_telephone;
use std::io::net::ip::{Ipv4Addr, SocketAddr};
use std::io;

use string_telephone::client;
use string_telephone::packet::{Packet, PacketMessage};

fn main () {
    match client::Client::connect(SocketAddr {ip: Ipv4Addr(0, 0, 0, 0), port: 0}, SocketAddr {ip: Ipv4Addr(127, 0, 0, 1), port: 6666}, 121) {
        Ok(ref mut connection) => {
            println!("Connected!")

            let (send, recv) = channel();

            spawn(proc() {
                loop {
                    let input = io::stdin().read_line()
                                           .ok()
                                           .expect("Failed to read line");

                    send.send(input);
                }
            });

            loop {
                match connection.poll() {
                    Some(message) => {
                        match message.packet_type {
                            PacketMessage => {
                                println!("{}", message.packet_content.into_ascii().into_string())
                            },
                            _ => ()
                        }
                    },
                    None => break
                };

                match recv.try_recv() {
                    Ok(text) => {
                        connection.send(&Packet::message(121, text.into_bytes()));
                    },
                    Err(_) => ()
                }
            }
        },
        Err(e) => {
            println!("Error {}", e)
        }
    };
}
<commit_msg>Corrected issue where not receiving a response killed the client<commit_after>extern crate string_telephone;
use std::io::net::ip::{Ipv4Addr, SocketAddr};
use std::io;

use string_telephone::client;
use string_telephone::packet::{Packet, PacketMessage};

fn main () {
    match client::Client::connect(SocketAddr {ip: Ipv4Addr(0, 0, 0, 0), port: 0}, SocketAddr {ip: Ipv4Addr(127, 0, 0, 1), port: 6666}, 121) {
        Ok(ref mut connection) => {
            println!("Connected!")

            let (send, recv) = channel();

            spawn(proc() {
                loop {
                    let input = io::stdin().read_line()
                                           .ok()
                                           .expect("Failed to read line");

                    send.send(input);
                }
            });

            loop {
                match connection.poll() {
                    Some(message) => {
                        match message.packet_type {
                            PacketMessage => {
                                println!("{}", message.packet_content.into_ascii().into_string())
                            },
                            _ => ()
                        }
                    },
                    None => ()
                };

                match recv.try_recv() {
                    Ok(text) => {
                        connection.send(&Packet::message(121, text.into_bytes()));
                    },
                    Err(_) => ()
                }
            }
        },
        Err(e) => {
            println!("Error {}", e)
        }
    };
}
<|endoftext|>


<commit_before>use super::{ffi, safe};
use std::{fmt, cmp};
use std::ffi::CStr;
use std::error::Error;

pub const MAX_DIAGNOSTIC_MESSAGE_SIZE: usize = 1024;

/// ODBC Diagnostic Record
///
/// The `description` method of the `std::error::Error` trait only returns the message. Use
/// `std::fmt::Display` to retrive status code and other information.
pub struct DiagnosticRecord {
    // All elements but the last one, may not be nul. The last one must be nul.
    state: [ffi::SQLCHAR; ffi::SQL_SQLSTATE_SIZE + 1],
    // Must at least contain one nul
    message: [ffi::SQLCHAR; MAX_DIAGNOSTIC_MESSAGE_SIZE],
    // The numbers of characters in message not nul
    message_length: ffi::SQLSMALLINT,
    native_error: ffi::SQLINTEGER,
    message_string: String,
}

impl DiagnosticRecord {
    /// get raw state string data.
    pub fn get_raw_state(&self) -> &[u8] {
        &self.state
    }
    /// get raw diagnostics message for avoiding encoding error.
    pub fn get_raw_message(&self) -> &[u8] {
        &self.message[0..self.message_length as usize]
    }
    /// get native odbc error number
    pub fn get_native_error(&self) -> i32 {
        self.native_error
    }
    /// constructs an empty diagnostics message.
    /// this is needed for errors where the driver doesn't return any diagnostics info.
    pub fn empty() -> DiagnosticRecord {
        let message = b"No SQL-driver error information available.";
        let mut rec = DiagnosticRecord {
            state: b"HY000\0".clone(),
            message: [0u8; MAX_DIAGNOSTIC_MESSAGE_SIZE],
            native_error: -1,
            message_length: message.len() as ffi::SQLSMALLINT,
            message_string: String::from(""),
        };
        rec.message[..message.len()].copy_from_slice(message);
        rec
    }
}

impl fmt::Display for DiagnosticRecord {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Todo: replace unwrap with `?` in Rust 1.17
        let state = CStr::from_bytes_with_nul(&self.state).unwrap();

        write!(
            f,
            "State: {}, Native error: {}, Message: {}",
            state.to_str().unwrap(),
            self.native_error,
            self.message_string,
        )
    }
}

impl fmt::Debug for DiagnosticRecord {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}

impl Error for DiagnosticRecord {
    fn description(&self) -> &str {
        &self.message_string
    }
    fn cause(&self) -> Option<&Error> {
        None
    }
}

/// Allows retriving a diagnostic record, describing errors (or lack thereof) during the last
/// operation.
pub trait GetDiagRec {
    /// Retrieves a diagnostic record
    ///
    /// `record_number` - Record numbers start at one. If you pass an number < 1 the function will
    /// panic. If no record is available for the number specified none is returned.
    fn get_diag_rec(&self, record_number: i16) -> Option<DiagnosticRecord>;
}

impl<D> GetDiagRec for D
where
    D: safe::Diagnostics,
{
    fn get_diag_rec(&self, record_number: i16) -> Option<(DiagnosticRecord)> {
        use safe::ReturnOption::*;
        let mut message = [0; MAX_DIAGNOSTIC_MESSAGE_SIZE];
        match self.diagnostics(record_number, &mut message) {
            Success(result) | Info(result) => {
                // The message could be larger than the supplied buffer, so we need to limit the message length to the buffer size.
                let mut message_length = cmp::min(result.text_length, MAX_DIAGNOSTIC_MESSAGE_SIZE as ffi::SQLSMALLINT - 1);
                // Some drivers pad the message with null-chars (which is still a valid C string, but not a valid Rust string).
                while message_length > 0 && message[(message_length - 1) as usize] == 0 {
                    message_length = message_length - 1;
                }
                Some(DiagnosticRecord {
                    state: result.state,
                    native_error: result.native_error,
                    message_length,
                    message,
                    message_string: unsafe { ::environment::OS_ENCODING.decode(&message[0..(message_length as usize + 1)]).0.into_owned()},
                })
            }
            NoData(()) => None,
            Error(()) => panic!("Diagnostics returned error for record number {}. Record numbers have to be at least 1.", record_number),
        }
    }
}

#[cfg(test)]
mod test {

    use super::*;

    impl DiagnosticRecord {
        fn new() -> DiagnosticRecord {
            DiagnosticRecord {
                state: [0u8; ffi::SQL_SQLSTATE_SIZE + 1],
                message: [0u8; MAX_DIAGNOSTIC_MESSAGE_SIZE],
                native_error: 0,
                message_length: 0,
                message_string: String::from(""),
            }
        }
    }

    #[test]
    fn formatting() {

        // build diagnostic record
        let message = b"[Microsoft][ODBC Driver Manager] Function sequence error\0";
        let mut rec = DiagnosticRecord::new();
        rec.state = b"HY010\0".clone();
        rec.message_string = CStr::from_bytes_with_nul(message).unwrap().to_str().unwrap().to_owned();
        rec.message_length = 56;
        for i in 0..(rec.message_length as usize) {
            rec.message[i] = message[i];
        }

        // test formatting
        assert_eq!(
            format!("{}", rec),
            "State: HY010, Native error: 0, Message: [Microsoft][ODBC Driver Manager] \
             Function sequence error"
        );
    }
}
<commit_msg>Prevent trailing zero byte<commit_after>use super::{ffi, safe};
use std::{fmt, cmp};
use std::ffi::CStr;
use std::error::Error;

pub const MAX_DIAGNOSTIC_MESSAGE_SIZE: usize = 1024;

/// ODBC Diagnostic Record
///
/// The `description` method of the `std::error::Error` trait only returns the message. Use
/// `std::fmt::Display` to retrive status code and other information.
pub struct DiagnosticRecord {
    // All elements but the last one, may not be nul. The last one must be nul.
    state: [ffi::SQLCHAR; ffi::SQL_SQLSTATE_SIZE + 1],
    // Must at least contain one nul
    message: [ffi::SQLCHAR; MAX_DIAGNOSTIC_MESSAGE_SIZE],
    // The numbers of characters in message not nul
    message_length: ffi::SQLSMALLINT,
    native_error: ffi::SQLINTEGER,
    message_string: String,
}

impl DiagnosticRecord {
    /// get raw state string data.
    pub fn get_raw_state(&self) -> &[u8] {
        &self.state
    }
    /// get raw diagnostics message for avoiding encoding error.
    pub fn get_raw_message(&self) -> &[u8] {
        &self.message[0..self.message_length as usize]
    }
    /// get native odbc error number
    pub fn get_native_error(&self) -> i32 {
        self.native_error
    }
    /// constructs an empty diagnostics message.
    /// this is needed for errors where the driver doesn't return any diagnostics info.
    pub fn empty() -> DiagnosticRecord {
        let message = b"No SQL-driver error information available.";
        let mut rec = DiagnosticRecord {
            state: b"HY000\0".clone(),
            message: [0u8; MAX_DIAGNOSTIC_MESSAGE_SIZE],
            native_error: -1,
            message_length: message.len() as ffi::SQLSMALLINT,
            message_string: String::from(""),
        };
        rec.message[..message.len()].copy_from_slice(message);
        rec
    }
}

impl fmt::Display for DiagnosticRecord {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Todo: replace unwrap with `?` in Rust 1.17
        let state = CStr::from_bytes_with_nul(&self.state).unwrap();

        write!(
            f,
            "State: {}, Native error: {}, Message: {}",
            state.to_str().unwrap(),
            self.native_error,
            self.message_string,
        )
    }
}

impl fmt::Debug for DiagnosticRecord {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}

impl Error for DiagnosticRecord {
    fn description(&self) -> &str {
        &self.message_string
    }
    fn cause(&self) -> Option<&Error> {
        None
    }
}

/// Allows retriving a diagnostic record, describing errors (or lack thereof) during the last
/// operation.
pub trait GetDiagRec {
    /// Retrieves a diagnostic record
    ///
    /// `record_number` - Record numbers start at one. If you pass an number < 1 the function will
    /// panic. If no record is available for the number specified none is returned.
    fn get_diag_rec(&self, record_number: i16) -> Option<DiagnosticRecord>;
}

impl<D> GetDiagRec for D
where
    D: safe::Diagnostics,
{
    fn get_diag_rec(&self, record_number: i16) -> Option<(DiagnosticRecord)> {
        use safe::ReturnOption::*;
        let mut message = [0; MAX_DIAGNOSTIC_MESSAGE_SIZE];
        match self.diagnostics(record_number, &mut message) {
            Success(result) | Info(result) => {
                // The message could be larger than the supplied buffer, so we need to limit the message length to the buffer size.
                let mut message_length = cmp::min(result.text_length, MAX_DIAGNOSTIC_MESSAGE_SIZE as ffi::SQLSMALLINT - 1);
                // Some drivers pad the message with null-chars (which is still a valid C string, but not a valid Rust string).
                while message_length > 0 && message[(message_length - 1) as usize] == 0 {
                    message_length = message_length - 1;
                }
                Some(DiagnosticRecord {
                    state: result.state,
                    native_error: result.native_error,
                    message_length,
                    message,
                    message_string: unsafe {
                        ::environment::OS_ENCODING.decode(&message[0..message_length as usize]).0.into_owned()
                    },
                })
            }
            NoData(()) => None,
            Error(()) => panic!("Diagnostics returned error for record number {}. Record numbers have to be at least 1.", record_number),
        }
    }
}

#[cfg(test)]
mod test {

    use super::*;

    impl DiagnosticRecord {
        fn new() -> DiagnosticRecord {
            DiagnosticRecord {
                state: [0u8; ffi::SQL_SQLSTATE_SIZE + 1],
                message: [0u8; MAX_DIAGNOSTIC_MESSAGE_SIZE],
                native_error: 0,
                message_length: 0,
                message_string: String::from(""),
            }
        }
    }

    #[test]
    fn formatting() {

        // build diagnostic record
        let message = b"[Microsoft][ODBC Driver Manager] Function sequence error\0";
        let mut rec = DiagnosticRecord::new();
        rec.state = b"HY010\0".clone();
        rec.message_string = CStr::from_bytes_with_nul(message).unwrap().to_str().unwrap().to_owned();
        rec.message_length = 56;
        for i in 0..(rec.message_length as usize) {
            rec.message[i] = message[i];
        }

        // test formatting
        assert_eq!(
            format!("{}", rec),
            "State: HY010, Native error: 0, Message: [Microsoft][ODBC Driver Manager] \
             Function sequence error"
        );
    }
}
<|endoftext|>


<commit_before>//  notty is a new kind of terminal emulator.
//  Copyright (C) 2016 Wayne Warren
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
use std::borrow::Cow;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::sync::atomic::Ordering::Relaxed;
use std::{error, fmt, io, result};

use notty::cfg::{SCROLLBACK, TAB_STOP};
use notty::datatypes::{CodeGroup, Color, ConfigStyle};
use notty::terminal::Styles;
use notty_cairo::{Config as CairoConfig, TrueColor, PALETTE};

use super::Config;

use toml::{self, Table, Value};

#[derive(Debug)]
pub enum ConfigError {
    Io(io::Error),
    Parse(String), // TODO: once https://github.com/alexcrichton/toml-rs/issue#69
                   // is closed, change this to Parse(toml::ParserError)
}

impl fmt::Display for ConfigError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConfigError::Io(ref err) => write!(f, "IO Error: {}", err),
            ConfigError::Parse(ref string) => write!(f, "{}", string),
        }
    }
}

impl error::Error for ConfigError {
    fn description(&self) -> &str {
        match *self {
            ConfigError::Io(ref err) => err.description(),
            ConfigError::Parse(ref string) => &string,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ConfigError::Io(ref err) => err.cause(),
            ConfigError::Parse(_) => None,
        }
    }
}

impl From<io::Error> for ConfigError {
    fn from(err: io::Error) -> ConfigError{
        ConfigError::Io(err)
    }
}

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

fn update_general(shell: &mut Cow<'static, str>, font: &mut Cow<'static, str>, table: &Table) {
    for (k, v) in table.iter() {
        match &k[..] {
            "shell" => {
                *shell = Cow::Owned(String::from(v.as_str().unwrap()))
            }
            "font" => {
                *font = Cow::Owned(String::from(v.as_str().unwrap()))
            }
            "tabstop" => TAB_STOP.store(v.as_integer().unwrap() as usize, Relaxed),
            "scrollback" => SCROLLBACK.store(v.as_integer().unwrap() as isize, Relaxed),
            _ => {},
        };
    }
}

fn update_colors(config: &mut CairoConfig, table: &Table) {
    for (k, v) in table.iter() {
        match &k[..] {
            "background"    => config.bg_color = convert_tomlv_to_color(v),
            "foreground"    => config.fg_color = convert_tomlv_to_color(v),
            "cursor"        => config.cursor_color = convert_tomlv_to_color(v),
            "palette"       => config.palette = convert_tomlv_to_palette(v),
            _               => {},
        };
    }
}

fn update_syntax(config: &mut CairoConfig, table: &Table) {
    for (k, v) in table.iter() {
        match &k[..] {
            "plain" => {
                let styles = convert_tomlv_to_styles(v.as_table().unwrap());
                config.styles.insert(ConfigStyle::Plain, styles);
            }
            "code"  => update_syntax_code(config, v.as_table().unwrap()),
            _       => {}
        }
    }
}

fn update_syntax_code(config: &mut CairoConfig, table: &Table) {
    for (k, v) in table.iter() {
        let code_group = match &k[..] {
            "comment"       => CodeGroup::Comment,
            "documentation" => CodeGroup::Documentation,
            "error"         => CodeGroup::Error,
            "identifier"    => CodeGroup::Identifier,
            "keyword"       => CodeGroup::Keyword,
            "literal"       => CodeGroup::Literal,
            "macro"         => CodeGroup::Macro,
            "special"       => CodeGroup::Special,
            "todo"          => CodeGroup::Todo,
            "type"          => CodeGroup::Type,
            _               => continue
        };
        let styles = convert_tomlv_to_styles(v.as_table().unwrap());
        config.styles.insert(ConfigStyle::CodeGroup(code_group), styles);
    }
}

/// Update &config from toml file identified by path string.
pub fn update_from_file<P: AsRef<Path>>(cfg: &mut Config, path: P) -> Result<()> {
    let table = try!(read_toml_file(path));

    for (k, v) in table.iter() {
        match &k[..] {
            "color"     => update_colors(&mut cfg.cairo, v.as_table().unwrap()),
            "general"   => update_general(&mut cfg.shell, &mut cfg.cairo.font, v.as_table().unwrap()),
            "syntax"    => update_syntax(&mut cfg.cairo, v.as_table().unwrap()),
            _ => {},
        };
    }
    Ok(())
}

fn convert_tomlv_to_color(value: &Value) -> TrueColor {
    match *value {
        Value::String(ref string)   => (
            u8::from_str_radix(&string[0..2], 16).unwrap(),
            u8::from_str_radix(&string[2..4], 16).unwrap(),
            u8::from_str_radix(&string[4..6], 16).unwrap(),
        ),
        Value::Array(ref array)     => (
            array[0].as_integer().unwrap() as u8,
            array[1].as_integer().unwrap() as u8,
            array[2].as_integer().unwrap() as u8
        ),
        _                           => panic!()
    }
}

fn convert_tomlv_to_palette(value: &Value) -> [TrueColor; 256] {
    let mut palette = PALETTE;
    for (key, value) in value.as_table().unwrap() {
        let n: usize = key.trim_left_matches("color").parse().unwrap();
        palette[n.saturating_sub(1)] = convert_tomlv_to_color(value);
    }
    palette
}

fn convert_tomlv_to_styles(table: &Table) -> Styles {
    let fg_color = table.get("foreground").map_or(Color::Default, |v| {
        let (r, g, b) = convert_tomlv_to_color(v);
        Color::True(r, g, b)
    });
    let bg_color = table.get("background").map_or(Color::Default, |v| {
        let (r, g, b) = convert_tomlv_to_color(v);
        Color::True(r, g, b)
    });
    let opacity = table.get("opacity").map_or(0xff, |v| v.as_integer().unwrap() as u8);
    let (underline, double_underline) = match table.get("underline") {
        Some(&Value::Boolean(true)) | Some(&Value::Integer(1))  => (true, false),
        Some(&Value::Integer(2))                                => (true, true),
        _                                                       => (false, false),
    };
    let bold = table.get("bold").map_or(false, |v| v.as_bool().unwrap());
    let italic = table.get("italic").map_or(false, |v| v.as_bool().unwrap());
    let strikethrough = table.get("strikethrough").map_or(false, |v| v.as_bool().unwrap());
    let inverted = table.get("inverted").map_or(false, |v| v.as_bool().unwrap());
    let blink = table.get("blink").map_or(false, |v| v.as_bool().unwrap());
    Styles {
        fg_color: fg_color,
        bg_color: bg_color,
        opacity: opacity,
        underline: underline,
        double_underline: double_underline,
        bold: bold,
        italic: italic,
        strikethrough: strikethrough,
        inverted: inverted,
        blink: blink,
    }
}

fn read_toml_file<P: AsRef<Path>>(path: P) -> Result<Table> {
    let mut file = try!(File::open(&path));
    let mut source = String::new();
    try!(file.read_to_string(&mut source));
    parse_toml(&source.to_string(), path)
}

fn parse_toml<P: AsRef<Path>>(toml_string: &String, toml_path: P)
                              -> Result<Table> {
    let mut parser = toml::Parser::new(toml_string);
    match parser.parse() {
        Some(toml) => {
            Ok(toml)
        }
        None => {
            let mut error_string = String::new();
            for err in &parser.errors {
                let (loline, locol) = parser.to_linecol(err.lo);
                let (hiline, hicol) = parser.to_linecol(err.hi);
                error_string = format!("{}\n{}:{}:{}:{}:{} error: {}",
                        error_string, toml_path.as_ref().display(), loline,
                        locol, hiline, hicol, err.desc);
            }
            Err(ConfigError::Parse(error_string))
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    use super::super::Config;
    use notty_cairo::Config as CairoConfig;

    fn test_default_config(config: &CairoConfig) {
        assert_eq!(config.font, "Inconsolata 10");
        assert_eq!(config.fg_color, (0xff,0xff,0xff));
        assert_eq!(config.bg_color, (0x00,0x00,0x00));
        assert_eq!(config.cursor_color, (0xbb,0xbb,0xbb));
        assert_eq!(config.palette[0], (0x00,0x00,0x00));
        assert_eq!(config.palette[5], (0xff,0x55,0xff));
    }

    #[test]
    fn test_default() {
        let config = CairoConfig::default();
        test_default_config(&config);
    }

    #[test]
    fn test_update_from_file() {
        let mut config = Config::default();
        test_default_config(&config.cairo);

        let update_path = "resources/update-config.toml".to_string();
        update_from_file(&mut config, &update_path).unwrap();
        assert_eq!(config.cairo.font, "Liberation Mono 8");
    }
}
<commit_msg>Fix test in scaffolding.<commit_after>//  notty is a new kind of terminal emulator.
//  Copyright (C) 2016 Wayne Warren
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
use std::borrow::Cow;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::sync::atomic::Ordering::Relaxed;
use std::{error, fmt, io, result};

use notty::cfg::{SCROLLBACK, TAB_STOP};
use notty::datatypes::{CodeGroup, Color, ConfigStyle};
use notty::terminal::Styles;
use notty_cairo::{Config as CairoConfig, TrueColor, PALETTE};

use super::Config;

use toml::{self, Table, Value};

#[derive(Debug)]
pub enum ConfigError {
    Io(io::Error),
    Parse(String), // TODO: once https://github.com/alexcrichton/toml-rs/issue#69
                   // is closed, change this to Parse(toml::ParserError)
}

impl fmt::Display for ConfigError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConfigError::Io(ref err) => write!(f, "IO Error: {}", err),
            ConfigError::Parse(ref string) => write!(f, "{}", string),
        }
    }
}

impl error::Error for ConfigError {
    fn description(&self) -> &str {
        match *self {
            ConfigError::Io(ref err) => err.description(),
            ConfigError::Parse(ref string) => &string,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ConfigError::Io(ref err) => err.cause(),
            ConfigError::Parse(_) => None,
        }
    }
}

impl From<io::Error> for ConfigError {
    fn from(err: io::Error) -> ConfigError{
        ConfigError::Io(err)
    }
}

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

fn update_general(shell: &mut Cow<'static, str>, font: &mut Cow<'static, str>, table: &Table) {
    for (k, v) in table.iter() {
        match &k[..] {
            "shell" => {
                *shell = Cow::Owned(String::from(v.as_str().unwrap()))
            }
            "font" => {
                *font = Cow::Owned(String::from(v.as_str().unwrap()))
            }
            "tabstop" => TAB_STOP.store(v.as_integer().unwrap() as usize, Relaxed),
            "scrollback" => SCROLLBACK.store(v.as_integer().unwrap() as isize, Relaxed),
            _ => {},
        };
    }
}

fn update_colors(config: &mut CairoConfig, table: &Table) {
    for (k, v) in table.iter() {
        match &k[..] {
            "background"    => config.bg_color = convert_tomlv_to_color(v),
            "foreground"    => config.fg_color = convert_tomlv_to_color(v),
            "cursor"        => config.cursor_color = convert_tomlv_to_color(v),
            "palette"       => config.palette = convert_tomlv_to_palette(v),
            _               => {},
        };
    }
}

fn update_syntax(config: &mut CairoConfig, table: &Table) {
    for (k, v) in table.iter() {
        match &k[..] {
            "plain" => {
                let styles = convert_tomlv_to_styles(v.as_table().unwrap());
                config.styles.insert(ConfigStyle::Plain, styles);
            }
            "code"  => update_syntax_code(config, v.as_table().unwrap()),
            _       => {}
        }
    }
}

fn update_syntax_code(config: &mut CairoConfig, table: &Table) {
    for (k, v) in table.iter() {
        let code_group = match &k[..] {
            "comment"       => CodeGroup::Comment,
            "documentation" => CodeGroup::Documentation,
            "error"         => CodeGroup::Error,
            "identifier"    => CodeGroup::Identifier,
            "keyword"       => CodeGroup::Keyword,
            "literal"       => CodeGroup::Literal,
            "macro"         => CodeGroup::Macro,
            "special"       => CodeGroup::Special,
            "todo"          => CodeGroup::Todo,
            "type"          => CodeGroup::Type,
            _               => continue
        };
        let styles = convert_tomlv_to_styles(v.as_table().unwrap());
        config.styles.insert(ConfigStyle::CodeGroup(code_group), styles);
    }
}

/// Update &config from toml file identified by path string.
pub fn update_from_file<P: AsRef<Path>>(cfg: &mut Config, path: P) -> Result<()> {
    let table = try!(read_toml_file(path));

    for (k, v) in table.iter() {
        match &k[..] {
            "color"     => update_colors(&mut cfg.cairo, v.as_table().unwrap()),
            "general"   => update_general(&mut cfg.shell, &mut cfg.cairo.font, v.as_table().unwrap()),
            "syntax"    => update_syntax(&mut cfg.cairo, v.as_table().unwrap()),
            _ => {},
        };
    }
    Ok(())
}

fn convert_tomlv_to_color(value: &Value) -> TrueColor {
    match *value {
        Value::String(ref string)   => (
            u8::from_str_radix(&string[0..2], 16).unwrap(),
            u8::from_str_radix(&string[2..4], 16).unwrap(),
            u8::from_str_radix(&string[4..6], 16).unwrap(),
        ),
        Value::Array(ref array)     => (
            array[0].as_integer().unwrap() as u8,
            array[1].as_integer().unwrap() as u8,
            array[2].as_integer().unwrap() as u8
        ),
        _                           => panic!()
    }
}

fn convert_tomlv_to_palette(value: &Value) -> [TrueColor; 256] {
    let mut palette = PALETTE;
    for (key, value) in value.as_table().unwrap() {
        let n: usize = key.trim_left_matches("color").parse().unwrap();
        palette[n.saturating_sub(1)] = convert_tomlv_to_color(value);
    }
    palette
}

fn convert_tomlv_to_styles(table: &Table) -> Styles {
    let fg_color = table.get("foreground").map_or(Color::Default, |v| {
        let (r, g, b) = convert_tomlv_to_color(v);
        Color::True(r, g, b)
    });
    let bg_color = table.get("background").map_or(Color::Default, |v| {
        let (r, g, b) = convert_tomlv_to_color(v);
        Color::True(r, g, b)
    });
    let opacity = table.get("opacity").map_or(0xff, |v| v.as_integer().unwrap() as u8);
    let (underline, double_underline) = match table.get("underline") {
        Some(&Value::Boolean(true)) | Some(&Value::Integer(1))  => (true, false),
        Some(&Value::Integer(2))                                => (true, true),
        _                                                       => (false, false),
    };
    let bold = table.get("bold").map_or(false, |v| v.as_bool().unwrap());
    let italic = table.get("italic").map_or(false, |v| v.as_bool().unwrap());
    let strikethrough = table.get("strikethrough").map_or(false, |v| v.as_bool().unwrap());
    let inverted = table.get("inverted").map_or(false, |v| v.as_bool().unwrap());
    let blink = table.get("blink").map_or(false, |v| v.as_bool().unwrap());
    Styles {
        fg_color: fg_color,
        bg_color: bg_color,
        opacity: opacity,
        underline: underline,
        double_underline: double_underline,
        bold: bold,
        italic: italic,
        strikethrough: strikethrough,
        inverted: inverted,
        blink: blink,
    }
}

fn read_toml_file<P: AsRef<Path>>(path: P) -> Result<Table> {
    let mut file = try!(File::open(&path));
    let mut source = String::new();
    try!(file.read_to_string(&mut source));
    parse_toml(&source.to_string(), path)
}

fn parse_toml<P: AsRef<Path>>(toml_string: &String, toml_path: P)
                              -> Result<Table> {
    let mut parser = toml::Parser::new(toml_string);
    match parser.parse() {
        Some(toml) => {
            Ok(toml)
        }
        None => {
            let mut error_string = String::new();
            for err in &parser.errors {
                let (loline, locol) = parser.to_linecol(err.lo);
                let (hiline, hicol) = parser.to_linecol(err.hi);
                error_string = format!("{}\n{}:{}:{}:{}:{} error: {}",
                        error_string, toml_path.as_ref().display(), loline,
                        locol, hiline, hicol, err.desc);
            }
            Err(ConfigError::Parse(error_string))
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    use super::super::Config;
    use notty_cairo::Config as CairoConfig;

    fn test_default_config(config: &CairoConfig) {
        assert_eq!(config.font, "Monospace 10");
        assert_eq!(config.fg_color, (0xff,0xff,0xff));
        assert_eq!(config.bg_color, (0x00,0x00,0x00));
        assert_eq!(config.cursor_color, (0xbb,0xbb,0xbb));
        assert_eq!(config.palette[0], (0x00,0x00,0x00));
        assert_eq!(config.palette[5], (0xff,0x55,0xff));
    }

    #[test]
    fn test_default() {
        let config = CairoConfig::default();
        test_default_config(&config);
    }

    #[test]
    fn test_update_from_file() {
        let mut config = Config::default();
        test_default_config(&config.cairo);

        let update_path = "resources/update-config.toml".to_string();
        update_from_file(&mut config, &update_path).unwrap();
        assert_eq!(config.cairo.font, "Liberation Mono 8");
    }
}
<|endoftext|>