<commit_before>use hal::{pass, image, memory, pso, IndexType};
use hal::format::Format;
use hal::pso::Comparison;
use metal::*;

pub fn map_format(format: Format) -> Option<MTLPixelFormat> {
    Some(match format {
        Format::R8Unorm        => MTLPixelFormat::R8Unorm,
        Format::R8Inorm        => MTLPixelFormat::R8Snorm,
        //Format::R8Srgb         => MTLPixelFormat::R8Unorm_sRGB, // !macOS
        Format::R8Uint         => MTLPixelFormat::R8Uint,
        Format::R8Int          => MTLPixelFormat::R8Sint,
        Format::Rg8Unorm       => MTLPixelFormat::RG8Unorm,
        Format::Rg8Inorm       => MTLPixelFormat::RG8Snorm,
        //Format::Rg8Srgb        => MTLPixelFormat::RG8Unorm_sRGB, // !macOS
        Format::Rg8Uint        => MTLPixelFormat::RG8Uint,
        Format::Rg8Int         => MTLPixelFormat::RG8Sint,
        Format::Rgba8Unorm     => MTLPixelFormat::RGBA8Unorm,
        Format::Rgba8Inorm     => MTLPixelFormat::RGBA8Snorm,
        Format::Rgba8Srgb      => MTLPixelFormat::RGBA8Unorm_sRGB,
        Format::Rgba8Uint      => MTLPixelFormat::RGBA8Uint,
        Format::Rgba8Int       => MTLPixelFormat::RGBA8Sint,
        Format::Bgra8Unorm     => MTLPixelFormat::BGRA8Unorm,
        Format::Bgra8Srgb      => MTLPixelFormat::BGRA8Unorm_sRGB,
        Format::Rgba32Float    => MTLPixelFormat::RGBA32Float,
        Format::D16Unorm       => MTLPixelFormat::Depth16Unorm,
        Format::D32Float       => MTLPixelFormat::Depth32Float,
        Format::D24UnormS8Uint => MTLPixelFormat::Depth24Unorm_Stencil8,
        Format::D32FloatS8Uint => MTLPixelFormat::Depth32Float_Stencil8,
        //Format::Bgra4Unorm =>
        //Format::R5g6b5Unorm =>
        //Format::A1r5g5b5Unorm =>
        _ => return None,
    })
}

pub fn map_load_operation(operation: pass::AttachmentLoadOp) -> MTLLoadAction {
    use self::pass::AttachmentLoadOp::*;

    match operation {
        Load => MTLLoadAction::Load,
        Clear => MTLLoadAction::Clear,
        DontCare => MTLLoadAction::DontCare,
    }
}

pub fn map_store_operation(operation: pass::AttachmentStoreOp) -> MTLStoreAction {
    use self::pass::AttachmentStoreOp::*;

    match operation {
        Store => MTLStoreAction::Store,
        DontCare => MTLStoreAction::DontCare,
    }
}

pub fn map_write_mask(mask: pso::ColorMask) -> MTLColorWriteMask {
    let mut mtl_mask = MTLColorWriteMask::MTLColorWriteMaskNone;

    if mask.contains(pso::ColorMask::RED) {
        mtl_mask |= MTLColorWriteMask::MTLColorWriteMaskRed;
    }
    if mask.contains(pso::ColorMask::GREEN) {
        mtl_mask |= MTLColorWriteMask::MTLColorWriteMaskGreen;
    }
    if mask.contains(pso::ColorMask::BLUE) {
        mtl_mask |= MTLColorWriteMask::MTLColorWriteMaskBlue;
    }
    if mask.contains(pso::ColorMask::ALPHA) {
        mtl_mask |= MTLColorWriteMask::MTLColorWriteMaskAlpha;
    }

    mtl_mask
}

fn map_factor(factor: pso::Factor) -> MTLBlendFactor {
    use hal::pso::Factor::*;

    match factor {
        Zero => MTLBlendFactor::Zero,
        One => MTLBlendFactor::One,
        SrcColor => MTLBlendFactor::SourceColor,
        OneMinusSrcColor => MTLBlendFactor::OneMinusSourceColor,
        DstColor => MTLBlendFactor::DestinationColor,
        OneMinusDstColor => MTLBlendFactor::OneMinusDestinationColor,
        SrcAlpha => MTLBlendFactor::SourceAlpha,
        OneMinusSrcAlpha => MTLBlendFactor::OneMinusSourceAlpha,
        DstAlpha => MTLBlendFactor::DestinationAlpha,
        OneMinusDstAlpha => MTLBlendFactor::OneMinusDestinationAlpha,
        ConstColor => MTLBlendFactor::BlendColor,
        OneMinusConstColor => MTLBlendFactor::OneMinusBlendColor,
        ConstAlpha => MTLBlendFactor::BlendAlpha,
        OneMinusConstAlpha => MTLBlendFactor::OneMinusBlendAlpha,
        SrcAlphaSaturate => MTLBlendFactor::SourceAlphaSaturated,
        Src1Color => MTLBlendFactor::Source1Color,
        OneMinusSrc1Color => MTLBlendFactor::OneMinusSource1Color,
        Src1Alpha => MTLBlendFactor::Source1Alpha,
        OneMinusSrc1Alpha => MTLBlendFactor::OneMinusSource1Alpha,
    }
}

pub fn map_blend_op(operation: &pso::BlendOp) -> (MTLBlendOperation, MTLBlendFactor, MTLBlendFactor) {
    use hal::pso::BlendOp::*;

    match *operation {
        Add    { src, dst } => (MTLBlendOperation::Add,             map_factor(src), map_factor(dst)),
        Sub    { src, dst } => (MTLBlendOperation::Subtract,        map_factor(src), map_factor(dst)),
        RevSub { src, dst } => (MTLBlendOperation::ReverseSubtract, map_factor(src), map_factor(dst)),
        Min => (MTLBlendOperation::Min, MTLBlendFactor::Zero, MTLBlendFactor::Zero),
        Max => (MTLBlendOperation::Max, MTLBlendFactor::Zero, MTLBlendFactor::Zero),
    }
}

pub fn map_vertex_format(format: Format) -> Option<MTLVertexFormat> {
    // TODO: more formats
    Some(match format {
        Format::Rgba32Float => MTLVertexFormat::Float4,
        Format::Rgb32Float => MTLVertexFormat::Float3,
        Format::Rg32Float => MTLVertexFormat::Float2,
        _ => return None,
    })
}

pub fn map_memory_properties_to_options(properties: memory::Properties) -> MTLResourceOptions {
    let mut options = MTLResourceOptions::empty();
    if properties.contains(memory::Properties::CPU_VISIBLE) {
        if properties.contains(memory::Properties::COHERENT) {
            options |= MTLResourceOptions::StorageModeShared;
        } else {
            options |= MTLResourceOptions::StorageModeManaged;
        }
    } else if properties.contains(memory::Properties::DEVICE_LOCAL) {
        options |= MTLResourceOptions::StorageModePrivate;
    } else {
        panic!("invalid heap properties");
    }
    if !properties.contains(memory::Properties::CPU_CACHED) {
        options |= MTLResourceOptions::CPUCacheModeWriteCombined;
    }
    options
}

pub fn map_memory_properties_to_storage_and_cache(properties: memory::Properties) -> (MTLStorageMode, MTLCPUCacheMode) {
    let storage = if properties.contains(memory::Properties::CPU_VISIBLE) {
        if properties.contains(memory::Properties::COHERENT) {
            MTLStorageMode::Shared
        } else {
            MTLStorageMode::Managed
        }
    } else if properties.contains(memory::Properties::DEVICE_LOCAL) {
        MTLStorageMode::Private
    } else {
        panic!("invalid heap properties");
    };
    let cpu = if properties.contains(memory::Properties::CPU_CACHED) {
        MTLCPUCacheMode::DefaultCache
    } else {
        MTLCPUCacheMode::WriteCombined
    };
    (storage, cpu)
}

pub fn resource_options_from_storage_and_cache(storage: MTLStorageMode, cache: MTLCPUCacheMode) -> MTLResourceOptions {
    MTLResourceOptions::from_bits(
        ((storage as u64) << MTLResourceStorageModeShift) | ((cache as u64) << MTLResourceCPUCacheModeShift)
    ).unwrap()
}

pub fn map_texture_usage(usage: image::Usage) -> MTLTextureUsage {
    let mut texture_usage = MTLTextureUsage::MTLTextureUsagePixelFormatView;
    if usage.contains(image::Usage::COLOR_ATTACHMENT) || usage.contains(image::Usage::DEPTH_STENCIL_ATTACHMENT) {
        texture_usage |= MTLTextureUsage::MTLTextureUsageRenderTarget;
    }
    if usage.contains(image::Usage::SAMPLED) {
        texture_usage |= MTLTextureUsage::MTLTextureUsageShaderRead;
    }
    if usage.contains(image::Usage::STORAGE) {
        texture_usage |= MTLTextureUsage::MTLTextureUsageShaderRead | MTLTextureUsage::MTLTextureUsageShaderWrite;
    }
    // TODO shader write
    texture_usage
}

pub fn map_index_type(index_type: IndexType) -> MTLIndexType {
    match index_type {
        IndexType::U16 => MTLIndexType::UInt16,
        IndexType::U32 => MTLIndexType::UInt32,
    }
}

pub fn map_compare_function(fun: Comparison) -> MTLCompareFunction {
    match fun {
        Comparison::Never => MTLCompareFunction::Never,
        Comparison::Less => MTLCompareFunction::Less,
        Comparison::LessEqual => MTLCompareFunction::LessEqual,
        Comparison::Equal => MTLCompareFunction::Equal,
        Comparison::GreaterEqual => MTLCompareFunction::GreaterEqual,
        Comparison::Greater => MTLCompareFunction::Greater,
        Comparison::NotEqual => MTLCompareFunction::NotEqual,
        Comparison::Always => MTLCompareFunction::Always,
    }
}

pub fn map_filter(filter: image::Filter) -> MTLSamplerMinMagFilter {
    match filter {
        image::Filter::Nearest => MTLSamplerMinMagFilter::Nearest,
        image::Filter::Linear => MTLSamplerMinMagFilter::Linear,
    }
}

pub fn map_wrap_mode(wrap: image::WrapMode) -> MTLSamplerAddressMode {
    match wrap {
        image::WrapMode::Tile => MTLSamplerAddressMode::Repeat,
        image::WrapMode::Mirror => MTLSamplerAddressMode::MirrorRepeat,
        image::WrapMode::Clamp => MTLSamplerAddressMode::ClampToEdge,
        image::WrapMode::Border => MTLSamplerAddressMode::ClampToBorderColor,
    }
}

pub fn map_extent(extent: image::Extent) -> MTLSize {
    MTLSize {
        width: extent.width as _,
        height: extent.height as _,
        depth: extent.depth as _,
    }
}
<commit_msg>mtl: Add more format conversions<commit_after>use hal::{pass, image, memory, pso, IndexType};
use hal::format::Format;
use hal::pso::Comparison;
use metal::*;

