<commit_before>use std::mem;
use std::ptr;
use std::sync::Arc;

use device::Device;
use format::Format;
use image::MipmapsCount;
use memory::ChunkProperties;
use sync::SharingMode;

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

/// A storage for pixels or arbitrary data.
pub struct UnsafeImage {
    image: vk::Image,
    device: Arc<Device>,
    usage: vk::ImageUsageFlagBits,
    format: Format,

    dimensions: [f32; 3],
    samples: u32,
    mipmaps: u32,

    // `vkDestroyImage` is called only if `needs_destruction` is true.
    needs_destruction: bool,
}

impl UnsafeImage {
    /// Creates a new image and allocates memory for it.
    ///
    /// # Panic
    ///
    /// - Panicks if one of the dimensions is 0.
    /// - Panicks if the number of mipmaps is 0.
    /// - Panicks if the number of samples is 0.
    ///
    pub fn new<'a, M, Mi, Sh>(device: &Arc<Device>, usage: &Usage, memory: M, format: Format,
                              dimensions: Dimensions, num_samples: u32, mipmaps: Mi, sharing: Sh,
                              linear_tiling: bool, preinitialized_layout: bool)
                              -> Result<UnsafeImage, OomError>
        where Mi: Into<MipmapsCount>, Sh: Into<SharingMode>,
              M: FnOnce(usize, usize, u32) -> ChunkProperties<'a>
    {
        // Preprocessing parameters.
        let sharing = sharing.into();
        let usage = usage.to_usage_bits();
        assert!(num_samples >= 1);

        // Compute the maximum number of mipmaps.
        // TODO: only compte if necessary?
        let max_mipmaps = {
            let smallest_dim: u32 = match dimensions {
                Dimensions::Dim1d { width } | Dimensions::Dim1dArray { width, .. } => width,
                Dimensions::Dim2d { width, height } | Dimensions::Dim2dArray { width, height, .. } => {
                    if width < height { width } else { height }
                },
                Dimensions::Dim3d { width, height, depth } => {
                    if width < height {
                        if depth < width { depth } else { width }
                    } else {
                        if depth < height { depth } else { height }
                    }
                },
            };

            assert!(smallest_dim >= 1);
            32 - smallest_dim.leading_zeros()
        };

        // Compute the number of mipmaps.
        let mipmaps = match mipmaps.into() {
            MipmapsCount::Specific(num) => {
                assert!(num >= 1);
                assert!(num <= max_mipmaps);
                num
            },
            MipmapsCount::Max => max_mipmaps,
            MipmapsCount::One => 1,
        };

        let vk = device.pointers();

        // TODO: check for limits
        let (ty, extent, array_layers, dims) = match dimensions {
            Dimensions::Dim1d { width } => {
                let extent = vk::Extent3D { width: width, height: 1, depth: 1 };
                let dims = [width as f32, 1.0, 1.0];
                (vk::IMAGE_TYPE_1D, extent, 1, dims)
            },
            Dimensions::Dim1dArray { width, array_layers } => {
                let extent = vk::Extent3D { width: width, height: 1, depth: 1 };
                let dims = [width as f32, 1.0, 1.0];
                (vk::IMAGE_TYPE_1D, extent, array_layers, dims)
            },
            Dimensions::Dim2d { width, height } => {
                let extent = vk::Extent3D { width: width, height: height, depth: 1 };
                let dims = [width as f32, height as f32, 1.0];
                (vk::IMAGE_TYPE_2D, extent, 1, dims)
            },
            Dimensions::Dim2dArray { width, height, array_layers } => {
                let extent = vk::Extent3D { width: width, height: height, depth: 1 };
                let dims = [width as f32, height as f32, 1.0];
                (vk::IMAGE_TYPE_2D, extent, array_layers, dims)
            },
            Dimensions::Dim3d { width, height, depth } => {
                let extent = vk::Extent3D { width: width, height: height, depth: depth };
                let dims = [width as f32, height as f32, depth as f32];
                (vk::IMAGE_TYPE_3D, extent, 1, dims)
            },
        };

        let image = unsafe {
            let (sh_mode, sh_count, sh_indices) = match sharing {
                SharingMode::Exclusive(id) => (vk::SHARING_MODE_EXCLUSIVE, 0, ptr::null()),
                SharingMode::Concurrent(ref ids) => (vk::SHARING_MODE_CONCURRENT, ids.len() as u32,
                                                     ids.as_ptr()),
            };

            let infos = vk::ImageCreateInfo {
                sType: vk::STRUCTURE_TYPE_IMAGE_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,                               // TODO:
                imageType: ty,
                format: format as u32,
                extent: extent,
                mipLevels: mipmaps,
                arrayLayers: array_layers,
                samples: num_samples,
                tiling: if linear_tiling {
                    vk::IMAGE_TILING_LINEAR     // FIXME: check whether it's supported
                } else {
                    vk::IMAGE_TILING_OPTIMAL
                },
                usage: usage,
                sharingMode: sh_mode,
                queueFamilyIndexCount: sh_count,
                pQueueFamilyIndices: sh_indices,
                initialLayout: if preinitialized_layout {
                    vk::IMAGE_LAYOUT_PREINITIALIZED
                } else {
                    vk::IMAGE_LAYOUT_UNDEFINED
                },
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateImage(device.internal_object(), &infos,
                                             ptr::null(), &mut output)));
            output
        };

        let mem_reqs: vk::MemoryRequirements = unsafe {
            let mut output = mem::uninitialized();
            vk.GetImageMemoryRequirements(device.internal_object(), image, &mut output);
            output
        };

        unsafe {
            match memory(mem_reqs.size as usize, mem_reqs.alignment as usize, mem_reqs.memoryTypeBits) {
                ChunkProperties::Regular { memory, offset, .. } => {
                    try!(check_errors(vk.BindImageMemory(device.internal_object(), image,
                                                         memory.internal_object(),
                                                         offset as vk::DeviceSize)));
                },
                _ => unimplemented!()
            }
        }

        Ok(UnsafeImage {
            device: device.clone(),
            image: image,
            usage: usage,
            format: format,
            dimensions: dims,
            samples: num_samples,
            mipmaps: mipmaps,
            needs_destruction: true,
        })
    }

    /// Creates an image from a raw handle. The image won't be destroyed.
    ///
    /// This function is for example used at the swapchain's initialization.
    pub unsafe fn from_raw_unowned<M>(device: &Arc<Device>, handle: u64, memory: M,
                                      sharing: SharingMode, usage: u32, format: Format,
                                      dimensions: Dimensions, samples: u32, mipmaps: u32)
                                      -> UnsafeImage
    {
        unimplemented!()/*
        ImagePrototype{
            image: Image {
                device: device.clone(),
                image: handle,
                memory: memory,
                usage: usage,
                format: format,
                dimensions: dimensions.clone(),
                samples: samples,
                mipmaps: mipmaps,
                sharing: sharing,
                needs_destruction: false,
                layout: Layout::Undefined,
            },
        }*/
    }
}

unsafe impl VulkanObject for UnsafeImage {
    type Object = vk::Image;

    #[inline]
    fn internal_object(&self) -> vk::Image {
        self.image
    }
}

impl Drop for UnsafeImage {
    #[inline]
    fn drop(&mut self) {
        if !self.needs_destruction {
            return;
        }

        unsafe {
            let vk = self.device.pointers();
            vk.DestroyImage(self.device.internal_object(), self.image, ptr::null());
        }
    }
}

pub enum Dimensions {
    Dim1d { width: u32 },
    Dim1dArray { width: u32, array_layers: u32 },
    Dim2d { width: u32, height: u32 },
    Dim2dArray { width: u32, height: u32, array_layers: u32 },
    Dim3d { width: u32, height: u32, depth: u32 }
}

/// Describes how an image is going to be used. This is **not** an optimization.
///
/// If you try to use an image in a way that you didn't declare, a panic will happen.
// TODO: enforce the fact that `transient_attachment` can't be set at the same time as other bits
#[derive(Debug, Copy, Clone)]
pub struct Usage {
    pub transfer_source: bool,
    pub transfer_dest: bool,
    pub sampled: bool,
    pub storage: bool,
    pub color_attachment: bool,
    pub depth_stencil_attachment: bool,
    pub transient_attachment: bool,
    pub input_attachment: bool,
}

impl Usage {
    /// Builds a `Usage` with all values set to true. Can be used for quick prototyping.
    #[inline]
    pub fn all() -> Usage {
        Usage {
            transfer_source: true,
            transfer_dest: true,
            sampled: true,
            storage: true,
            color_attachment: true,
            depth_stencil_attachment: true,
            transient_attachment: true,
            input_attachment: true,
        }
    }

    /// Builds a `Usage` with all values set to false. Useful as a default value.
    ///
    /// # Example
    ///
    /// ```rust
    /// use vulkano::image::Usage as ImageUsage;
    ///
    /// let _usage = ImageUsage {
    ///     transfer_dest: true,
    ///     sampled: true,
    ///     .. ImageUsage::none()
    /// };
    /// ```
    #[inline]
    pub fn none() -> Usage {
        Usage {
            transfer_source: false,
            transfer_dest: false,
            sampled: false,
            storage: false,
            color_attachment: false,
            depth_stencil_attachment: false,
            transient_attachment: false,
            input_attachment: false,
        }
    }

    #[doc(hidden)]
    #[inline]
    pub fn to_usage_bits(&self) -> vk::ImageUsageFlagBits {
        let mut result = 0;
        if self.transfer_source { result |= vk::IMAGE_USAGE_TRANSFER_SRC_BIT; }
        if self.transfer_dest { result |= vk::IMAGE_USAGE_TRANSFER_DST_BIT; }
        if self.sampled { result |= vk::IMAGE_USAGE_SAMPLED_BIT; }
        if self.storage { result |= vk::IMAGE_USAGE_STORAGE_BIT; }
        if self.color_attachment { result |= vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT; }
        if self.depth_stencil_attachment { result |= vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; }
        if self.transient_attachment { result |= vk::IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; }
        if self.input_attachment { result |= vk::IMAGE_USAGE_INPUT_ATTACHMENT_BIT; }
        result
    }

