<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

/// Buffer whose content is read-written by the GPU only.
///
/// Each access from the CPU or from the GPU locks the whole buffer for either reading or writing.
/// You can read the buffer multiple times simultaneously from multiple queues. Trying to read and
/// write simultaneously, or write and write simultaneously will block with a semaphore.
///
use super::{
    sys::{UnsafeBuffer, UnsafeBufferCreateInfo},
    BufferAccess, BufferAccessObject, BufferContents, BufferCreationError, BufferInner,
    BufferUsage, TypedBufferAccess,
};
use crate::{
    device::{physical::QueueFamily, Device, DeviceOwned},
    memory::{
        pool::{
            alloc_dedicated_with_exportable_fd, AllocFromRequirementsFilter, AllocLayout,
            MappingRequirement, MemoryPoolAlloc, PotentialDedicatedAllocation, StdMemoryPoolAlloc,
        },
        DedicatedAllocation, DeviceMemoryAllocationError, DeviceMemoryExportError,
        ExternalMemoryHandleType, MemoryPool, MemoryRequirements,
    },
    sync::Sharing,
    DeviceSize,
};
use smallvec::SmallVec;
use std::{
    fs::File,
    hash::{Hash, Hasher},
    marker::PhantomData,
    mem::size_of,
    sync::Arc,
};

/// Buffer whose content is in device-local memory.
///
/// This buffer type is useful in order to store intermediary data. For example you execute a
/// compute shader that writes to this buffer, then read the content of the buffer in a following
/// compute or graphics pipeline.
///
/// The `DeviceLocalBuffer` will be in device-local memory, unless the device doesn't provide any
/// device-local memory.
///
/// # Usage
///
/// Since a `DeviceLocalBuffer` can only be directly accessed by the GPU, data cannot be transfered between
/// the host process and the buffer alone. One must use additional buffers which are accessible to the CPU as
/// staging areas, then use command buffers to execute the necessary data transfers.
///
/// Despite this, if one knows in advance that a buffer will not need to be frequently accessed by the host,
/// then there may be significant performance gains by using a `DeviceLocalBuffer` over a buffer type which
/// allows host access.
///
/// # Example
///
/// The following example outlines the general strategy one may take when initializing a `DeviceLocalBuffer`.
///
/// ```
/// use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, DeviceLocalBuffer};
/// use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, CopyBufferInfo, PrimaryCommandBuffer};
/// use vulkano::sync::GpuFuture;
/// # let device: std::sync::Arc<vulkano::device::Device> = return;
/// # let queue: std::sync::Arc<vulkano::device::Queue> = return;
///
/// // Simple iterator to construct test data.
/// let data = (0..10_000).map(|i| i as f32);
///
/// // Create a CPU accessible buffer initialized with the data.
/// let temporary_accessible_buffer = CpuAccessibleBuffer::from_iter(
///     device.clone(),
///     BufferUsage::transfer_src(), // Specify this buffer will be used as a transfer source.
///     false,
///     data,
/// )
/// .unwrap();
///
/// // Create a buffer array on the GPU with enough space for `10_000` floats.
/// let device_local_buffer = DeviceLocalBuffer::<[f32]>::array(
///     device.clone(),
///     10_000 as vulkano::DeviceSize,
///     BufferUsage::storage_buffer() | BufferUsage::transfer_dst(), // Specify use as a storage buffer and transfer destination.
///     device.active_queue_families(),
/// )
/// .unwrap();
///
/// // Create a one-time command to copy between the buffers.
/// let mut cbb = AutoCommandBufferBuilder::primary(
///     device.clone(),
///     queue.family(),
///     CommandBufferUsage::OneTimeSubmit,
/// )
/// .unwrap();
/// cbb.copy_buffer(CopyBufferInfo::buffers(
///     temporary_accessible_buffer,
///     device_local_buffer.clone(),
/// ))
/// .unwrap();
/// let cb = cbb.build().unwrap();
///
/// // Execute copy command and wait for completion before proceeding.
/// cb.execute(queue.clone())
/// .unwrap()
/// .then_signal_fence_and_flush()
/// .unwrap()
/// .wait(None /* timeout */)
/// .unwrap()
/// ```
///
#[derive(Debug)]
pub struct DeviceLocalBuffer<T, A = PotentialDedicatedAllocation<StdMemoryPoolAlloc>>
where
    T: BufferContents + ?Sized,
{
    // Inner content.
    inner: Arc<UnsafeBuffer>,

    // The memory held by the buffer.
    memory: A,

    // Queue families allowed to access this buffer.
    queue_families: SmallVec<[u32; 4]>,

    // Necessary to make it compile.
    marker: PhantomData<Box<T>>,
}

#[derive(Debug, Copy, Clone)]
enum GpuAccess {
    None,
    NonExclusive { num: u32 },
    Exclusive { num: u32 },
}

impl<T> DeviceLocalBuffer<T>
where
    T: BufferContents,
{
    /// Builds a new buffer. Only allowed for sized data.
    ///
    /// # Panics
    ///
    /// - Panics if `T` has zero size.
    #[inline]
    pub fn new<'a, I>(
        device: Arc<Device>,
        usage: BufferUsage,
        queue_families: I,
    ) -> Result<Arc<DeviceLocalBuffer<T>>, DeviceMemoryAllocationError>
    where
        I: IntoIterator<Item = QueueFamily<'a>>,
    {
        unsafe {
            DeviceLocalBuffer::raw(device, size_of::<T>() as DeviceSize, usage, queue_families)
        }
    }
}

impl<T> DeviceLocalBuffer<[T]>
where
    [T]: BufferContents,
{
    /// Builds a new buffer. Can be used for arrays.
    ///
    /// # Panics
    ///
    /// - Panics if `T` has zero size.
    /// - Panics if `len` is zero.
    #[inline]
    pub fn array<'a, I>(
        device: Arc<Device>,
        len: DeviceSize,
        usage: BufferUsage,
        queue_families: I,
    ) -> Result<Arc<DeviceLocalBuffer<[T]>>, DeviceMemoryAllocationError>
    where
        I: IntoIterator<Item = QueueFamily<'a>>,
    {
        unsafe {
            DeviceLocalBuffer::raw(
                device,
                len * size_of::<T>() as DeviceSize,
                usage,
                queue_families,
            )
        }
    }
}

impl<T> DeviceLocalBuffer<T>
where
    T: BufferContents + ?Sized,
{
    /// Builds a new buffer without checking the size.
    ///
    /// # Safety
    ///
    /// - You must ensure that the size that you pass is correct for `T`.
    ///
    /// # Panics
    ///
    /// - Panics if `size` is zero.
    pub unsafe fn raw<'a, I>(
        device: Arc<Device>,
        size: DeviceSize,
        usage: BufferUsage,
        queue_families: I,
    ) -> Result<Arc<DeviceLocalBuffer<T>>, DeviceMemoryAllocationError>
    where
        I: IntoIterator<Item = QueueFamily<'a>>,
    {
        let queue_families = queue_families
            .into_iter()
            .map(|f| f.id())
            .collect::<SmallVec<[u32; 4]>>();

        let (buffer, mem_reqs) = Self::build_buffer(&device, size, usage, &queue_families)?;

        let memory = MemoryPool::alloc_from_requirements(
            &Device::standard_pool(&device),
            &mem_reqs,
            AllocLayout::Linear,
            MappingRequirement::DoNotMap,
            Some(DedicatedAllocation::Buffer(&buffer)),
            |t| {
                if t.is_device_local() {
                    AllocFromRequirementsFilter::Preferred
                } else {
                    AllocFromRequirementsFilter::Allowed
                }
            },
        )?;
        debug_assert!((memory.offset() % mem_reqs.alignment) == 0);
        buffer.bind_memory(memory.memory(), memory.offset())?;

        Ok(Arc::new(DeviceLocalBuffer {
            inner: buffer,
            memory,
            queue_families,
            marker: PhantomData,
        }))
    }

    /// Same as `raw` but with exportable fd option for the allocated memory on Linux/BSD
    ///
    /// # Panics
    ///
    /// - Panics if `size` is zero.
    pub unsafe fn raw_with_exportable_fd<'a, I>(
        device: Arc<Device>,
        size: DeviceSize,
        usage: BufferUsage,
        queue_families: I,
    ) -> Result<Arc<DeviceLocalBuffer<T>>, DeviceMemoryAllocationError>
    where
        I: IntoIterator<Item = QueueFamily<'a>>,
    {
        assert!(device.enabled_extensions().khr_external_memory_fd);
        assert!(device.enabled_extensions().khr_external_memory);

        let queue_families = queue_families
            .into_iter()
            .map(|f| f.id())
            .collect::<SmallVec<[u32; 4]>>();

        let (buffer, mem_reqs) = Self::build_buffer(&device, size, usage, &queue_families)?;

        let memory = alloc_dedicated_with_exportable_fd(
            device.clone(),
            &mem_reqs,
            AllocLayout::Linear,
            MappingRequirement::DoNotMap,
            DedicatedAllocation::Buffer(&buffer),
            |t| {
                if t.is_device_local() {
                    AllocFromRequirementsFilter::Preferred
                } else {
                    AllocFromRequirementsFilter::Allowed
                }
            },
        )?;
        let mem_offset = memory.offset();
        debug_assert!((mem_offset % mem_reqs.alignment) == 0);
        buffer.bind_memory(memory.memory(), mem_offset)?;

        Ok(Arc::new(DeviceLocalBuffer {
            inner: buffer,
            memory,
            queue_families,
            marker: PhantomData,
        }))
    }

    unsafe fn build_buffer(
        device: &Arc<Device>,
        size: DeviceSize,
        usage: BufferUsage,
        queue_families: &SmallVec<[u32; 4]>,
    ) -> Result<(Arc<UnsafeBuffer>, MemoryRequirements), DeviceMemoryAllocationError> {
        let buffer = {
            match UnsafeBuffer::new(
                device.clone(),
                UnsafeBufferCreateInfo {
                    sharing: if queue_families.len() >= 2 {
                        Sharing::Concurrent(queue_families.clone())
                    } else {
                        Sharing::Exclusive
                    },
                    size,
                    usage,
                    ..Default::default()
                },
            ) {
                Ok(b) => b,
                Err(BufferCreationError::AllocError(err)) => return Err(err),
                Err(_) => unreachable!(), // We don't use sparse binding, therefore the other
                                          // errors can't happen
            }
        };
        let mem_reqs = buffer.memory_requirements();
        Ok((buffer, mem_reqs))
    }

    /// Exports posix file descriptor for the allocated memory
    /// requires `khr_external_memory_fd` and `khr_external_memory` extensions to be loaded.
    /// Only works on Linux/BSD.
    pub fn export_posix_fd(&self) -> Result<File, DeviceMemoryExportError> {
        self.memory
            .memory()
            .export_fd(ExternalMemoryHandleType::OpaqueFd)
    }
}