pub fn map_format(format: Format) -> Option<MTLPixelFormat> {
    use metal::MTLPixelFormat::*;
    use hal::format::Format as f;
    Some(match format {
        //f::B5g6r5Unorm       => B5G6R5Unorm, // !macOS
        //f::B5g5r5a1Unorm     => BGR5A1Unorm, // !macOS
        f::R8Unorm           => R8Unorm,
        f::R8Inorm           => R8Snorm,
        //f::R8Srgb            => R8Unorm_sRGB, // !macOS
        f::R8Uint            => R8Uint,
        f::R8Int             => R8Sint,
        f::Rg8Unorm          => RG8Unorm,
        f::Rg8Inorm          => RG8Snorm,
        //f::Rg8Srgb           => RG8Unorm_sRGB, // !macOS
        f::Rg8Uint           => RG8Uint,
        f::Rg8Int            => RG8Sint,
        f::Rgba8Unorm        => RGBA8Unorm,
        f::Rgba8Inorm        => RGBA8Snorm,
        f::Rgba8Srgb         => RGBA8Unorm_sRGB,
        f::Rgba8Uint         => RGBA8Uint,
        f::Rgba8Int          => RGBA8Sint,
        f::Bgra8Unorm        => BGRA8Unorm,
        f::Bgra8Srgb         => BGRA8Unorm_sRGB,
        f::R16Unorm          => R16Unorm,
        f::R16Inorm          => R16Snorm,
        f::R16Uint           => R16Uint,
        f::R16Int            => R16Sint,
        f::R16Float          => R16Float,
        f::Rg16Unorm         => RG16Unorm,
        f::Rg16Inorm         => RG16Snorm,
        f::Rg16Uint          => RG16Uint,
        f::Rg16Int           => RG16Sint,
        f::Rg16Float         => RG16Float,
        f::Rgba16Unorm       => RGBA16Unorm,
        f::Rgba16Inorm       => RGBA16Snorm,
        f::Rgba16Uint        => RGBA16Uint,
        f::Rgba16Int         => RGBA16Sint,
        f::Rgba16Float       => RGBA16Float,
        f::R32Uint           => R32Uint,
        f::R32Int            => R32Sint,
        f::R32Float          => R32Float,
        f::Rg32Uint          => RG32Uint,
        f::Rg32Int           => RG32Sint,
        f::Rg32Float         => RG32Float,
        f::Rgba32Uint        => RGBA32Uint,
        f::Rgba32Int         => RGBA32Sint,
        f::Rgba32Float       => RGBA32Float,
        f::D16Unorm          => Depth16Unorm,
        f::D32Float          => Depth32Float,
        f::D24UnormS8Uint    => Depth24Unorm_Stencil8,
        f::D32FloatS8Uint    => Depth32Float_Stencil8,
        f::Bc1RgbUnorm       => BC1_RGBA,
        f::Bc1RgbSrgb        => BC1_RGBA_sRGB,
        f::Bc2Unorm          => BC2_RGBA,
        f::Bc2Srgb           => BC2_RGBA_sRGB,
        f::Bc3Unorm          => BC3_RGBA,
        f::Bc3Srgb           => BC3_RGBA_sRGB,
        f::Bc4Unorm          => BC4_RUnorm,
        f::Bc4Inorm          => BC4_RSnorm,
        f::Bc5Unorm          => BC5_RGUnorm,
        f::Bc5Inorm          => BC5_RGSnorm,
        f::Bc6hUfloat        => BC6H_RGBUfloat,
        f::Bc6hFloat         => BC6H_RGBFloat,
        f::Bc7Unorm          => BC7_RGBAUnorm,
        f::Bc7Srgb           => BC7_RGBAUnorm_sRGB,
        //f::EacR11Unorm       => EAC_R11Unorm, // !macOS
        //f::EacR11Inorm       => EAC_R11Snorm, // !macOS
        //f::EacR11g11Unorm    => EAC_RG11Unorm, // !macOS
        //f::EacR11g11Inorm    => EAC_RG11Snorm, // !macOS
        //f::Etc2R8g8b8Unorm   => ETC2_RGB8, // !macOS
        //f::Etc2R8g8b8Srgb    => ETC2_RGB8_sRGB, // !macOS
        //f::Etc2R8g8b8a1Unorm => ETC2_RGB8A1, // !macOS
        //f::Etc2R8g8b8a1Srgb  => ETC2_RGBA1_sRGB, // !macOS
        //f::Astc4x4Unorm      => ASTC_4x4_LDR, // !macOS
        //f::Astc4x4Srgb       => ASTC_4x4_sRGB, // !macOS
        //f::Astc5x4Unorm      => ASTC_5x4_LDR, // !macOS
        //f::Astc5x4Srgb       => ASTC_5x4_sRGB, // !macOS
        //f::Astc5x5Unorm      => ASTC_5x5_LDR, // !macOS
        //f::Astc5x5Srgb       => ASTC_5x5_sRGB, // !macOS
        //f::Astc6x5Unorm      => ASTC_6x5_LDR, // !macOS
        //f::Astc6x5Srgb       => ASTC_6x5_sRGB, // !macOS
        //f::Astc6x6Unorm      => ASTC_6x6_LDR, // !macOS
        //f::Astc6x6Srgb       => ASTC_6x6_sRGB, // !macOS
        //f::Astc8x5Unorm      => ASTC_8x5_LDR, // !macOS
        //f::Astc8x5Srgb       => ASTC_8x5_sRGB, // !macOS
        //f::Astc8x6Unorm      => ASTC_8x6_LDR, // !macOS
        //f::Astc8x6Srgb       => ASTC_8x6_sRGB, // !macOS
        //f::Astc8x8Unorm      => ASTC_8x8_LDR, // !macOS
        //f::Astc8x8Srgb       => ASTC_8x8_sRGB, // !macOS
        //f::Astc10x5Unorm     => ASTC_10x5_LDR, // !macOS
        //f::Astc10x5Srgb      => ASTC_10x5_sRGB, // !macOS
        //f::Astc10x6Unorm     => ASTC_10x6_LDR, // !macOS
        //f::Astc10x6Srgb      => ASTC_10x6_sRGB, // !macOS
        //f::Astc10x8Unorm     => ASTC_10x8_LDR, // !macOS
        //f::Astc10x8Srgb      => ASTC_10x8_sRGB, // !macOS
        //f::Astc10x10Unorm    => ASTC_10x10_LDR, // !macOS
        //f::Astc10x10Srgb     => ASTC_10x10_sRGB, // !macOS
        //f::Astc12x10Unorm    => ASTC_12x10_LDR, // !macOS
        //f::Astc12x10Srgb     => ASTC_12x10_sRGB, // !macOS
        //f::Astc12x12Unorm    => ASTC_12x12_LDR, // !macOS
        //f::Astc12x12Srgb     => ASTC_12x12_sRGB, // !macOS
        //f::Bgra4Unorm =>
        //f::R5g6b5Unorm =>
        //f::A1r5g5b5Unorm =>
        _ => return None,
    })
}

pub fn map_load_operation(operation: pass::AttachmentLoadOp) -> MTLLoadAction {
    use self::pass::AttachmentLoadOp::*;

    match operation {
        Load => MTLLoadAction::Load,
        Clear => MTLLoadAction::Clear,
        DontCare => MTLLoadAction::DontCare,
    }
}

pub fn map_store_operation(operation: pass::AttachmentStoreOp) -> MTLStoreAction {
    use self::pass::AttachmentStoreOp::*;

    match operation {
        Store => MTLStoreAction::Store,
        DontCare => MTLStoreAction::DontCare,
    }
}

pub fn map_write_mask(mask: pso::ColorMask) -> MTLColorWriteMask {
    let mut mtl_mask = MTLColorWriteMask::MTLColorWriteMaskNone;

    if mask.contains(pso::ColorMask::RED) {
        mtl_mask |= MTLColorWriteMask::MTLColorWriteMaskRed;
    }
    if mask.contains(pso::ColorMask::GREEN) {
        mtl_mask |= MTLColorWriteMask::MTLColorWriteMaskGreen;
    }
    if mask.contains(pso::ColorMask::BLUE) {
        mtl_mask |= MTLColorWriteMask::MTLColorWriteMaskBlue;
    }
    if mask.contains(pso::ColorMask::ALPHA) {
        mtl_mask |= MTLColorWriteMask::MTLColorWriteMaskAlpha;
    }

    mtl_mask
}

fn map_factor(factor: pso::Factor) -> MTLBlendFactor {
    use hal::pso::Factor::*;

    match factor {
        Zero => MTLBlendFactor::Zero,
        One => MTLBlendFactor::One,
        SrcColor => MTLBlendFactor::SourceColor,
        OneMinusSrcColor => MTLBlendFactor::OneMinusSourceColor,
        DstColor => MTLBlendFactor::DestinationColor,
        OneMinusDstColor => MTLBlendFactor::OneMinusDestinationColor,
        SrcAlpha => MTLBlendFactor::SourceAlpha,
        OneMinusSrcAlpha => MTLBlendFactor::OneMinusSourceAlpha,
        DstAlpha => MTLBlendFactor::DestinationAlpha,
        OneMinusDstAlpha => MTLBlendFactor::OneMinusDestinationAlpha,
        ConstColor => MTLBlendFactor::BlendColor,
        OneMinusConstColor => MTLBlendFactor::OneMinusBlendColor,
        ConstAlpha => MTLBlendFactor::BlendAlpha,
        OneMinusConstAlpha => MTLBlendFactor::OneMinusBlendAlpha,
        SrcAlphaSaturate => MTLBlendFactor::SourceAlphaSaturated,
        Src1Color => MTLBlendFactor::Source1Color,
        OneMinusSrc1Color => MTLBlendFactor::OneMinusSource1Color,
        Src1Alpha => MTLBlendFactor::Source1Alpha,
        OneMinusSrc1Alpha => MTLBlendFactor::OneMinusSource1Alpha,
    }
}

pub fn map_blend_op(operation: &pso::BlendOp) -> (MTLBlendOperation, MTLBlendFactor, MTLBlendFactor) {
    use hal::pso::BlendOp::*;

    match *operation {
        Add    { src, dst } => (MTLBlendOperation::Add,             map_factor(src), map_factor(dst)),
        Sub    { src, dst } => (MTLBlendOperation::Subtract,        map_factor(src), map_factor(dst)),
        RevSub { src, dst } => (MTLBlendOperation::ReverseSubtract, map_factor(src), map_factor(dst)),
        Min => (MTLBlendOperation::Min, MTLBlendFactor::Zero, MTLBlendFactor::Zero),
        Max => (MTLBlendOperation::Max, MTLBlendFactor::Zero, MTLBlendFactor::Zero),
    }
}

pub fn map_vertex_format(format: Format) -> Option<MTLVertexFormat> {
    // TODO: more formats
    Some(match format {
        Format::Rgba32Float => MTLVertexFormat::Float4,
        Format::Rgb32Float => MTLVertexFormat::Float3,
        Format::Rg32Float => MTLVertexFormat::Float2,
        _ => return None,
    })
}

pub fn map_memory_properties_to_options(properties: memory::Properties) -> MTLResourceOptions {
    let mut options = MTLResourceOptions::empty();
    if properties.contains(memory::Properties::CPU_VISIBLE) {
        if properties.contains(memory::Properties::COHERENT) {
            options |= MTLResourceOptions::StorageModeShared;
        } else {
            options |= MTLResourceOptions::StorageModeManaged;
        }
    } else if properties.contains(memory::Properties::DEVICE_LOCAL) {
        options |= MTLResourceOptions::StorageModePrivate;
    } else {
        panic!("invalid heap properties");
    }
    if !properties.contains(memory::Properties::CPU_CACHED) {
        options |= MTLResourceOptions::CPUCacheModeWriteCombined;
    }
    options
}

pub fn map_memory_properties_to_storage_and_cache(properties: memory::Properties) -> (MTLStorageMode, MTLCPUCacheMode) {
    let storage = if properties.contains(memory::Properties::CPU_VISIBLE) {
        if properties.contains(memory::Properties::COHERENT) {
            MTLStorageMode::Shared
        } else {
            MTLStorageMode::Managed
        }
    } else if properties.contains(memory::Properties::DEVICE_LOCAL) {
        MTLStorageMode::Private
    } else {
        panic!("invalid heap properties");
    };
    let cpu = if properties.contains(memory::Properties::CPU_CACHED) {
        MTLCPUCacheMode::DefaultCache
    } else {
        MTLCPUCacheMode::WriteCombined
    };
    (storage, cpu)
}

pub fn resource_options_from_storage_and_cache(storage: MTLStorageMode, cache: MTLCPUCacheMode) -> MTLResourceOptions {
    MTLResourceOptions::from_bits(
        ((storage as u64) << MTLResourceStorageModeShift) | ((cache as u64) << MTLResourceCPUCacheModeShift)
    ).unwrap()
}

pub fn map_texture_usage(usage: image::Usage) -> MTLTextureUsage {
    let mut texture_usage = MTLTextureUsage::MTLTextureUsagePixelFormatView;
    if usage.contains(image::Usage::COLOR_ATTACHMENT) || usage.contains(image::Usage::DEPTH_STENCIL_ATTACHMENT) {
        texture_usage |= MTLTextureUsage::MTLTextureUsageRenderTarget;
    }
    if usage.contains(image::Usage::SAMPLED) {
        texture_usage |= MTLTextureUsage::MTLTextureUsageShaderRead;
    }
    if usage.contains(image::Usage::STORAGE) {
        texture_usage |= MTLTextureUsage::MTLTextureUsageShaderRead | MTLTextureUsage::MTLTextureUsageShaderWrite;
    }
    // TODO shader write
    texture_usage
}