    #[inline]
    #[doc(hidden)]
    pub fn from_bits(val: u32) -> Usage {
        Usage {
            transfer_source: (val & vk::IMAGE_USAGE_TRANSFER_SRC_BIT) != 0,
            transfer_dest: (val & vk::IMAGE_USAGE_TRANSFER_DST_BIT) != 0,
            sampled: (val & vk::IMAGE_USAGE_SAMPLED_BIT) != 0,
            storage: (val & vk::IMAGE_USAGE_STORAGE_BIT) != 0,
            color_attachment: (val & vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0,
            depth_stencil_attachment: (val & vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0,
            transient_attachment: (val & vk::IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) != 0,
            input_attachment: (val & vk::IMAGE_USAGE_INPUT_ATTACHMENT_BIT) != 0,
        }
    }
}
<commit_msg>Add UnsafeImageView<commit_after>use std::mem;
use std::ptr;
use std::sync::Arc;

use device::Device;
use format::Format;
use format::FormatTy;
use image::MipmapsCount;
use memory::ChunkProperties;
use sync::SharingMode;

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

/// A storage for pixels or arbitrary data.
pub struct UnsafeImage {
    image: vk::Image,
    device: Arc<Device>,
    usage: vk::ImageUsageFlagBits,
    format: Format,

    dimensions: [f32; 3],
    array_layers: u32,
    samples: u32,
    mipmaps: u32,

    // `vkDestroyImage` is called only if `needs_destruction` is true.
    needs_destruction: bool,
}

impl UnsafeImage {
    /// Creates a new image and allocates memory for it.
    ///
    /// # Panic
    ///
    /// - Panicks if one of the dimensions is 0.
    /// - Panicks if the number of mipmaps is 0.
    /// - Panicks if the number of samples is 0.
    ///
    pub unsafe fn new<'a, M, Mi, Sh>(device: &Arc<Device>, usage: &Usage, memory: M, format: Format,
                                     dimensions: Dimensions, num_samples: u32, mipmaps: Mi, sharing: Sh,
                                     linear_tiling: bool, preinitialized_layout: bool)
                                     -> Result<UnsafeImage, OomError>
        where Mi: Into<MipmapsCount>, Sh: Into<SharingMode>,
              M: FnOnce(usize, usize, u32) -> ChunkProperties<'a>
    {
        // Preprocessing parameters.
        let sharing = sharing.into();
        let usage = usage.to_usage_bits();
        assert!(num_samples >= 1);

        // Compute the maximum number of mipmaps.
        // TODO: only compte if necessary?
        let max_mipmaps = {
            let smallest_dim: u32 = match dimensions {
                Dimensions::Dim1d { width } | Dimensions::Dim1dArray { width, .. } => width,
                Dimensions::Dim2d { width, height } | Dimensions::Dim2dArray { width, height, .. } => {
                    if width < height { width } else { height }
                },
                Dimensions::Dim3d { width, height, depth } => {
                    if width < height {
                        if depth < width { depth } else { width }
                    } else {
                        if depth < height { depth } else { height }
                    }
                },
            };

            assert!(smallest_dim >= 1);
            32 - smallest_dim.leading_zeros()
        };

        // Compute the number of mipmaps.
        let mipmaps = match mipmaps.into() {
            MipmapsCount::Specific(num) => {
                assert!(num >= 1);
                assert!(num <= max_mipmaps);
                num
            },
            MipmapsCount::Max => max_mipmaps,
            MipmapsCount::One => 1,
        };

        let vk = device.pointers();

        // TODO: check for limits
        let (ty, extent, array_layers, dims) = match dimensions {
            Dimensions::Dim1d { width } => {
                let extent = vk::Extent3D { width: width, height: 1, depth: 1 };
                let dims = [width as f32, 1.0, 1.0];
                (vk::IMAGE_TYPE_1D, extent, 1, dims)
            },
            Dimensions::Dim1dArray { width, array_layers } => {
                let extent = vk::Extent3D { width: width, height: 1, depth: 1 };
                let dims = [width as f32, 1.0, 1.0];
                (vk::IMAGE_TYPE_1D, extent, array_layers, dims)
            },
            Dimensions::Dim2d { width, height } => {
                let extent = vk::Extent3D { width: width, height: height, depth: 1 };
                let dims = [width as f32, height as f32, 1.0];
                (vk::IMAGE_TYPE_2D, extent, 1, dims)
            },
            Dimensions::Dim2dArray { width, height, array_layers } => {
                let extent = vk::Extent3D { width: width, height: height, depth: 1 };
                let dims = [width as f32, height as f32, 1.0];
                (vk::IMAGE_TYPE_2D, extent, array_layers, dims)
            },
            Dimensions::Dim3d { width, height, depth } => {
                let extent = vk::Extent3D { width: width, height: height, depth: depth };
                let dims = [width as f32, height as f32, depth as f32];
                (vk::IMAGE_TYPE_3D, extent, 1, dims)
            },
        };

        let image = {
            let (sh_mode, sh_count, sh_indices) = match sharing {
                SharingMode::Exclusive(id) => (vk::SHARING_MODE_EXCLUSIVE, 0, ptr::null()),
                SharingMode::Concurrent(ref ids) => (vk::SHARING_MODE_CONCURRENT, ids.len() as u32,
                                                     ids.as_ptr()),
            };

            let infos = vk::ImageCreateInfo {
                sType: vk::STRUCTURE_TYPE_IMAGE_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,                               // TODO:
                imageType: ty,
                format: format as u32,
                extent: extent,
                mipLevels: mipmaps,
                arrayLayers: array_layers,
                samples: num_samples,
                tiling: if linear_tiling {
                    vk::IMAGE_TILING_LINEAR     // FIXME: check whether it's supported
                } else {
                    vk::IMAGE_TILING_OPTIMAL
                },
                usage: usage,
                sharingMode: sh_mode,
                queueFamilyIndexCount: sh_count,
                pQueueFamilyIndices: sh_indices,
                initialLayout: if preinitialized_layout {
                    vk::IMAGE_LAYOUT_PREINITIALIZED
                } else {
                    vk::IMAGE_LAYOUT_UNDEFINED
                },
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateImage(device.internal_object(), &infos,
                                             ptr::null(), &mut output)));
            output
        };

        let mem_reqs: vk::MemoryRequirements = {
            let mut output = mem::uninitialized();
            vk.GetImageMemoryRequirements(device.internal_object(), image, &mut output);
            output
        };

        {
            match memory(mem_reqs.size as usize, mem_reqs.alignment as usize, mem_reqs.memoryTypeBits) {
                ChunkProperties::Regular { memory, offset, .. } => {
                    try!(check_errors(vk.BindImageMemory(device.internal_object(), image,
                                                         memory.internal_object(),
                                                         offset as vk::DeviceSize)));
                },
                _ => unimplemented!()
            }
        }

        Ok(UnsafeImage {
            device: device.clone(),
            image: image,
            usage: usage,
            format: format,
            dimensions: dims,
            array_layers: array_layers,
            samples: num_samples,
            mipmaps: mipmaps,
            needs_destruction: true,
        })
    }

    /// Creates an image from a raw handle. The image won't be destroyed.
    ///
    /// This function is for example used at the swapchain's initialization.
    pub unsafe fn from_raw_unowned<M>(device: &Arc<Device>, handle: u64, memory: M,
                                      sharing: SharingMode, usage: u32, format: Format,
                                      dimensions: Dimensions, samples: u32, mipmaps: u32)
                                      -> UnsafeImage
    {
        unimplemented!()/*
        ImagePrototype{
            image: Image {
                device: device.clone(),
                image: handle,
                memory: memory,
                usage: usage,
                format: format,
                dimensions: dimensions.clone(),
                samples: samples,
                mipmaps: mipmaps,
                sharing: sharing,
                needs_destruction: false,
                layout: Layout::Undefined,
            },
        }*/
    }
}

unsafe impl VulkanObject for UnsafeImage {
    type Object = vk::Image;

    #[inline]
    fn internal_object(&self) -> vk::Image {
        self.image
    }
}

impl Drop for UnsafeImage {
    #[inline]
    fn drop(&mut self) {
        if !self.needs_destruction {
            return;
        }

        unsafe {
            let vk = self.device.pointers();
            vk.DestroyImage(self.device.internal_object(), self.image, ptr::null());
        }
    }
}

pub struct UnsafeImageView {
    view: vk::ImageView,
    device: Arc<Device>,
    identity_swizzle: bool,
}

impl UnsafeImageView {
    /// Creates a new view from an image.
    ///
    /// Note that you must create the view with identity swizzling if you want to use this view
    /// as a framebuffer attachment.
    pub unsafe fn new(image: &UnsafeImage) -> Result<UnsafeImageView, OomError> {
        let vk = image.device.pointers();

        let aspect_mask = match image.format.ty() {
            FormatTy::Float | FormatTy::Uint | FormatTy::Sint | FormatTy::Compressed => {
                vk::IMAGE_ASPECT_COLOR_BIT
            },
            FormatTy::Depth => vk::IMAGE_ASPECT_DEPTH_BIT,
            FormatTy::Stencil => vk::IMAGE_ASPECT_STENCIL_BIT,
            FormatTy::DepthStencil => vk::IMAGE_ASPECT_DEPTH_BIT | vk::IMAGE_ASPECT_STENCIL_BIT,
        };

        let view = {
            let infos = vk::ImageViewCreateInfo {
                sType: vk::STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                image: image.internal_object(),
                viewType: vk::IMAGE_VIEW_TYPE_2D,     // FIXME:
                format: image.format as u32,
                components: vk::ComponentMapping { r: 0, g: 0, b: 0, a: 0 },     // FIXME:
                subresourceRange: vk::ImageSubresourceRange {
                    aspectMask: aspect_mask,
                    baseMipLevel: 0,            // TODO:
                    levelCount: image.mipmaps,          // TODO:
                    baseArrayLayer: 0,          // TODO:
                    layerCount: 1,          // TODO:
                },
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateImageView(image.device.internal_object(), &infos,
                                                 ptr::null(), &mut output)));
            output
        };

        Ok(UnsafeImageView {
            view: view,
            device: image.device.clone(),
            identity_swizzle: true,     // FIXME:
        })
    }
}

impl Drop for UnsafeImageView {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroyImageView(self.device.internal_object(), self.view, ptr::null());
        }
    }
}