impl<T, A> DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
{
    /// Returns the queue families this buffer can be used on.
    // TODO: use a custom iterator
    #[inline]
    pub fn queue_families(&self) -> Vec<QueueFamily> {
        self.queue_families
            .iter()
            .map(|&num| {
                self.device()
                    .physical_device()
                    .queue_family_by_id(num)
                    .unwrap()
            })
            .collect()
    }
}

unsafe impl<T, A> DeviceOwned for DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
{
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.device()
    }
}

unsafe impl<T, A> BufferAccess for DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
    A: Send + Sync,
{
    #[inline]
    fn inner(&self) -> BufferInner {
        BufferInner {
            buffer: &self.inner,
            offset: 0,
        }
    }

    #[inline]
    fn size(&self) -> DeviceSize {
        self.inner.size()
    }
}

impl<T, A> BufferAccessObject for Arc<DeviceLocalBuffer<T, A>>
where
    T: BufferContents + ?Sized,
    A: Send + Sync + 'static,
{
    #[inline]
    fn as_buffer_access_object(&self) -> Arc<dyn BufferAccess> {
        self.clone()
    }
}

unsafe impl<T, A> TypedBufferAccess for DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
    A: Send + Sync,
{
    type Content = T;
}

impl<T, A> PartialEq for DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
    A: Send + Sync,
{
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.inner() == other.inner() && self.size() == other.size()
    }
}

impl<T, A> Eq for DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
    A: Send + Sync,
{
}

impl<T, A> Hash for DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
    A: Send + Sync,
{
    #[inline]
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.inner().hash(state);
        self.size().hash(state);
    }
}
<commit_msg>Fixing Module comments that were accidentally changed (#1891)<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Buffer whose content is read-written by the GPU only.
//!
//! Each access from the CPU or from the GPU locks the whole buffer for either reading or writing.
//! You can read the buffer multiple times simultaneously from multiple queues. Trying to read and
//! write simultaneously, or write and write simultaneously will block with a semaphore.
//!

use super::{
    sys::{UnsafeBuffer, UnsafeBufferCreateInfo},
    BufferAccess, BufferAccessObject, BufferContents, BufferCreationError, BufferInner,
    BufferUsage, TypedBufferAccess,
};
use crate::{
    device::{physical::QueueFamily, Device, DeviceOwned},
    memory::{
        pool::{
            alloc_dedicated_with_exportable_fd, AllocFromRequirementsFilter, AllocLayout,
            MappingRequirement, MemoryPoolAlloc, PotentialDedicatedAllocation, StdMemoryPoolAlloc,
        },
        DedicatedAllocation, DeviceMemoryAllocationError, DeviceMemoryExportError,
        ExternalMemoryHandleType, MemoryPool, MemoryRequirements,
    },
    sync::Sharing,
    DeviceSize,
};
use smallvec::SmallVec;
use std::{
    fs::File,
    hash::{Hash, Hasher},
    marker::PhantomData,
    mem::size_of,
    sync::Arc,
};

/// Buffer whose content is in device-local memory.
///
/// This buffer type is useful in order to store intermediary data. For example you execute a
/// compute shader that writes to this buffer, then read the content of the buffer in a following
/// compute or graphics pipeline.
///
/// The `DeviceLocalBuffer` will be in device-local memory, unless the device doesn't provide any
/// device-local memory.
///
/// # Usage
///
/// Since a `DeviceLocalBuffer` can only be directly accessed by the GPU, data cannot be transfered between
/// the host process and the buffer alone. One must use additional buffers which are accessible to the CPU as
/// staging areas, then use command buffers to execute the necessary data transfers.
///
/// Despite this, if one knows in advance that a buffer will not need to be frequently accessed by the host,
/// then there may be significant performance gains by using a `DeviceLocalBuffer` over a buffer type which
/// allows host access.
///
/// # Example
///
/// The following example outlines the general strategy one may take when initializing a `DeviceLocalBuffer`.
///
/// ```
/// use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, DeviceLocalBuffer};
/// use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, CopyBufferInfo, PrimaryCommandBuffer};
/// use vulkano::sync::GpuFuture;
/// # let device: std::sync::Arc<vulkano::device::Device> = return;
/// # let queue: std::sync::Arc<vulkano::device::Queue> = return;
///
/// // Simple iterator to construct test data.
/// let data = (0..10_000).map(|i| i as f32);
///
/// // Create a CPU accessible buffer initialized with the data.
/// let temporary_accessible_buffer = CpuAccessibleBuffer::from_iter(
///     device.clone(),
///     BufferUsage::transfer_src(), // Specify this buffer will be used as a transfer source.
///     false,
///     data,
/// )
/// .unwrap();
///
/// // Create a buffer array on the GPU with enough space for `10_000` floats.
/// let device_local_buffer = DeviceLocalBuffer::<[f32]>::array(
///     device.clone(),
///     10_000 as vulkano::DeviceSize,
///     BufferUsage::storage_buffer() | BufferUsage::transfer_dst(), // Specify use as a storage buffer and transfer destination.
///     device.active_queue_families(),
/// )
/// .unwrap();
///
/// // Create a one-time command to copy between the buffers.
/// let mut cbb = AutoCommandBufferBuilder::primary(
///     device.clone(),
///     queue.family(),
///     CommandBufferUsage::OneTimeSubmit,
/// )
/// .unwrap();
/// cbb.copy_buffer(CopyBufferInfo::buffers(
///     temporary_accessible_buffer,
///     device_local_buffer.clone(),
/// ))
/// .unwrap();
/// let cb = cbb.build().unwrap();
///
/// // Execute copy command and wait for completion before proceeding.
/// cb.execute(queue.clone())
/// .unwrap()
/// .then_signal_fence_and_flush()
/// .unwrap()
/// .wait(None /* timeout */)
/// .unwrap()
/// ```
///
#[derive(Debug)]
pub struct DeviceLocalBuffer<T, A = PotentialDedicatedAllocation<StdMemoryPoolAlloc>>
where
    T: BufferContents + ?Sized,
{
    // Inner content.
    inner: Arc<UnsafeBuffer>,

    // The memory held by the buffer.
    memory: A,

    // Queue families allowed to access this buffer.
    queue_families: SmallVec<[u32; 4]>,

    // Necessary to make it compile.
    marker: PhantomData<Box<T>>,
}

#[derive(Debug, Copy, Clone)]
enum GpuAccess {
    None,
    NonExclusive { num: u32 },
    Exclusive { num: u32 },
}

impl<T> DeviceLocalBuffer<T>
where
    T: BufferContents,
{
    /// Builds a new buffer. Only allowed for sized data.
    ///
    /// # Panics
    ///
    /// - Panics if `T` has zero size.
    #[inline]
    pub fn new<'a, I>(
        device: Arc<Device>,
        usage: BufferUsage,
        queue_families: I,
    ) -> Result<Arc<DeviceLocalBuffer<T>>, DeviceMemoryAllocationError>
    where
        I: IntoIterator<Item = QueueFamily<'a>>,
    {
        unsafe {
            DeviceLocalBuffer::raw(device, size_of::<T>() as DeviceSize, usage, queue_families)
        }
    }
}