pub fn map_index_type(index_type: IndexType) -> MTLIndexType {
    match index_type {
        IndexType::U16 => MTLIndexType::UInt16,
        IndexType::U32 => MTLIndexType::UInt32,
    }
}

pub fn map_compare_function(fun: Comparison) -> MTLCompareFunction {
    match fun {
        Comparison::Never => MTLCompareFunction::Never,
        Comparison::Less => MTLCompareFunction::Less,
        Comparison::LessEqual => MTLCompareFunction::LessEqual,
        Comparison::Equal => MTLCompareFunction::Equal,
        Comparison::GreaterEqual => MTLCompareFunction::GreaterEqual,
        Comparison::Greater => MTLCompareFunction::Greater,
        Comparison::NotEqual => MTLCompareFunction::NotEqual,
        Comparison::Always => MTLCompareFunction::Always,
    }
}

pub fn map_filter(filter: image::Filter) -> MTLSamplerMinMagFilter {
    match filter {
        image::Filter::Nearest => MTLSamplerMinMagFilter::Nearest,
        image::Filter::Linear => MTLSamplerMinMagFilter::Linear,
    }
}

pub fn map_wrap_mode(wrap: image::WrapMode) -> MTLSamplerAddressMode {
    match wrap {
        image::WrapMode::Tile => MTLSamplerAddressMode::Repeat,
        image::WrapMode::Mirror => MTLSamplerAddressMode::MirrorRepeat,
        image::WrapMode::Clamp => MTLSamplerAddressMode::ClampToEdge,
        image::WrapMode::Border => MTLSamplerAddressMode::ClampToBorderColor,
    }
}

pub fn map_extent(extent: image::Extent) -> MTLSize {
    MTLSize {
        width: extent.width as _,
        height: extent.height as _,
        depth: extent.depth as _,
    }
}
<|endoftext|>


<commit_before>extern crate rand;

use rand::random;
use std::io::BufferedReader;
use std::io;

fn main() {
    println!("I'm thinking of a number between 1 and 100. Can you guess it?");
    let x = random::<int>();
    let mut reader = BufferedReader::new(io::stdin());
    while true {
        let line = reader.read_line().unwrap();
        let num = from_str::<int>(line.slice_to(line.len() - 1));
        match num {
            Some(number_string) => println!("Well, it was a number."),
            None                => println!("Doesn't count.")
        }
    }
}
<commit_msg>Unused variables follow Prolog convention.<commit_after>extern crate rand;

use rand::random;
use std::io::BufferedReader;
use std::io;

fn main() {
    println!("I'm thinking of a number between 1 and 100. Can you guess it?");
    let x = random::<int>();
    let mut reader = BufferedReader::new(io::stdin());
    while true {
        let line = reader.read_line().unwrap();
        let num = from_str::<int>(line.slice_to(line.len() - 1));
        match num {
            Some(_number_string) => println!("Well, it was a number."),
            None                => println!("Doesn't count.")
        }
    }
}
<|endoftext|>


<commit_before>extern crate sdl2;

use sdl2::render::Renderer as SdlRenderer;
use sdl2::render::Texture as SdlTexture;
use sdl2_image::LoadTexture;
use std::path::Path;
use std::collections::HashMap;
use std::cell::RefCell;
use std::rc::Rc;
use sdl2::rect;

trait Renderer {
    type Texture;
    fn load_texture(&self, path: &Path) -> Result<Self::Texture, String>;
    fn output_size(&self) -> Result<(u32, u32), String>;

    fn clear(&mut self);
    fn present(&mut self);
    fn copy(&mut self,
            texture: Rc<Self::Texture>,
            src: Option<rect::Rect>,
            dst: Option<rect::Rect>)
            -> Result<(), String>;
}

impl<'a> Renderer for SdlRenderer<'a> {
    type Texture = SdlTexture;

    fn load_texture(&self, path: &Path) -> Result<SdlTexture, String> {
        LoadTexture::load_texture(self, path)
    }

    fn output_size(&self) -> Result<(u32, u32), String> {
        self.output_size()
    }

    fn copy(&mut self,
            texture: Rc<SdlTexture>,
            src: Option<rect::Rect>,
            dst: Option<rect::Rect>)
            -> Result<(), String> {
        self.copy(&*texture, src, dst)
    }

    fn clear(&mut self) {
        self.clear();
    }

    fn present(&mut self) {
        self.present();
    }
}

struct ResourceManager<'a, I: Renderer> {
    texture_cache: RefCell<HashMap<&'a str, Rc<I::Texture>>>,
    renderer: I,
}

impl<'a, I: Renderer> ResourceManager<'a, I> {
    pub fn new(renderer: I) -> Self {
        ResourceManager {
            texture_cache: RefCell::new(HashMap::new()),
            renderer: renderer,
        }
    }

    pub fn load_texture(&self, path: &'a str) -> Result<Rc<I::Texture>, String> {
        {
            let cache = self.texture_cache.borrow();
            let image = cache.get(path);
            if let Some(x) = image {
                return Ok(x.clone());
            }
        }
        let mut cache = self.texture_cache.borrow_mut();
        let image_path = Path::new(path);
        let image = Rc::new(try!(self.renderer.load_texture(image_path)));
        cache.insert(path, image.clone());
        Ok(image.clone())
    }
}

#[cfg(test)]
mod test {
    use std::path::Path;
    use std::cell::RefCell;
    use std::rc::Rc;
    use sdl2::rect;
    use super::Renderer;
    use super::ResourceManager;

    #[test]
    fn loads_image() {
        let (subject, tracker) = new_subject(None);
        let image = subject.load_texture("mypath/").unwrap();
        assert_eq!(image.path, String::from("mypath/"));
        assert_eq!(tracker.borrow().load_count, 1);
    }

    #[test]
    fn returns_error() {
        let (subject, tracker) = new_subject(Some("FAIL".into()));
        let image = subject.load_texture("mypath/");
        assert_eq!(image.err(), Some("FAIL".into()));
        assert_eq!(tracker.borrow().load_count, 1);
    }

    #[test]
    fn caches_images() {
        let (subject, tracker) = new_subject(None);

        // get a new image - number of calls is 1
        let image1 = subject.load_texture("mypath/1").unwrap();
        assert_eq!(image1.path, String::from("mypath/1"));
        assert_eq!(tracker.borrow().load_count, 1);

        // load the same image - number of calls should still be 1
        let image2 = subject.load_texture("mypath/1").unwrap();
        assert_eq!(image2.path, String::from("mypath/1"));
        assert_eq!(tracker.borrow().load_count, 1);

        // load a different image - number of calls should increase
        let image3 = subject.load_texture("mypath/2").unwrap();
        assert_eq!(image3.path, String::from("mypath/2"));
        assert_eq!(tracker.borrow().load_count, 2);
    }

    #[test]
    fn draws_images() {
        let (subject, tracker) = new_subject(None);
        let image = subject.load_texture("mypath/").unwrap();
    }

    #[derive(Debug)]
    struct MockTexture {
        path: String,
    }

    struct RendererTracker {
        load_count: u16,
        last_img: Rc<MockTexture>,
        last_src: Option<rect::Rect>,
        last_dst: Option<rect::Rect>,
    }

    impl RendererTracker {
        fn new() -> Self {
            RendererTracker {
                load_count: 0,
                last_img: Rc::new(MockTexture { path: "NULL".into() }),
                last_dst: None,
                last_src: None,
            }
        }
    }

    struct MockRenderer {
        error: Option<String>,
        tracker: Rc<RefCell<RendererTracker>>,
    }

    impl Renderer for MockRenderer {
        type Texture = MockTexture;

        fn load_texture(&self, path: &Path) -> Result<MockTexture, String> {
            self.tracker.borrow_mut().load_count += 1;
            match self.error {
                None => Ok(MockTexture { path: path.to_str().unwrap_or("").into() }),
                Some(ref e) => Err(e.clone()),
            }
        }

        fn copy(&mut self,
                image: Rc<MockTexture>,
                src: Option<rect::Rect>,
                dst: Option<rect::Rect>)
                -> Result<(), String> {
            match self.error {
                None => {
                    let mut tracker = self.tracker.borrow_mut();
                    tracker.last_img = image;
                    tracker.last_src = src;
                    tracker.last_dst = dst;
                    Ok(())
                }
                Some(ref e) => Err(e.clone()),
            }
        }

        fn clear(&mut self) {}

        fn present(&mut self) {}

        fn output_size(&self) -> Result<(u32, u32), String> {
            Ok((0, 0))
        }
    }

    fn new_subject<'a>(error: Option<String>)
                       -> (ResourceManager<'a, MockRenderer>, Rc<RefCell<RendererTracker>>) {
        let tracker = Rc::new(RefCell::new(RendererTracker::new()));
        let renderer = MockRenderer {
            error: error,
            tracker: tracker.clone(),
        };

        let subject = ResourceManager::new(renderer);
        (subject, tracker)
    }
}
<commit_msg>WIP<commit_after>extern crate sdl2;
extern crate glm;

use sdl2::render::Renderer as SdlRenderer;
use sdl2::render::Texture as SdlTexture;
use sdl2_image::LoadTexture;
use std::path::Path;
use std::collections::HashMap;
use std::cell::RefCell;
use std::rc::Rc;
use sdl2::rect;

trait Renderer {
    type Texture;
    fn load_texture(&self, path: &Path) -> Result<Self::Texture, String>;
    fn output_size(&self) -> Result<(u32, u32), String>;

    fn clear(&mut self);
    fn present(&mut self);
    fn copy(&mut self,
            texture: Rc<Self::Texture>,
            src: Option<rect::Rect>,
            dst: Option<rect::Rect>)
            -> Result<(), String>;
}

struct TextureData<T> {
    texture: T,
    size: glm::IVec2,
}

impl<'a> Renderer for SdlRenderer<'a> {
    type Texture = SdlTexture;

    fn load_texture(&self, path: &Path) -> Result<SdlTexture, String> {
        LoadTexture::load_texture(self, path)
    }

    fn output_size(&self) -> Result<(u32, u32), String> {
        self.output_size()
    }

    fn copy(&mut self,
            texture: Rc<SdlTexture>,
            src: Option<rect::Rect>,
            dst: Option<rect::Rect>)
            -> Result<(), String> {
        self.copy(&*texture, src, dst)
    }

    fn clear(&mut self) {
        self.clear();
    }

    fn present(&mut self) {
        self.present();
    }
}

struct ResourceManager<'a, I: Renderer> {
    texture_cache: RefCell<HashMap<&'a str, Rc<I::Texture>>>,
    renderer: I,
}

impl<'a, I: Renderer> ResourceManager<'a, I> {
    pub fn new(renderer: I) -> Self {
        ResourceManager {
            texture_cache: RefCell::new(HashMap::new()),
            renderer: renderer,
        }
    }

    pub fn load_texture(&self, path: &'a str) -> Result<Rc<I::Texture>, String> {
        {
            let cache = self.texture_cache.borrow();
            let image = cache.get(path);
            if let Some(x) = image {
                return Ok(x.clone());
            }
        }
        let mut cache = self.texture_cache.borrow_mut();
        let image_path = Path::new(path);
        let image = Rc::new(try!(self.renderer.load_texture(image_path)));
        cache.insert(path, image.clone());
        Ok(image.clone())
    }

    pub fn clear(&mut self) {
        self.renderer.clear();
    }

    pub fn present(&mut self) {
        self.renderer.present();
    }
}

#[cfg(test)]
mod test {
    use std::path::Path;
    use std::cell::RefCell;
    use std::rc::Rc;
    use sdl2::rect;
    use super::Renderer;
    use super::ResourceManager;

    #[test]
    fn loads_image() {
        let (subject, tracker) = new_subject(None);
        let image = subject.load_texture("mypath/").unwrap();
        assert_eq!(image.path, String::from("mypath/"));
        assert_eq!(tracker.borrow().load_count, 1);
    }

    #[test]
    fn returns_error() {
        let (subject, tracker) = new_subject(Some("FAIL".into()));
        let image = subject.load_texture("mypath/");
        assert_eq!(image.err(), Some("FAIL".into()));
        assert_eq!(tracker.borrow().load_count, 1);
    }