pub enum Dimensions {
    Dim1d { width: u32 },
    Dim1dArray { width: u32, array_layers: u32 },
    Dim2d { width: u32, height: u32 },
    Dim2dArray { width: u32, height: u32, array_layers: u32 },
    Dim3d { width: u32, height: u32, depth: u32 }
}

/// Describes how an image is going to be used. This is **not** an optimization.
///
/// If you try to use an image in a way that you didn't declare, a panic will happen.
// TODO: enforce the fact that `transient_attachment` can't be set at the same time as other bits
#[derive(Debug, Copy, Clone)]
pub struct Usage {
    pub transfer_source: bool,
    pub transfer_dest: bool,
    pub sampled: bool,
    pub storage: bool,
    pub color_attachment: bool,
    pub depth_stencil_attachment: bool,
    pub transient_attachment: bool,
    pub input_attachment: bool,
}

impl Usage {
    /// Builds a `Usage` with all values set to true. Can be used for quick prototyping.
    #[inline]
    pub fn all() -> Usage {
        Usage {
            transfer_source: true,
            transfer_dest: true,
            sampled: true,
            storage: true,
            color_attachment: true,
            depth_stencil_attachment: true,
            transient_attachment: true,
            input_attachment: true,
        }
    }

    /// Builds a `Usage` with all values set to false. Useful as a default value.
    ///
    /// # Example
    ///
    /// ```rust
    /// use vulkano::image::Usage as ImageUsage;
    ///
    /// let _usage = ImageUsage {
    ///     transfer_dest: true,
    ///     sampled: true,
    ///     .. ImageUsage::none()
    /// };
    /// ```
    #[inline]
    pub fn none() -> Usage {
        Usage {
            transfer_source: false,
            transfer_dest: false,
            sampled: false,
            storage: false,
            color_attachment: false,
            depth_stencil_attachment: false,
            transient_attachment: false,
            input_attachment: false,
        }
    }

    #[doc(hidden)]
    #[inline]
    pub fn to_usage_bits(&self) -> vk::ImageUsageFlagBits {
        let mut result = 0;
        if self.transfer_source { result |= vk::IMAGE_USAGE_TRANSFER_SRC_BIT; }
        if self.transfer_dest { result |= vk::IMAGE_USAGE_TRANSFER_DST_BIT; }
        if self.sampled { result |= vk::IMAGE_USAGE_SAMPLED_BIT; }
        if self.storage { result |= vk::IMAGE_USAGE_STORAGE_BIT; }
        if self.color_attachment { result |= vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT; }
        if self.depth_stencil_attachment { result |= vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; }
        if self.transient_attachment { result |= vk::IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; }
        if self.input_attachment { result |= vk::IMAGE_USAGE_INPUT_ATTACHMENT_BIT; }
        result
    }

    #[inline]
    #[doc(hidden)]
    pub fn from_bits(val: u32) -> Usage {
        Usage {
            transfer_source: (val & vk::IMAGE_USAGE_TRANSFER_SRC_BIT) != 0,
            transfer_dest: (val & vk::IMAGE_USAGE_TRANSFER_DST_BIT) != 0,
            sampled: (val & vk::IMAGE_USAGE_SAMPLED_BIT) != 0,
            storage: (val & vk::IMAGE_USAGE_STORAGE_BIT) != 0,
            color_attachment: (val & vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0,
            depth_stencil_attachment: (val & vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0,
            transient_attachment: (val & vk::IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) != 0,
            input_attachment: (val & vk::IMAGE_USAGE_INPUT_ATTACHMENT_BIT) != 0,
        }
    }
}
<|endoftext|>


<commit_before>extern crate curl;

use std::str;
use std::time::Duration;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
    })
}

use curl::easy::{Easy, Form};

use server::Server;
mod server;

fn handle() -> Easy {
    let mut e = Easy::new();
    t!(e.timeout(Duration::new(20, 0)));
    return e
}

#[test]
fn custom() {
    let s = Server::new();
    s.receive("\
POST / HTTP/1.1\r\n\
Host: 127.0.0.1:$PORT\r\n\
Accept: */*\r\n\
Content-Length: 142\r\n\
Expect: 100-continue\r\n\
Content-Type: multipart/form-data; boundary=--[..]\r\n\
\r\n\
--[..]\r\n\
Content-Disposition: form-data; name=\"foo\"\r\n\
\r\n\
1234\r\n\
--[..]\r\n");
    s.send("HTTP/1.1 200 OK\r\n\r\n");

    let mut handle = handle();
    let mut form = Form::new();
    t!(form.part("foo").contents(b"1234").add());
    t!(handle.url(&s.url("/")));
    t!(handle.httppost(form));
    t!(handle.perform());
}

#[test]
fn buffer() {
    let s = Server::new();
    s.receive("\
POST / HTTP/1.1\r\n\
Host: 127.0.0.1:$PORT\r\n\
Accept: */*\r\n\
Content-Length: 181\r\n\
Expect: 100-continue\r\n\
Content-Type: multipart/form-data; boundary=--[..]\r\n\
\r\n\
--[..]\r\n\
Content-Disposition: form-data; name=\"foo\"; filename=\"bar\"\r\n\
Content-Type: foo/bar\r\n\
\r\n\
1234\r\n\
--[..]\r\n");
    s.send("HTTP/1.1 200 OK\r\n\r\n");

    let mut handle = handle();
    let mut form = Form::new();
    t!(form.part("foo")
           .buffer("bar", b"1234".to_vec())
           .content_type("foo/bar")
           .add());
    t!(handle.url(&s.url("/")));
    t!(handle.httppost(form));
    t!(handle.perform());
}

#[test]
fn file() {
    let s = Server::new();
    s.receive("\
POST / HTTP/1.1\r\n\
Host: 127.0.0.1:$PORT\r\n\
Accept: */*\r\n\
Content-Length: 205\r\n\
Expect: 100-continue\r\n\
Content-Type: multipart/form-data; boundary=--[..]\r\n\
\r\n\
--[..]\r\n\
Content-Disposition: form-data; name=\"foo\"; filename=\"formdata\"\r\n\
Content-Type: application/octet-stream\r\n\
\r\n\
hello\n\
\r\n\
--[..]\r\n");
    s.send("HTTP/1.1 200 OK\r\n\r\n");

    let mut handle = handle();
    let mut form = Form::new();
    t!(form.part("foo")
           .file("tests/formdata")
           .add());
    t!(handle.url(&s.url("/")));
    t!(handle.httppost(form));
    t!(handle.perform());
}
<commit_msg>make file upload test tolerant of linending changes in test file<commit_after>extern crate curl;

use std::str;
use std::time::Duration;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
    })
}

use curl::easy::{Easy, Form};

use server::Server;
mod server;

fn handle() -> Easy {
    let mut e = Easy::new();
    t!(e.timeout(Duration::new(20, 0)));
    return e
}

#[test]
fn custom() {
    let s = Server::new();
    s.receive("\
POST / HTTP/1.1\r\n\
Host: 127.0.0.1:$PORT\r\n\
Accept: */*\r\n\
Content-Length: 142\r\n\
Expect: 100-continue\r\n\
Content-Type: multipart/form-data; boundary=--[..]\r\n\
\r\n\
--[..]\r\n\
Content-Disposition: form-data; name=\"foo\"\r\n\
\r\n\
1234\r\n\
--[..]\r\n");
    s.send("HTTP/1.1 200 OK\r\n\r\n");

    let mut handle = handle();
    let mut form = Form::new();
    t!(form.part("foo").contents(b"1234").add());
    t!(handle.url(&s.url("/")));
    t!(handle.httppost(form));
    t!(handle.perform());
}

#[test]
fn buffer() {
    let s = Server::new();
    s.receive("\
POST / HTTP/1.1\r\n\
Host: 127.0.0.1:$PORT\r\n\
Accept: */*\r\n\
Content-Length: 181\r\n\
Expect: 100-continue\r\n\
Content-Type: multipart/form-data; boundary=--[..]\r\n\
\r\n\
--[..]\r\n\
Content-Disposition: form-data; name=\"foo\"; filename=\"bar\"\r\n\
Content-Type: foo/bar\r\n\
\r\n\
1234\r\n\
--[..]\r\n");
    s.send("HTTP/1.1 200 OK\r\n\r\n");

    let mut handle = handle();
    let mut form = Form::new();
    t!(form.part("foo")
           .buffer("bar", b"1234".to_vec())
           .content_type("foo/bar")
           .add());
    t!(handle.url(&s.url("/")));
    t!(handle.httppost(form));
    t!(handle.perform());
}

#[test]
fn file() {
    let s = Server::new();
    let formdata = include_str!("formdata");
    s.receive(format!("\
POST / HTTP/1.1\r\n\
Host: 127.0.0.1:$PORT\r\n\
Accept: */*\r\n\
Content-Length: {}\r\n\
Expect: 100-continue\r\n\
Content-Type: multipart/form-data; boundary=--[..]\r\n\
\r\n\
--[..]\r\n\
Content-Disposition: form-data; name=\"foo\"; filename=\"formdata\"\r\n\
Content-Type: application/octet-stream\r\n\
\r\n\
{}\
\r\n\
--[..]\r\n", 199 + formdata.len(), formdata).as_str());
    s.send("HTTP/1.1 200 OK\r\n\r\n");

    let mut handle = handle();
    let mut form = Form::new();
    t!(form.part("foo")
           .file("tests/formdata")
           .add());
    t!(handle.url(&s.url("/")));
    t!(handle.httppost(form));
    t!(handle.perform());
}
<|endoftext|>


<commit_before>#[macro_use]
mod common;

use common::util::*;

static UTIL_NAME: &'static str = "sort";

#[test]
fn test_numeric_floats_and_ints() {
    test_helper("numeric_floats_and_ints", &String::from("-n"));
}

#[test]
fn test_numeric_floats() {
    test_helper("numeric_floats", &String::from("-n"));
}

#[test]
fn test_numeric_unfixed_floats() {
    test_helper("numeric_unfixed_floats", &String::from("-n"));
}

#[test]
fn test_numeric_fixed_floats() {
    test_helper("numeric_fixed_floats", &String::from("-n"));
}

#[test]
fn test_numeric_unsorted_ints() {
    test_helper("numeric_unsorted_ints", &String::from("-n"));
}