impl<T> DeviceLocalBuffer<[T]>
where
    [T]: BufferContents,
{
    /// Builds a new buffer. Can be used for arrays.
    ///
    /// # Panics
    ///
    /// - Panics if `T` has zero size.
    /// - Panics if `len` is zero.
    #[inline]
    pub fn array<'a, I>(
        device: Arc<Device>,
        len: DeviceSize,
        usage: BufferUsage,
        queue_families: I,
    ) -> Result<Arc<DeviceLocalBuffer<[T]>>, DeviceMemoryAllocationError>
    where
        I: IntoIterator<Item = QueueFamily<'a>>,
    {
        unsafe {
            DeviceLocalBuffer::raw(
                device,
                len * size_of::<T>() as DeviceSize,
                usage,
                queue_families,
            )
        }
    }
}

impl<T> DeviceLocalBuffer<T>
where
    T: BufferContents + ?Sized,
{
    /// Builds a new buffer without checking the size.
    ///
    /// # Safety
    ///
    /// - You must ensure that the size that you pass is correct for `T`.
    ///
    /// # Panics
    ///
    /// - Panics if `size` is zero.
    pub unsafe fn raw<'a, I>(
        device: Arc<Device>,
        size: DeviceSize,
        usage: BufferUsage,
        queue_families: I,
    ) -> Result<Arc<DeviceLocalBuffer<T>>, DeviceMemoryAllocationError>
    where
        I: IntoIterator<Item = QueueFamily<'a>>,
    {
        let queue_families = queue_families
            .into_iter()
            .map(|f| f.id())
            .collect::<SmallVec<[u32; 4]>>();

        let (buffer, mem_reqs) = Self::build_buffer(&device, size, usage, &queue_families)?;

        let memory = MemoryPool::alloc_from_requirements(
            &Device::standard_pool(&device),
            &mem_reqs,
            AllocLayout::Linear,
            MappingRequirement::DoNotMap,
            Some(DedicatedAllocation::Buffer(&buffer)),
            |t| {
                if t.is_device_local() {
                    AllocFromRequirementsFilter::Preferred
                } else {
                    AllocFromRequirementsFilter::Allowed
                }
            },
        )?;
        debug_assert!((memory.offset() % mem_reqs.alignment) == 0);
        buffer.bind_memory(memory.memory(), memory.offset())?;

        Ok(Arc::new(DeviceLocalBuffer {
            inner: buffer,
            memory,
            queue_families,
            marker: PhantomData,
        }))
    }

    /// Same as `raw` but with exportable fd option for the allocated memory on Linux/BSD
    ///
    /// # Panics
    ///
    /// - Panics if `size` is zero.
    pub unsafe fn raw_with_exportable_fd<'a, I>(
        device: Arc<Device>,
        size: DeviceSize,
        usage: BufferUsage,
        queue_families: I,
    ) -> Result<Arc<DeviceLocalBuffer<T>>, DeviceMemoryAllocationError>
    where
        I: IntoIterator<Item = QueueFamily<'a>>,
    {
        assert!(device.enabled_extensions().khr_external_memory_fd);
        assert!(device.enabled_extensions().khr_external_memory);

        let queue_families = queue_families
            .into_iter()
            .map(|f| f.id())
            .collect::<SmallVec<[u32; 4]>>();

        let (buffer, mem_reqs) = Self::build_buffer(&device, size, usage, &queue_families)?;

        let memory = alloc_dedicated_with_exportable_fd(
            device.clone(),
            &mem_reqs,
            AllocLayout::Linear,
            MappingRequirement::DoNotMap,
            DedicatedAllocation::Buffer(&buffer),
            |t| {
                if t.is_device_local() {
                    AllocFromRequirementsFilter::Preferred
                } else {
                    AllocFromRequirementsFilter::Allowed
                }
            },
        )?;
        let mem_offset = memory.offset();
        debug_assert!((mem_offset % mem_reqs.alignment) == 0);
        buffer.bind_memory(memory.memory(), mem_offset)?;

        Ok(Arc::new(DeviceLocalBuffer {
            inner: buffer,
            memory,
            queue_families,
            marker: PhantomData,
        }))
    }

    unsafe fn build_buffer(
        device: &Arc<Device>,
        size: DeviceSize,
        usage: BufferUsage,
        queue_families: &SmallVec<[u32; 4]>,
    ) -> Result<(Arc<UnsafeBuffer>, MemoryRequirements), DeviceMemoryAllocationError> {
        let buffer = {
            match UnsafeBuffer::new(
                device.clone(),
                UnsafeBufferCreateInfo {
                    sharing: if queue_families.len() >= 2 {
                        Sharing::Concurrent(queue_families.clone())
                    } else {
                        Sharing::Exclusive
                    },
                    size,
                    usage,
                    ..Default::default()
                },
            ) {
                Ok(b) => b,
                Err(BufferCreationError::AllocError(err)) => return Err(err),
                Err(_) => unreachable!(), // We don't use sparse binding, therefore the other
                                          // errors can't happen
            }
        };
        let mem_reqs = buffer.memory_requirements();
        Ok((buffer, mem_reqs))
    }

    /// Exports posix file descriptor for the allocated memory
    /// requires `khr_external_memory_fd` and `khr_external_memory` extensions to be loaded.
    /// Only works on Linux/BSD.
    pub fn export_posix_fd(&self) -> Result<File, DeviceMemoryExportError> {
        self.memory
            .memory()
            .export_fd(ExternalMemoryHandleType::OpaqueFd)
    }
}

impl<T, A> DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
{
    /// Returns the queue families this buffer can be used on.
    // TODO: use a custom iterator
    #[inline]
    pub fn queue_families(&self) -> Vec<QueueFamily> {
        self.queue_families
            .iter()
            .map(|&num| {
                self.device()
                    .physical_device()
                    .queue_family_by_id(num)
                    .unwrap()
            })
            .collect()
    }
}

unsafe impl<T, A> DeviceOwned for DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
{
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.device()
    }
}

unsafe impl<T, A> BufferAccess for DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
    A: Send + Sync,
{
    #[inline]
    fn inner(&self) -> BufferInner {
        BufferInner {
            buffer: &self.inner,
            offset: 0,
        }
    }

    #[inline]
    fn size(&self) -> DeviceSize {
        self.inner.size()
    }
}

impl<T, A> BufferAccessObject for Arc<DeviceLocalBuffer<T, A>>
where
    T: BufferContents + ?Sized,
    A: Send + Sync + 'static,
{
    #[inline]
    fn as_buffer_access_object(&self) -> Arc<dyn BufferAccess> {
        self.clone()
    }
}

unsafe impl<T, A> TypedBufferAccess for DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
    A: Send + Sync,
{
    type Content = T;
}

impl<T, A> PartialEq for DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
    A: Send + Sync,
{
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.inner() == other.inner() && self.size() == other.size()
    }
}

impl<T, A> Eq for DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
    A: Send + Sync,
{
}

impl<T, A> Hash for DeviceLocalBuffer<T, A>
where
    T: BufferContents + ?Sized,
    A: Send + Sync,
{
    #[inline]
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.inner().hash(state);
        self.size().hash(state);
    }
}
<|endoftext|>


<commit_before>use readhex::*;

const HEX : [u8 ; 16] = [
    b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9',
    b'A', b'B', b'C', b'D', b'E', b'F'
];