    #[test]
    fn caches_images() {
        let (subject, tracker) = new_subject(None);

        // get a new image - number of calls is 1
        let image1 = subject.load_texture("mypath/1").unwrap();
        assert_eq!(image1.path, String::from("mypath/1"));
        assert_eq!(tracker.borrow().load_count, 1);

        // load the same image - number of calls should still be 1
        let image2 = subject.load_texture("mypath/1").unwrap();
        assert_eq!(image2.path, String::from("mypath/1"));
        assert_eq!(tracker.borrow().load_count, 1);

        // load a different image - number of calls should increase
        let image3 = subject.load_texture("mypath/2").unwrap();
        assert_eq!(image3.path, String::from("mypath/2"));
        assert_eq!(tracker.borrow().load_count, 2);
    }

    #[test]
    fn draws_images() {
        let (subject, tracker) = new_subject(None);
        let image = subject.load_texture("mypath/").unwrap();
    }

    #[derive(Debug)]
    struct MockTexture {
        path: String,
    }

    struct RendererTracker {
        load_count: u16,
        last_img: Rc<MockTexture>,
        last_src: Option<rect::Rect>,
        last_dst: Option<rect::Rect>,
    }

    impl RendererTracker {
        fn new() -> Self {
            RendererTracker {
                load_count: 0,
                last_img: Rc::new(MockTexture { path: "NULL".into() }),
                last_dst: None,
                last_src: None,
            }
        }
    }

    struct MockRenderer {
        error: Option<String>,
        tracker: Rc<RefCell<RendererTracker>>,
    }

    impl Renderer for MockRenderer {
        type Texture = MockTexture;

        fn load_texture(&self, path: &Path) -> Result<MockTexture, String> {
            self.tracker.borrow_mut().load_count += 1;
            match self.error {
                None => Ok(MockTexture { path: path.to_str().unwrap_or("").into() }),
                Some(ref e) => Err(e.clone()),
            }
        }

        fn copy(&mut self,
                image: Rc<MockTexture>,
                src: Option<rect::Rect>,
                dst: Option<rect::Rect>)
                -> Result<(), String> {
            match self.error {
                None => {
                    let mut tracker = self.tracker.borrow_mut();
                    tracker.last_img = image;
                    tracker.last_src = src;
                    tracker.last_dst = dst;
                    Ok(())
                }
                Some(ref e) => Err(e.clone()),
            }
        }

        fn clear(&mut self) {}

        fn present(&mut self) {}

        fn output_size(&self) -> Result<(u32, u32), String> {
            Ok((0, 0))
        }
    }

    fn new_subject<'a>(error: Option<String>)
                       -> (ResourceManager<'a, MockRenderer>, Rc<RefCell<RendererTracker>>) {
        let tracker = Rc::new(RefCell::new(RendererTracker::new()));
        let renderer = MockRenderer {
            error: error,
            tracker: tracker.clone(),
        };

        let subject = ResourceManager::new(renderer);
        (subject, tracker)
    }
}
<|endoftext|>


<commit_before>use parser::{Expression, Block};
use memory::MemoryBlock;

use super::{Operation, OperationsResult, expression, Target, block};
use super::scope::ScopeStack;

pub fn into_operations(
    scope: &mut ScopeStack,
    condition: Expression,
    body: Block,
) -> OperationsResult {
    let unit_type = scope.primitives().unit();
    let bool_type = scope.primitives().bool();
    let cond_mem = scope.allocate(bool_type);

    let cond_ops = expression::into_operations(scope, condition, Target::TypedBlock {
        type_id: bool_type,
        memory: cond_mem,
    })?;
    let loop_body = block::into_operations(scope, body, Target::TypedBlock {
        type_id: unit_type,
        memory: MemoryBlock::default(),
    })?;

    // While loops need to evaluate the condition both before the loop and at the end
    // of the loop body
    let mut ops = Vec::new();
    ops.extend(cond_ops.clone());
    ops.push(Operation::Loop {
        cond: cond_mem.position(),
        body: loop_body.into_iter().chain(cond_ops).collect(),
    });

    Ok(vec![Operation::TempAllocate {
        temp: cond_mem,
        body: ops,
        should_zero: true,
    }])
}
<commit_msg>Fixed a bug where while loops weren't zeroing their condition cell before re-evaluating it<commit_after>use std::iter::once;

use parser::{Expression, Block};
use memory::MemoryBlock;

use super::{Operation, OperationsResult, expression, Target, block};
use super::scope::ScopeStack;