#[test]
fn test_human_block_sizes() {
    test_helper("human_block_sizes", &String::from("-h"));
}

#[test]
fn test_month_default() {
    test_helper("month_default", &String::from("-M"));
}

#[test]
fn test_default_unsorted_ints() {
    test_helper("default_unsorted_ints", &String::from(""));
}

fn test_helper(file_name: &str, args: &String) {
    let (at, mut ucmd) = testing(UTIL_NAME);
    ucmd.arg(args);
    let out = ucmd.arg(format!("{}{}", file_name, ".txt")).run().stdout;

    let filename = format!("{}{}", file_name, ".expected");
    assert_eq!(out, at.read(&filename));
}
<commit_msg>tests/sort: simplify input to helper<commit_after>#[macro_use]
mod common;

use common::util::*;

static UTIL_NAME: &'static str = "sort";

#[test]
fn test_numeric_floats_and_ints() {
    test_helper("numeric_floats_and_ints", "-n");
}

#[test]
fn test_numeric_floats() {
    test_helper("numeric_floats", "-n");
}

#[test]
fn test_numeric_unfixed_floats() {
    test_helper("numeric_unfixed_floats", "-n");
}

#[test]
fn test_numeric_fixed_floats() {
    test_helper("numeric_fixed_floats", "-n");
}

#[test]
fn test_numeric_unsorted_ints() {
    test_helper("numeric_unsorted_ints", "-n");
}

#[test]
fn test_human_block_sizes() {
    test_helper("human_block_sizes", "-h");
}

#[test]
fn test_month_default() {
    test_helper("month_default", "-M");
}

#[test]
fn test_default_unsorted_ints() {
    test_helper("default_unsorted_ints", "");
}

fn test_helper(file_name: &str, args: &str) {
    let (at, mut ucmd) = testing(UTIL_NAME);
    ucmd.arg(args);
    let out = ucmd.arg(format!("{}{}", file_name, ".txt")).run().stdout;

    let filename = format!("{}{}", file_name, ".expected");
    assert_eq!(out, at.read(&filename));
}
<|endoftext|>