fn compact_vector(input: &[u8], output: &mut Vec<u8>) -> bool {
    let mut input_pos = 0;

    loop {
        let input_char = match input.get(input_pos) {
            None => return true,
            Some(value) => *value,
        };
        input_pos += 1;
        if input_char <= b' ' { // Whitespace '\n', '\r', '\t', ' '
            continue;
        }
        output.push(input_char);
        if input_char == b'\"' { // Double Quote '\"'
            loop {
                let input_char = match input.get(input_pos) {
                    None => return false,
                    Some(value) => *value,
                };
                input_pos += 1;
                if input_char == b'\\' { // Back Slash '\\'
                    let input_char = match input.get(input_pos) {
                        None => return false,
                        Some(value) => *value,
                    };
                    input_pos += 1;
                    if input_char == b'u' { // Unicode escape "u"
                        let h0 = match input.get(input_pos) {
                            None => return false,
                            Some(value) => *value
                        };
                        let h1 = match input.get(input_pos + 1) {
                            None => return false,
                            Some(value) => *value
                        };
                        let h2 = match input.get(input_pos + 2) {
                            None => return false,
                            Some(value) => *value
                        };
                        let h3 = match input.get(input_pos + 3) {
                            None => return false,
                            Some(value) => *value
                        };
                        let escaped = read_hexdigits(h0, h1, h2, h3);
                        input_pos += 4;
                        if escaped < 0x20 {
                            output.push(b'\\');
                            match escaped {
                                0x08 => output.push(b'b'),
                                0x09 => output.push(b't'),
                                0x0A => output.push(b'n'),
                                0x0C => output.push(b'f'),
                                0x0D => output.push(b'r'),
                                _ => {
                                    output.push(b'u');
                                    output.push(b'0');
                                    output.push(b'0');
                                    output.push(b'0' + (escaped as u8 >> 4));
                                    output.push(HEX[(escaped & 0xF) as usize]);
                                },
                            }
                        } else if escaped < 0x80 {
                            if escaped as u8 == b'\"' || escaped as u8 == b'\\' {
                                output.push(b'\\');
                            }
                            output.push(escaped as u8);
                        } else if escaped < 0x800 {
                            output.push((escaped >> 6) as u8 | 0xC0);
                            output.push((escaped as u8 & 0x3F) | 0x80);
                        } else {
                            if escaped < 0xD800 || escaped >= 0xE000 {
                                output.push((escaped >> 12) as u8 | 0xE0);
                                output.push(((escaped >> 6) & 0x3F) as u8 | 0x80);
                                output.push((escaped as u8 & 0x3F) | 0x80);
                            } else {
                                // surrogate pair
                                let h0 = match input.get(input_pos + 2) {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let h1 = match input.get(input_pos + 3) {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let h2 = match input.get(input_pos + 4) {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let h3 = match input.get(input_pos + 5) {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let surrogate = read_hexdigits(h0, h1, h2, h3);
                                input_pos += 6;
                                let codepoint = 0x10000 + (
                                    ((escaped & 0x3FF) << 10) | (surrogate & 0x3FF)
                                );
                                output.push((codepoint >> 18) as u8 | 0xF0);
                                output.push(((codepoint >> 12) & 0x3F) as u8 | 0x80);
                                output.push(((codepoint >> 6) & 0x3F) as u8 | 0x80);
                                output.push((codepoint as u8 & 0x3F) | 0x80);
                            }
                        }

                    } else if input_char == b'/' { // Forward Slash '/'
                        output.push(input_char);
                    } else {
                        output.push(b'\\');
                        output.push(input_char);
                    }
                } else {
                    output.push(input_char);
                }
                if input_char == b'\"' {
                    break;
                }
            }
        }
    }
}

pub fn compact(input_json: &str) -> String {
    let mut output : Vec<u8> = Vec::with_capacity(input_json.as_bytes().len());
    if compact_vector(input_json.as_bytes(), &mut output) {
        return String::from_utf8(output).unwrap();
    } else {
        return "FAIL".to_string();
    }
}

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

    #[test]
    fn compact_json_object() {
        assert_eq!("{}", compact("{ }"));
    }

    #[test]
    fn compact_json_string() {
        assert_eq!(
            r#"{"key":"\"hello / world\""}"#,
            compact(r#" { "key" : "\"hello \/ world\"" } "#)
        );
    }

    #[test]
    fn compact_unicode_escapes() {
        assert_eq!(
            r#"["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"]"#,
            compact(r#"["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"]"#)
        );
        assert_eq!(
            r#"["\b\t\n\u000B\f\r\u000E\u000F"]"#,
            compact(r#"["\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F"]"#)
        );
        assert_eq!(
            r#"["\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"]"#,
            compact(r#"["\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"]"#)
        );
        assert_eq!(
            r#"["\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"]"#,
            compact(r#"["\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"]"#)
        );
    }

    #[test]
    fn compact_unicode_utf8() {
        assert_eq!(
            "[\"a\\\\B\\\"\"]",
            compact("[\"\\u0061\\u005C\\u0042\\u0022\"]")
        );
        assert_eq!("[\"\u{120}\"]", compact("[\"\\u0120\"]"));
        assert_eq!("[\"\u{FFF}\"]", compact("[\"\\u0FFF\"]"));
        assert_eq!("[\"\u{1820}\"]", compact("[\"\\u1820\"]"));
        assert_eq!("[\"\u{FFFF}\"]", compact("[\"\\uFFFF\"]"));
        assert_eq!("[\"\u{20820}\"]", compact("[\"\\uD842\\uDC20\"]"));
        assert_eq!("[\"\u{10FFFF}\"]", compact("[\"\\uDBFF\\uDFFF\"]"));
    }

}
<commit_msg>Add a silly benchmark<commit_after>use readhex::*;

const HEX : [u8 ; 16] = [
    b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9',
    b'A', b'B', b'C', b'D', b'E', b'F'
];

pub fn compact_vector(input: &[u8], output: &mut Vec<u8>) -> bool {
    let mut input_pos = 0;

    loop {
        let input_char = match input.get(input_pos) {
            None => return true,
            Some(value) => *value,
        };
        input_pos += 1;
        if input_char <= b' ' { // Whitespace '\n', '\r', '\t', ' '
            continue;
        }
        output.push(input_char);
        if input_char == b'\"' { // Double Quote '\"'
            loop {
                let input_char = match input.get(input_pos) {
                    None => return false,
                    Some(value) => *value,
                };
                input_pos += 1;
                if input_char == b'\\' { // Back Slash '\\'
                    let input_char = match input.get(input_pos) {
                        None => return false,
                        Some(value) => *value,
                    };
                    input_pos += 1;
                    if input_char == b'u' { // Unicode escape "u"
                        let h0 = match input.get(input_pos) {
                            None => return false,
                            Some(value) => *value
                        };
                        let h1 = match input.get(input_pos + 1) {
                            None => return false,
                            Some(value) => *value
                        };
                        let h2 = match input.get(input_pos + 2) {
                            None => return false,
                            Some(value) => *value
                        };
                        let h3 = match input.get(input_pos + 3) {
                            None => return false,
                            Some(value) => *value
                        };
                        let escaped = read_hexdigits(h0, h1, h2, h3);
                        input_pos += 4;
                        if escaped < 0x20 {
                            output.push(b'\\');
                            match escaped {
                                0x08 => output.push(b'b'),
                                0x09 => output.push(b't'),
                                0x0A => output.push(b'n'),
                                0x0C => output.push(b'f'),
                                0x0D => output.push(b'r'),
                                _ => {
                                    output.push(b'u');
                                    output.push(b'0');
                                    output.push(b'0');
                                    output.push(b'0' + (escaped as u8 >> 4));
                                    output.push(HEX[(escaped & 0xF) as usize]);
                                },
                            }
                        } else if escaped < 0x80 {
                            if escaped as u8 == b'\"' || escaped as u8 == b'\\' {
                                output.push(b'\\');
                            }
                            output.push(escaped as u8);
                        } else if escaped < 0x800 {
                            output.push((escaped >> 6) as u8 | 0xC0);
                            output.push((escaped as u8 & 0x3F) | 0x80);
                        } else {
                            if escaped < 0xD800 || escaped >= 0xE000 {
                                output.push((escaped >> 12) as u8 | 0xE0);
                                output.push(((escaped >> 6) & 0x3F) as u8 | 0x80);
                                output.push((escaped as u8 & 0x3F) | 0x80);
                            } else {
                                // surrogate pair
                                let h0 = match input.get(input_pos + 2) {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let h1 = match input.get(input_pos + 3) {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let h2 = match input.get(input_pos + 4) {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let h3 = match input.get(input_pos + 5) {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let surrogate = read_hexdigits(h0, h1, h2, h3);
                                input_pos += 6;
                                let codepoint = 0x10000 + (
                                    ((escaped & 0x3FF) << 10) | (surrogate & 0x3FF)
                                );
                                output.push((codepoint >> 18) as u8 | 0xF0);
                                output.push(((codepoint >> 12) & 0x3F) as u8 | 0x80);
                                output.push(((codepoint >> 6) & 0x3F) as u8 | 0x80);
                                output.push((codepoint as u8 & 0x3F) | 0x80);
                            }
                        }

                    } else if input_char == b'/' { // Forward Slash '/'
                        output.push(input_char);
                    } else {
                        output.push(b'\\');
                        output.push(input_char);
                    }
                } else {
                    output.push(input_char);
                }
                if input_char == b'\"' {
                    break;
                }
            }
        }
    }
}

pub fn compact(input_json: &str) -> String {
    let mut output : Vec<u8> = Vec::with_capacity(input_json.as_bytes().len());
    if compact_vector(input_json.as_bytes(), &mut output) {
        return String::from_utf8(output).unwrap();
    } else {
        return "FAIL".to_string();
    }
}

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

    #[test]
    fn compact_json_object() {
        assert_eq!("{}", compact("{ }"));
    }

    #[test]
    fn compact_json_string() {
        assert_eq!(
            r#"{"key":"\"hello / world\""}"#,
            compact(r#" { "key" : "\"hello \/ world\"" } "#)
        );
    }

    #[test]
    fn compact_unicode_escapes() {
        assert_eq!(
            r#"["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"]"#,
            compact(r#"["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"]"#)
        );
        assert_eq!(
            r#"["\b\t\n\u000B\f\r\u000E\u000F"]"#,
            compact(r#"["\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F"]"#)
        );
        assert_eq!(
            r#"["\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"]"#,
            compact(r#"["\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"]"#)
        );
        assert_eq!(
            r#"["\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"]"#,
            compact(r#"["\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"]"#)
        );
    }

    #[test]
    fn compact_unicode_utf8() {
        assert_eq!(
            "[\"a\\\\B\\\"\"]",
            compact("[\"\\u0061\\u005C\\u0042\\u0022\"]")
        );
        assert_eq!("[\"\u{120}\"]", compact("[\"\\u0120\"]"));
        assert_eq!("[\"\u{FFF}\"]", compact("[\"\\u0FFF\"]"));
        assert_eq!("[\"\u{1820}\"]", compact("[\"\\u1820\"]"));
        assert_eq!("[\"\u{FFFF}\"]", compact("[\"\\uFFFF\"]"));
        assert_eq!("[\"\u{20820}\"]", compact("[\"\\uD842\\uDC20\"]"));
        assert_eq!("[\"\u{10FFFF}\"]", compact("[\"\\uDBFF\\uDFFF\"]"));
    }

    #[bench]
    fn benchmark_compact(b : &mut Bencher) {
        let test_string = black_box(r#"{
            "A longish bit of JSON": true,
            "containing": {
                "whitespace": " ",
                "unicode escapes ": "\uFFFF\u0FFF\u007F\uDBFF\uDFFF",
                "other sorts of esacpes": "\b\t\n\f\r\"\\\/",
                "unicode escapes for the other sorts of escapes":
                    "\u0008\u0009\u000A\u000C\u000D\u005C\u0022",
                "numbers": [0, 1, 1e4, 1.0, -1.0e7 ],
                "and more": [ true, false, null ]
            }
        }"#.as_bytes());
        let mut output : Vec<u8> = Vec::with_capacity(test_string.len());
        b.iter(|| { output.clear(); compact_vector(test_string, &mut output) });
    }
}
<|endoftext|>


<commit_before>//! State machine compiler.

use parse;
use parse::{Expr, Greedy, NonGreedy};
use vm::{Program, State, Want, Range};

/// Compile an AST into a `Program`.
pub fn compile(e: &Expr) -> Program {
    let mut b = Builder::new();
    let tails = compile_expr(&mut b, &[Initial], e);
    b.reify(tails)
}

#[deriving(Clone)]
enum Position {
    Initial,
    Index(uint)
}

struct Builder {
    initial: ~[uint],
    states: ~[State]
}

impl Builder {
    fn new() -> Builder {
        Builder {
            initial: ~[],
            states: ~[]
        }
    }

    fn push(&mut self, w: Want) -> ~[Position] {
        let tail = self.states.len();
        self.states.push((w, ~[]));
        ~[Index(tail)]
    }

    fn connect(&mut self, tails: &[Position]) {
        let head = self.states.len();
        for &tail in tails.iter() {
            match tail {
                Initial => self.initial.push(head),
                Index(index) => match self.states[index] {
                    (_, ref mut targets) => targets.push(head)
                }
            }
        }
    }

    fn reify(mut self, tails: &[Position]) -> (~[uint], ~[State]) {
        self.connect(tails);
        let Builder { initial, states } = self;
        (initial, states)
    }
}

fn compile_expr(b: &mut Builder, prev: &[Position], e: &Expr) -> ~[Position] {
    match *e {
        parse::Empty => prev.to_owned(),
        parse::Range(lo, hi) => {
            b.connect(prev);
            b.push(Range(lo, hi))
        },
        parse::Concatenate(ref inners) => {
            let mut tails = prev.to_owned();
            for inner in inners.iter() {
                tails = compile_expr(b, tails, inner);
            }
            tails
        },
        parse::Alternate(ref inners) => {
            let mut tails = ~[];
            for inner in inners.iter() {
                tails.push_all_move(compile_expr(b, prev, inner));
            }
            tails
        },
        //parse::Repeat(ref inner, min, max, greedy) => {},
        parse::Repeat(..) => fail!("repeats not implemented yet"),
        parse::Capture(..) => fail!("captures not implemented yet")
    }
}
<commit_msg>Compile bounded repetitions<commit_after>//! State machine compiler.

use std::mem::replace;

use parse;
use parse::{Expr, NonGreedy};
use vm::{Program, State, Want, Range};

/// Compile an AST into a `Program`.
pub fn compile(e: &Expr) -> Program {
    let mut b = Builder::new();
    let tails = compile_expr(&mut b, &[(Initial, Low)], e);
    b.reify(tails)
}

type Position = (Pos, Prec);

#[deriving(Clone)]
enum Pos {
    Initial,
    Index(uint)
}

#[deriving(Clone)]
enum Prec {
    Low,
    High
}

struct Builder {
    initial: ~[uint],
    states: ~[State]
}

impl Builder {
    fn new() -> Builder {
        Builder {
            initial: ~[],
            states: ~[]
        }
    }

    fn push(&mut self, w: Want) -> ~[Position] {
        let tail = self.states.len();
        self.states.push((w, ~[]));
        ~[(Index(tail), Low)]
    }

    fn connect(&mut self, tails: &[Position]) {
        let head = self.states.len();
        for &(pos, prec) in tails.iter() {
            let targets: &mut ~[uint] = match pos {
                Initial => &mut self.initial,
                Index(index) => match self.states[index] {
                    (_, ref mut targets) => targets
                }
            };
            match prec {
                Low => targets.push(head),
                High => targets.insert(0, head)
            };
        }
    }

    fn reify(mut self, tails: &[Position]) -> (~[uint], ~[State]) {
        self.connect(tails);
        let Builder { initial, states } = self;
        (initial, states)
    }
}

fn compile_expr(b: &mut Builder, prev: &[Position], e: &Expr) -> ~[Position] {
    match *e {
        parse::Empty => prev.to_owned(),
        parse::Range(lo, hi) => {
            b.connect(prev);
            b.push(Range(lo, hi))
        },
        parse::Concatenate(ref inners) => {
            let mut tails = prev.to_owned();
            for inner in inners.iter() {
                tails = compile_expr(b, tails, inner);
            }
            tails
        },
        parse::Alternate(ref inners) => {
            let mut tails = ~[];
            for inner in inners.iter() {
                tails.push_all_move(compile_expr(b, prev, inner));
            }
            tails
        },
        parse::Repeat(ref inner, min, Some(max), greedy) => {
            let mut tails = ~[];
            let mut last = prev.to_owned();
            for i in range(0, max) {
                if i >= min {
                    tails.push_all(last);
                }
                last = compile_expr(b, last, *inner);
            }

            if greedy == NonGreedy {
                // Give all early-exit transitions higher priority
                for tail in tails.mut_iter() {
                    let &(pos, _) = tail;
                    replace(tail, (pos, High));
                }
            }

            tails.push_all(last);
            tails
        },
        parse::Repeat(..) => fail!("repeats not implemented yet"),
        parse::Capture(..) => fail!("captures not implemented yet")
    }
}
<|endoftext|>


<commit_before>// check-pass
// edition:2018

#![feature(async_await)]
#![feature(existential_type)]
#![feature(impl_trait_in_bindings)]
//~^ WARNING the feature `impl_trait_in_bindings` is incomplete

// See issue 60414

/////////////////////////////////////////////
// Reduction to `impl Trait`

struct Foo<T>(T);

trait FooLike { type Output; }

impl<T> FooLike for Foo<T> {
    type Output = T;
}

mod impl_trait {
    use super::*;

    trait Trait {
        type Assoc;
    }

    /// `T::Assoc` should be normalized to `()` here.
    fn foo_pass<T: Trait<Assoc=()>>() -> impl FooLike<Output=T::Assoc> {
        Foo(())
    }
}

/////////////////////////////////////////////
// Same with lifetimes in the trait

mod lifetimes {
    use super::*;

    trait Trait<'a> {
        type Assoc;
    }

    /// Like above.
    fn foo2_pass<'a, T: Trait<'a, Assoc=()> + 'a>() -> impl FooLike<Output=T::Assoc> + 'a {
        Foo(())
    }

    /// Normalization to type containing bound region.
    fn foo2_pass2<'a, T: Trait<'a, Assoc=&'a ()> + 'a>() -> impl FooLike<Output=T::Assoc> + 'a {
        Foo(&())
    }
}

/////////////////////////////////////////////
// Reduction using `impl Trait` in bindings

mod impl_trait_in_bindings {
    struct Foo;

    trait FooLike { type Output; }

    impl FooLike for Foo {
        type Output = u32;
    }

    trait Trait {
        type Assoc;
    }

    fn foo<T: Trait<Assoc=u32>>() {
        let _: impl FooLike<Output=T::Assoc> = Foo;
    }
}

/////////////////////////////////////////////
// The same applied to `existential type`s

mod existential_types {
    trait Implemented {
        type Assoc;
    }
    impl<T> Implemented for T {
        type Assoc = u8;
    }

    trait Trait {
        type Out;
    }

    impl Trait for () {
        type Out = u8;
    }

    existential type Ex: Trait<Out = <() as Implemented>::Assoc>;

    fn define() -> Ex {
        ()
    }
}

fn main() {}
<commit_msg>rewrite the test to workaround #51525<commit_after>// check-pass
// edition:2018

#![feature(async_await)]
#![feature(existential_type)]
#![feature(impl_trait_in_bindings)]
//~^ WARNING the feature `impl_trait_in_bindings` is incomplete

// See issue 60414

/////////////////////////////////////////////
// Reduction to `impl Trait`

struct Foo<T>(T);

trait FooLike { type Output; }

impl<T> FooLike for Foo<T> {
    type Output = T;
}

mod impl_trait {
    use super::*;

    trait Trait {
        type Assoc;
    }

    /// `T::Assoc` should be normalized to `()` here.
    fn foo_pass<T: Trait<Assoc=()>>() -> impl FooLike<Output=T::Assoc> {
        Foo(())
    }
}

/////////////////////////////////////////////
// Same with lifetimes in the trait

mod lifetimes {
    use super::*;

    trait Trait<'a> {
        type Assoc;
    }

    /// Like above.
    ///
    /// FIXME(#51525) -- the shorter notation `T::Assoc` winds up referencing `'static` here
    fn foo2_pass<'a, T: Trait<'a, Assoc=()> + 'a>(
    ) -> impl FooLike<Output=<T as Trait<'a>>::Assoc> + 'a {
        Foo(())
    }

    /// Normalization to type containing bound region.
    ///
    /// FIXME(#51525) -- the shorter notation `T::Assoc` winds up referencing `'static` here
    fn foo2_pass2<'a, T: Trait<'a, Assoc=&'a ()> + 'a>(
    ) -> impl FooLike<Output=<T as Trait<'a>>::Assoc> + 'a {
        Foo(&())
    }
}

/////////////////////////////////////////////
// Reduction using `impl Trait` in bindings

mod impl_trait_in_bindings {
    struct Foo;

    trait FooLike { type Output; }

    impl FooLike for Foo {
        type Output = u32;
    }

    trait Trait {
        type Assoc;
    }

    fn foo<T: Trait<Assoc=u32>>() {
        let _: impl FooLike<Output=T::Assoc> = Foo;
    }
}

/////////////////////////////////////////////
// The same applied to `existential type`s

mod existential_types {
    trait Implemented {
        type Assoc;
    }
    impl<T> Implemented for T {
        type Assoc = u8;
    }

    trait Trait {
        type Out;
    }

    impl Trait for () {
        type Out = u8;
    }

    existential type Ex: Trait<Out = <() as Implemented>::Assoc>;

    fn define() -> Ex {
        ()
    }
}

fn main() {}
<|endoftext|>


<commit_before>use crate::utils::{
    in_macro_or_desugar, match_def_path,
    paths::{BEGIN_PANIC, BEGIN_PANIC_FMT},
    resolve_node, snippet_opt, span_lint_and_then,
};
use if_chain::if_chain;
use rustc::{
    declare_lint_pass, declare_tool_lint,
    hir::{intravisit::FnKind, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind},
    lint::{LateContext, LateLintPass, LintArray, LintPass},
};
use rustc_errors::Applicability;
use syntax::source_map::Span;

declare_clippy_lint! {
    /// **What it does:** Checks for missing return statements at the end of a block.
    ///
    /// **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers
    /// coming from other languages might prefer the expressiveness of `return`. It's possible to miss
    /// the last returning statement because the only difference is a missing `;`. Especially in bigger
    /// code with multiple return paths having a `return` keyword makes it easier to find the
    /// corresponding statements.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(x: usize) -> usize {
    ///     x
    /// }
    /// ```
    /// add return
    /// ```rust
    /// fn foo(x: usize) -> usize {
    ///     return x;
    /// }
    /// ```
    pub IMPLICIT_RETURN,
    restriction,
    "use a return statement like `return expr` instead of an expression"
}

declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);

static LINT_BREAK: &str = "change `break` to `return` as shown";
static LINT_RETURN: &str = "add `return` as shown";

fn lint(cx: &LateContext<'_, '_>, outer_span: Span, inner_span: Span, msg: &str) {
    let outer_span = span_to_outer_expn(outer_span);
    let inner_span = span_to_outer_expn(inner_span);

    span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing return statement", |db| {
        if let Some(snippet) = snippet_opt(cx, inner_span) {
            db.span_suggestion(
                outer_span,
                msg,
                format!("return {}", snippet),
                Applicability::MachineApplicable,
            );
        }
    });
}

fn span_to_outer_expn(span: Span) -> Span {
    if let Some(expr) = span.ctxt().outer_expn_info() {
        span_to_outer_expn(expr.call_site)
    } else {
        span
    }
}

fn expr_match(cx: &LateContext<'_, '_>, expr: &Expr) {
    match &expr.node {
        // loops could be using `break` instead of `return`
        ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => {
            if let Some(expr) = &block.expr {
                expr_match(cx, expr);
            }
            // only needed in the case of `break` with `;` at the end
            else if let Some(stmt) = block.stmts.last() {
                if_chain! {
                    if let StmtKind::Semi(expr, ..) = &stmt.node;
                    // make sure it's a break, otherwise we want to skip
                    if let ExprKind::Break(.., break_expr) = &expr.node;
                    if let Some(break_expr) = break_expr;
                    then {
                            lint(cx, expr.span, break_expr.span, LINT_BREAK);
                    }
                }
            }
        },
        // use `return` instead of `break`
        ExprKind::Break(.., break_expr) => {
            if let Some(break_expr) = break_expr {
                lint(cx, expr.span, break_expr.span, LINT_BREAK);
            }
        },
        ExprKind::Match(.., arms, source) => {
            let check_all_arms = match source {
                MatchSource::IfLetDesugar {
                    contains_else_clause: has_else,
                } => *has_else,
                _ => true,
            };

            if check_all_arms {
                for arm in arms {
                    expr_match(cx, &arm.body);
                }
            } else {
                expr_match(cx, &arms.first().expect("if let doesn't have a single arm").body);
            }
        },
        // skip if it already has a return statement
        ExprKind::Ret(..) => (),
        // make sure it's not a call that panics
        ExprKind::Call(expr, ..) => {
            if_chain! {
                if let ExprKind::Path(qpath) = &expr.node;
                if let Some(path_def_id) = resolve_node(cx, qpath, expr.hir_id).opt_def_id();
                if match_def_path(cx, path_def_id, &BEGIN_PANIC) ||
                    match_def_path(cx, path_def_id, &BEGIN_PANIC_FMT);
                then { }
                else {
                    lint(cx, expr.span, expr.span, LINT_RETURN)
                }
            }
        },
        // everything else is missing `return`
        _ => lint(cx, expr.span, expr.span, LINT_RETURN),
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitReturn {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        _: FnKind<'tcx>,
        _: &'tcx FnDecl,
        body: &'tcx Body,
        span: Span,
        _: HirId,
    ) {
        let def_id = cx.tcx.hir().body_owner_def_id(body.id());
        let mir = cx.tcx.optimized_mir(def_id);

        // checking return type through MIR, HIR is not able to determine inferred closure return types
        // make sure it's not a macro
        if !mir.return_ty().is_unit() && !in_macro_or_desugar(span) {
            expr_match(cx, &body.value);
        }
    }
}
<commit_msg>Auto merge of #4358 - lzutao:cleanup_implicit_return, r=flip1995<commit_after>use crate::utils::{
    in_macro_or_desugar, match_def_path,
    paths::{BEGIN_PANIC, BEGIN_PANIC_FMT},
    resolve_node, snippet_opt, span_lint_and_then,
};
use if_chain::if_chain;
use rustc::{
    declare_lint_pass, declare_tool_lint,
    hir::{intravisit::FnKind, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind},
    lint::{LateContext, LateLintPass, LintArray, LintPass},
};
use rustc_errors::Applicability;
use syntax::source_map::Span;

declare_clippy_lint! {
    /// **What it does:** Checks for missing return statements at the end of a block.
    ///
    /// **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers
    /// coming from other languages might prefer the expressiveness of `return`. It's possible to miss
    /// the last returning statement because the only difference is a missing `;`. Especially in bigger
    /// code with multiple return paths having a `return` keyword makes it easier to find the
    /// corresponding statements.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(x: usize) -> usize {
    ///     x
    /// }
    /// ```
    /// add return
    /// ```rust
    /// fn foo(x: usize) -> usize {
    ///     return x;
    /// }
    /// ```
    pub IMPLICIT_RETURN,
    restriction,
    "use a return statement like `return expr` instead of an expression"
}

declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);

static LINT_BREAK: &str = "change `break` to `return` as shown";
static LINT_RETURN: &str = "add `return` as shown";

fn lint(cx: &LateContext<'_, '_>, outer_span: Span, inner_span: Span, msg: &str) {
    let outer_span = outer_span.source_callsite();
    let inner_span = inner_span.source_callsite();

    span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing return statement", |db| {
        if let Some(snippet) = snippet_opt(cx, inner_span) {
            db.span_suggestion(
                outer_span,
                msg,
                format!("return {}", snippet),
                Applicability::MachineApplicable,
            );
        }
    });
}

fn expr_match(cx: &LateContext<'_, '_>, expr: &Expr) {
    match &expr.node {
        // loops could be using `break` instead of `return`
        ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => {
            if let Some(expr) = &block.expr {
                expr_match(cx, expr);
            }
            // only needed in the case of `break` with `;` at the end
            else if let Some(stmt) = block.stmts.last() {
                if_chain! {
                    if let StmtKind::Semi(expr, ..) = &stmt.node;
                    // make sure it's a break, otherwise we want to skip
                    if let ExprKind::Break(.., break_expr) = &expr.node;
                    if let Some(break_expr) = break_expr;
                    then {
                            lint(cx, expr.span, break_expr.span, LINT_BREAK);
                    }
                }
            }
        },
        // use `return` instead of `break`
        ExprKind::Break(.., break_expr) => {
            if let Some(break_expr) = break_expr {
                lint(cx, expr.span, break_expr.span, LINT_BREAK);
            }
        },
        ExprKind::Match(.., arms, source) => {
            let check_all_arms = match source {
                MatchSource::IfLetDesugar {
                    contains_else_clause: has_else,
                } => *has_else,
                _ => true,
            };

            if check_all_arms {
                for arm in arms {
                    expr_match(cx, &arm.body);
                }
            } else {
                expr_match(cx, &arms.first().expect("if let doesn't have a single arm").body);
            }
        },
        // skip if it already has a return statement
        ExprKind::Ret(..) => (),
        // make sure it's not a call that panics
        ExprKind::Call(expr, ..) => {
            if_chain! {
                if let ExprKind::Path(qpath) = &expr.node;
                if let Some(path_def_id) = resolve_node(cx, qpath, expr.hir_id).opt_def_id();
                if match_def_path(cx, path_def_id, &BEGIN_PANIC) ||
                    match_def_path(cx, path_def_id, &BEGIN_PANIC_FMT);
                then { }
                else {
                    lint(cx, expr.span, expr.span, LINT_RETURN)
                }
            }
        },
        // everything else is missing `return`
        _ => lint(cx, expr.span, expr.span, LINT_RETURN),
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitReturn {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        _: FnKind<'tcx>,
        _: &'tcx FnDecl,
        body: &'tcx Body,
        span: Span,
        _: HirId,
    ) {
        let def_id = cx.tcx.hir().body_owner_def_id(body.id());
        let mir = cx.tcx.optimized_mir(def_id);

        // checking return type through MIR, HIR is not able to determine inferred closure return types
        // make sure it's not a macro
        if !mir.return_ty().is_unit() && !in_macro_or_desugar(span) {
            expr_match(cx, &body.value);
        }
    }
}
<|endoftext|>


<commit_before>use gl;
use gl::types::*;
use types::{Color, Viewport, GLError};
use buffer::{ArrayBufferBinder, ElementArrayBufferBinder};
use program::{ProgramBinder, ProgramAttrib};
use texture_units::{TextureUnits};

pub struct Context {
    pub array_buffer: ArrayBufferBinder,
    pub element_array_buffer: ElementArrayBufferBinder,
    pub program: ProgramBinder,
    pub tex_units: TextureUnits
}

impl Context {
    pub unsafe fn current_context() -> Self {
        Context {
            array_buffer: ArrayBufferBinder,
            element_array_buffer: ElementArrayBufferBinder,
            program: ProgramBinder,
            tex_units: TextureUnits::current()
        }
    }

    pub fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    pub fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    pub fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }
}

#[macro_export]
macro_rules! bind_array_buffer {
    ($gl:expr, $buffer:expr) => {
        $gl.array_buffer.bind($buffer)
    }
}

#[macro_export]
macro_rules! bind_element_array_buffer {
    ($gl:expr, $buffer:expr) => {
        $gl.element_array_buffer.bind($buffer)
    }
}

#[macro_export]
macro_rules! use_program {
    ($gl:expr, $program:expr) => {
        $gl.program.bind($program)
    }
}

// HACK: Workaround for issue described here:
// https://www.reddit.com/r/rust/comments/339yj3/tuple_indexing_in_a_macro/cqiyv4n
#[macro_export]
macro_rules! _glitter_expr {
    ($x:expr) => ($x)
}

#[macro_export]
macro_rules! active_texture {
    ($gl:expr, $idx:tt) => {
        _glitter_expr!($gl.tex_units.$idx.active())
    }
}

#[macro_export]
macro_rules! active_texture_n {
    ($gl:expr, $idx:expr) => {
        $gl.tex_units.nth_unit($idx).active()
    }
}
<commit_msg>Add `FramebufferBinder` field to `Context`<commit_after>use gl;
use gl::types::*;
use types::{Color, Viewport, GLError};
use buffer::{ArrayBufferBinder, ElementArrayBufferBinder};
use program::{ProgramBinder, ProgramAttrib};
use framebuffer::FramebufferBinder;
use texture_units::{TextureUnits};

pub struct Context {
    pub array_buffer: ArrayBufferBinder,
    pub element_array_buffer: ElementArrayBufferBinder,
    pub program: ProgramBinder,
    pub framebuffer: FramebufferBinder,
    pub tex_units: TextureUnits
}

impl Context {
    pub unsafe fn current_context() -> Self {
        Context {
            array_buffer: ArrayBufferBinder,
            element_array_buffer: ElementArrayBufferBinder,
            program: ProgramBinder,
            framebuffer: FramebufferBinder,
            tex_units: TextureUnits::current()
        }
    }

    pub fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    pub fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    pub fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }
}

#[macro_export]
macro_rules! bind_array_buffer {
    ($gl:expr, $buffer:expr) => {
        $gl.array_buffer.bind($buffer)
    }
}

#[macro_export]
macro_rules! bind_element_array_buffer {
    ($gl:expr, $buffer:expr) => {
        $gl.element_array_buffer.bind($buffer)
    }
}

#[macro_export]
macro_rules! use_program {
    ($gl:expr, $program:expr) => {
        $gl.program.bind($program)
    }
}

// HACK: Workaround for issue described here:
// https://www.reddit.com/r/rust/comments/339yj3/tuple_indexing_in_a_macro/cqiyv4n
#[macro_export]
macro_rules! _glitter_expr {
    ($x:expr) => ($x)
}

#[macro_export]
macro_rules! active_texture {
    ($gl:expr, $idx:tt) => {
        _glitter_expr!($gl.tex_units.$idx.active())
    }
}

#[macro_export]
macro_rules! active_texture_n {
    ($gl:expr, $idx:expr) => {
        $gl.tex_units.nth_unit($idx).active()
    }
}
<|endoftext|>


<commit_before>//! The initializer module parses arguments and verfies commands are valid
//! The creator module handles creating the muxed directory and project files.
#![allow(experimental)]

use std::io::{File,fs};
use std::io::process::Command;
use std::path::posix::Path;
use std::os::homedir;
use libc::funcs::c95::stdlib::system;
#[cfg(test)] use std::rand::random;
#[cfg(test)] use std::finally::Finally;

static TEMPLATE: &'static str = include_str!("creator/template.yml");
static DEFAULT_MUXED_DIR: &'static str = "muxed";

pub fn new(name: &str) {
    let muxed_dir = if muxed_dir_exists(&DEFAULT_MUXED_DIR.to_string()) {
        Path::new(format!("{}/.{}/", homedir_string(), &DEFAULT_MUXED_DIR.to_string()))
    } else {
        create_muxed_dir(&DEFAULT_MUXED_DIR.to_string())
    };

    let path = &Path::new(format!("{}/{}", muxed_dir.display(), name));
    if !path.exists() {
        create_project_file(path)
    } else {
        println!("Project already exists.");
    }
}

fn default_editor_set() -> bool {
  let output = match Command::new("which").arg("$EDITOR").output() {
      Ok(output) => output.output.to_string(),
      Err(e) => fail!("failed to execute process: {}", e),
  };

  !output.is_empty()
}

#[cfg(not(test))] fn open_project_file(path: &Path) {
    let method = format!("$EDITOR {}", path.display()).to_c_str();
    unsafe { system(method.unwrap()); };
}

#[cfg(test)] fn open_project_file(_path: &Path) { }

/// Copy and create the new project file from a template. Attempt to open the
/// users default editor to make changes.
fn create_project_file(path: &Path) {
    match File::create(path).write(TEMPLATE.as_bytes()) {
        Ok(()) => (),
        Err(_e) => println!("Failed to create project {}", path.filename()),
    }
}

/// Create the muxed directory and return the path if creation is successful.
fn create_muxed_dir(name: &String) -> Path {
    let path = &Path::new(format!("{}/.{}", homedir_string(), name));
    match fs::mkdir(path, ::std::io::UserRWX) {
        Ok(()) => (),
        Err(_e) => println!("Failed to create project {}", path.filename()),
    }

    path.clone()
}

/// Return a boolean if the ~/.muxed/ dir exists.
fn muxed_dir_exists(name: &String) -> bool {
    let path = &Path::new(format!("{}/.{}", homedir_string(), name));
    path.exists()
}

/// Return the users current homedir as a string.
fn homedir_string() -> String {
    let home_unwrap = homedir().unwrap();
    format!("{}", home_unwrap.display())
}

/// Test helper to standardize how random files and directories are generated.
#[cfg(test)]
fn random_name() -> String {
    format!("test_{}", random::<f64>())
}

#[test]
fn muxed_dir_exists_returns_false() {
    assert!(!muxed_dir_exists(&random_name()));
}

#[test]
fn muxed_dir_exists_returns_true() {
    let dir = random_name();
    create_muxed_dir(&dir);
    assert!(muxed_dir_exists(&dir));

    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    match fs::rmdir_recursive(muxed_path) {
        Ok(()) => (), // succeeded
        Err(e) => println!("Failed to remove the path {} with error {}", muxed_path.display(), e),
    }
}

#[test]
fn creates_muxed_dir() {
    let dir = random_name();
    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    create_muxed_dir(&dir);
    assert!(muxed_path.exists());
    match fs::rmdir_recursive(muxed_path) {
        Ok(()) => (), // succeeded
        Err(e) => println!("Failed to remove the path {} with error {}", muxed_path.display(), e),
    }
}

#[test]
fn new_writes_file_to_muxed_dir() {
    let name = random_name();
    let path = &Path::new(format!("{}/.muxed/{}", homedir_string(), name));
    new(name.as_slice());
    assert!(path.exists());
    match fs::unlink(path) {
        Ok(()) => (), // succeeded
        Err(e) => println!("Failed to unlink the path {} with error {}", path.display(), e),
    }
}

#[test]
// TODO: Fix this test so it verifies something better.
fn new_doesnt_overwrite_existing_file() {
    let name = random_name();
    let path = &Path::new(format!("{}/.muxed/{}", homedir_string(), name));
    new(name.as_slice());
    (|| {
        new(name.as_slice());
    }).finally(|| {
        match fs::unlink(path) {
            Ok(()) => (), // succeeded
            Err(e) => println!("Failed to unlink the path {} with error {}", path.display(), e),
        }
    })
}
<commit_msg>Add a conditional to only open the file only if a default editor is set.<commit_after>//! The initializer module parses arguments and verfies commands are valid
//! The creator module handles creating the muxed directory and project files.
#![allow(experimental)]

use std::io::{File,fs};
use std::io::process::Command;
use std::path::posix::Path;
use std::os::homedir;
use libc::funcs::c95::stdlib::system;
#[cfg(test)] use std::rand::random;
#[cfg(test)] use std::finally::Finally;

static TEMPLATE: &'static str = include_str!("creator/template.yml");
static DEFAULT_MUXED_DIR: &'static str = "muxed";

pub fn new(name: &str) {
    let muxed_dir = if muxed_dir_exists(&DEFAULT_MUXED_DIR.to_string()) {
        Path::new(format!("{}/.{}/", homedir_string(), &DEFAULT_MUXED_DIR.to_string()))
    } else {
        create_muxed_dir(&DEFAULT_MUXED_DIR.to_string())
    };

    let path = &Path::new(format!("{}/{}", muxed_dir.display(), name));
    if !path.exists() {
        create_project_file(path);

        match default_editor_set() {
            true  => open_project_file(path),
            false => println!("Default editor is not set.")
        }
    } else {
        println!("Project already exists.");
    }
}

fn default_editor_set() -> bool {
  let output = match Command::new("which").arg("$EDITOR").output() {
      Ok(output) => output.output.to_string(),
      Err(e) => fail!("failed to execute process: {}", e),
  };

  !output.is_empty()
}

#[cfg(not(test))] fn open_project_file(path: &Path) {
    let method = format!("$EDITOR {}", path.display()).to_c_str();
    unsafe { system(method.unwrap()); };
}

#[cfg(test)] fn open_project_file(_path: &Path) { }

/// Copy and create the new project file from a template. Attempt to open the
/// users default editor to make changes.
fn create_project_file(path: &Path) {
    match File::create(path).write(TEMPLATE.as_bytes()) {
        Ok(()) => (),
        Err(_e) => println!("Failed to create project {}", path.filename()),
    }
}

/// Create the muxed directory and return the path if creation is successful.
fn create_muxed_dir(name: &String) -> Path {
    let path = &Path::new(format!("{}/.{}", homedir_string(), name));
    match fs::mkdir(path, ::std::io::UserRWX) {
        Ok(()) => (),
        Err(_e) => println!("Failed to create project {}", path.filename()),
    }

    path.clone()
}

/// Return a boolean if the ~/.muxed/ dir exists.
fn muxed_dir_exists(name: &String) -> bool {
    let path = &Path::new(format!("{}/.{}", homedir_string(), name));
    path.exists()
}

/// Return the users current homedir as a string.
fn homedir_string() -> String {
    let home_unwrap = homedir().unwrap();
    format!("{}", home_unwrap.display())
}

/// Test helper to standardize how random files and directories are generated.
#[cfg(test)]
fn random_name() -> String {
    format!("test_{}", random::<f64>())
}

#[test]
fn muxed_dir_exists_returns_false() {
    assert!(!muxed_dir_exists(&random_name()));
}

#[test]
fn muxed_dir_exists_returns_true() {
    let dir = random_name();
    create_muxed_dir(&dir);
    assert!(muxed_dir_exists(&dir));

    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    match fs::rmdir_recursive(muxed_path) {
        Ok(()) => (), // succeeded
        Err(e) => println!("Failed to remove the path {} with error {}", muxed_path.display(), e),
    }
}

#[test]
fn creates_muxed_dir() {
    let dir = random_name();
    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    create_muxed_dir(&dir);
    assert!(muxed_path.exists());
    match fs::rmdir_recursive(muxed_path) {
        Ok(()) => (), // succeeded
        Err(e) => println!("Failed to remove the path {} with error {}", muxed_path.display(), e),
    }
}

#[test]
fn new_writes_file_to_muxed_dir() {
    let name = random_name();
    let path = &Path::new(format!("{}/.muxed/{}", homedir_string(), name));
    new(name.as_slice());
    assert!(path.exists());
    match fs::unlink(path) {
        Ok(()) => (), // succeeded
        Err(e) => println!("Failed to unlink the path {} with error {}", path.display(), e),
    }
}

#[test]
// TODO: Fix this test so it verifies something better.
fn new_doesnt_overwrite_existing_file() {
    let name = random_name();
    let path = &Path::new(format!("{}/.muxed/{}", homedir_string(), name));
    new(name.as_slice());
    (|| {
        new(name.as_slice());
    }).finally(|| {
        match fs::unlink(path) {
            Ok(()) => (), // succeeded
            Err(e) => println!("Failed to unlink the path {} with error {}", path.display(), e),
        }
    })
}
<|endoftext|>