pub fn into_operations(
    scope: &mut ScopeStack,
    condition: Expression,
    body: Block,
) -> OperationsResult {
    let unit_type = scope.primitives().unit();
    let bool_type = scope.primitives().bool();
    let cond_mem = scope.allocate(bool_type);

    let cond_ops = expression::into_operations(scope, condition, Target::TypedBlock {
        type_id: bool_type,
        memory: cond_mem,
    })?;
    let loop_body = block::into_operations(scope, body, Target::TypedBlock {
        type_id: unit_type,
        memory: MemoryBlock::default(),
    })?;

    // While loops need to evaluate the condition both before the loop and at the end
    // of the loop body
    let mut ops = Vec::new();
    ops.extend(cond_ops.clone());
    ops.push(Operation::Loop {
        cond: cond_mem.position(),
        body: loop_body.into_iter().chain(once(Operation::Zero {
            target: cond_mem,
        })).chain(cond_ops).collect(),
    });

    Ok(vec![Operation::TempAllocate {
        temp: cond_mem,
        body: ops,
        should_zero: true,
    }])
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::sync::Arc;
use super::types::{
    AttestationRecord,
    AttesterMap,
};
use super::attestation_parent_hashes::{
    attestation_parent_hashes,
    ParentHashesError,
};
use super::db::{
    ClientDB,
    DBError
};
use super::db::stores::{
    BlockStore,
    BlockAtSlotError,
    ValidatorStore,
};
use super::types::{
    Hash256,
};
use super::message_generation::generate_signed_message;
use super::signature_verification::{
    verify_aggregate_signature_for_indices,
    SignatureVerificationError,
};

#[derive(Debug,PartialEq)]
pub enum AttestationValidationError {
    SlotTooHigh,
    SlotTooLow,
    JustifiedSlotIncorrect,
    InvalidJustifiedBlockHash,
    TooManyObliqueHashes,
    BadCurrentHashes,
    BadObliqueHashes,
    BadAttesterMap,
    IntWrapping,
    PublicKeyCorrupt,
    NoPublicKeyForValidator,
    BadBitfieldLength,
    InvalidBitfield,
    InvalidBitfieldEndBits,
    NoSignatures,
    NonZeroTrailingBits,
    BadAggregateSignature,
    DBError(String),
}

/// The context against which some attestation should be validated.
pub struct AttestationValidationContext<T>
    where T: ClientDB + Sized
{
    /// The slot as determined by the system time.
    pub block_slot: u64,
    /// The cycle_length as determined by the chain configuration.
    pub cycle_length: u8,
    /// The last justified slot as per the client's view of the canonical chain.
    pub last_justified_slot: u64,
    /// A vec of the hashes of the blocks preceeding the present slot.
    pub parent_hashes: Arc<Vec<Hash256>>,
    /// The store containing block information.
    pub block_store: Arc<BlockStore<T>>,
    /// The store containing validator information.
    pub validator_store: Arc<ValidatorStore<T>>,
    /// A map of (slot, shard_id) to the attestation set of validation indices.
    pub attester_map: Arc<AttesterMap>,
}

impl<T> AttestationValidationContext<T>
    where T: ClientDB
{
    /// Validate a (fully deserialized) AttestationRecord against this context.
    ///
    /// The function will return a HashSet of validator indices (canonical validator indices not
    /// attestation indices) if the validation passed successfully, or an error otherwise.
    ///
    /// The attestation's aggregate signature will be verified, therefore the function must able to
    /// access all required validation public keys via the `validator_store`.
    pub fn validate_attestation(&self, a: &AttestationRecord)
        -> Result<HashSet<usize>, AttestationValidationError>
    {
        /*
         * The attesation slot must not be higher than the block that contained it.
         */
        if a.slot > self.block_slot {
            return Err(AttestationValidationError::SlotTooHigh);
        }

        /*
         * The slot of this attestation must not be more than cycle_length + 1 distance
         * from the block that contained it.
         */
        if a.slot < self.block_slot
            .saturating_sub(u64::from(self.cycle_length).saturating_add(1)) {
            return Err(AttestationValidationError::SlotTooLow);
        }

        /*
         * The attestation must indicate that its last justified slot is the same as the last justified
         * slot known to us.
         */
        if a.justified_slot > self.last_justified_slot {
            return Err(AttestationValidationError::JustifiedSlotIncorrect);
        }

        /*
         * There is no need to include more oblique parents hashes than there are blocks
         * in a cycle.
         */
        if a.oblique_parent_hashes.len() > usize::from(self.cycle_length) {
            return Err(AttestationValidationError::TooManyObliqueHashes);
        }

        /*
         * Retrieve the set of attestation indices for this slot and shard id.
         *
         * This is an array mapping the order that validators will appear in the bitfield to the
         * canonincal index of a validator.
         */
        let attestation_indices = self.attester_map.get(&(a.slot, a.shard_id))
            .ok_or(AttestationValidationError::BadAttesterMap)?;

        /*
         * The bitfield must be no longer than the minimum required to represent each validator in the
         * attestation indicies for this slot and shard id.
         */
        if a.attester_bitfield.num_bytes() !=
            bytes_for_bits(attestation_indices.len())
        {
            return Err(AttestationValidationError::BadBitfieldLength);
       }

        /*
         * If there are excess bits in the bitfield because the number of a validators in not a
         * multiple of 8, reject this attestation record.
         *
         * Allow extra set bits would permit mutliple different byte layouts (and therefore hashes) to
         * refer to the same AttesationRecord.
         */
        if a.attester_bitfield.len() > attestation_indices.len() {
            return Err(AttestationValidationError::InvalidBitfieldEndBits)
        }

        /*
         * Generate the parent hashes for this attestation
         */
        let parent_hashes = attestation_parent_hashes(
            self.cycle_length,
            self.block_slot,
            a.slot,
            &self.parent_hashes,
            &a.oblique_parent_hashes)?;

        /*
         * The specified justified block hash supplied in the attestation must be in the chain at
         * the given slot number.
         *
         * First, we find the latest parent hash from the parent_hashes array. Then, using the
         * block store (database) we iterate back through the blocks until we find (or fail to
         * find) the justified block hash referenced in the attestation record.
         */
        let latest_parent_hash = parent_hashes.last()
            .ok_or(AttestationValidationError::BadCurrentHashes)?;
        match self.block_store.block_at_slot(&latest_parent_hash, a.justified_slot)? {
            Some((ref hash, _)) if *hash == a.justified_block_hash.to_vec() => (),
            _ => return Err(AttestationValidationError::InvalidJustifiedBlockHash)
        };

        /*
         * Generate the message that this attestation aggregate signature must sign across.
         */
        let signed_message = {
            generate_signed_message(
                a.slot,
                &parent_hashes,
                a.shard_id,
                &a.shard_block_hash,
                a.justified_slot)
        };

        let voted_hashset =
            verify_aggregate_signature_for_indices(
                &signed_message,
                &a.aggregate_sig,
                &attestation_indices,
                &a.attester_bitfield,
                &self.validator_store)?;

        /*
         * If the hashmap of voters is None, the signature verification failed.
         */
        match voted_hashset {
            None => Err(AttestationValidationError::BadAggregateSignature),
            Some(hashmap) => Ok(hashmap),
        }
    }
}

fn bytes_for_bits(bits: usize) -> usize {
    (bits.saturating_sub(1) / 8) + 1
}

impl From<ParentHashesError> for AttestationValidationError {
    fn from(e: ParentHashesError) -> Self {
        match e {
            ParentHashesError::BadCurrentHashes
                => AttestationValidationError::BadCurrentHashes,
            ParentHashesError::BadObliqueHashes
                => AttestationValidationError::BadObliqueHashes,
            ParentHashesError::SlotTooLow
                => AttestationValidationError::SlotTooLow,
            ParentHashesError::SlotTooHigh
                => AttestationValidationError::SlotTooHigh,
            ParentHashesError::IntWrapping
                => AttestationValidationError::IntWrapping
        }
    }
}

impl From<BlockAtSlotError> for AttestationValidationError {
    fn from(e: BlockAtSlotError) -> Self {
        match e {
            BlockAtSlotError::DBError(s) => AttestationValidationError::DBError(s),
            _ => AttestationValidationError::InvalidJustifiedBlockHash

        }
    }
}

impl From<DBError> for AttestationValidationError {
    fn from(e: DBError) -> Self {
        AttestationValidationError::DBError(e.message)
    }
}

impl From<SignatureVerificationError> for AttestationValidationError {
    fn from(e: SignatureVerificationError) -> Self {
        match e {
            SignatureVerificationError::BadValidatorIndex
                => AttestationValidationError::BadAttesterMap,
            SignatureVerificationError::PublicKeyCorrupt
                => AttestationValidationError::PublicKeyCorrupt,
            SignatureVerificationError::NoPublicKeyForValidator
                => AttestationValidationError::NoPublicKeyForValidator,
            SignatureVerificationError::DBError(s)
                => AttestationValidationError::DBError(s),
        }
    }
}
<commit_msg>Replace hashmap with hashset<commit_after>use std::collections::HashSet;
use std::sync::Arc;
use super::types::{
    AttestationRecord,
    AttesterMap,
};
use super::attestation_parent_hashes::{
    attestation_parent_hashes,
    ParentHashesError,
};
use super::db::{
    ClientDB,
    DBError
};
use super::db::stores::{
    BlockStore,
    BlockAtSlotError,
    ValidatorStore,
};
use super::types::{
    Hash256,
};
use super::message_generation::generate_signed_message;
use super::signature_verification::{
    verify_aggregate_signature_for_indices,
    SignatureVerificationError,
};

#[derive(Debug,PartialEq)]
pub enum AttestationValidationError {
    SlotTooHigh,
    SlotTooLow,
    JustifiedSlotIncorrect,
    InvalidJustifiedBlockHash,
    TooManyObliqueHashes,
    BadCurrentHashes,
    BadObliqueHashes,
    BadAttesterMap,
    IntWrapping,
    PublicKeyCorrupt,
    NoPublicKeyForValidator,
    BadBitfieldLength,
    InvalidBitfield,
    InvalidBitfieldEndBits,
    NoSignatures,
    NonZeroTrailingBits,
    BadAggregateSignature,
    DBError(String),
}

/// The context against which some attestation should be validated.
pub struct AttestationValidationContext<T>
    where T: ClientDB + Sized
{
    /// The slot as determined by the system time.
    pub block_slot: u64,
    /// The cycle_length as determined by the chain configuration.
    pub cycle_length: u8,
    /// The last justified slot as per the client's view of the canonical chain.
    pub last_justified_slot: u64,
    /// A vec of the hashes of the blocks preceeding the present slot.
    pub parent_hashes: Arc<Vec<Hash256>>,
    /// The store containing block information.
    pub block_store: Arc<BlockStore<T>>,
    /// The store containing validator information.
    pub validator_store: Arc<ValidatorStore<T>>,
    /// A map of (slot, shard_id) to the attestation set of validation indices.
    pub attester_map: Arc<AttesterMap>,
}

impl<T> AttestationValidationContext<T>
    where T: ClientDB
{
    /// Validate a (fully deserialized) AttestationRecord against this context.
    ///
    /// The function will return a HashSet of validator indices (canonical validator indices not
    /// attestation indices) if the validation passed successfully, or an error otherwise.
    ///
    /// The attestation's aggregate signature will be verified, therefore the function must able to
    /// access all required validation public keys via the `validator_store`.
    pub fn validate_attestation(&self, a: &AttestationRecord)
        -> Result<HashSet<usize>, AttestationValidationError>
    {
        /*
         * The attesation slot must not be higher than the block that contained it.
         */
        if a.slot > self.block_slot {
            return Err(AttestationValidationError::SlotTooHigh);
        }

        /*
         * The slot of this attestation must not be more than cycle_length + 1 distance
         * from the block that contained it.
         */
        if a.slot < self.block_slot
            .saturating_sub(u64::from(self.cycle_length).saturating_add(1)) {
            return Err(AttestationValidationError::SlotTooLow);
        }

        /*
         * The attestation must indicate that its last justified slot is the same as the last justified
         * slot known to us.
         */
        if a.justified_slot > self.last_justified_slot {
            return Err(AttestationValidationError::JustifiedSlotIncorrect);
        }

        /*
         * There is no need to include more oblique parents hashes than there are blocks
         * in a cycle.
         */
        if a.oblique_parent_hashes.len() > usize::from(self.cycle_length) {
            return Err(AttestationValidationError::TooManyObliqueHashes);
        }

        /*
         * Retrieve the set of attestation indices for this slot and shard id.
         *
         * This is an array mapping the order that validators will appear in the bitfield to the
         * canonincal index of a validator.
         */
        let attestation_indices = self.attester_map.get(&(a.slot, a.shard_id))
            .ok_or(AttestationValidationError::BadAttesterMap)?;

        /*
         * The bitfield must be no longer than the minimum required to represent each validator in the
         * attestation indicies for this slot and shard id.
         */
        if a.attester_bitfield.num_bytes() !=
            bytes_for_bits(attestation_indices.len())
        {
            return Err(AttestationValidationError::BadBitfieldLength);
       }

        /*
         * If there are excess bits in the bitfield because the number of a validators in not a
         * multiple of 8, reject this attestation record.
         *
         * Allow extra set bits would permit mutliple different byte layouts (and therefore hashes) to
         * refer to the same AttesationRecord.
         */
        if a.attester_bitfield.len() > attestation_indices.len() {
            return Err(AttestationValidationError::InvalidBitfieldEndBits)
        }

        /*
         * Generate the parent hashes for this attestation
         */
        let parent_hashes = attestation_parent_hashes(
            self.cycle_length,
            self.block_slot,
            a.slot,
            &self.parent_hashes,
            &a.oblique_parent_hashes)?;

        /*
         * The specified justified block hash supplied in the attestation must be in the chain at
         * the given slot number.
         *
         * First, we find the latest parent hash from the parent_hashes array. Then, using the
         * block store (database) we iterate back through the blocks until we find (or fail to
         * find) the justified block hash referenced in the attestation record.
         */
        let latest_parent_hash = parent_hashes.last()
            .ok_or(AttestationValidationError::BadCurrentHashes)?;
        match self.block_store.block_at_slot(&latest_parent_hash, a.justified_slot)? {
            Some((ref hash, _)) if *hash == a.justified_block_hash.to_vec() => (),
            _ => return Err(AttestationValidationError::InvalidJustifiedBlockHash)
        };

        /*
         * Generate the message that this attestation aggregate signature must sign across.
         */
        let signed_message = {
            generate_signed_message(
                a.slot,
                &parent_hashes,
                a.shard_id,
                &a.shard_block_hash,
                a.justified_slot)
        };

        let voted_hashset =
            verify_aggregate_signature_for_indices(
                &signed_message,
                &a.aggregate_sig,
                &attestation_indices,
                &a.attester_bitfield,
                &self.validator_store)?;

        /*
         * If the hashset of voters is None, the signature verification failed.
         */
        match voted_hashset {
            None => Err(AttestationValidationError::BadAggregateSignature),
            Some(hashset) => Ok(hashset),
        }
    }
}

fn bytes_for_bits(bits: usize) -> usize {
    (bits.saturating_sub(1) / 8) + 1
}

impl From<ParentHashesError> for AttestationValidationError {
    fn from(e: ParentHashesError) -> Self {
        match e {
            ParentHashesError::BadCurrentHashes
                => AttestationValidationError::BadCurrentHashes,
            ParentHashesError::BadObliqueHashes
                => AttestationValidationError::BadObliqueHashes,
            ParentHashesError::SlotTooLow
                => AttestationValidationError::SlotTooLow,
            ParentHashesError::SlotTooHigh
                => AttestationValidationError::SlotTooHigh,
            ParentHashesError::IntWrapping
                => AttestationValidationError::IntWrapping
        }
    }
}

impl From<BlockAtSlotError> for AttestationValidationError {
    fn from(e: BlockAtSlotError) -> Self {
        match e {
            BlockAtSlotError::DBError(s) => AttestationValidationError::DBError(s),
            _ => AttestationValidationError::InvalidJustifiedBlockHash

        }
    }
}

impl From<DBError> for AttestationValidationError {
    fn from(e: DBError) -> Self {
        AttestationValidationError::DBError(e.message)
    }
}

impl From<SignatureVerificationError> for AttestationValidationError {
    fn from(e: SignatureVerificationError) -> Self {
        match e {
            SignatureVerificationError::BadValidatorIndex
                => AttestationValidationError::BadAttesterMap,
            SignatureVerificationError::PublicKeyCorrupt
                => AttestationValidationError::PublicKeyCorrupt,
            SignatureVerificationError::NoPublicKeyForValidator
                => AttestationValidationError::NoPublicKeyForValidator,
            SignatureVerificationError::DBError(s)
                => AttestationValidationError::DBError(s),
        }
    }
}
<|endoftext|>


<commit_before>#![feature(unboxed_closures)]

extern crate web_framework;

pub struct CustomRequest<Rq> {
    original: Rq
}

/*pub fn my_middleware<Rq: web_framework::Request, Rp: web_framework::Response>(request: Rq, response: Rp)
    -> (CustomRequest<Rq>, Rp)
{
    (CustomRequest { original: request }, response)
}*/

fn main() {
    let server = web_framework::Server::new();

    let server = server.with(web_framework::to_middleware(|&: rq, rp| {
        (rq, rp)
    }));

    let server = server.with(web_framework::route::get(|&: _, _| {
        use std::io::MemReader;
        //response.set_body(MemReader::new("hello world".as_bytes().to_vec()));
    }));

    server.listen();
}
<commit_msg>Minor example cleanup<commit_after>#![feature(unboxed_closures)]

extern crate web_framework;

fn main() {
    let server = web_framework::Server::new();

    let server = server.with(web_framework::to_middleware(|&: request, response| {
        (request, response)
    }));

    let server = server.with(web_framework::route::get(|&: request, response| {
        use std::io::MemReader;
        //response.set_body(MemReader::new("hello world".as_bytes().to_vec()));
    }));

    server.listen();
}
<|endoftext|>


<commit_before>extern crate bev;

#[macro_use]
extern crate gfx;
extern crate gfx_window_glutin;
extern crate glutin;

use bev::{NBez, Point2d, Vector2d};
use bev::traits::*;

use gfx::traits::FactoryExt;
use gfx::{Factory, Device, Primitive, BufferRole, Bind, Slice, IndexBuffer};
use gfx::state::Rasterizer;

use glutin::{Event, ElementState, MouseButton};

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

gfx_vertex_struct!{ Vertex {
    pos: [f32; 2] = "v_pos",
    col: [f32; 3] = "v_col",
}}

gfx_pipeline!{ pipe {
    vbuf: gfx::VertexBuffer<Vertex> = (),
    window_matrix: gfx::Global<[[f32; 2]; 2]> = "window_matrix",
    offset: gfx::Global<[f32; 2]> = "offset",
    out: gfx::RenderTarget<ColorFormat> = "r_target",
}}

fn main() {
    let (mut win_x, mut win_y) = (720, 720);

    let builder = glutin::WindowBuilder::new()
        .with_dimensions(win_x, win_y)
        .with_title("Hello Bézier");
    let (window, mut device, mut factory, main_color, mut main_depth) =
        gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder);
    let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
    let sset = factory.create_shader_set(VERT, FRAG).unwrap();
    let pso = factory.create_pipeline_state(
        &sset,
        Primitive::TriangleStrip,
        Rasterizer::new_fill(),
        pipe::new()
    ).unwrap();

    let mut curve: NBez<Point2d<_>, Vector2d<_>, _, _> = NBez::from_container([
        Point2d::new(-0.5, -0.5),
        Point2d::new(-0.5,  0.5),
        Point2d::new( 0.5,  0.5)
    ]).elevate();

    let radius = 0.02;
    let circle = gen_circle(16, radius, [1.0, 0.0, 0.0]);
    let mut indices = vec![0u16; 42];
    for (ind, i) in indices.iter_mut().enumerate() {
        let ind = ind + 3;
        if ind % 3 == 1 {
            *i = ind as u16/3;
        } else if ind % 3 == 2 {
            *i = ind as u16/3 + 1
        }
    }

    let window_matrix: [[f32; 2]; 2] = [
        [1.0, 0.0],
        [0.0, 1.0],
    ];

    // A vec of curve vertices
    const SAMPLES: usize = 31;
    let mut cverts = [Vertex{ pos: [0.0, 0.0], col: [1.0, 1.0, 1.0] }; SAMPLES * 2];
    let cvert_buffer = factory.create_buffer_dynamic(cverts.len(), BufferRole::Vertex, Bind::empty()).unwrap();
    let cvert_slice = Slice {
        start: 0,
        end: cvert_buffer.len() as u32,
        base_vertex: 0,
        instances: None,
        buffer: IndexBuffer::Auto
    };

    let (cir_buffer, cir_slice) = factory.create_vertex_buffer_with_slice(&circle, &indices[..]);
    let mut data = pipe::Data {
        vbuf: cvert_buffer.clone(),
        window_matrix: window_matrix,
        offset: [0.0, 0.0],
        out: main_color
    };

    // The index of the selected control point. If -1, no control point is selected.
    let mut selected = -1;
    // Mouse x, mouse y
    let (mut mox, mut moy) = (0.0, 0.0);
    'main: loop {
        for event in window.poll_events() {
            match event {
                Event::Closed => break 'main,
                Event::Resized(x, y) => {
                    gfx_window_glutin::update_views(&window, &mut data.out, &mut main_depth);
                    win_x = x;
                    win_y = y;
                    data.window_matrix = [
                        [720.0/win_x as f32, 0.0],
                        [0.0, 720.0/win_y as f32]
                    ];
                }
                Event::MouseMoved(x, y) => {
                    mox =  pix_to_float(x, win_x) / data.window_matrix[0][0];
                    moy = -pix_to_float(y, win_y) / data.window_matrix[1][1];
                }
                Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                    for (i, c) in curve.as_ref().iter().enumerate() {
                        let dist = (c.x-mox).hypot(c.y-moy);
                        if dist <= radius {
                            selected = i as isize;
                        }
                    }
                }
                Event::MouseInput(ElementState::Released, MouseButton::Left) => selected = -1,
                _ => ()
            }
        }

        // If any control point is selected, move that control point with the mouse.
        if 0 <= selected {
            let c = &mut curve.as_mut()[selected as usize];
            c.x = mox;
            c.y = moy;
        }

        // Calculate curve vertices based on control point position.
        for i in 0..SAMPLES {
            let t = i as f32/(SAMPLES-1) as f32;

            let interp = curve.interp(t).unwrap();

            let perp = curve.slope(t).unwrap().normalize().perp() * 0.01;
            cverts[i*2].pos = (-perp + interp).into();
            cverts[i*2 + 1].pos = (perp + interp).into();
        }
        encoder.update_buffer(&cvert_buffer, &cverts, 0).unwrap(); // Upload calculated vertex positions to vertex buffer

        // Drawing code
        encoder.clear(&data.out, [0.0, 0.0, 0.0, 1.0]);

        data.offset = [0.0, 0.0];
        data.vbuf = cvert_buffer.clone();
        encoder.draw(&cvert_slice, &pso, &data);

        data.vbuf = cir_buffer.clone();
        for p in curve.as_ref().iter() {
            data.offset = (*p).into();
            encoder.draw(&cir_slice, &pso, &data);
        }

        encoder.flush(&mut device);
        window.swap_buffers().unwrap();
        device.cleanup();
    }
}