<commit_before>/*
 * Copyright 2019 Jeehoon Kang
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#![allow(non_camel_case_types)]

use core::ffi;

use crate::page::*;

pub type c_void = ffi::c_void;
pub type c_int = i32;
pub type c_char = u8;
pub type size_t = usize;
pub type rsize_t = usize;
pub type uintreg_t = usize;

/// The ID of a VM. These are assigned sequentially.
pub type spci_vm_id_t = u16;
pub type spci_memory_handle_t = u32;

/// A count of VMs. This has the same range as the VM IDs but we give it a
/// different name to make the different semantics clear.
pub type spci_vm_count_t = spci_vm_id_t;

/// The index of a vCPU within a particular VM.
pub type spci_vcpu_index_t = u16;

/// A count of vCPUs. This has the same range as the vCPU indices but we give
/// it a different name to make the different semantics clear.
pub type spci_vcpu_count_t = spci_vcpu_index_t;

/// Return type of SPCI functions.
/// TODO: Reuse spci_return_t type on all SPCI functions declarations.
pub type spci_return_t = i32;

pub const RSIZE_MAX: rsize_t = rsize_t::max_value() >> 1;

pub const HF_NUM_INTIDS: usize = 64;

// These constants are originally from build scripts. Fortunately most
// testing environments have same conditions (MAX_CPUS=8, MAX_VMS=16.) And
// only one environment (host_fake) doesn't but it's for the unit test, so
// works fine under this settting (See //project/reference/BUILD.gn.)
pub const MAX_CPUS: usize = 8;
pub const MAX_VMS: usize = 16;

pub const HF_MAILBOX_SIZE: usize = PAGE_SIZE;
<commit_msg>Add TODO comment on non_camel_case_types.<commit_after>/*
 * Copyright 2019 Jeehoon Kang
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// TODO: Refactor type names and remove this.
#![allow(non_camel_case_types)]

use core::ffi;

use crate::page::*;

pub type c_void = ffi::c_void;
pub type c_int = i32;
pub type c_char = u8;
pub type size_t = usize;
pub type rsize_t = usize;
pub type uintreg_t = usize;

/// The ID of a VM. These are assigned sequentially.
pub type spci_vm_id_t = u16;
pub type spci_memory_handle_t = u32;

/// A count of VMs. This has the same range as the VM IDs but we give it a
/// different name to make the different semantics clear.
pub type spci_vm_count_t = spci_vm_id_t;

/// The index of a vCPU within a particular VM.
pub type spci_vcpu_index_t = u16;

/// A count of vCPUs. This has the same range as the vCPU indices but we give
/// it a different name to make the different semantics clear.
pub type spci_vcpu_count_t = spci_vcpu_index_t;

/// Return type of SPCI functions.
/// TODO: Reuse spci_return_t type on all SPCI functions declarations.
pub type spci_return_t = i32;

pub const RSIZE_MAX: rsize_t = rsize_t::max_value() >> 1;

pub const HF_NUM_INTIDS: usize = 64;

// These constants are originally from build scripts. Fortunately most
// testing environments have same conditions (MAX_CPUS=8, MAX_VMS=16.) And
// only one environment (host_fake) doesn't but it's for the unit test, so
// works fine under this settting (See //project/reference/BUILD.gn.)
pub const MAX_CPUS: usize = 8;
pub const MAX_VMS: usize = 16;

pub const HF_MAILBOX_SIZE: usize = PAGE_SIZE;
<|endoftext|>


<commit_before>extern crate clap;
extern crate serde_json;
extern crate jmespath;

use std::rc::Rc;
use std::io::prelude::*;
use std::io;
use std::fs::File;
use std::process::exit;

use clap::{Arg, App};
use jmespath::RcVar;
use jmespath::{Variable, Expression};

macro_rules! die(
    ($msg:expr) => (
        match writeln!(&mut ::std::io::stderr(), "{}", $msg) {
            Ok(_) => exit(1),
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

fn main() {
    let matches = App::new("jp")
        .version("1.0")
        .author("Michael Dowling <mtdowling@gmail.com>")
        .about("JMESPath command line interface")
        .arg(Arg::with_name("filename")
            .help("Read input JSON from a file instead of stdin.")
            .short("f")
            .takes_value(true)
            .long("filename"))
        .arg(Arg::with_name("unquoted")
            .help("If the final result is a string, it will be printed without quotes.")
            .short("u")
            .long("unquoted")
            .multiple(false))
        .arg(Arg::with_name("ast")
            .help("Only print the AST of the parsed expression.  Do not rely on this output, \
                  only useful for debugging purposes.")
            .long("ast")
            .multiple(false))
        .arg(Arg::with_name("expr-file")
            .help("Read JMESPath expression from the specified file.")
            .short("e")
            .takes_value(true)
            .long("expr-file")
            .conflicts_with("expression")
            .required(true))
        .arg(Arg::with_name("expression")
            .help("JMESPath expression to evaluate")
            .index(1)
            .conflicts_with("expr-file")
            .required(true))
        .get_matches();

    let file_expression = matches.value_of("expr-file")
        .map(|f| read_file("expression", f));

    let expr = if let Some(ref e) = file_expression {
        Expression::new(e)
    } else {
        Expression::new(matches.value_of("expression").unwrap())
    }.map_err(|e| die!(e.to_string())).unwrap();

    if matches.is_present("ast") {
        println!("{:#?}", expr.as_ast());
        exit(0);
    }

    let json = Rc::new(get_json(matches.value_of("filename")));

    match expr.search_variable(&json) {
        Err(e) => die!(e.to_string()),
        Ok(result) => show_result(result, matches.is_present("unquoted"))
    }
}

fn show_result(result: RcVar, unquoted: bool) {
    if unquoted && result.is_string() {
        println!("{}", result.as_string().unwrap());
    } else {
        let mut out = io::stdout();
        serde_json::to_writer_pretty(&mut out, &result)
            .map(|_| out.write(&['\n' as u8]))
            .map_err(|e| die!(format!("Error converting result to string: {}", e)))
            .ok();
    }
}

fn read_file(label: &str, filename: &str) -> String {
    match File::open(filename) {
        Err(e) => die!(format!("Error opening {} file at {}: {}", label, filename, e)),
        Ok(mut file) => {
            let mut buffer = String::new();
            file.read_to_string(&mut buffer)
                .map_err(|e| die!(format!("Error reading {} from {}: {}", label, filename, e)))
                .map(|_| buffer)
                .unwrap()
        }
    }
}

fn get_json(filename: Option<&str>) -> Variable {
    let buffer = match filename {
        Some(f) => read_file("JSON", f),
        None => {
            let mut buffer = String::new();
            match io::stdin().read_to_string(&mut buffer) {
                Ok(_) => buffer,
                Err(e) => die!(format!("Error reading JSON from stdin: {}", e))
            }
        }
    };
    Variable::from_json(&buffer)
        .map_err(|e| die!(format!("Error parsing JSON: {}", e)))
        .unwrap()
}
<commit_msg>Updating CLI binary<commit_after>extern crate clap;
extern crate serde_json;
extern crate jmespath;

use std::rc::Rc;
use std::io::prelude::*;
use std::io;
use std::fs::File;
use std::process::exit;

use clap::{Arg, App};
use jmespath::RcVar;
use jmespath::{Variable, Expression};

macro_rules! die(
    ($msg:expr) => (
        match writeln!(&mut ::std::io::stderr(), "{}", $msg) {
            Ok(_) => exit(1),
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

fn main() {
    let matches = App::new("jp")
        .version("0.0.1")
        .about("JMESPath command line interface")
        .arg(Arg::with_name("filename")
            .help("Read input JSON from a file instead of stdin.")
            .short("f")
            .takes_value(true)
            .long("filename"))
        .arg(Arg::with_name("unquoted")
            .help("If the final result is a string, it will be printed without quotes.")
            .short("u")
            .long("unquoted")
            .multiple(false))
        .arg(Arg::with_name("ast")
            .help("Only print the AST of the parsed expression.  Do not rely on this output, \
                  only useful for debugging purposes.")
            .long("ast")
            .multiple(false))
        .arg(Arg::with_name("expr-file")
            .help("Read JMESPath expression from the specified file.")
            .short("e")
            .takes_value(true)
            .long("expr-file")
            .conflicts_with("expression")
            .required(true))
        .arg(Arg::with_name("expression")
            .help("JMESPath expression to evaluate")
            .index(1)
            .conflicts_with("expr-file")
            .required(true))
        .get_matches();

    let file_expression = matches.value_of("expr-file")
        .map(|f| read_file("expression", f));

    let expr = if let Some(ref e) = file_expression {
        Expression::new(e)
    } else {
        Expression::new(matches.value_of("expression").unwrap())
    }.map_err(|e| die!(e.to_string())).unwrap();

    if matches.is_present("ast") {
        println!("{:#?}", expr.as_ast());
        exit(0);
    }

    let json = Rc::new(get_json(matches.value_of("filename")));

    match expr.search_variable(&json) {
        Err(e) => die!(e.to_string()),
        Ok(result) => show_result(result, matches.is_present("unquoted"))
    }
}

fn show_result(result: RcVar, unquoted: bool) {
    if unquoted && result.is_string() {
        println!("{}", result.as_string().unwrap());
    } else {
        let mut out = io::stdout();
        serde_json::to_writer_pretty(&mut out, &result)
            .map(|_| out.write(&['\n' as u8]))
            .map_err(|e| die!(format!("Error converting result to string: {}", e)))
            .ok();
    }
}

fn read_file(label: &str, filename: &str) -> String {
    match File::open(filename) {
        Err(e) => die!(format!("Error opening {} file at {}: {}", label, filename, e)),
        Ok(mut file) => {
            let mut buffer = String::new();
            file.read_to_string(&mut buffer)
                .map_err(|e| die!(format!("Error reading {} from {}: {}", label, filename, e)))
                .map(|_| buffer)
                .unwrap()
        }
    }
}

fn get_json(filename: Option<&str>) -> Variable {
    let buffer = match filename {
        Some(f) => read_file("JSON", f),
        None => {
            let mut buffer = String::new();
            match io::stdin().read_to_string(&mut buffer) {
                Ok(_) => buffer,
                Err(e) => die!(format!("Error reading JSON from stdin: {}", e))
            }
        }
    };
    Variable::from_json(&buffer)
        .map_err(|e| die!(format!("Error parsing JSON: {}", e)))
        .unwrap()
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Code for type-checking closure expressions.

use super::{check_fn, Expectation, FnCtxt};

use astconv;
use middle::region::CodeExtent;
use middle::subst;
use middle::ty::{self, ToPolyTraitRef, Ty};
use rscope::RegionScope;
use syntax::abi;
use syntax::ast;
use syntax::ast_util;
use util::ppaux::Repr;

pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                                   expr: &ast::Expr,
                                   _capture: ast::CaptureClause,
                                   opt_kind: Option<ast::ClosureKind>,
                                   decl: &ast::FnDecl,
                                   body: &ast::Block,
                                   expected: Expectation<'tcx>) {
    debug!("check_expr_closure(expr={},expected={})",
           expr.repr(fcx.tcx()),
           expected.repr(fcx.tcx()));

    let expected_sig_and_kind = expected.to_option(fcx).and_then(|ty| {
        deduce_closure_expectations_from_expected_type(fcx, ty)
    });

    match opt_kind {
        None => {
            // If users didn't specify what sort of closure they want,
            // examine the expected type. For now, if we see explicit
            // evidence than an unboxed closure is desired, we'll use
            // that, otherwise we'll error, requesting an annotation.
            match expected_sig_and_kind {
                None => { // don't have information about the kind, request explicit annotation
                    // NB We still need to typeck the body, so assume `FnMut` kind just for that
                    let kind = ty::FnMutClosureKind;

                    check_closure(fcx, expr, kind, decl, body, None);

                    span_err!(fcx.ccx.tcx.sess, expr.span, E0187,
                        "can't infer the \"kind\" of the closure; explicitly annotate it; e.g. \
                        `|&:| {{}}`");
                },
                Some((sig, kind)) => {
                    check_closure(fcx, expr, kind, decl, body, Some(sig));
                }
            }
        }

        Some(kind) => {
            let kind = match kind {
                ast::FnClosureKind => ty::FnClosureKind,
                ast::FnMutClosureKind => ty::FnMutClosureKind,
                ast::FnOnceClosureKind => ty::FnOnceClosureKind,
            };

            let expected_sig = expected_sig_and_kind.map(|t| t.0);
            check_closure(fcx, expr, kind, decl, body, expected_sig);
        }
    }
}

fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                          expr: &ast::Expr,
                          kind: ty::ClosureKind,
                          decl: &ast::FnDecl,
                          body: &ast::Block,
                          expected_sig: Option<ty::FnSig<'tcx>>) {
    let expr_def_id = ast_util::local_def(expr.id);

    debug!("check_closure kind={:?} expected_sig={}",
           kind,
           expected_sig.repr(fcx.tcx()));

    let mut fn_ty = astconv::ty_of_closure(
        fcx,
        ast::Unsafety::Normal,
        decl,
        abi::RustCall,
        expected_sig);

    let region = match fcx.anon_regions(expr.span, 1) {
        Err(_) => {
            fcx.ccx.tcx.sess.span_bug(expr.span,
                                      "can't make anon regions here?!")
        }
        Ok(regions) => regions[0],
    };

    let closure_type = ty::mk_closure(fcx.ccx.tcx,
                                      expr_def_id,
                                      fcx.ccx.tcx.mk_region(region),
                                      fcx.ccx.tcx.mk_substs(
                                        fcx.inh.param_env.free_substs.clone()));

    fcx.write_ty(expr.id, closure_type);

    let fn_sig =
        ty::liberate_late_bound_regions(fcx.tcx(), CodeExtent::from_node_id(body.id), &fn_ty.sig);

    check_fn(fcx.ccx,
             ast::Unsafety::Normal,
             expr.id,
             &fn_sig,
             decl,
             expr.id,
             &*body,
             fcx.inh);

    // Tuple up the arguments and insert the resulting function type into
    // the `closures` table.
    fn_ty.sig.0.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.0.inputs)];

    debug!("closure for {} --> sig={} kind={:?}",
           expr_def_id.repr(fcx.tcx()),
           fn_ty.sig.repr(fcx.tcx()),
           kind);

    let closure = ty::Closure {
        closure_type: fn_ty,
        kind: kind,
    };

    fcx.inh.closures.borrow_mut().insert(expr_def_id, closure);
}

fn deduce_closure_expectations_from_expected_type<'a,'tcx>(
    fcx: &FnCtxt<'a,'tcx>,
    expected_ty: Ty<'tcx>)
    -> Option<(ty::FnSig<'tcx>,ty::ClosureKind)>
{
    match expected_ty.sty {
        ty::ty_trait(ref object_type) => {
            let trait_ref =
                object_type.principal_trait_ref_with_self_ty(fcx.tcx(),
                                                             fcx.tcx().types.err);
            deduce_closure_expectations_from_trait_ref(fcx, &trait_ref)
        }
        ty::ty_infer(ty::TyVar(vid)) => {
            deduce_closure_expectations_from_obligations(fcx, vid)
        }
        _ => {
            None
        }
    }
}

fn deduce_closure_expectations_from_trait_ref<'a,'tcx>(
    fcx: &FnCtxt<'a,'tcx>,
    trait_ref: &ty::PolyTraitRef<'tcx>)
    -> Option<(ty::FnSig<'tcx>, ty::ClosureKind)>
{
    let tcx = fcx.tcx();

    debug!("deduce_closure_expectations_from_object_type({})",
           trait_ref.repr(tcx));

    let kind = match tcx.lang_items.fn_trait_kind(trait_ref.def_id()) {
        Some(k) => k,
        None => { return None; }
    };

    debug!("found object type {:?}", kind);

    let arg_param_ty = *trait_ref.substs().types.get(subst::TypeSpace, 0);
    let arg_param_ty = fcx.infcx().resolve_type_vars_if_possible(&arg_param_ty);
    debug!("arg_param_ty {}", arg_param_ty.repr(tcx));

    let input_tys = match arg_param_ty.sty {
        ty::ty_tup(ref tys) => { (*tys).clone() }
        _ => { return None; }
    };
    debug!("input_tys {}", input_tys.repr(tcx));

    let ret_param_ty = *trait_ref.substs().types.get(subst::TypeSpace, 1);
    let ret_param_ty = fcx.infcx().resolve_type_vars_if_possible(&ret_param_ty);
    debug!("ret_param_ty {}", ret_param_ty.repr(tcx));

    let fn_sig = ty::FnSig {
        inputs: input_tys,
        output: ty::FnConverging(ret_param_ty),
        variadic: false
    };
    debug!("fn_sig {}", fn_sig.repr(tcx));

    return Some((fn_sig, kind));
}

fn deduce_closure_expectations_from_obligations<'a,'tcx>(
    fcx: &FnCtxt<'a,'tcx>,
    expected_vid: ty::TyVid)
    -> Option<(ty::FnSig<'tcx>, ty::ClosureKind)>
{
    // Here `expected_ty` is known to be a type inference variable.
    for obligation in fcx.inh.fulfillment_cx.borrow().pending_obligations().iter() {
        match obligation.predicate {
            ty::Predicate::Trait(ref trait_predicate) => {
                let trait_ref = trait_predicate.to_poly_trait_ref();
                let self_ty = fcx.infcx().shallow_resolve(trait_ref.self_ty());
                match self_ty.sty {
                    ty::ty_infer(ty::TyVar(v)) if expected_vid == v => { }
                    _ => { continue; }
                }

                match deduce_closure_expectations_from_trait_ref(fcx, &trait_ref) {
                    Some(e) => { return Some(e); }
                    None => { }
                }
            }
            _ => { }
        }
    }

    None
}
<commit_msg>Extract expectations from the projection obligation, which includes all relevant information.<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Code for type-checking closure expressions.

use super::{check_fn, Expectation, FnCtxt};

use astconv;
use middle::region::CodeExtent;
use middle::subst;
use middle::ty::{self, ToPolyTraitRef, Ty};
use rscope::RegionScope;
use syntax::abi;
use syntax::ast;
use syntax::ast_util;
use util::ppaux::Repr;

pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                                   expr: &ast::Expr,
                                   _capture: ast::CaptureClause,
                                   opt_kind: Option<ast::ClosureKind>,
                                   decl: &ast::FnDecl,
                                   body: &ast::Block,
                                   expected: Expectation<'tcx>) {
    debug!("check_expr_closure(expr={},expected={})",
           expr.repr(fcx.tcx()),
           expected.repr(fcx.tcx()));

    let expected_sig_and_kind = expected.to_option(fcx).and_then(|ty| {
        deduce_expectations_from_expected_type(fcx, ty)
    });

    match opt_kind {
        None => {
            // If users didn't specify what sort of closure they want,
            // examine the expected type. For now, if we see explicit
            // evidence than an unboxed closure is desired, we'll use
            // that, otherwise we'll error, requesting an annotation.
            match expected_sig_and_kind {
                None => { // don't have information about the kind, request explicit annotation
                    // NB We still need to typeck the body, so assume `FnMut` kind just for that
                    let kind = ty::FnMutClosureKind;

                    check_closure(fcx, expr, kind, decl, body, None);

                    span_err!(fcx.ccx.tcx.sess, expr.span, E0187,
                        "can't infer the \"kind\" of the closure; explicitly annotate it; e.g. \
                        `|&:| {{}}`");
                },
                Some((sig, kind)) => {
                    check_closure(fcx, expr, kind, decl, body, Some(sig));
                }
            }
        }

        Some(kind) => {
            let kind = match kind {
                ast::FnClosureKind => ty::FnClosureKind,
                ast::FnMutClosureKind => ty::FnMutClosureKind,
                ast::FnOnceClosureKind => ty::FnOnceClosureKind,
            };

            let expected_sig = expected_sig_and_kind.map(|t| t.0);
            check_closure(fcx, expr, kind, decl, body, expected_sig);
        }
    }
}

fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                          expr: &ast::Expr,
                          kind: ty::ClosureKind,
                          decl: &ast::FnDecl,
                          body: &ast::Block,
                          expected_sig: Option<ty::FnSig<'tcx>>) {
    let expr_def_id = ast_util::local_def(expr.id);

    debug!("check_closure kind={:?} expected_sig={}",
           kind,
           expected_sig.repr(fcx.tcx()));

    let mut fn_ty = astconv::ty_of_closure(
        fcx,
        ast::Unsafety::Normal,
        decl,
        abi::RustCall,
        expected_sig);

    let region = match fcx.anon_regions(expr.span, 1) {
        Err(_) => {
            fcx.ccx.tcx.sess.span_bug(expr.span,
                                      "can't make anon regions here?!")
        }
        Ok(regions) => regions[0],
    };

    let closure_type = ty::mk_closure(fcx.ccx.tcx,
                                      expr_def_id,
                                      fcx.ccx.tcx.mk_region(region),
                                      fcx.ccx.tcx.mk_substs(
                                        fcx.inh.param_env.free_substs.clone()));

    fcx.write_ty(expr.id, closure_type);

    let fn_sig =
        ty::liberate_late_bound_regions(fcx.tcx(), CodeExtent::from_node_id(body.id), &fn_ty.sig);

    check_fn(fcx.ccx,
             ast::Unsafety::Normal,
             expr.id,
             &fn_sig,
             decl,
             expr.id,
             &*body,
             fcx.inh);

    // Tuple up the arguments and insert the resulting function type into
    // the `closures` table.
    fn_ty.sig.0.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.0.inputs)];

    debug!("closure for {} --> sig={} kind={:?}",
           expr_def_id.repr(fcx.tcx()),
           fn_ty.sig.repr(fcx.tcx()),
           kind);

    let closure = ty::Closure {
        closure_type: fn_ty,
        kind: kind,
    };

    fcx.inh.closures.borrow_mut().insert(expr_def_id, closure);
}

fn deduce_expectations_from_expected_type<'a,'tcx>(
    fcx: &FnCtxt<'a,'tcx>,
    expected_ty: Ty<'tcx>)
    -> Option<(ty::FnSig<'tcx>,ty::ClosureKind)>
{
    match expected_ty.sty {
        ty::ty_trait(ref object_type) => {
            let proj_bounds = object_type.projection_bounds_with_self_ty(fcx.tcx(),
                                                                         fcx.tcx().types.err);
            proj_bounds.iter()
                       .filter_map(|pb| deduce_expectations_from_projection(fcx, pb))
                       .next()
        }
        ty::ty_infer(ty::TyVar(vid)) => {
            deduce_expectations_from_obligations(fcx, vid)
        }
        _ => {
            None
        }
    }
}

fn deduce_expectations_from_obligations<'a,'tcx>(
    fcx: &FnCtxt<'a,'tcx>,
    expected_vid: ty::TyVid)
    -> Option<(ty::FnSig<'tcx>, ty::ClosureKind)>
{
    let fulfillment_cx = fcx.inh.fulfillment_cx.borrow();
    // Here `expected_ty` is known to be a type inference variable.

    fulfillment_cx.pending_obligations()
                  .iter()
                  .filter_map(|obligation| {
                      match obligation.predicate {
                          ty::Predicate::Projection(ref proj_predicate) => {
                              let trait_ref = proj_predicate.to_poly_trait_ref();
                              let self_ty = fcx.infcx().shallow_resolve(trait_ref.self_ty());
                              match self_ty.sty {
                                  ty::ty_infer(ty::TyVar(v)) if expected_vid == v => {
                                      deduce_expectations_from_projection(fcx, proj_predicate)
                                  }
                                  _ => {
                                      None
                                  }
                              }
                          }
                          _ => {
                              None
                          }
                      }
                  })
                  .next()
}

/// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
/// everything we need to know about a closure.
fn deduce_expectations_from_projection<'a,'tcx>(
    fcx: &FnCtxt<'a,'tcx>,
    projection: &ty::PolyProjectionPredicate<'tcx>)
    -> Option<(ty::FnSig<'tcx>, ty::ClosureKind)>
{
    let tcx = fcx.tcx();

    debug!("deduce_expectations_from_projection({})",
           projection.repr(tcx));

    let trait_ref = projection.to_poly_trait_ref();

    let kind = match tcx.lang_items.fn_trait_kind(trait_ref.def_id()) {
        Some(k) => k,
        None => { return None; }
    };

    debug!("found object type {:?}", kind);

    let arg_param_ty = *trait_ref.substs().types.get(subst::TypeSpace, 0);
    let arg_param_ty = fcx.infcx().resolve_type_vars_if_possible(&arg_param_ty);
    debug!("arg_param_ty {}", arg_param_ty.repr(tcx));

    let input_tys = match arg_param_ty.sty {
        ty::ty_tup(ref tys) => { (*tys).clone() }
        _ => { return None; }
    };
    debug!("input_tys {}", input_tys.repr(tcx));

    let ret_param_ty = projection.0.ty;
    let ret_param_ty = fcx.infcx().resolve_type_vars_if_possible(&ret_param_ty);
    debug!("ret_param_ty {}", ret_param_ty.repr(tcx));

    let fn_sig = ty::FnSig {
        inputs: input_tys,
        output: ty::FnConverging(ret_param_ty),
        variadic: false
    };
    debug!("fn_sig {}", fn_sig.repr(tcx));

    return Some((fn_sig, kind));
}

<|endoftext|>


<commit_before>use crate::core::{GitReference, PackageId, SourceId};
use crate::sources::git;
use crate::sources::registry::download;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData};
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::{Config, Filesystem};
use anyhow::Context as _;
use cargo_util::paths;
use lazycell::LazyCell;
use log::{debug, trace};
use std::cell::{Cell, Ref, RefCell};
use std::fs::File;
use std::mem;
use std::path::Path;
use std::str;
use std::task::Poll;

/// A remote registry is a registry that lives at a remote URL (such as
/// crates.io). The git index is cloned locally, and `.crate` files are
/// downloaded as needed and cached locally.
pub struct RemoteRegistry<'cfg> {
    index_path: Filesystem,
    /// Path to the cache of `.crate` files (`$CARGO_HOME/registry/path/$REG-HASH`).
    cache_path: Filesystem,
    source_id: SourceId,
    index_git_ref: GitReference,
    config: &'cfg Config,
    tree: RefCell<Option<git2::Tree<'static>>>,
    repo: LazyCell<git2::Repository>,
    head: Cell<Option<git2::Oid>>,
    current_sha: Cell<Option<InternedString>>,
    needs_update: bool, // Does this registry need to be updated?
    updated: bool,      // Has this registry been updated this session?
}

impl<'cfg> RemoteRegistry<'cfg> {
    pub fn new(source_id: SourceId, config: &'cfg Config, name: &str) -> RemoteRegistry<'cfg> {
        RemoteRegistry {
            index_path: config.registry_index_path().join(name),
            cache_path: config.registry_cache_path().join(name),
            source_id,
            config,
            // TODO: we should probably make this configurable
            index_git_ref: GitReference::DefaultBranch,
            tree: RefCell::new(None),
            repo: LazyCell::new(),
            head: Cell::new(None),
            current_sha: Cell::new(None),
            needs_update: false,
            updated: false,
        }
    }

    fn repo(&self) -> CargoResult<&git2::Repository> {
        self.repo.try_borrow_with(|| {
            let path = self.config.assert_package_cache_locked(&self.index_path);

            // Fast path without a lock
            if let Ok(repo) = git2::Repository::open(&path) {
                trace!("opened a repo without a lock");
                return Ok(repo);
            }

            // Ok, now we need to lock and try the whole thing over again.
            trace!("acquiring registry index lock");
            match git2::Repository::open(&path) {
                Ok(repo) => Ok(repo),
                Err(_) => {
                    drop(paths::remove_dir_all(&path));
                    paths::create_dir_all(&path)?;

                    // Note that we'd actually prefer to use a bare repository
                    // here as we're not actually going to check anything out.
                    // All versions of Cargo, though, share the same CARGO_HOME,
                    // so for compatibility with older Cargo which *does* do
                    // checkouts we make sure to initialize a new full
                    // repository (not a bare one).
                    //
                    // We should change this to `init_bare` whenever we feel
                    // like enough time has passed or if we change the directory
                    // that the folder is located in, such as by changing the
                    // hash at the end of the directory.
                    //
                    // Note that in the meantime we also skip `init.templatedir`
                    // as it can be misconfigured sometimes or otherwise add
                    // things that we don't want.
                    let mut opts = git2::RepositoryInitOptions::new();
                    opts.external_template(false);
                    Ok(git2::Repository::init_opts(&path, &opts).with_context(|| {
                        format!("failed to initialize index git repository (in {:?})", path)
                    })?)
                }
            }
        })
    }

    fn head(&self) -> CargoResult<git2::Oid> {
        if self.head.get().is_none() {
            let repo = self.repo()?;
            let oid = self.index_git_ref.resolve(repo)?;
            self.head.set(Some(oid));
        }
        Ok(self.head.get().unwrap())
    }

    fn tree(&self) -> CargoResult<Ref<'_, git2::Tree<'_>>> {
        {
            let tree = self.tree.borrow();
            if tree.is_some() {
                return Ok(Ref::map(tree, |s| s.as_ref().unwrap()));
            }
        }
        let repo = self.repo()?;
        let commit = repo.find_commit(self.head()?)?;
        let tree = commit.tree()?;

        // Unfortunately in libgit2 the tree objects look like they've got a
        // reference to the repository object which means that a tree cannot
        // outlive the repository that it came from. Here we want to cache this
        // tree, though, so to accomplish this we transmute it to a static
        // lifetime.
        //
        // Note that we don't actually hand out the static lifetime, instead we
        // only return a scoped one from this function. Additionally the repo
        // we loaded from (above) lives as long as this object
        // (`RemoteRegistry`) so we then just need to ensure that the tree is
        // destroyed first in the destructor, hence the destructor on
        // `RemoteRegistry` below.
        let tree = unsafe { mem::transmute::<git2::Tree<'_>, git2::Tree<'static>>(tree) };
        *self.tree.borrow_mut() = Some(tree);
        Ok(Ref::map(self.tree.borrow(), |s| s.as_ref().unwrap()))
    }

    fn current_version(&self) -> Option<InternedString> {
        if let Some(sha) = self.current_sha.get() {
            return Some(sha);
        }
        let sha = InternedString::new(&self.head().ok()?.to_string());
        self.current_sha.set(Some(sha));
        Some(sha)
    }
}

const LAST_UPDATED_FILE: &str = ".last-updated";

impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
    fn prepare(&self) -> CargoResult<()> {
        self.repo()?; // create intermediate dirs and initialize the repo
        Ok(())
    }

    fn index_path(&self) -> &Filesystem {
        &self.index_path
    }

    fn assert_index_locked<'a>(&self, path: &'a Filesystem) -> &'a Path {
        self.config.assert_package_cache_locked(path)
    }

    // `index_version` Is a string representing the version of the file used to construct the cached copy.
    // Older versions of Cargo used the single value of the hash of the HEAD commit as a `index_version`.
    // This is technically correct but a little too conservative. If a new commit is fetched all cached
    // files need to be regenerated even if a particular file was not changed.
    // Cargo now checks the `index_version` in two parts the cache file is considered valid if `index_version`
    // ends with the hash of the HEAD commit OR if it starts with the hash of the file's contents.
    // Cargo writes the cached files with `index_version` = `git_file_hash` + ":" + `git_commit_hash`.
    fn load(
        &mut self,
        _root: &Path,
        path: &Path,
        index_version: Option<&str>,
    ) -> Poll<CargoResult<LoadResponse>> {
        if self.needs_update {
            return Poll::Pending;
        }
        // Check if the cache is valid.
        let git_commit_hash = self.current_version();
        if let (Some(i), Some(c)) = (index_version, git_commit_hash) {
            if i.ends_with(c.as_str()) {
                return Poll::Ready(Ok(LoadResponse::CacheValid));
            }
        }
        // Note that the index calls this method and the filesystem is locked
        // in the index, so we don't need to worry about an `update_index`
        // happening in a different process.
        fn load_helper(
            registry: &RemoteRegistry<'_>,
            path: &Path,
            index_version: Option<&str>,
            git_commit_hash: Option<&str>,
        ) -> CargoResult<LoadResponse> {
            let repo = registry.repo()?;
            let tree = registry.tree()?;
            let entry = tree.get_path(path);
            let entry = entry?;
            let git_file_hash = entry.id().to_string();

            if let Some(i) = index_version {
                if i.starts_with(git_file_hash.as_str()) {
                    return Ok(LoadResponse::CacheValid);
                }
            }

            let object = entry.to_object(repo)?;
            let blob = match object.as_blob() {
                Some(blob) => blob,
                None => anyhow::bail!("path `{}` is not a blob in the git repo", path.display()),
            };

            Ok(LoadResponse::Data {
                raw_data: blob.content().to_vec(),
                index_version: git_commit_hash.map(|c| git_file_hash + c),
            })
        }

        match load_helper(&self, path, index_version, git_commit_hash.as_deref()) {
            Ok(result) => Poll::Ready(Ok(result)),
            Err(_) if !self.updated => {
                // If git returns an error and we haven't updated the repo, return
                // pending to allow an update to try again.
                self.needs_update = true;
                Poll::Pending
            }
            Err(e)
                if e.downcast_ref::<git2::Error>()
                    .map(|e| e.code() == git2::ErrorCode::NotFound)
                    .unwrap_or_default() =>
            {
                // The repo has been updated and the file does not exist.
                Poll::Ready(Ok(LoadResponse::NotFound))
            }
            Err(e) => Poll::Ready(Err(e)),
        }
    }

    fn config(&mut self) -> Poll<CargoResult<Option<RegistryConfig>>> {
        debug!("loading config");
        self.prepare()?;
        self.config.assert_package_cache_locked(&self.index_path);
        match self.load(Path::new(""), Path::new("config.json"), None)? {
            Poll::Ready(LoadResponse::Data { raw_data, .. }) => {
                trace!("config loaded");
                Poll::Ready(Ok(Some(serde_json::from_slice(&raw_data)?)))
            }
            Poll::Ready(_) => Poll::Ready(Ok(None)),
            Poll::Pending => Poll::Pending,
        }
    }

    fn block_until_ready(&mut self) -> CargoResult<()> {
        if !self.needs_update {
            return Ok(());
        }

        self.updated = true;
        self.needs_update = false;

        if self.config.offline() {
            return Ok(());
        }
        if self.config.cli_unstable().no_index_update {
            return Ok(());
        }
        // Make sure the index is only updated once per session since it is an
        // expensive operation. This generally only happens when the resolver
        // is run multiple times, such as during `cargo publish`.
        if self.config.updated_sources().contains(&self.source_id) {
            return Ok(());
        }

        debug!("updating the index");

        // Ensure that we'll actually be able to acquire an HTTP handle later on
        // once we start trying to download crates. This will weed out any
        // problems with `.cargo/config` configuration related to HTTP.
        //
        // This way if there's a problem the error gets printed before we even
        // hit the index, which may not actually read this configuration.
        self.config.http()?;

        self.prepare()?;
        self.head.set(None);
        *self.tree.borrow_mut() = None;
        self.current_sha.set(None);
        let path = self.config.assert_package_cache_locked(&self.index_path);
        self.config
            .shell()
            .status("Updating", self.source_id.display_index())?;

        // Fetch the latest version of our `index_git_ref` into the index
        // checkout.
        let url = self.source_id.url();
        let repo = self.repo.borrow_mut().unwrap();
        git::fetch(repo, url.as_str(), &self.index_git_ref, self.config)
            .with_context(|| format!("failed to fetch `{}`", url))?;
        self.config.updated_sources().insert(self.source_id);

        // Create a dummy file to record the mtime for when we updated the
        // index.
        paths::create(&path.join(LAST_UPDATED_FILE))?;

        Ok(())
    }

    fn invalidate_cache(&mut self) {
        if !self.updated {
            self.needs_update = true;
        }
    }

    fn is_updated(&self) -> bool {
        self.updated
    }

    fn download(&mut self, pkg: PackageId, checksum: &str) -> CargoResult<MaybeLock> {
        let registry_config = loop {
            match self.config()? {
                Poll::Pending => self.block_until_ready()?,
                Poll::Ready(cfg) => break cfg.unwrap(),
            }
        };

        download::download(
            &self.cache_path,
            &self.config,
            pkg,
            checksum,
            registry_config,
        )
    }

    fn finish_download(
        &mut self,
        pkg: PackageId,
        checksum: &str,
        data: &[u8],
    ) -> CargoResult<File> {
        download::finish_download(&self.cache_path, &self.config, pkg, checksum, data)
    }

    fn is_crate_downloaded(&self, pkg: PackageId) -> bool {
        download::is_crate_downloaded(&self.cache_path, &self.config, pkg)
    }
}

impl<'cfg> Drop for RemoteRegistry<'cfg> {
    fn drop(&mut self) {
        // Just be sure to drop this before our other fields
        self.tree.borrow_mut().take();
    }
}
<commit_msg>only wright contents hash<commit_after>use crate::core::{GitReference, PackageId, SourceId};
use crate::sources::git;
use crate::sources::registry::download;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData};
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::{Config, Filesystem};
use anyhow::Context as _;
use cargo_util::paths;
use lazycell::LazyCell;
use log::{debug, trace};
use std::cell::{Cell, Ref, RefCell};
use std::fs::File;
use std::mem;
use std::path::Path;
use std::str;
use std::task::Poll;

/// A remote registry is a registry that lives at a remote URL (such as
/// crates.io). The git index is cloned locally, and `.crate` files are
/// downloaded as needed and cached locally.
pub struct RemoteRegistry<'cfg> {
    index_path: Filesystem,
    /// Path to the cache of `.crate` files (`$CARGO_HOME/registry/path/$REG-HASH`).
    cache_path: Filesystem,
    source_id: SourceId,
    index_git_ref: GitReference,
    config: &'cfg Config,
    tree: RefCell<Option<git2::Tree<'static>>>,
    repo: LazyCell<git2::Repository>,
    head: Cell<Option<git2::Oid>>,
    current_sha: Cell<Option<InternedString>>,
    needs_update: bool, // Does this registry need to be updated?
    updated: bool,      // Has this registry been updated this session?
}

impl<'cfg> RemoteRegistry<'cfg> {
    pub fn new(source_id: SourceId, config: &'cfg Config, name: &str) -> RemoteRegistry<'cfg> {
        RemoteRegistry {
            index_path: config.registry_index_path().join(name),
            cache_path: config.registry_cache_path().join(name),
            source_id,
            config,
            // TODO: we should probably make this configurable
            index_git_ref: GitReference::DefaultBranch,
            tree: RefCell::new(None),
            repo: LazyCell::new(),
            head: Cell::new(None),
            current_sha: Cell::new(None),
            needs_update: false,
            updated: false,
        }
    }

    fn repo(&self) -> CargoResult<&git2::Repository> {
        self.repo.try_borrow_with(|| {
            let path = self.config.assert_package_cache_locked(&self.index_path);

            // Fast path without a lock
            if let Ok(repo) = git2::Repository::open(&path) {
                trace!("opened a repo without a lock");
                return Ok(repo);
            }

            // Ok, now we need to lock and try the whole thing over again.
            trace!("acquiring registry index lock");
            match git2::Repository::open(&path) {
                Ok(repo) => Ok(repo),
                Err(_) => {
                    drop(paths::remove_dir_all(&path));
                    paths::create_dir_all(&path)?;

                    // Note that we'd actually prefer to use a bare repository
                    // here as we're not actually going to check anything out.
                    // All versions of Cargo, though, share the same CARGO_HOME,
                    // so for compatibility with older Cargo which *does* do
                    // checkouts we make sure to initialize a new full
                    // repository (not a bare one).
                    //
                    // We should change this to `init_bare` whenever we feel
                    // like enough time has passed or if we change the directory
                    // that the folder is located in, such as by changing the
                    // hash at the end of the directory.
                    //
                    // Note that in the meantime we also skip `init.templatedir`
                    // as it can be misconfigured sometimes or otherwise add
                    // things that we don't want.
                    let mut opts = git2::RepositoryInitOptions::new();
                    opts.external_template(false);
                    Ok(git2::Repository::init_opts(&path, &opts).with_context(|| {
                        format!("failed to initialize index git repository (in {:?})", path)
                    })?)
                }
            }
        })
    }

    fn head(&self) -> CargoResult<git2::Oid> {
        if self.head.get().is_none() {
            let repo = self.repo()?;
            let oid = self.index_git_ref.resolve(repo)?;
            self.head.set(Some(oid));
        }
        Ok(self.head.get().unwrap())
    }

    fn tree(&self) -> CargoResult<Ref<'_, git2::Tree<'_>>> {
        {
            let tree = self.tree.borrow();
            if tree.is_some() {
                return Ok(Ref::map(tree, |s| s.as_ref().unwrap()));
            }
        }
        let repo = self.repo()?;
        let commit = repo.find_commit(self.head()?)?;
        let tree = commit.tree()?;

        // Unfortunately in libgit2 the tree objects look like they've got a
        // reference to the repository object which means that a tree cannot
        // outlive the repository that it came from. Here we want to cache this
        // tree, though, so to accomplish this we transmute it to a static
        // lifetime.
        //
        // Note that we don't actually hand out the static lifetime, instead we
        // only return a scoped one from this function. Additionally the repo
        // we loaded from (above) lives as long as this object
        // (`RemoteRegistry`) so we then just need to ensure that the tree is
        // destroyed first in the destructor, hence the destructor on
        // `RemoteRegistry` below.
        let tree = unsafe { mem::transmute::<git2::Tree<'_>, git2::Tree<'static>>(tree) };
        *self.tree.borrow_mut() = Some(tree);
        Ok(Ref::map(self.tree.borrow(), |s| s.as_ref().unwrap()))
    }

    fn current_version(&self) -> Option<InternedString> {
        if let Some(sha) = self.current_sha.get() {
            return Some(sha);
        }
        let sha = InternedString::new(&self.head().ok()?.to_string());
        self.current_sha.set(Some(sha));
        Some(sha)
    }
}

const LAST_UPDATED_FILE: &str = ".last-updated";

impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
    fn prepare(&self) -> CargoResult<()> {
        self.repo()?; // create intermediate dirs and initialize the repo
        Ok(())
    }

    fn index_path(&self) -> &Filesystem {
        &self.index_path
    }

    fn assert_index_locked<'a>(&self, path: &'a Filesystem) -> &'a Path {
        self.config.assert_package_cache_locked(path)
    }

    // `index_version` Is a string representing the version of the file used to construct the cached copy.
    // Older versions of Cargo used the single value of the hash of the HEAD commit as a `index_version`.
    // This is technically correct but a little too conservative. If a new commit is fetched all cached
    // files need to be regenerated even if a particular file was not changed.
    // However if an old cargo has written such a file we still know how to read it, as long as we check for that hash value.
    //
    // Cargo now uses a hash of the file's contents as provided by git.
    fn load(
        &mut self,
        _root: &Path,
        path: &Path,
        index_version: Option<&str>,
    ) -> Poll<CargoResult<LoadResponse>> {
        if self.needs_update {
            return Poll::Pending;
        }
        // Check if the cache is valid.
        let git_commit_hash = self.current_version();
        if index_version.is_some() && index_version == git_commit_hash.as_deref() {
            // This file was written by an old version of cargo, but it is still up-to-date.
            return Poll::Ready(Ok(LoadResponse::CacheValid));
        }
        // Note that the index calls this method and the filesystem is locked
        // in the index, so we don't need to worry about an `update_index`
        // happening in a different process.
        fn load_helper(
            registry: &RemoteRegistry<'_>,
            path: &Path,
            index_version: Option<&str>,
        ) -> CargoResult<LoadResponse> {
            let repo = registry.repo()?;
            let tree = registry.tree()?;
            let entry = tree.get_path(path);
            let entry = entry?;
            let git_file_hash = Some(entry.id().to_string());

            // Check if the cache is valid.
            if index_version.is_some() && index_version == git_file_hash.as_deref() {
                return Ok(LoadResponse::CacheValid);
            }

            let object = entry.to_object(repo)?;
            let blob = match object.as_blob() {
                Some(blob) => blob,
                None => anyhow::bail!("path `{}` is not a blob in the git repo", path.display()),
            };

            Ok(LoadResponse::Data {
                raw_data: blob.content().to_vec(),
                index_version: git_file_hash,
            })
        }

        match load_helper(&self, path, index_version) {
            Ok(result) => Poll::Ready(Ok(result)),
            Err(_) if !self.updated => {
                // If git returns an error and we haven't updated the repo, return
                // pending to allow an update to try again.
                self.needs_update = true;
                Poll::Pending
            }
            Err(e)
                if e.downcast_ref::<git2::Error>()
                    .map(|e| e.code() == git2::ErrorCode::NotFound)
                    .unwrap_or_default() =>
            {
                // The repo has been updated and the file does not exist.
                Poll::Ready(Ok(LoadResponse::NotFound))
            }
            Err(e) => Poll::Ready(Err(e)),
        }
    }

    fn config(&mut self) -> Poll<CargoResult<Option<RegistryConfig>>> {
        debug!("loading config");
        self.prepare()?;
        self.config.assert_package_cache_locked(&self.index_path);
        match self.load(Path::new(""), Path::new("config.json"), None)? {
            Poll::Ready(LoadResponse::Data { raw_data, .. }) => {
                trace!("config loaded");
                Poll::Ready(Ok(Some(serde_json::from_slice(&raw_data)?)))
            }
            Poll::Ready(_) => Poll::Ready(Ok(None)),
            Poll::Pending => Poll::Pending,
        }
    }

    fn block_until_ready(&mut self) -> CargoResult<()> {
        if !self.needs_update {
            return Ok(());
        }

        self.updated = true;
        self.needs_update = false;

        if self.config.offline() {
            return Ok(());
        }
        if self.config.cli_unstable().no_index_update {
            return Ok(());
        }
        // Make sure the index is only updated once per session since it is an
        // expensive operation. This generally only happens when the resolver
        // is run multiple times, such as during `cargo publish`.
        if self.config.updated_sources().contains(&self.source_id) {
            return Ok(());
        }

        debug!("updating the index");

        // Ensure that we'll actually be able to acquire an HTTP handle later on
        // once we start trying to download crates. This will weed out any
        // problems with `.cargo/config` configuration related to HTTP.
        //
        // This way if there's a problem the error gets printed before we even
        // hit the index, which may not actually read this configuration.
        self.config.http()?;

        self.prepare()?;
        self.head.set(None);
        *self.tree.borrow_mut() = None;
        self.current_sha.set(None);
        let path = self.config.assert_package_cache_locked(&self.index_path);
        self.config
            .shell()
            .status("Updating", self.source_id.display_index())?;

        // Fetch the latest version of our `index_git_ref` into the index
        // checkout.
        let url = self.source_id.url();
        let repo = self.repo.borrow_mut().unwrap();
        git::fetch(repo, url.as_str(), &self.index_git_ref, self.config)
            .with_context(|| format!("failed to fetch `{}`", url))?;
        self.config.updated_sources().insert(self.source_id);

        // Create a dummy file to record the mtime for when we updated the
        // index.
        paths::create(&path.join(LAST_UPDATED_FILE))?;

        Ok(())
    }

    fn invalidate_cache(&mut self) {
        if !self.updated {
            self.needs_update = true;
        }
    }

    fn is_updated(&self) -> bool {
        self.updated
    }

    fn download(&mut self, pkg: PackageId, checksum: &str) -> CargoResult<MaybeLock> {
        let registry_config = loop {
            match self.config()? {
                Poll::Pending => self.block_until_ready()?,
                Poll::Ready(cfg) => break cfg.unwrap(),
            }
        };

        download::download(
            &self.cache_path,
            &self.config,
            pkg,
            checksum,
            registry_config,
        )
    }

    fn finish_download(
        &mut self,
        pkg: PackageId,
        checksum: &str,
        data: &[u8],
    ) -> CargoResult<File> {
        download::finish_download(&self.cache_path, &self.config, pkg, checksum, data)
    }

    fn is_crate_downloaded(&self, pkg: PackageId) -> bool {
        download::is_crate_downloaded(&self.cache_path, &self.config, pkg)
    }
}

impl<'cfg> Drop for RemoteRegistry<'cfg> {
    fn drop(&mut self) {
        // Just be sure to drop this before our other fields
        self.tree.borrow_mut().take();
    }
}
<|endoftext|>