pub fn pix_to_float(p: i32, s: u32) -> f32 {
    let s = s as f32;
    let p = p as f32;
    2.0 * (p - s/2.0)/s
}

fn gen_circle(divs: u16, scale: f32, col: [f32; 3]) -> Vec<Vertex> {
    use std::f32::consts::PI;
    let mut verts = vec![Vertex{ pos: [0.0, 0.0], col: col}; divs as usize];

    for d in 0..divs {
        let theta = 2.0 * PI * (d as f32/divs as f32);
        let d = d as usize;
        verts[d].pos[0] = theta.sin() * scale;
        verts[d].pos[1] = theta.cos() * scale;
    }

    verts
}

const VERT: &'static [u8] = br#"
    #version 150 core

    uniform vec2 offset;
    uniform mat2 window_matrix;

    in vec2 v_pos;
    in vec3 v_col;
    out vec4 f_col;

    void main() {
        f_col = vec4(v_col, 1.0);
        gl_Position = vec4(window_matrix * (v_pos + offset), 0.0, 1.0);
    }
"#;

const FRAG: &'static [u8] = br#"
    #version 150 core

    in vec4 f_col;
    out vec4 r_target;

    void main() {
        r_target = f_col;
    }
"#;<commit_msg>Added ability to raise curve over in `curve.rs` example by pressing spacebar<commit_after>extern crate bev;

#[macro_use]
extern crate gfx;
extern crate gfx_window_glutin;
extern crate glutin;

use bev::{NBez, Point2d, Vector2d};
use bev::traits::*;

use gfx::traits::FactoryExt;
use gfx::{Factory, Device, Primitive, BufferRole, Bind, Slice, IndexBuffer};
use gfx::state::Rasterizer;

use glutin::{Event, ElementState, MouseButton, VirtualKeyCode as VKeyCode};

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

gfx_vertex_struct!{ Vertex {
    pos: [f32; 2] = "v_pos",
    col: [f32; 3] = "v_col",
}}

gfx_pipeline!{ pipe {
    vbuf: gfx::VertexBuffer<Vertex> = (),
    window_matrix: gfx::Global<[[f32; 2]; 2]> = "window_matrix",
    offset: gfx::Global<[f32; 2]> = "offset",
    out: gfx::RenderTarget<ColorFormat> = "r_target",
}}

fn main() {
    let (mut win_x, mut win_y) = (720, 720);

    let builder = glutin::WindowBuilder::new()
        .with_dimensions(win_x, win_y)
        .with_multisampling(16)
        .with_title("Hello Bézier".into());
    let (window, mut device, mut factory, main_color, mut main_depth) =
        gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder);
    let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
    let sset = factory.create_shader_set(VERT, FRAG).unwrap();
    let pso = factory.create_pipeline_state(
        &sset,
        Primitive::TriangleStrip,
        Rasterizer::new_fill(),
        pipe::new()
    ).unwrap();

    let mut curve: NBez<Point2d<_>, Vector2d<_>, _, _> = NBez::from_container([
        Point2d::new(-0.5, -0.5),
        Point2d::new(-0.5,  0.5),
        Point2d::new( 0.5,  0.5)
    ]).elevate();

    let radius = 0.02;
    let circle = gen_circle(16, radius, [1.0, 0.0, 0.0]);
    let mut indices = vec![0u16; 42];
    for (ind, i) in indices.iter_mut().enumerate() {
        let ind = ind + 3;
        if ind % 3 == 1 {
            *i = ind as u16/3;
        } else if ind % 3 == 2 {
            *i = ind as u16/3 + 1
        }
    }

    let window_matrix: [[f32; 2]; 2] = [
        [1.0, 0.0],
        [0.0, 1.0],
    ];

    // A vec of curve vertices
    const SAMPLES: usize = 31;
    let mut cverts = [Vertex{ pos: [0.0, 0.0], col: [1.0, 1.0, 1.0] }; SAMPLES * 2];
    let cvert_buffer = factory.create_buffer_dynamic(cverts.len(), BufferRole::Vertex, Bind::empty()).unwrap();
    let cvert_slice = Slice {
        start: 0,
        end: cvert_buffer.len() as u32,
        base_vertex: 0,
        instances: None,
        buffer: IndexBuffer::Auto
    };

    let (cir_buffer, cir_slice) = factory.create_vertex_buffer_with_slice(&circle, &indices[..]);
    let mut data = pipe::Data {
        vbuf: cvert_buffer.clone(),
        window_matrix: window_matrix,
        offset: [0.0, 0.0],
        out: main_color
    };

    // The index of the selected control point. If -1, no control point is selected.
    let mut selected = -1;
    // Mouse x, mouse y
    let (mut mox, mut moy) = (0.0, 0.0);
    'main: loop {
        for event in window.poll_events() {
            match event {
                Event::Closed => break 'main,
                Event::Resized(x, y) => {
                    gfx_window_glutin::update_views(&window, &mut data.out, &mut main_depth);
                    win_x = x;
                    win_y = y;
                    data.window_matrix = [
                        [720.0/win_x as f32, 0.0],
                        [0.0, 720.0/win_y as f32]
                    ];
                }
                Event::KeyboardInput(ElementState::Pressed, _, Some(VKeyCode::Space)) =>
                    curve = curve.elevate(),
                Event::MouseMoved(x, y) => {
                    mox =  pix_to_float(x, win_x) / data.window_matrix[0][0];
                    moy = -pix_to_float(y, win_y) / data.window_matrix[1][1];
                }
                Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                    for (i, c) in curve.as_ref().iter().enumerate() {
                        let dist = (c.x-mox).hypot(c.y-moy);
                        if dist <= radius {
                            selected = i as isize;
                        }
                    }
                }
                Event::MouseInput(ElementState::Released, MouseButton::Left) => selected = -1,
                _ => ()
            }
        }

        // If any control point is selected, move that control point with the mouse.
        if 0 <= selected {
            let c = &mut curve.as_mut()[selected as usize];
            c.x = mox;
            c.y = moy;
        }

        // Calculate curve vertices based on control point position.
        for i in 0..SAMPLES {
            let t = i as f32/(SAMPLES-1) as f32;

            let interp = curve.interp(t).unwrap();

            let perp = curve.slope(t).unwrap().normalize().perp() * 0.01;
            cverts[i*2].pos = (-perp + interp).into();
            cverts[i*2 + 1].pos = (perp + interp).into();
        }
        encoder.update_buffer(&cvert_buffer, &cverts, 0).unwrap(); // Upload calculated vertex positions to vertex buffer

        // Drawing code
        encoder.clear(&data.out, [0.0, 0.0, 0.0, 1.0]);

        data.offset = [0.0, 0.0];
        data.vbuf = cvert_buffer.clone();
        encoder.draw(&cvert_slice, &pso, &data);

        data.vbuf = cir_buffer.clone();
        for p in curve.as_ref().iter() {
            data.offset = (*p).into();
            encoder.draw(&cir_slice, &pso, &data);
        }

        encoder.flush(&mut device);
        window.swap_buffers().unwrap();
        device.cleanup();
    }
}

pub fn pix_to_float(p: i32, s: u32) -> f32 {
    let s = s as f32;
    let p = p as f32;
    2.0 * (p - s/2.0)/s
}

fn gen_circle(divs: u16, scale: f32, col: [f32; 3]) -> Vec<Vertex> {
    use std::f32::consts::PI;
    let mut verts = vec![Vertex{ pos: [0.0, 0.0], col: col}; divs as usize];

    for d in 0..divs {
        let theta = 2.0 * PI * (d as f32/divs as f32);
        let d = d as usize;
        verts[d].pos[0] = theta.sin() * scale;
        verts[d].pos[1] = theta.cos() * scale;
    }

    verts
}

const VERT: &'static [u8] = br#"
    #version 150 core

    uniform vec2 offset;
    uniform mat2 window_matrix;

    in vec2 v_pos;
    in vec3 v_col;
    out vec4 f_col;

    void main() {
        f_col = vec4(v_col, 1.0);
        gl_Position = vec4(window_matrix * (v_pos + offset), 0.0, 1.0);
    }
"#;

const FRAG: &'static [u8] = br#"
    #version 150 core

    in vec4 f_col;
    out vec4 r_target;

    void main() {
        r_target = f_col;
    }
"#;<|endoftext|>


<commit_before>use ast::*;
use std::collections::HashMap;

pub type Depth = usize;

pub struct LexicalScopes {
    depths: HashMap<VariableUseHandle, Depth>,
}

impl LexicalScopes {
    fn new() -> LexicalScopes {
        LexicalScopes { depths: HashMap::new() }
    }

    pub fn get_depth(&self, handle: &VariableUseHandle) -> Option<&Depth> {
        self.depths.get(handle)
    }
}

#[derive(Debug)]
pub enum LexicalScopesResolutionError {
    ReadLocalInItsOwnInitializer,
    VariableAlreadyExistsInScope,
    ReturnFromTopLevelCode,
}

trait LexicalScopesResolver {
    fn resolve(&self,
               &mut ProgramLexicalScopesResolver)
               -> Result<(), LexicalScopesResolutionError>;
}

#[derive(PartialEq)]
enum VariableDefinition {
    Undefined,
    Declared,
    Defined,
}

#[derive(Clone, Copy, PartialEq)]
enum FunctionType {
    None,
    Function,
}

pub struct ProgramLexicalScopesResolver {
    // Note that this doesn't track globals at all
    scopes: Vec<HashMap<Identifier, VariableDefinition>>,
    current_function: FunctionType,
    lexical_scopes: LexicalScopes,
}

impl ProgramLexicalScopesResolver {
    pub fn new() -> ProgramLexicalScopesResolver {
        ProgramLexicalScopesResolver {
            scopes: vec![],
            current_function: FunctionType::None,
            lexical_scopes: LexicalScopes::new(),
        }
    }

    fn begin_scope(&mut self) -> () {
        self.scopes.push(HashMap::new());
    }

    fn end_scope(&mut self) -> () {
        self.scopes.pop();
    }

    fn declare(&mut self, identifier: &Identifier) -> Result<(), LexicalScopesResolutionError> {
        let scopes = self.scopes.len();
        if scopes == 0 {
            return Ok(());
        };
        if self.scopes[scopes - 1].contains_key(&identifier) {
            Err(LexicalScopesResolutionError::VariableAlreadyExistsInScope)
        } else {
            self.scopes[scopes - 1].insert(*identifier, VariableDefinition::Declared);
            Ok(())
        }
    }

    fn define(&mut self, identifier: &Identifier) -> () {
        let scopes = self.scopes.len();
        if scopes == 0 {
            return;
        };
        self.scopes[scopes - 1].insert(*identifier, VariableDefinition::Defined);
    }

    fn resolve_local(&mut self, handle: VariableUseHandle, identifier: &Identifier) -> () {
        let max_depth = self.scopes.len();
        for depth in 0..max_depth {
            if self.scopes[max_depth - depth - 1].contains_key(identifier) {
                self.lexical_scopes.depths.insert(handle, depth);
                return;
            }
        }
        // If we failed to find it in the locals, it must be a global.
        // It might not be there right now, but it might appear later on.
        // We will know it only at runtime.
        self.lexical_scopes.depths.insert(handle, max_depth);
    }

    pub fn resolve(&mut self, statement: &Statement) -> Result<(), LexicalScopesResolutionError> {
        statement.resolve(self)
    }

    pub fn get_depth(&self, handle: &VariableUseHandle) -> Option<&Depth> {
        self.lexical_scopes.get_depth(handle)
    }
}

impl LexicalScopesResolver for Statement {
    fn resolve(&self,
               resolver: &mut ProgramLexicalScopesResolver)
               -> Result<(), LexicalScopesResolutionError> {
        match *self {
            Statement::Block(ref b) => {
                resolver.begin_scope();
                for statement in &b.statements {
                    try!(statement.resolve(resolver));
                }
                resolver.end_scope();
                Ok(())
            }
            Statement::VariableDefinition(ref identifier) => {
                let _ = try!(resolver.declare(identifier));
                resolver.define(identifier);
                Ok(())
            }
            Statement::VariableDefinitionWithInitalizer(ref identifier, ref initializer) => {
                let _ = try!(resolver.declare(identifier));
                try!(initializer.resolve(resolver));
                resolver.define(identifier);
                Ok(())
            }
            Statement::FunctionDefinition(ref f) => f.resolve(resolver),
            Statement::Class(ref c) => {
                let _ = try!(resolver.declare(&c.name));
                resolver.define(&c.name);
                Ok(())
            }
            Statement::Expression(ref e) => e.resolve(resolver),
            Statement::IfThen(ref s) => {
                s.condition
                    .resolve(resolver)
                    .and_then(|_| s.then_branch.resolve(resolver))
            }
            Statement::IfThenElse(ref s) => {
                s.condition
                    .resolve(resolver)
                    .and_then(|_| s.then_branch.resolve(resolver))
                    .and_then(|_| s.else_branch.resolve(resolver))
            }
            Statement::While(ref s) => {
                s.condition
                    .resolve(resolver)
                    .and_then(|_| s.body.resolve(resolver))
            }
            Statement::Print(ref e) => e.resolve(resolver),
            Statement::Return(ref r) => {
                if resolver.current_function == FunctionType::None {
                    Err(LexicalScopesResolutionError::ReturnFromTopLevelCode)
                } else {
                    match *r {
                        None => Ok(()),
                        Some(ref e) => e.resolve(resolver),
                    }
                }
            }
        }
    }
}

impl LexicalScopesResolver for Expr {
    fn resolve(&self,
               resolver: &mut ProgramLexicalScopesResolver)
               -> Result<(), LexicalScopesResolutionError> {
        match *self {
            Expr::Identifier(ref handle, ref identifier) => {
                let scopes = resolver.scopes.len();
                if scopes != 0 &&
                   resolver.scopes[scopes - 1]
                       .get(&identifier)
                       .unwrap_or(&VariableDefinition::Undefined) ==
                   &VariableDefinition::Declared {
                    Err(LexicalScopesResolutionError::ReadLocalInItsOwnInitializer)
                } else {
                    resolver.resolve_local(*handle, &identifier);
                    Ok(())
                }
            }
            Expr::Assignment(ref assigment) => assigment.resolve(resolver),
            Expr::Literal(_) => Ok(()),
            Expr::Unary(ref e) => e.right.resolve(resolver),
            Expr::Binary(ref e) => {
                e.left
                    .resolve(resolver)
                    .and_then(|_| e.right.resolve(resolver))
            }
            Expr::Logic(ref e) => {
                e.left
                    .resolve(resolver)
                    .and_then(|_| e.right.resolve(resolver))
            }
            Expr::Grouping(ref e) => e.expr.resolve(resolver),
            Expr::Call(ref e) => {
                try!(e.callee.resolve(resolver));
                for argument in e.arguments.iter() {
                    try!(argument.resolve(resolver));
                }
                Ok(())
            }
            Expr::Get(ref g) => {
                try!(g.instance.resolve(resolver));
                Ok(())
            }
            Expr::Set(ref s) => unimplemented!(),
        }
    }
}

impl LexicalScopesResolver for Assignment {
    fn resolve(&self,
               resolver: &mut ProgramLexicalScopesResolver)
               -> Result<(), LexicalScopesResolutionError> {
        try!{self.rvalue.resolve(resolver)};
        let Target::Identifier(ref identifier) = self.lvalue;
        resolver.resolve_local(self.handle, identifier);
        Ok(())
    }
}

impl LexicalScopesResolver for FunctionDefinition {
    fn resolve(&self,
               resolver: &mut ProgramLexicalScopesResolver)
               -> Result<(), LexicalScopesResolutionError> {
        let _ = try!(resolver.declare(&self.name));
        let enclosing_function = resolver.current_function;
        resolver.current_function = FunctionType::Function;
        resolver.define(&self.name);
        resolver.begin_scope();
        for argument in self.arguments.iter() {
            let _ = try!(resolver.declare(argument));
            resolver.define(argument);
        }
        let _ = try!(self.body.resolve(resolver));
        resolver.end_scope();
        resolver.current_function = enclosing_function;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use scanner::*;
    use parser::*;
    use lexical_scope_resolver::*;

    #[test]
    fn global_variable() {
        let (tokens, _) = scan(&"var a = 0;{fun f() {print a;}}");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        for statement in statements.iter() {
            assert!(lexical_scope_resolver.resolve(&statement).is_ok());
        }
        let lexical_scopes = lexical_scope_resolver.lexical_scopes;
        let mut handle_factory = VariableUseHandleFactory::new();
        let handle = handle_factory.next(); // Use of a in the function
        assert_eq!(Some(&3), lexical_scopes.get_depth(&handle));
    }

    #[test]
    fn captured_variable() {
        let (tokens, _) = scan(&"{var a = 0;fun f() {print a;}}");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        for statement in statements.iter() {
            assert!(lexical_scope_resolver.resolve(&statement).is_ok());
        }
        let lexical_scopes = lexical_scope_resolver.lexical_scopes;
        let mut handle_factory = VariableUseHandleFactory::new();
        let handle = handle_factory.next(); // Use of a in the function
        assert_eq!(Some(&2), lexical_scopes.get_depth(&handle));
    }

    #[test]
    fn lexical_capture() {
        let (tokens, _) = scan(&"var a = 0;{fun f() {print a;} var a = 1;}");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        for statement in statements.iter() {
            assert!(lexical_scope_resolver.resolve(&statement).is_ok());
        }
        let lexical_scopes = lexical_scope_resolver.lexical_scopes;
        let mut handle_factory = VariableUseHandleFactory::new();
        let handle = handle_factory.next(); // Use of a in the function
        assert_eq!(Some(&3), lexical_scopes.get_depth(&handle));
    }

    #[test]
    fn error_on_shadowing() {
        let (tokens, _) = scan(&"var a = 0;{var a = a;}");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        assert!(lexical_scope_resolver.resolve(&statements[0]).is_ok());
        assert!(lexical_scope_resolver.resolve(&statements[1]).is_err());
    }

    #[test]
    fn error_on_local_redeclaration() {
        let (tokens, _) = scan(&"fun bad() {var a = 1;var a = 2;}");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        assert!(lexical_scope_resolver.resolve(&statements[0]).is_err());
    }

    #[test]
    fn global_redeclaration_is_allowed() {
        let (tokens, _) = scan(&"var a = 1;var a = 2;");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        assert!(lexical_scope_resolver.resolve(&statements[0]).is_ok());
        assert!(lexical_scope_resolver.resolve(&statements[1]).is_ok());
    }

    #[test]
    fn error_on_return_outside_a_function() {
        let (tokens, _) = scan(&"return;");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        assert!(lexical_scope_resolver.resolve(&statements[0]).is_err());
    }
}
<commit_msg>Resolution for set<commit_after>use ast::*;
use std::collections::HashMap;

pub type Depth = usize;

pub struct LexicalScopes {
    depths: HashMap<VariableUseHandle, Depth>,
}

impl LexicalScopes {
    fn new() -> LexicalScopes {
        LexicalScopes { depths: HashMap::new() }
    }

    pub fn get_depth(&self, handle: &VariableUseHandle) -> Option<&Depth> {
        self.depths.get(handle)
    }
}

#[derive(Debug)]
pub enum LexicalScopesResolutionError {
    ReadLocalInItsOwnInitializer,
    VariableAlreadyExistsInScope,
    ReturnFromTopLevelCode,
}

trait LexicalScopesResolver {
    fn resolve(&self,
               &mut ProgramLexicalScopesResolver)
               -> Result<(), LexicalScopesResolutionError>;
}

#[derive(PartialEq)]
enum VariableDefinition {
    Undefined,
    Declared,
    Defined,
}

#[derive(Clone, Copy, PartialEq)]
enum FunctionType {
    None,
    Function,
}

pub struct ProgramLexicalScopesResolver {
    // Note that this doesn't track globals at all
    scopes: Vec<HashMap<Identifier, VariableDefinition>>,
    current_function: FunctionType,
    lexical_scopes: LexicalScopes,
}

impl ProgramLexicalScopesResolver {
    pub fn new() -> ProgramLexicalScopesResolver {
        ProgramLexicalScopesResolver {
            scopes: vec![],
            current_function: FunctionType::None,
            lexical_scopes: LexicalScopes::new(),
        }
    }

    fn begin_scope(&mut self) -> () {
        self.scopes.push(HashMap::new());
    }

    fn end_scope(&mut self) -> () {
        self.scopes.pop();
    }

    fn declare(&mut self, identifier: &Identifier) -> Result<(), LexicalScopesResolutionError> {
        let scopes = self.scopes.len();
        if scopes == 0 {
            return Ok(());
        };
        if self.scopes[scopes - 1].contains_key(&identifier) {
            Err(LexicalScopesResolutionError::VariableAlreadyExistsInScope)
        } else {
            self.scopes[scopes - 1].insert(*identifier, VariableDefinition::Declared);
            Ok(())
        }
    }

    fn define(&mut self, identifier: &Identifier) -> () {
        let scopes = self.scopes.len();
        if scopes == 0 {
            return;
        };
        self.scopes[scopes - 1].insert(*identifier, VariableDefinition::Defined);
    }

    fn resolve_local(&mut self, handle: VariableUseHandle, identifier: &Identifier) -> () {
        let max_depth = self.scopes.len();
        for depth in 0..max_depth {
            if self.scopes[max_depth - depth - 1].contains_key(identifier) {
                self.lexical_scopes.depths.insert(handle, depth);
                return;
            }
        }
        // If we failed to find it in the locals, it must be a global.
        // It might not be there right now, but it might appear later on.
        // We will know it only at runtime.
        self.lexical_scopes.depths.insert(handle, max_depth);
    }

    pub fn resolve(&mut self, statement: &Statement) -> Result<(), LexicalScopesResolutionError> {
        statement.resolve(self)
    }

    pub fn get_depth(&self, handle: &VariableUseHandle) -> Option<&Depth> {
        self.lexical_scopes.get_depth(handle)
    }
}

impl LexicalScopesResolver for Statement {
    fn resolve(&self,
               resolver: &mut ProgramLexicalScopesResolver)
               -> Result<(), LexicalScopesResolutionError> {
        match *self {
            Statement::Block(ref b) => {
                resolver.begin_scope();
                for statement in &b.statements {
                    try!(statement.resolve(resolver));
                }
                resolver.end_scope();
                Ok(())
            }
            Statement::VariableDefinition(ref identifier) => {
                let _ = try!(resolver.declare(identifier));
                resolver.define(identifier);
                Ok(())
            }
            Statement::VariableDefinitionWithInitalizer(ref identifier, ref initializer) => {
                let _ = try!(resolver.declare(identifier));
                try!(initializer.resolve(resolver));
                resolver.define(identifier);
                Ok(())
            }
            Statement::FunctionDefinition(ref f) => f.resolve(resolver),
            Statement::Class(ref c) => {
                let _ = try!(resolver.declare(&c.name));
                resolver.define(&c.name);
                Ok(())
            }
            Statement::Expression(ref e) => e.resolve(resolver),
            Statement::IfThen(ref s) => {
                s.condition
                    .resolve(resolver)
                    .and_then(|_| s.then_branch.resolve(resolver))
            }
            Statement::IfThenElse(ref s) => {
                s.condition
                    .resolve(resolver)
                    .and_then(|_| s.then_branch.resolve(resolver))
                    .and_then(|_| s.else_branch.resolve(resolver))
            }
            Statement::While(ref s) => {
                s.condition
                    .resolve(resolver)
                    .and_then(|_| s.body.resolve(resolver))
            }
            Statement::Print(ref e) => e.resolve(resolver),
            Statement::Return(ref r) => {
                if resolver.current_function == FunctionType::None {
                    Err(LexicalScopesResolutionError::ReturnFromTopLevelCode)
                } else {
                    match *r {
                        None => Ok(()),
                        Some(ref e) => e.resolve(resolver),
                    }
                }
            }
        }
    }
}

impl LexicalScopesResolver for Expr {
    fn resolve(&self,
               resolver: &mut ProgramLexicalScopesResolver)
               -> Result<(), LexicalScopesResolutionError> {
        match *self {
            Expr::Identifier(ref handle, ref identifier) => {
                let scopes = resolver.scopes.len();
                if scopes != 0 &&
                   resolver.scopes[scopes - 1]
                       .get(&identifier)
                       .unwrap_or(&VariableDefinition::Undefined) ==
                   &VariableDefinition::Declared {
                    Err(LexicalScopesResolutionError::ReadLocalInItsOwnInitializer)
                } else {
                    resolver.resolve_local(*handle, &identifier);
                    Ok(())
                }
            }
            Expr::Assignment(ref assigment) => assigment.resolve(resolver),
            Expr::Literal(_) => Ok(()),
            Expr::Unary(ref e) => e.right.resolve(resolver),
            Expr::Binary(ref e) => {
                e.left
                    .resolve(resolver)
                    .and_then(|_| e.right.resolve(resolver))
            }
            Expr::Logic(ref e) => {
                e.left
                    .resolve(resolver)
                    .and_then(|_| e.right.resolve(resolver))
            }
            Expr::Grouping(ref e) => e.expr.resolve(resolver),
            Expr::Call(ref e) => {
                try!(e.callee.resolve(resolver));
                for argument in e.arguments.iter() {
                    try!(argument.resolve(resolver));
                }
                Ok(())
            }
            Expr::Get(ref g) => {
                try!(g.instance.resolve(resolver));
                Ok(())
            }
            Expr::Set(ref s) => {
                try!(s.value.resolve(resolver));
                try!(s.instance.resolve(resolver));
                Ok(())
            },
        }
    }
}

impl LexicalScopesResolver for Assignment {
    fn resolve(&self,
               resolver: &mut ProgramLexicalScopesResolver)
               -> Result<(), LexicalScopesResolutionError> {
        try!{self.rvalue.resolve(resolver)};
        let Target::Identifier(ref identifier) = self.lvalue;
        resolver.resolve_local(self.handle, identifier);
        Ok(())
    }
}

impl LexicalScopesResolver for FunctionDefinition {
    fn resolve(&self,
               resolver: &mut ProgramLexicalScopesResolver)
               -> Result<(), LexicalScopesResolutionError> {
        let _ = try!(resolver.declare(&self.name));
        let enclosing_function = resolver.current_function;
        resolver.current_function = FunctionType::Function;
        resolver.define(&self.name);
        resolver.begin_scope();
        for argument in self.arguments.iter() {
            let _ = try!(resolver.declare(argument));
            resolver.define(argument);
        }
        let _ = try!(self.body.resolve(resolver));
        resolver.end_scope();
        resolver.current_function = enclosing_function;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use scanner::*;
    use parser::*;
    use lexical_scope_resolver::*;

    #[test]
    fn global_variable() {
        let (tokens, _) = scan(&"var a = 0;{fun f() {print a;}}");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        for statement in statements.iter() {
            assert!(lexical_scope_resolver.resolve(&statement).is_ok());
        }
        let lexical_scopes = lexical_scope_resolver.lexical_scopes;
        let mut handle_factory = VariableUseHandleFactory::new();
        let handle = handle_factory.next(); // Use of a in the function
        assert_eq!(Some(&3), lexical_scopes.get_depth(&handle));
    }

    #[test]
    fn captured_variable() {
        let (tokens, _) = scan(&"{var a = 0;fun f() {print a;}}");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        for statement in statements.iter() {
            assert!(lexical_scope_resolver.resolve(&statement).is_ok());
        }
        let lexical_scopes = lexical_scope_resolver.lexical_scopes;
        let mut handle_factory = VariableUseHandleFactory::new();
        let handle = handle_factory.next(); // Use of a in the function
        assert_eq!(Some(&2), lexical_scopes.get_depth(&handle));
    }

    #[test]
    fn lexical_capture() {
        let (tokens, _) = scan(&"var a = 0;{fun f() {print a;} var a = 1;}");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        for statement in statements.iter() {
            assert!(lexical_scope_resolver.resolve(&statement).is_ok());
        }
        let lexical_scopes = lexical_scope_resolver.lexical_scopes;
        let mut handle_factory = VariableUseHandleFactory::new();
        let handle = handle_factory.next(); // Use of a in the function
        assert_eq!(Some(&3), lexical_scopes.get_depth(&handle));
    }

    #[test]
    fn error_on_shadowing() {
        let (tokens, _) = scan(&"var a = 0;{var a = a;}");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        assert!(lexical_scope_resolver.resolve(&statements[0]).is_ok());
        assert!(lexical_scope_resolver.resolve(&statements[1]).is_err());
    }

    #[test]
    fn error_on_local_redeclaration() {
        let (tokens, _) = scan(&"fun bad() {var a = 1;var a = 2;}");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        assert!(lexical_scope_resolver.resolve(&statements[0]).is_err());
    }

    #[test]
    fn global_redeclaration_is_allowed() {
        let (tokens, _) = scan(&"var a = 1;var a = 2;");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        assert!(lexical_scope_resolver.resolve(&statements[0]).is_ok());
        assert!(lexical_scope_resolver.resolve(&statements[1]).is_ok());
    }

    #[test]
    fn error_on_return_outside_a_function() {
        let (tokens, _) = scan(&"return;");
        let statements = Parser::new().parse(&tokens).unwrap();
        let mut lexical_scope_resolver = ProgramLexicalScopesResolver::new();
        assert!(lexical_scope_resolver.resolve(&statements[0]).is_err());
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

//! Prune things that are private

use core::prelude::*;

use astsrv;
use doc;
use fold::Fold;
use fold;
use pass::Pass;

use core::util;

pub fn mk_pass() -> Pass {
    Pass {
        name: ~"prune_private",
        f: run
    }
}

pub fn run(srv: astsrv::Srv, doc: doc::Doc) -> doc::Doc {
    let fold = Fold {
        ctxt: srv.clone(),
        fold_mod: fold_mod,
        .. fold::default_any_fold(srv)
    };
    (fold.fold_doc)(&fold, doc)
}

fn fold_mod(
    fold: &fold::Fold<astsrv::Srv>,
    doc: doc::ModDoc
) -> doc::ModDoc {
    let doc = fold::default_any_fold_mod(fold, doc);

    doc::ModDoc {
        items: doc.items.filtered(|ItemTag| {
            is_visible(fold.ctxt.clone(), ItemTag.item())
        }),
        .. doc
    }
}

fn is_visible(srv: astsrv::Srv, doc: doc::ItemDoc) -> bool {
    use syntax::ast_map;
    use syntax::ast;

    let id = doc.id;

    do astsrv::exec(srv) |ctxt| {
        match ctxt.ast_map.get(&id) {
            ast_map::node_item(item, _) => {
                item.vis == ast::public
            }
            _ => util::unreachable()
        }
    }
}

#[test]
fn should_prune_items_without_pub_modifier() {
    let doc = test::mk_doc(~"mod a { }");
    fail_unless!(vec::is_empty(doc.cratemod().mods()));
}

#[cfg(test)]
pub mod test {
    use astsrv;
    use doc;
    use extract;
    use prune_private_pass::run;

    pub fn mk_doc(source: ~str) -> doc::Doc {
        do astsrv::from_str(copy source) |srv| {
            let doc = extract::from_srv(srv.clone(), ~"");
            run(srv.clone(), doc)
        }
    }
}

<commit_msg>auto merge of #5415 : brson/rust/rustdoc, r=thestinger<commit_after>// Copyright 2012 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.

//! Prune things that are private

use core::prelude::*;

use astsrv;
use doc;
use fold::Fold;
use fold;
use pass::Pass;

use core::util;

pub fn mk_pass() -> Pass {
    Pass {
        name: ~"prune_private",
        f: run
    }
}

pub fn run(srv: astsrv::Srv, doc: doc::Doc) -> doc::Doc {
    let fold = Fold {
        ctxt: srv.clone(),
        fold_mod: fold_mod,
        .. fold::default_any_fold(srv)
    };
    (fold.fold_doc)(&fold, doc)
}

fn fold_mod(
    fold: &fold::Fold<astsrv::Srv>,
    doc: doc::ModDoc
) -> doc::ModDoc {
    let doc = fold::default_any_fold_mod(fold, doc);

    doc::ModDoc {
        items: doc.items.filtered(|ItemTag| {
            is_visible(fold.ctxt.clone(), ItemTag.item())
        }),
        .. doc
    }
}

fn is_visible(srv: astsrv::Srv, doc: doc::ItemDoc) -> bool {
    use syntax::ast_map;
    use syntax::ast;

    let id = doc.id;

    do astsrv::exec(srv) |ctxt| {
        match ctxt.ast_map.get(&id) {
            ast_map::node_item(item, _) => {
                match item.node {
                    ast::item_impl(_, Some(_), _, _) => {
                        // This is a trait implementation, make it visible
                        // NOTE: This is not quite right since this could be an impl
                        // of a private trait. We can't know that without running
                        // resolve though.
                        true
                    }
                    _ => {
                        // Otherwise just look at the visibility
                        item.vis == ast::public
                    }
                }
            }
            _ => util::unreachable()
        }
    }
}

#[test]
fn should_prune_items_without_pub_modifier() {
    let doc = test::mk_doc(~"mod a { }");
    fail_unless!(vec::is_empty(doc.cratemod().mods()));
}

#[test]
fn unless_they_are_trait_impls() {
    let doc = test::mk_doc(
        ~" \
          trait Foo { } \
          impl Foo for int { } \
          ");
    fail_unless!(!doc.cratemod().impls().is_empty());
}

#[cfg(test)]
pub mod test {
    use astsrv;
    use doc;
    use extract;
    use prune_private_pass::run;

    pub fn mk_doc(source: ~str) -> doc::Doc {
        do astsrv::from_str(copy source) |srv| {
            let doc = extract::from_srv(srv.clone(), ~"");
            run(srv.clone(), doc)
        }
    }
}

<|endoftext|>