<commit_before>//! OpenGL implementation of a device, striving to support OpenGL 2.0 with at
//! least VAOs, but using newer extensions when available.

#![allow(missing_docs, missing_copy_implementations)]

#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate log;
extern crate gfx_gl as gl;
extern crate gfx_hal as hal;
#[cfg(feature = "glutin")]
pub extern crate glutin;
extern crate smallvec;
extern crate spirv_cross;

use std::cell::Cell;
use std::fmt;
use std::ops::Deref;
use std::sync::{Arc, Weak};
use std::thread::{self, ThreadId};

use hal::queue::{QueueFamilyId, Queues};
use hal::{error, image, pso};

pub use self::device::Device;
pub use self::info::{Info, PlatformName, Version};

mod command;
mod conv;
mod device;
mod info;
mod native;
mod pool;
mod queue;
mod state;
mod window;

#[cfg(feature = "glutin")]
pub use window::glutin::{config_context, Headless, Surface, Swapchain};

pub(crate) struct GlContainer {
    context: gl::Gl,
}

impl GlContainer {
    fn make_current(&self) {
        // Unimplemented
    }
}

impl Deref for GlContainer {
    type Target = gl::Gl;
    fn deref(&self) -> &gl::Gl {
        #[cfg(feature = "glutin")]
        self.make_current();
        &self.context
    }
}

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Backend {}
impl hal::Backend for Backend {
    type PhysicalDevice = PhysicalDevice;
    type Device = Device;

    type Surface = Surface;
    type Swapchain = Swapchain;

    type QueueFamily = QueueFamily;
    type CommandQueue = queue::CommandQueue;
    type CommandBuffer = command::RawCommandBuffer;

    type Memory = native::Memory;
    type CommandPool = pool::RawCommandPool;

    type ShaderModule = native::ShaderModule;
    type RenderPass = native::RenderPass;
    type Framebuffer = native::FrameBuffer;

    type Buffer = native::Buffer;
    type BufferView = native::BufferView;
    type Image = native::Image;
    type ImageView = native::ImageView;
    type Sampler = native::FatSampler;

    type ComputePipeline = native::ComputePipeline;
    type GraphicsPipeline = native::GraphicsPipeline;
    type PipelineLayout = native::PipelineLayout;
    type PipelineCache = ();
    type DescriptorSetLayout = native::DescriptorSetLayout;
    type DescriptorPool = native::DescriptorPool;
    type DescriptorSet = native::DescriptorSet;

    type Fence = native::Fence;
    type Semaphore = native::Semaphore;
    type QueryPool = ();
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Error {
    NoError,
    InvalidEnum,
    InvalidValue,
    InvalidOperation,
    InvalidFramebufferOperation,
    OutOfMemory,
    UnknownError,
}

impl Error {
    pub fn from_error_code(error_code: gl::types::GLenum) -> Error {
        match error_code {
            gl::NO_ERROR => Error::NoError,
            gl::INVALID_ENUM => Error::InvalidEnum,
            gl::INVALID_VALUE => Error::InvalidValue,
            gl::INVALID_OPERATION => Error::InvalidOperation,
            gl::INVALID_FRAMEBUFFER_OPERATION => Error::InvalidFramebufferOperation,
            gl::OUT_OF_MEMORY => Error::OutOfMemory,
            _ => Error::UnknownError,
        }
    }
}

/// Internal struct of shared data between the physical and logical device.
struct Share {
    context: GlContainer,
    info: Info,
    features: hal::Features,
    legacy_features: info::LegacyFeatures,
    limits: hal::Limits,
    private_caps: info::PrivateCaps,
    // Indicates if there is an active logical device.
    open: Cell<bool>,
}

impl Share {
    /// Fails during a debug build if the implementation's error flag was set.
    fn check(&self) -> Result<(), Error> {
        if cfg!(debug_assertions) {
            let gl = &self.context;
            let err = Error::from_error_code(unsafe { gl.GetError() });
            if err != Error::NoError {
                return Err(err);
            }
        }
        Ok(())
    }
}

/// Single-threaded `Arc`.
/// Wrapper for `Arc` that allows you to `Send` it even if `T: !Sync`.
/// Yet internal data cannot be accessed outside of the thread where it was created.
pub struct Starc<T: ?Sized> {
    arc: Arc<T>,
    thread: ThreadId,
}

impl<T: ?Sized> Clone for Starc<T> {
    fn clone(&self) -> Self {
        Self {
            arc: self.arc.clone(),
            thread: self.thread,
        }
    }
}

impl<T: ?Sized> fmt::Debug for Starc<T> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{:p}@{:?}", self.arc, self.thread)
    }
}

impl<T> Starc<T> {
    #[inline]
    fn new(value: T) -> Self {
        Starc {
            arc: Arc::new(value),
            thread: thread::current().id(),
        }
    }

    #[inline]
    pub fn try_unwrap(self) -> Result<T, Self> {
        let a = Arc::try_unwrap(self.arc);
        let thread = self.thread;
        a.map_err(|a| Starc {
            arc: a,
            thread: thread,
        })
    }

    #[inline]
    pub fn downgrade(this: &Starc<T>) -> Wstarc<T> {
        Wstarc {
            weak: Arc::downgrade(&this.arc),
            thread: this.thread,
        }
    }

    #[inline]
    pub fn get_mut(this: &mut Starc<T>) -> Option<&mut T> {
        Arc::get_mut(&mut this.arc)
    }
}

unsafe impl<T: ?Sized> Send for Starc<T> {}
unsafe impl<T: ?Sized> Sync for Starc<T> {}

impl<T: ?Sized> Deref for Starc<T> {
    type Target = T;
    fn deref(&self) -> &T {
        assert_eq!(thread::current().id(), self.thread);
        &*self.arc
    }
}

/// Single-threaded `Weak`.
/// Wrapper for `Weak` that allows you to `Send` it even if `T: !Sync`.
/// Yet internal data cannot be accessed outside of the thread where it was created.
pub struct Wstarc<T: ?Sized> {
    weak: Weak<T>,
    thread: ThreadId,
}
impl<T> Wstarc<T> {
    pub fn upgrade(&self) -> Option<Starc<T>> {
        let thread = self.thread;
        self.weak.upgrade().map(|arc| Starc { arc, thread })
    }
}
unsafe impl<T: ?Sized> Send for Wstarc<T> {}
unsafe impl<T: ?Sized> Sync for Wstarc<T> {}

#[derive(Debug)]
pub struct PhysicalDevice(Starc<Share>);

impl PhysicalDevice {
    fn new_adapter<F>(fn_proc: F) -> hal::Adapter<Backend>
    where
        F: FnMut(&str) -> *const std::os::raw::c_void,
    {
        let gl = GlContainer {
            context: gl::Gl::load_with(fn_proc),
        };

        // query information
        let (info, features, legacy_features, limits, private_caps) = info::query_all(&gl);
        info!("Vendor: {:?}", info.platform_name.vendor);
        info!("Renderer: {:?}", info.platform_name.renderer);
        info!("Version: {:?}", info.version);
        info!("Shading Language: {:?}", info.shading_language);
        info!("Features: {:?}", features);
        info!("Legacy Features: {:?}", legacy_features);
        debug!("Loaded Extensions:");
        for extension in info.extensions.iter() {
            debug!("- {}", *extension);
        }
        let name = info.platform_name.renderer.into();
        let vendor: std::string::String = info.platform_name.vendor.into();

        // create the shared context
        let share = Share {
            context: gl,
            info,
            features,
            legacy_features,
            limits,
            private_caps,
            open: Cell::new(false),
        };
        if let Err(err) = share.check() {
            panic!("Error querying info: {:?}", err);
        }

        // opengl has no way to discern device_type, so we can try to infer it from the vendor string
        let vender_lower = vendor.to_lowercase();
        let inferred_device_type = if vender_lower.contains("intel") {
            hal::adapter::DeviceType::IntegratedGpu
        } else if vender_lower.contains("nvidia")
            || vender_lower.contains("ati")
            || vender_lower.contains("amd")
        {
            hal::adapter::DeviceType::DiscreteGpu
        } else {
            hal::adapter::DeviceType::Other
        };

        hal::Adapter {
            info: hal::AdapterInfo {
                name,
                vendor: 0,
                device: 0,
                device_type: inferred_device_type,
            },
            physical_device: PhysicalDevice(Starc::new(share)),
            queue_families: vec![QueueFamily],
        }
    }

    /// Get GL-specific legacy feature flags.
    pub fn legacy_features(&self) -> &info::LegacyFeatures {
        &self.0.legacy_features
    }
}

impl hal::PhysicalDevice<Backend> for PhysicalDevice {
    unsafe fn open(
        &self,
        families: &[(&QueueFamily, &[hal::QueuePriority])],
        requested_features: hal::Features,
    ) -> Result<hal::Gpu<Backend>, error::DeviceCreationError> {
        // Can't have multiple logical devices at the same time
        // as they would share the same context.
        if self.0.open.get() {
            return Err(error::DeviceCreationError::TooManyObjects);
        }
        self.0.open.set(true);

        // TODO: Check for support in the LeagcyFeatures struct too
        if !self.features().contains(requested_features) {
            return Err(error::DeviceCreationError::MissingFeature);
        }

        // initialize permanent states
        let gl = &self.0.context;
        if self
            .0
            .legacy_features
            .contains(info::LegacyFeatures::SRGB_COLOR)
        {
            // TODO: Find way to emulate this on older Opengl versions.
            unsafe {
                gl.Enable(gl::FRAMEBUFFER_SRGB);
            }
        }
        unsafe {
            gl.PixelStorei(gl::UNPACK_ALIGNMENT, 1);

            if !self.0.info.version.is_embedded {
                gl.Enable(gl::PROGRAM_POINT_SIZE);
            }
        }

        // create main VAO and bind it
        let mut vao = 0;
        if self.0.private_caps.vertex_array {
            unsafe {
                gl.GenVertexArrays(1, &mut vao);
                gl.BindVertexArray(vao);
            }
        }

        if let Err(err) = self.0.check() {
            panic!("Error opening adapter: {:?}", err);
        }

        Ok(hal::Gpu {
            device: Device::new(self.0.clone()),
            queues: Queues::new(
                families
                    .into_iter()
                    .map(|&(proto_family, priorities)| {
                        assert_eq!(priorities.len(), 1);
                        let mut family = hal::backend::RawQueueGroup::new(proto_family.clone());
                        let queue = queue::CommandQueue::new(&self.0, vao);
                        family.add_queue(queue);
                        family
                    })
                    .collect(),
            ),
        })
    }

    fn format_properties(&self, _: Option<hal::format::Format>) -> hal::format::Properties {
        unimplemented!()
    }

    fn image_format_properties(
        &self,
        _format: hal::format::Format,
        _dimensions: u8,
        _tiling: image::Tiling,
        _usage: image::Usage,
        _view_caps: image::ViewCapabilities,
    ) -> Option<image::FormatProperties> {
        None //TODO
    }

    fn memory_properties(&self) -> hal::MemoryProperties {
        use hal::memory::Properties;

        // COHERENT flags require that the backend does flushing and invalidation
        // by itself. If we move towards persistent mapping we need to re-evaluate it.
        let memory_types = if self.0.private_caps.map {
            vec![
                hal::MemoryType {
                    properties: Properties::DEVICE_LOCAL,
                    heap_index: 1,
                },
                hal::MemoryType {
                    // upload
                    properties: Properties::CPU_VISIBLE | Properties::COHERENT,
                    heap_index: 0,
                },
                hal::MemoryType {
                    // download
                    properties: Properties::CPU_VISIBLE
                        | Properties::COHERENT
                        | Properties::CPU_CACHED,
                    heap_index: 0,
                },
            ]
        } else {
            vec![hal::MemoryType {
                properties: Properties::DEVICE_LOCAL,
                heap_index: 0,
            }]
        };

        hal::MemoryProperties {
            memory_types,
            memory_heaps: vec![!0, !0],
        }
    }

    fn features(&self) -> hal::Features {
        self.0.features
    }

    fn limits(&self) -> hal::Limits {
        self.0.limits
    }
}

#[derive(Debug, Clone, Copy)]
pub struct QueueFamily;

impl hal::QueueFamily for QueueFamily {
    fn queue_type(&self) -> hal::QueueType {
        hal::QueueType::General
    }
    fn max_queues(&self) -> usize {
        1
    }
    fn id(&self) -> QueueFamilyId {
        QueueFamilyId(0)
    }
}
<commit_msg>better device_type inference<commit_after>//! OpenGL implementation of a device, striving to support OpenGL 2.0 with at
//! least VAOs, but using newer extensions when available.

#![allow(missing_docs, missing_copy_implementations)]

#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate log;
extern crate gfx_gl as gl;
extern crate gfx_hal as hal;
#[cfg(feature = "glutin")]
pub extern crate glutin;
extern crate smallvec;
extern crate spirv_cross;

use std::cell::Cell;
use std::fmt;
use std::ops::Deref;
use std::sync::{Arc, Weak};
use std::thread::{self, ThreadId};

use hal::queue::{QueueFamilyId, Queues};
use hal::{error, image, pso};

pub use self::device::Device;
pub use self::info::{Info, PlatformName, Version};

mod command;
mod conv;
mod device;
mod info;
mod native;
mod pool;
mod queue;
mod state;
mod window;

#[cfg(feature = "glutin")]
pub use window::glutin::{config_context, Headless, Surface, Swapchain};

pub(crate) struct GlContainer {
    context: gl::Gl,
}

impl GlContainer {
    fn make_current(&self) {
        // Unimplemented
    }
}

impl Deref for GlContainer {
    type Target = gl::Gl;
    fn deref(&self) -> &gl::Gl {
        #[cfg(feature = "glutin")]
        self.make_current();
        &self.context
    }
}

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Backend {}
impl hal::Backend for Backend {
    type PhysicalDevice = PhysicalDevice;
    type Device = Device;

    type Surface = Surface;
    type Swapchain = Swapchain;

    type QueueFamily = QueueFamily;
    type CommandQueue = queue::CommandQueue;
    type CommandBuffer = command::RawCommandBuffer;

    type Memory = native::Memory;
    type CommandPool = pool::RawCommandPool;

    type ShaderModule = native::ShaderModule;
    type RenderPass = native::RenderPass;
    type Framebuffer = native::FrameBuffer;

    type Buffer = native::Buffer;
    type BufferView = native::BufferView;
    type Image = native::Image;
    type ImageView = native::ImageView;
    type Sampler = native::FatSampler;

    type ComputePipeline = native::ComputePipeline;
    type GraphicsPipeline = native::GraphicsPipeline;
    type PipelineLayout = native::PipelineLayout;
    type PipelineCache = ();
    type DescriptorSetLayout = native::DescriptorSetLayout;
    type DescriptorPool = native::DescriptorPool;
    type DescriptorSet = native::DescriptorSet;

    type Fence = native::Fence;
    type Semaphore = native::Semaphore;
    type QueryPool = ();
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Error {
    NoError,
    InvalidEnum,
    InvalidValue,
    InvalidOperation,
    InvalidFramebufferOperation,
    OutOfMemory,
    UnknownError,
}

impl Error {
    pub fn from_error_code(error_code: gl::types::GLenum) -> Error {
        match error_code {
            gl::NO_ERROR => Error::NoError,
            gl::INVALID_ENUM => Error::InvalidEnum,
            gl::INVALID_VALUE => Error::InvalidValue,
            gl::INVALID_OPERATION => Error::InvalidOperation,
            gl::INVALID_FRAMEBUFFER_OPERATION => Error::InvalidFramebufferOperation,
            gl::OUT_OF_MEMORY => Error::OutOfMemory,
            _ => Error::UnknownError,
        }
    }
}

/// Internal struct of shared data between the physical and logical device.
struct Share {
    context: GlContainer,
    info: Info,
    features: hal::Features,
    legacy_features: info::LegacyFeatures,
    limits: hal::Limits,
    private_caps: info::PrivateCaps,
    // Indicates if there is an active logical device.
    open: Cell<bool>,
}

impl Share {
    /// Fails during a debug build if the implementation's error flag was set.
    fn check(&self) -> Result<(), Error> {
        if cfg!(debug_assertions) {
            let gl = &self.context;
            let err = Error::from_error_code(unsafe { gl.GetError() });
            if err != Error::NoError {
                return Err(err);
            }
        }
        Ok(())
    }
}

/// Single-threaded `Arc`.
/// Wrapper for `Arc` that allows you to `Send` it even if `T: !Sync`.
/// Yet internal data cannot be accessed outside of the thread where it was created.
pub struct Starc<T: ?Sized> {
    arc: Arc<T>,
    thread: ThreadId,
}

impl<T: ?Sized> Clone for Starc<T> {
    fn clone(&self) -> Self {
        Self {
            arc: self.arc.clone(),
            thread: self.thread,
        }
    }
}

impl<T: ?Sized> fmt::Debug for Starc<T> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{:p}@{:?}", self.arc, self.thread)
    }
}

impl<T> Starc<T> {
    #[inline]
    fn new(value: T) -> Self {
        Starc {
            arc: Arc::new(value),
            thread: thread::current().id(),
        }
    }

    #[inline]
    pub fn try_unwrap(self) -> Result<T, Self> {
        let a = Arc::try_unwrap(self.arc);
        let thread = self.thread;
        a.map_err(|a| Starc {
            arc: a,
            thread: thread,
        })
    }

    #[inline]
    pub fn downgrade(this: &Starc<T>) -> Wstarc<T> {
        Wstarc {
            weak: Arc::downgrade(&this.arc),
            thread: this.thread,
        }
    }

    #[inline]
    pub fn get_mut(this: &mut Starc<T>) -> Option<&mut T> {
        Arc::get_mut(&mut this.arc)
    }
}

unsafe impl<T: ?Sized> Send for Starc<T> {}
unsafe impl<T: ?Sized> Sync for Starc<T> {}

impl<T: ?Sized> Deref for Starc<T> {
    type Target = T;
    fn deref(&self) -> &T {
        assert_eq!(thread::current().id(), self.thread);
        &*self.arc
    }
}

/// Single-threaded `Weak`.
/// Wrapper for `Weak` that allows you to `Send` it even if `T: !Sync`.
/// Yet internal data cannot be accessed outside of the thread where it was created.
pub struct Wstarc<T: ?Sized> {
    weak: Weak<T>,
    thread: ThreadId,
}
impl<T> Wstarc<T> {
    pub fn upgrade(&self) -> Option<Starc<T>> {
        let thread = self.thread;
        self.weak.upgrade().map(|arc| Starc { arc, thread })
    }
}
unsafe impl<T: ?Sized> Send for Wstarc<T> {}
unsafe impl<T: ?Sized> Sync for Wstarc<T> {}

#[derive(Debug)]
pub struct PhysicalDevice(Starc<Share>);

impl PhysicalDevice {
    fn new_adapter<F>(fn_proc: F) -> hal::Adapter<Backend>
    where
        F: FnMut(&str) -> *const std::os::raw::c_void,
    {
        let gl = GlContainer {
            context: gl::Gl::load_with(fn_proc),
        };

        // query information
        let (info, features, legacy_features, limits, private_caps) = info::query_all(&gl);
        info!("Vendor: {:?}", info.platform_name.vendor);
        info!("Renderer: {:?}", info.platform_name.renderer);
        info!("Version: {:?}", info.version);
        info!("Shading Language: {:?}", info.shading_language);
        info!("Features: {:?}", features);
        info!("Legacy Features: {:?}", legacy_features);
        debug!("Loaded Extensions:");
        for extension in info.extensions.iter() {
            debug!("- {}", *extension);
        }
        let name = info.platform_name.renderer.into();
        let renderer: std::string::String = info.platform_name.renderer.into();

        // create the shared context
        let share = Share {
            context: gl,
            info,
            features,
            legacy_features,
            limits,
            private_caps,
            open: Cell::new(false),
        };
        if let Err(err) = share.check() {
            panic!("Error querying info: {:?}", err);
        }

        // opengl has no way to discern device_type, so we can try to infer it from the renderer string

        let renderer_lower = renderer.to_lowercase();
        let strings_that_imply_integrated = [
            " xpress", // space here is on purpose so we don't match express
            "radeon hd 4200",
            "radeon hd 4250",
            "radeon hd 4290",
            "radeon hd 4270",
            "radeon hd 4225",
            "radeon hd 3100",
            "radeon hd 3200",
            "radeon hd 3000",
            "radeon hd 3300",
            "nforce", // All nvidia nforce appear to be integrated
            "igp",
            "intel", // todo Intel will release a discrete gpu soon, and we will need to update this logic when they do
        ];

        let inferred_device_type = if strings_that_imply_integrated
            .into_iter()
            .any(|&s| renderer_lower.contains(s))
        {
            hal::adapter::DeviceType::IntegratedGpu
        } else {
            hal::adapter::DeviceType::DiscreteGpu
        };

        hal::Adapter {
            info: hal::AdapterInfo {
                name,
                vendor: 0,
                device: 0,
                device_type: inferred_device_type,
            },
            physical_device: PhysicalDevice(Starc::new(share)),
            queue_families: vec![QueueFamily],
        }
    }

    /// Get GL-specific legacy feature flags.
    pub fn legacy_features(&self) -> &info::LegacyFeatures {
        &self.0.legacy_features
    }
}

impl hal::PhysicalDevice<Backend> for PhysicalDevice {
    unsafe fn open(
        &self,
        families: &[(&QueueFamily, &[hal::QueuePriority])],
        requested_features: hal::Features,
    ) -> Result<hal::Gpu<Backend>, error::DeviceCreationError> {
        // Can't have multiple logical devices at the same time
        // as they would share the same context.
        if self.0.open.get() {
            return Err(error::DeviceCreationError::TooManyObjects);
        }
        self.0.open.set(true);

        // TODO: Check for support in the LeagcyFeatures struct too
        if !self.features().contains(requested_features) {
            return Err(error::DeviceCreationError::MissingFeature);
        }

        // initialize permanent states
        let gl = &self.0.context;
        if self
            .0
            .legacy_features
            .contains(info::LegacyFeatures::SRGB_COLOR)
        {
            // TODO: Find way to emulate this on older Opengl versions.
            unsafe {
                gl.Enable(gl::FRAMEBUFFER_SRGB);
            }
        }
        unsafe {
            gl.PixelStorei(gl::UNPACK_ALIGNMENT, 1);

            if !self.0.info.version.is_embedded {
                gl.Enable(gl::PROGRAM_POINT_SIZE);
            }
        }

        // create main VAO and bind it
        let mut vao = 0;
        if self.0.private_caps.vertex_array {
            unsafe {
                gl.GenVertexArrays(1, &mut vao);
                gl.BindVertexArray(vao);
            }
        }

        if let Err(err) = self.0.check() {
            panic!("Error opening adapter: {:?}", err);
        }

        Ok(hal::Gpu {
            device: Device::new(self.0.clone()),
            queues: Queues::new(
                families
                    .into_iter()
                    .map(|&(proto_family, priorities)| {
                        assert_eq!(priorities.len(), 1);
                        let mut family = hal::backend::RawQueueGroup::new(proto_family.clone());
                        let queue = queue::CommandQueue::new(&self.0, vao);
                        family.add_queue(queue);
                        family
                    })
                    .collect(),
            ),
        })
    }

    fn format_properties(&self, _: Option<hal::format::Format>) -> hal::format::Properties {
        unimplemented!()
    }

    fn image_format_properties(
        &self,
        _format: hal::format::Format,
        _dimensions: u8,
        _tiling: image::Tiling,
        _usage: image::Usage,
        _view_caps: image::ViewCapabilities,
    ) -> Option<image::FormatProperties> {
        None //TODO
    }

    fn memory_properties(&self) -> hal::MemoryProperties {
        use hal::memory::Properties;

        // COHERENT flags require that the backend does flushing and invalidation
        // by itself. If we move towards persistent mapping we need to re-evaluate it.
        let memory_types = if self.0.private_caps.map {
            vec![
                hal::MemoryType {
                    properties: Properties::DEVICE_LOCAL,
                    heap_index: 1,
                },
                hal::MemoryType {
                    // upload
                    properties: Properties::CPU_VISIBLE | Properties::COHERENT,
                    heap_index: 0,
                },
                hal::MemoryType {
                    // download
                    properties: Properties::CPU_VISIBLE
                        | Properties::COHERENT
                        | Properties::CPU_CACHED,
                    heap_index: 0,
                },
            ]
        } else {
            vec![hal::MemoryType {
                properties: Properties::DEVICE_LOCAL,
                heap_index: 0,
            }]
        };

        hal::MemoryProperties {
            memory_types,
            memory_heaps: vec![!0, !0],
        }
    }

    fn features(&self) -> hal::Features {
        self.0.features
    }

    fn limits(&self) -> hal::Limits {
        self.0.limits
    }
}

#[derive(Debug, Clone, Copy)]
pub struct QueueFamily;

impl hal::QueueFamily for QueueFamily {
    fn queue_type(&self) -> hal::QueueType {
        hal::QueueType::General
    }
    fn max_queues(&self) -> usize {
        1
    }
    fn id(&self) -> QueueFamilyId {
        QueueFamilyId(0)
    }
}
<|endoftext|>


<commit_before>#![feature(nll)]
#![feature(proc_macro_hygiene, decl_macro)]

extern crate getopts;

#[macro_use] extern crate rocket;
#[macro_use] extern crate rocket_contrib;
use rocket::response::content;

#[macro_use] extern crate serde_derive;

extern crate pombase;

use std::sync::Mutex;
use std::process;
use std::env;
use std::path::{Path, PathBuf};

use getopts::Options;
use rocket_contrib::json::{Json, JsonValue};
use rocket::response::NamedFile;

use pombase::api::query::Query as PomBaseQuery;
use pombase::api::result::QueryAPIResult;
use pombase::api::search::Search;
use pombase::api::query_exec::QueryExec;
use pombase::api_data::APIData;
use pombase::api::site_db::SiteDB;

use pombase::data_types::{SolrTermSummary, SolrReferenceSummary, GeneDetails, GenotypeDetails,
                         TermDetails, ReferenceDetails};
use pombase::web::simple_pages::{render_simple_gene_page, render_simple_reference_page,
                                 render_simple_term_page};
use pombase::web::config::Config;

const PKG_NAME: &str = env!("CARGO_PKG_NAME");
const VERSION: &str = env!("CARGO_PKG_VERSION");

struct StaticFileState {
    web_root_dir: String,
}

// If the path is a directory, return path+"/index.html".  Otherwise
// try the path, then try path + ".json", then default to loading the
// Angular app from /index.html
#[get("/<path..>", rank=3)]
fn get_misc(path: PathBuf, state: rocket::State<Mutex<StaticFileState>>) -> Option<NamedFile> {
    let web_root_dir = &state.lock().expect("failed to lock").web_root_dir;
    let root_dir_path = Path::new("/").join(web_root_dir);
    let full_path = root_dir_path.join(path);

    if full_path.is_dir() {
        let index_path = full_path.join("index.html");
        return NamedFile::open(index_path).ok();
    }

    if full_path.exists() {
        return NamedFile::open(full_path).ok();
    }

    let mut json_path_str = full_path.to_str().unwrap().to_owned();
    json_path_str += ".json";
    let json_path: PathBuf = json_path_str.into();

    if json_path.exists() {
        return NamedFile::open(json_path).ok();
    }

    NamedFile::open(root_dir_path.join("index.html")).ok()
}

#[get("/api/v1/dataset/latest/data/gene/<id>", rank=2)]
fn get_gene(id: String, state: rocket::State<Mutex<QueryExec>>) -> Option<Json<GeneDetails>> {
    let query_exec = state.lock().expect("failed to lock");
    if let Some(gene) = query_exec.get_api_data().get_full_gene_details(&id) {
        Some(Json(gene))
    } else {
        None
    }
}

#[get("/api/v1/dataset/latest/data/genotype/<id>", rank=2)]
fn get_genotype(id: String, state: rocket::State<Mutex<QueryExec>>) -> Option<Json<GenotypeDetails>> {
    let query_exec = state.lock().expect("failed to lock");
    if let Some(genotype) = query_exec.get_api_data().get_genotype_details(&id) {
        Some(Json(genotype))
    } else {
        None
    }
}

#[get("/api/v1/dataset/latest/data/term/<id>", rank=2)]
fn get_term(id: String, state: rocket::State<Mutex<QueryExec>>) -> Option<Json<TermDetails>> {
    let query_exec = state.lock().expect("failed to lock");
    if let Some(term) = query_exec.get_api_data().get_term_details(&id) {
        Some(Json(term))
    } else {
        None
    }
}

#[get("/api/v1/dataset/latest/data/reference/<id>", rank=2)]
fn get_reference(id: String, state: rocket::State<Mutex<QueryExec>>) -> Option<Json<ReferenceDetails>> {
    let query_exec = state.lock().expect("failed to lock");
    if let Some(reference) = query_exec.get_api_data().get_reference_details(&id) {
        Some(Json(reference))
    } else {
        None
    }
}

#[get("/", rank=1)]
fn get_index(state: rocket::State<Mutex<StaticFileState>>) -> Option<NamedFile> {
    let web_root_dir = &state.lock().expect("failed to lock").web_root_dir;
    let root_dir_path = Path::new("/").join(web_root_dir);
    NamedFile::open(root_dir_path.join("index.html")).ok()
}

/*
Return a simple HTML version a gene page for search engines
*/
#[get("/simple/gene/<id>", rank=1)]
fn get_simple_gene(id: String, query_exec_state: rocket::State<Mutex<QueryExec>>,
                   config: rocket::State<Config>) -> Option<content::Html<String>> {
    let query_exec = query_exec_state.lock().expect("failed to lock QueryExec");
    if let Some(gene) = query_exec.get_api_data().get_full_gene_details(&id) {
        Some(content::Html(render_simple_gene_page(&config, &gene)))
    } else {
        None
    }
}

/*
Return a simple HTML version a reference page for search engines
*/
#[get("/simple/reference/<id>", rank=1)]
fn get_simple_reference(id: String, query_exec_state: rocket::State<Mutex<QueryExec>>,
                        config: rocket::State<Config>) -> Option<content::Html<String>> {
    let query_exec = query_exec_state.lock().expect("failed to lock QueryExec");
    if let Some(reference) = query_exec.get_api_data().get_reference_details(&id) {
        Some(content::Html(render_simple_reference_page(&config, &reference)))
    } else {
        None
    }
}
/*
Return a simple HTML version a term page for search engines
*/
#[get("/simple/term/<id>", rank=1)]
fn get_simple_term(id: String, query_exec_state: rocket::State<Mutex<QueryExec>>,
                   config: rocket::State<Config>) -> Option<content::Html<String>> {
    let query_exec = query_exec_state.lock().expect("failed to lock QueryExec");
    if let Some(term) = query_exec.get_api_data().get_term_details(&id) {
        Some(content::Html(render_simple_term_page(&config, &term)))
    } else {
        None
    }
}

#[post("/api/v1/dataset/latest/query", rank=1, data="<q>", format = "application/json")]
fn query_post(q: Json<PomBaseQuery>, state: rocket::State<Mutex<QueryExec>>)
              -> Option<Json<QueryAPIResult>>
{
    let query_exec = state.lock().expect("failed to lock");
    Some(Json(query_exec.exec(&q.into_inner())))
}

#[derive(Serialize, Debug)]
struct TermCompletionResponse {
    status: String,
    matches: Vec<SolrTermSummary>,
}

#[derive(Serialize, Debug)]
struct RefCompletionResponse {
    status: String,
    matches: Vec<SolrReferenceSummary>,
}

#[get ("/api/v1/dataset/latest/complete/term/<cv_name>/<q>", rank=1)]
fn term_complete(cv_name: String, q: String, state: rocket::State<Mutex<Search>>)
              -> Option<Json<TermCompletionResponse>>
{
    let search = state.lock().expect("failed to lock");
    let res = search.term_complete(&cv_name, &q);

    let completion_response =
        match res {
            Ok(matches) => {
                TermCompletionResponse {
                    status: "Ok".to_owned(),
                    matches: matches,
                }
            },
            Err(err) => {
                println!("{:?}", err);
                TermCompletionResponse {
                    status: "Error".to_owned(),
                    matches: vec![],
                }
            },
        };

    Some(Json(completion_response))
}

#[get ("/api/v1/dataset/latest/complete/ref/<q>", rank=1)]
fn ref_complete(q: String, state: rocket::State<Mutex<Search>>)
                -> Option<Json<RefCompletionResponse>>
{
    let search = state.lock().expect("failed to lock");
    let res = search.ref_complete(&q);

    let completion_response =
        match res {
            Ok(matches) => {
                RefCompletionResponse {
                    status: "Ok".to_owned(),
                    matches: matches,
                }
            },
            Err(err) => {
                println!("{:?}", err);
                RefCompletionResponse {
                    status: "Error".to_owned(),
                    matches: vec![],
                }
            },
        };

    Some(Json(completion_response))
}

#[get ("/api/v1/dataset/latest/motif_search/<scope>/<q>", rank=1)]
fn motif_search(scope: String, q: String, state: rocket::State<Mutex<Search>>)
                -> Option<String>
{
    let search = state.lock().expect("failed to lock");
    let res = search.motif_search(&scope, &q);

    match res {
        Ok(search_result) => {
            Some(search_result)
        },
        Err(err) => {
            println!("Motif search error: {:?}", err);
            None
        },
    }
}

#[get ("/ping", rank=1)]
fn ping() -> Option<String> {
    Some(String::from("OK") + " " + PKG_NAME + " " + VERSION)
}

#[catch(404)]
fn not_found() -> JsonValue {
    json!({
        "status": "error",
        "reason": "Resource was not found."
    })
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    print!("{} v{}\n", PKG_NAME, VERSION);

    let args: Vec<String> = env::args().collect();
    let mut opts = Options::new();

    opts.optflag("h", "help", "print this help message");
    opts.optopt("c", "config-file", "Configuration file name", "CONFIG");
    opts.optopt("m", "search-maps", "Search data", "MAPS_JSON_FILE");
    opts.optopt("w", "web-root-dir", "Root web data directory", "WEB_ROOT_DIR");
    opts.optopt("s", "site-db", "Connection string for the site local databae", "SITE_DB");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!("Invalid options\n{}", f)
    };

    let program = args[0].clone();

    if matches.opt_present("help") {
        print_usage(&program, opts);
        process::exit(0);
    }

    if !matches.opt_present("config-file") {
        print!("no -c|--config-file option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("search-maps") {
        print!("no --search-maps|-m option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("web-root-dir") {
        print!("no --web-root-dir|-w option\n");
        print_usage(&program, opts);
        process::exit(1);
    }

    let site_db_conn_string = matches.opt_str("s");

    let search_maps_filename = matches.opt_str("m").unwrap();
    println!("Reading data files ...");

    let config_file_name = matches.opt_str("c").unwrap();
    let config = Config::read(&config_file_name);
    let api_data = APIData::new_from_file(&config, &search_maps_filename);
    let site_db = site_db_conn_string.map(|conn_str| SiteDB::new(&conn_str));
    let query_exec = QueryExec::new(api_data, site_db);
    let searcher = Search::new(&config);

    let web_root_dir = matches.opt_str("w").unwrap();
    let static_file_state = StaticFileState {
        web_root_dir: web_root_dir,
    };

    println!("Starting server ...");
    rocket::ignite()
        .mount("/", routes![get_index, get_misc, query_post,
                            get_gene, get_genotype, get_term, get_reference,
                            get_simple_gene, get_simple_reference, get_simple_term,
                            term_complete, ref_complete,
                            motif_search, ping])
        .register(catchers![not_found])
        .manage(Mutex::new(query_exec))
        .manage(Mutex::new(searcher))
        .manage(Mutex::new(static_file_state))
        .manage(config)
        .launch();
}
<commit_msg>Return 404 for missing JBrowse data<commit_after>#![feature(nll)]
#![feature(proc_macro_hygiene, decl_macro)]

extern crate getopts;

#[macro_use] extern crate rocket;
#[macro_use] extern crate rocket_contrib;
use rocket::response::content;

#[macro_use] extern crate serde_derive;

extern crate pombase;

use std::sync::Mutex;
use std::process;
use std::env;
use std::path::{Path, PathBuf};

use getopts::Options;
use rocket_contrib::json::{Json, JsonValue};
use rocket::response::NamedFile;

use pombase::api::query::Query as PomBaseQuery;
use pombase::api::result::QueryAPIResult;
use pombase::api::search::Search;
use pombase::api::query_exec::QueryExec;
use pombase::api_data::APIData;
use pombase::api::site_db::SiteDB;

use pombase::data_types::{SolrTermSummary, SolrReferenceSummary, GeneDetails, GenotypeDetails,
                         TermDetails, ReferenceDetails};
use pombase::web::simple_pages::{render_simple_gene_page, render_simple_reference_page,
                                 render_simple_term_page};
use pombase::web::config::Config;

const PKG_NAME: &str = env!("CARGO_PKG_NAME");
const VERSION: &str = env!("CARGO_PKG_VERSION");

struct StaticFileState {
    web_root_dir: String,
}

// If the path is a directory, return path+"/index.html".  Otherwise
// try the path, then try path + ".json", then default to loading the
// Angular app from /index.html
#[get("/<path..>", rank=3)]
fn get_misc(path: PathBuf, state: rocket::State<Mutex<StaticFileState>>) -> Option<NamedFile> {
    let web_root_dir = &state.lock().expect("failed to lock").web_root_dir;
    let root_dir_path = Path::new("/").join(web_root_dir);
    let is_jbrowse_path = path.starts_with("jbrowse/");

    let full_path = root_dir_path.join(path);

    if full_path.is_dir() {
        let index_path = full_path.join("index.html");
        return NamedFile::open(index_path).ok();
    }

    if full_path.exists() {
        return NamedFile::open(full_path).ok();
    }

    let mut json_path_str = full_path.to_str().unwrap().to_owned();
    json_path_str += ".json";
    let json_path: PathBuf = json_path_str.into();

    if json_path.exists() {
        return NamedFile::open(json_path).ok();
    }

    // special case for missing JBrowse files - return 4044
    if is_jbrowse_path {
        return None;
    }

    NamedFile::open(root_dir_path.join("index.html")).ok()
}

#[get("/api/v1/dataset/latest/data/gene/<id>", rank=2)]
fn get_gene(id: String, state: rocket::State<Mutex<QueryExec>>) -> Option<Json<GeneDetails>> {
    let query_exec = state.lock().expect("failed to lock");
    if let Some(gene) = query_exec.get_api_data().get_full_gene_details(&id) {
        Some(Json(gene))
    } else {
        None
    }
}

#[get("/api/v1/dataset/latest/data/genotype/<id>", rank=2)]
fn get_genotype(id: String, state: rocket::State<Mutex<QueryExec>>) -> Option<Json<GenotypeDetails>> {
    let query_exec = state.lock().expect("failed to lock");
    if let Some(genotype) = query_exec.get_api_data().get_genotype_details(&id) {
        Some(Json(genotype))
    } else {
        None
    }
}

#[get("/api/v1/dataset/latest/data/term/<id>", rank=2)]
fn get_term(id: String, state: rocket::State<Mutex<QueryExec>>) -> Option<Json<TermDetails>> {
    let query_exec = state.lock().expect("failed to lock");
    if let Some(term) = query_exec.get_api_data().get_term_details(&id) {
        Some(Json(term))
    } else {
        None
    }
}

#[get("/api/v1/dataset/latest/data/reference/<id>", rank=2)]
fn get_reference(id: String, state: rocket::State<Mutex<QueryExec>>) -> Option<Json<ReferenceDetails>> {
    let query_exec = state.lock().expect("failed to lock");
    if let Some(reference) = query_exec.get_api_data().get_reference_details(&id) {
        Some(Json(reference))
    } else {
        None
    }
}

#[get("/", rank=1)]
fn get_index(state: rocket::State<Mutex<StaticFileState>>) -> Option<NamedFile> {
    let web_root_dir = &state.lock().expect("failed to lock").web_root_dir;
    let root_dir_path = Path::new("/").join(web_root_dir);
    NamedFile::open(root_dir_path.join("index.html")).ok()
}

/*
Return a simple HTML version a gene page for search engines
*/
#[get("/simple/gene/<id>", rank=1)]
fn get_simple_gene(id: String, query_exec_state: rocket::State<Mutex<QueryExec>>,
                   config: rocket::State<Config>) -> Option<content::Html<String>> {
    let query_exec = query_exec_state.lock().expect("failed to lock QueryExec");
    if let Some(gene) = query_exec.get_api_data().get_full_gene_details(&id) {
        Some(content::Html(render_simple_gene_page(&config, &gene)))
    } else {
        None
    }
}

/*
Return a simple HTML version a reference page for search engines
*/
#[get("/simple/reference/<id>", rank=1)]
fn get_simple_reference(id: String, query_exec_state: rocket::State<Mutex<QueryExec>>,
                        config: rocket::State<Config>) -> Option<content::Html<String>> {
    let query_exec = query_exec_state.lock().expect("failed to lock QueryExec");
    if let Some(reference) = query_exec.get_api_data().get_reference_details(&id) {
        Some(content::Html(render_simple_reference_page(&config, &reference)))
    } else {
        None
    }
}
/*
Return a simple HTML version a term page for search engines
*/
#[get("/simple/term/<id>", rank=1)]
fn get_simple_term(id: String, query_exec_state: rocket::State<Mutex<QueryExec>>,
                   config: rocket::State<Config>) -> Option<content::Html<String>> {
    let query_exec = query_exec_state.lock().expect("failed to lock QueryExec");
    if let Some(term) = query_exec.get_api_data().get_term_details(&id) {
        Some(content::Html(render_simple_term_page(&config, &term)))
    } else {
        None
    }
}

#[post("/api/v1/dataset/latest/query", rank=1, data="<q>", format = "application/json")]
fn query_post(q: Json<PomBaseQuery>, state: rocket::State<Mutex<QueryExec>>)
              -> Option<Json<QueryAPIResult>>
{
    let query_exec = state.lock().expect("failed to lock");
    Some(Json(query_exec.exec(&q.into_inner())))
}

#[derive(Serialize, Debug)]
struct TermCompletionResponse {
    status: String,
    matches: Vec<SolrTermSummary>,
}

#[derive(Serialize, Debug)]
struct RefCompletionResponse {
    status: String,
    matches: Vec<SolrReferenceSummary>,
}

#[get ("/api/v1/dataset/latest/complete/term/<cv_name>/<q>", rank=1)]
fn term_complete(cv_name: String, q: String, state: rocket::State<Mutex<Search>>)
              -> Option<Json<TermCompletionResponse>>
{
    let search = state.lock().expect("failed to lock");
    let res = search.term_complete(&cv_name, &q);

    let completion_response =
        match res {
            Ok(matches) => {
                TermCompletionResponse {
                    status: "Ok".to_owned(),
                    matches: matches,
                }
            },
            Err(err) => {
                println!("{:?}", err);
                TermCompletionResponse {
                    status: "Error".to_owned(),
                    matches: vec![],
                }
            },
        };

    Some(Json(completion_response))
}

#[get ("/api/v1/dataset/latest/complete/ref/<q>", rank=1)]
fn ref_complete(q: String, state: rocket::State<Mutex<Search>>)
                -> Option<Json<RefCompletionResponse>>
{
    let search = state.lock().expect("failed to lock");
    let res = search.ref_complete(&q);

    let completion_response =
        match res {
            Ok(matches) => {
                RefCompletionResponse {
                    status: "Ok".to_owned(),
                    matches: matches,
                }
            },
            Err(err) => {
                println!("{:?}", err);
                RefCompletionResponse {
                    status: "Error".to_owned(),
                    matches: vec![],
                }
            },
        };

    Some(Json(completion_response))
}

#[get ("/api/v1/dataset/latest/motif_search/<scope>/<q>", rank=1)]
fn motif_search(scope: String, q: String, state: rocket::State<Mutex<Search>>)
                -> Option<String>
{
    let search = state.lock().expect("failed to lock");
    let res = search.motif_search(&scope, &q);

    match res {
        Ok(search_result) => {
            Some(search_result)
        },
        Err(err) => {
            println!("Motif search error: {:?}", err);
            None
        },
    }
}

#[get ("/ping", rank=1)]
fn ping() -> Option<String> {
    Some(String::from("OK") + " " + PKG_NAME + " " + VERSION)
}

#[catch(404)]
fn not_found() -> JsonValue {
    json!({
        "status": "error",
        "reason": "Resource was not found."
    })
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    print!("{} v{}\n", PKG_NAME, VERSION);

    let args: Vec<String> = env::args().collect();
    let mut opts = Options::new();

    opts.optflag("h", "help", "print this help message");
    opts.optopt("c", "config-file", "Configuration file name", "CONFIG");
    opts.optopt("m", "search-maps", "Search data", "MAPS_JSON_FILE");
    opts.optopt("w", "web-root-dir", "Root web data directory", "WEB_ROOT_DIR");
    opts.optopt("s", "site-db", "Connection string for the site local databae", "SITE_DB");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!("Invalid options\n{}", f)
    };

    let program = args[0].clone();

    if matches.opt_present("help") {
        print_usage(&program, opts);
        process::exit(0);
    }

    if !matches.opt_present("config-file") {
        print!("no -c|--config-file option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("search-maps") {
        print!("no --search-maps|-m option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("web-root-dir") {
        print!("no --web-root-dir|-w option\n");
        print_usage(&program, opts);
        process::exit(1);
    }

    let site_db_conn_string = matches.opt_str("s");

    let search_maps_filename = matches.opt_str("m").unwrap();
    println!("Reading data files ...");

    let config_file_name = matches.opt_str("c").unwrap();
    let config = Config::read(&config_file_name);
    let api_data = APIData::new_from_file(&config, &search_maps_filename);
    let site_db = site_db_conn_string.map(|conn_str| SiteDB::new(&conn_str));
    let query_exec = QueryExec::new(api_data, site_db);
    let searcher = Search::new(&config);

    let web_root_dir = matches.opt_str("w").unwrap();
    let static_file_state = StaticFileState {
        web_root_dir: web_root_dir,
    };

    println!("Starting server ...");
    rocket::ignite()
        .mount("/", routes![get_index, get_misc, query_post,
                            get_gene, get_genotype, get_term, get_reference,
                            get_simple_gene, get_simple_reference, get_simple_term,
                            term_complete, ref_complete,
                            motif_search, ping])
        .register(catchers![not_found])
        .manage(Mutex::new(query_exec))
        .manage(Mutex::new(searcher))
        .manage(Mutex::new(static_file_state))
        .manage(config)
        .launch();
}
<|endoftext|>


<commit_before>extern crate multipart;

extern crate log;

use log::{LogRecord, LogMetadata, LogLevelFilter};

use multipart::server::Multipart;

use std::fs::File;
use std::env;

const LOG_LEVEL: LogLevelFilter = LogLevelFilter::Off;

struct SimpleLogger;

impl log::Log for SimpleLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        LOG_LEVEL.to_log_level()
            .map_or(false, |level| metadata.level() <= level)
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            println!("{} - {}", record.level(), record.args());
        }
    }
}

fn main() {
    log::set_logger(|max_log_level| {
        max_log_level.set(LOG_LEVEL);
        Box::new(SimpleLogger)
    });

    let mut args = env::args().skip(1);

    let boundary = args.next().expect("Boundary must be provided as the first argument");

    let file = args.next().expect("Filename must be provided as the second argument");

    let file = File::open(file).expect("Could not open file");

    let mut multipart = Multipart::with_body(file, boundary);

    while let Some(field) = multipart.read_entry().unwrap() {
        println!("Read field: {:?}", field.name);
    }

    println!("All entries read!");
}<commit_msg>Set `debug` log level in `test_multipart.rs`<commit_after>extern crate multipart;

extern crate log;

use log::{LogRecord, LogMetadata, LogLevelFilter};

use multipart::server::Multipart;

use std::fs::File;
use std::env;

const LOG_LEVEL: LogLevelFilter = LogLevelFilter::Debug;

struct SimpleLogger;

impl log::Log for SimpleLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        LOG_LEVEL.to_log_level()
            .map_or(false, |level| metadata.level() <= level)
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            println!("{} - {}", record.level(), record.args());
        }
    }
}

fn main() {
    log::set_logger(|max_log_level| {
        max_log_level.set(LOG_LEVEL);
        Box::new(SimpleLogger)
    });

    let mut args = env::args().skip(1);

    let boundary = args.next().expect("Boundary must be provided as the first argument");

    let file = args.next().expect("Filename must be provided as the second argument");

    let file = File::open(file).expect("Could not open file");

    let mut multipart = Multipart::with_body(file, boundary);

    while let Some(field) = multipart.read_entry().unwrap() {
        println!("Read field: {:?}", field.name);
    }

    println!("All entries read!");
}<|endoftext|>


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

#[cfg(feature = "builtins")]
use chrono::prelude::*;
#[cfg(feature = "builtins")]
use rand::Rng;
use serde_json::value::{from_value, to_value, Value};

use crate::errors::{Error, Result};

/// The global function type definition
pub trait Function: Sync + Send {
    /// The global function type definition
    fn call(&self, args: &HashMap<String, Value>) -> Result<Value>;
}

impl<F> Function for F
where
    F: Fn(&HashMap<String, Value>) -> Result<Value> + Sync + Send,
{
    fn call(&self, args: &HashMap<String, Value>) -> Result<Value> {
        self(args)
    }
}

pub fn range(args: &HashMap<String, Value>) -> Result<Value> {
    let start = match args.get("start") {
        Some(val) => match from_value::<usize>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `range` received start={} but `start` can only be a number",
                    val
                )));
            }
        },
        None => 0,
    };
    let step_by = match args.get("step_by") {
        Some(val) => match from_value::<usize>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `range` received step_by={} but `step` can only be a number",
                    val
                )));
            }
        },
        None => 1,
    };
    let end = match args.get("end") {
        Some(val) => match from_value::<usize>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `range` received end={} but `end` can only be a number",
                    val
                )));
            }
        },
        None => {
            return Err(Error::msg("Function `range` was called without a `end` argument"));
        }
    };

    if start > end {
        return Err(Error::msg(
            "Function `range` was called without a `start` argument greater than the `end` one",
        ));
    }

    let mut i = start;
    let mut res = vec![];
    while i < end {
        res.push(i);
        i += step_by;
    }
    Ok(to_value(res).unwrap())
}

#[cfg(feature = "builtins")]
pub fn now(args: &HashMap<String, Value>) -> Result<Value> {
    let utc = match args.get("utc") {
        Some(val) => match from_value::<bool>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `now` received utc={} but `utc` can only be a boolean",
                    val
                )));
            }
        },
        None => false,
    };
    let timestamp = match args.get("timestamp") {
        Some(val) => match from_value::<bool>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `now` received timestamp={} but `timestamp` can only be a boolean",
                    val
                )));
            }
        },
        None => false,
    };

    if utc {
        let datetime = Utc::now();
        if timestamp {
            return Ok(to_value(datetime.timestamp()).unwrap());
        }
        Ok(to_value(datetime.to_rfc3339()).unwrap())
    } else {
        let datetime = Local::now();
        if timestamp {
            return Ok(to_value(datetime.timestamp()).unwrap());
        }
        Ok(to_value(datetime.to_rfc3339()).unwrap())
    }
}

pub fn throw(args: &HashMap<String, Value>) -> Result<Value> {
    match args.get("message") {
        Some(val) => match from_value::<String>(val.clone()) {
            Ok(v) => Err(Error::msg(v)),
            Err(_) => Err(Error::msg(format!(
                "Function `throw` received message={} but `message` can only be a string",
                val
            ))),
        },
        None => Err(Error::msg("Function `throw` was called without a `message` argument")),
    }
}

#[cfg(feature = "builtins")]
pub fn get_random(args: &HashMap<String, Value>) -> Result<Value> {
    let start = match args.get("start") {
        Some(val) => match from_value::<i32>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `get_random` received start={} but `start` can only be a boolean",
                    val
                )));
            }
        },
        None => 0,
    };

    let end = match args.get("end") {
        Some(val) => match from_value::<i32>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `get_random` received end={} but `end` can only be a boolean",
                    val
                )));
            }
        },
        None => return Err(Error::msg("Function `get_random` didn't receive an `end` argument")),
    };
    let mut rng = rand::thread_rng();
    let res = rng.gen_range(start, end);

    Ok(Value::Number(res.into()))
}

pub fn get_env(args: &HashMap<String, Value>) -> Result<Value> {
    let name = match args.get("name") {
        Some(val) => match from_value::<String>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `get_env` received name={} but `name` can only be a string",
                    val
                )));
            }
        },
        None => return Err(Error::msg("Function `get_env` didn't receive a `name` argument")),
    };

    match std::env::var(&name).ok() {
        Some(res) => Ok(Value::String(res)),
        None => match args.get("default") {
            Some(default) => Ok(default.clone()),
            None => Err(Error::msg(format!("Environment variable `{}` not found", &name)))
        }
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    use serde_json::value::to_value;

    use super::*;

    #[test]
    fn range_default() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(5).unwrap());

        let res = range(&args).unwrap();
        assert_eq!(res, to_value(vec![0, 1, 2, 3, 4]).unwrap());
    }

    #[test]
    fn range_start() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(5).unwrap());
        args.insert("start".to_string(), to_value(1).unwrap());

        let res = range(&args).unwrap();
        assert_eq!(res, to_value(vec![1, 2, 3, 4]).unwrap());
    }

    #[test]
    fn range_start_greater_than_end() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(5).unwrap());
        args.insert("start".to_string(), to_value(6).unwrap());

        assert!(range(&args).is_err());
    }

    #[test]
    fn range_step_by() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(10).unwrap());
        args.insert("step_by".to_string(), to_value(2).unwrap());

        let res = range(&args).unwrap();
        assert_eq!(res, to_value(vec![0, 2, 4, 6, 8]).unwrap());
    }

    #[cfg(feature = "builtins")]
    #[test]
    fn now_default() {
        let args = HashMap::new();

        let res = now(&args).unwrap();
        assert!(res.is_string());
        assert!(res.as_str().unwrap().contains("T"));
    }

    #[cfg(feature = "builtins")]
    #[test]
    fn now_datetime_utc() {
        let mut args = HashMap::new();
        args.insert("utc".to_string(), to_value(true).unwrap());

        let res = now(&args).unwrap();
        assert!(res.is_string());
        let val = res.as_str().unwrap();
        println!("{}", val);
        assert!(val.contains("T"));
        assert!(val.contains("+00:00"));
    }

    #[cfg(feature = "builtins")]
    #[test]
    fn now_timestamp() {
        let mut args = HashMap::new();
        args.insert("timestamp".to_string(), to_value(true).unwrap());

        let res = now(&args).unwrap();
        assert!(res.is_number());
    }

    #[test]
    fn throw_errors_with_message() {
        let mut args = HashMap::new();
        args.insert("message".to_string(), to_value("Hello").unwrap());

        let res = throw(&args);
        assert!(res.is_err());
        let err = res.unwrap_err();
        assert_eq!(err.to_string(), "Hello");
    }

    #[cfg(feature = "builtins")]
    #[test]
    fn get_random_no_start() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(10).unwrap());
        let res = get_random(&args).unwrap();
        println!("{}", res);
        assert!(res.is_number());
        assert!(res.as_i64().unwrap() >= 0);
        assert!(res.as_i64().unwrap() < 10);
    }

    #[cfg(feature = "builtins")]
    #[test]
    fn get_random_with_start() {
        let mut args = HashMap::new();
        args.insert("start".to_string(), to_value(5).unwrap());
        args.insert("end".to_string(), to_value(10).unwrap());
        let res = get_random(&args).unwrap();
        println!("{}", res);
        assert!(res.is_number());
        assert!(res.as_i64().unwrap() >= 5);
        assert!(res.as_i64().unwrap() < 10);
    }

    #[test]
    fn get_env_existing() {
        std::env::set_var("TERA_TEST", "true");
        let mut args = HashMap::new();
        args.insert("name".to_string(), to_value("TERA_TEST").unwrap());
        let res = get_env(&args).unwrap();
        assert!(res.is_string());
        assert_eq!(res.as_str().unwrap(), "true");
        std::env::remove_var("TERA_TEST");
    }

    #[test]
    fn get_env_non_existing_no_default() {
        let mut args = HashMap::new();
        args.insert("name".to_string(), to_value("TERA_TEST").unwrap());
        let res = get_env(&args);
        assert!(res.is_err());
    }

    #[test]
    fn get_env_non_existing_with_default() {
        let mut args = HashMap::new();
        args.insert("name".to_string(), to_value("TERA_TEST").unwrap());
        args.insert("default".to_string(), to_value("false").unwrap());
        let res = get_env(&args).unwrap();
        assert!(res.is_string());
        assert_eq!(res.as_str().unwrap(), "false");
    }
}
<commit_msg>Change var name in tests<commit_after>use std::collections::HashMap;

#[cfg(feature = "builtins")]
use chrono::prelude::*;
#[cfg(feature = "builtins")]
use rand::Rng;
use serde_json::value::{from_value, to_value, Value};

use crate::errors::{Error, Result};

/// The global function type definition
pub trait Function: Sync + Send {
    /// The global function type definition
    fn call(&self, args: &HashMap<String, Value>) -> Result<Value>;
}

impl<F> Function for F
where
    F: Fn(&HashMap<String, Value>) -> Result<Value> + Sync + Send,
{
    fn call(&self, args: &HashMap<String, Value>) -> Result<Value> {
        self(args)
    }
}

pub fn range(args: &HashMap<String, Value>) -> Result<Value> {
    let start = match args.get("start") {
        Some(val) => match from_value::<usize>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `range` received start={} but `start` can only be a number",
                    val
                )));
            }
        },
        None => 0,
    };
    let step_by = match args.get("step_by") {
        Some(val) => match from_value::<usize>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `range` received step_by={} but `step` can only be a number",
                    val
                )));
            }
        },
        None => 1,
    };
    let end = match args.get("end") {
        Some(val) => match from_value::<usize>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `range` received end={} but `end` can only be a number",
                    val
                )));
            }
        },
        None => {
            return Err(Error::msg("Function `range` was called without a `end` argument"));
        }
    };

    if start > end {
        return Err(Error::msg(
            "Function `range` was called without a `start` argument greater than the `end` one",
        ));
    }

    let mut i = start;
    let mut res = vec![];
    while i < end {
        res.push(i);
        i += step_by;
    }
    Ok(to_value(res).unwrap())
}

#[cfg(feature = "builtins")]
pub fn now(args: &HashMap<String, Value>) -> Result<Value> {
    let utc = match args.get("utc") {
        Some(val) => match from_value::<bool>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `now` received utc={} but `utc` can only be a boolean",
                    val
                )));
            }
        },
        None => false,
    };
    let timestamp = match args.get("timestamp") {
        Some(val) => match from_value::<bool>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `now` received timestamp={} but `timestamp` can only be a boolean",
                    val
                )));
            }
        },
        None => false,
    };

    if utc {
        let datetime = Utc::now();
        if timestamp {
            return Ok(to_value(datetime.timestamp()).unwrap());
        }
        Ok(to_value(datetime.to_rfc3339()).unwrap())
    } else {
        let datetime = Local::now();
        if timestamp {
            return Ok(to_value(datetime.timestamp()).unwrap());
        }
        Ok(to_value(datetime.to_rfc3339()).unwrap())
    }
}

pub fn throw(args: &HashMap<String, Value>) -> Result<Value> {
    match args.get("message") {
        Some(val) => match from_value::<String>(val.clone()) {
            Ok(v) => Err(Error::msg(v)),
            Err(_) => Err(Error::msg(format!(
                "Function `throw` received message={} but `message` can only be a string",
                val
            ))),
        },
        None => Err(Error::msg("Function `throw` was called without a `message` argument")),
    }
}

#[cfg(feature = "builtins")]
pub fn get_random(args: &HashMap<String, Value>) -> Result<Value> {
    let start = match args.get("start") {
        Some(val) => match from_value::<i32>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `get_random` received start={} but `start` can only be a boolean",
                    val
                )));
            }
        },
        None => 0,
    };

    let end = match args.get("end") {
        Some(val) => match from_value::<i32>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `get_random` received end={} but `end` can only be a boolean",
                    val
                )));
            }
        },
        None => return Err(Error::msg("Function `get_random` didn't receive an `end` argument")),
    };
    let mut rng = rand::thread_rng();
    let res = rng.gen_range(start, end);

    Ok(Value::Number(res.into()))
}

pub fn get_env(args: &HashMap<String, Value>) -> Result<Value> {
    let name = match args.get("name") {
        Some(val) => match from_value::<String>(val.clone()) {
            Ok(v) => v,
            Err(_) => {
                return Err(Error::msg(format!(
                    "Function `get_env` received name={} but `name` can only be a string",
                    val
                )));
            }
        },
        None => return Err(Error::msg("Function `get_env` didn't receive a `name` argument")),
    };

    match std::env::var(&name).ok() {
        Some(res) => Ok(Value::String(res)),
        None => match args.get("default") {
            Some(default) => Ok(default.clone()),
            None => Err(Error::msg(format!("Environment variable `{}` not found", &name)))
        }
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    use serde_json::value::to_value;

    use super::*;

    #[test]
    fn range_default() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(5).unwrap());

        let res = range(&args).unwrap();
        assert_eq!(res, to_value(vec![0, 1, 2, 3, 4]).unwrap());
    }

    #[test]
    fn range_start() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(5).unwrap());
        args.insert("start".to_string(), to_value(1).unwrap());

        let res = range(&args).unwrap();
        assert_eq!(res, to_value(vec![1, 2, 3, 4]).unwrap());
    }

    #[test]
    fn range_start_greater_than_end() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(5).unwrap());
        args.insert("start".to_string(), to_value(6).unwrap());

        assert!(range(&args).is_err());
    }

    #[test]
    fn range_step_by() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(10).unwrap());
        args.insert("step_by".to_string(), to_value(2).unwrap());

        let res = range(&args).unwrap();
        assert_eq!(res, to_value(vec![0, 2, 4, 6, 8]).unwrap());
    }

    #[cfg(feature = "builtins")]
    #[test]
    fn now_default() {
        let args = HashMap::new();

        let res = now(&args).unwrap();
        assert!(res.is_string());
        assert!(res.as_str().unwrap().contains("T"));
    }

    #[cfg(feature = "builtins")]
    #[test]
    fn now_datetime_utc() {
        let mut args = HashMap::new();
        args.insert("utc".to_string(), to_value(true).unwrap());

        let res = now(&args).unwrap();
        assert!(res.is_string());
        let val = res.as_str().unwrap();
        println!("{}", val);
        assert!(val.contains("T"));
        assert!(val.contains("+00:00"));
    }

    #[cfg(feature = "builtins")]
    #[test]
    fn now_timestamp() {
        let mut args = HashMap::new();
        args.insert("timestamp".to_string(), to_value(true).unwrap());

        let res = now(&args).unwrap();
        assert!(res.is_number());
    }

    #[test]
    fn throw_errors_with_message() {
        let mut args = HashMap::new();
        args.insert("message".to_string(), to_value("Hello").unwrap());

        let res = throw(&args);
        assert!(res.is_err());
        let err = res.unwrap_err();
        assert_eq!(err.to_string(), "Hello");
    }

    #[cfg(feature = "builtins")]
    #[test]
    fn get_random_no_start() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(10).unwrap());
        let res = get_random(&args).unwrap();
        println!("{}", res);
        assert!(res.is_number());
        assert!(res.as_i64().unwrap() >= 0);
        assert!(res.as_i64().unwrap() < 10);
    }

    #[cfg(feature = "builtins")]
    #[test]
    fn get_random_with_start() {
        let mut args = HashMap::new();
        args.insert("start".to_string(), to_value(5).unwrap());
        args.insert("end".to_string(), to_value(10).unwrap());
        let res = get_random(&args).unwrap();
        println!("{}", res);
        assert!(res.is_number());
        assert!(res.as_i64().unwrap() >= 5);
        assert!(res.as_i64().unwrap() < 10);
    }

    #[test]
    fn get_env_existing() {
        std::env::set_var("TERA_TEST", "true");
        let mut args = HashMap::new();
        args.insert("name".to_string(), to_value("TERA_TEST").unwrap());
        let res = get_env(&args).unwrap();
        assert!(res.is_string());
        assert_eq!(res.as_str().unwrap(), "true");
        std::env::remove_var("TERA_TEST");
    }

    #[test]
    fn get_env_non_existing_no_default() {
        let mut args = HashMap::new();
        args.insert("name".to_string(), to_value("UNKNOWN_VAR").unwrap());
        let res = get_env(&args);
        assert!(res.is_err());
    }

    #[test]
    fn get_env_non_existing_with_default() {
        let mut args = HashMap::new();
        args.insert("name".to_string(), to_value("UNKNOWN_VAR").unwrap());
        args.insert("default".to_string(), to_value("false").unwrap());
        let res = get_env(&args).unwrap();
        assert!(res.is_string());
        assert_eq!(res.as_str().unwrap(), "false");
    }
}
<|endoftext|>


<commit_before>use super::class_stream::ClassStream;
use super::class_stream::ReadChunks;
use super::class_stream::ReadMapper;

/// Represents a reference to an entry in the class file's constant pool
pub struct ConstantReference {
    pub constant_idx: u16
}

impl ConstantReference {
    ///
    /// Create a new constant reference pointing to the idx-th constant in the pool.
    /// Note that the value of 0 is deemed invalid by the specification and is used to indicate
    /// an unknown reference here.
    pub fn new(idx: u16) -> ConstantReference {
        ConstantReference { constant_idx: idx }
    }

    /// Return a new constant reference pointing to an unknown constant in the pool
    pub fn unknown() -> ConstantReference {
        ConstantReference { constant_idx: 0 }
    }
}

pub enum ConstantType {
    Utf8 { length: u16, bytes: Vec<u8> }, // 1,
    Integer { bytes: u32 }, // 3,
    Float { bytes: u32 }, // 4,
    Long { high_bytes: u32, low_bytes: u32 }, // 5,
    Double { high_bytes: u32, low_bytes: u32 }, // 6,
    Class { name_index: u16 }, // 7
    String { string_index: u16 }, // 8,
    FieldRef { class_index: u16, name_and_type_index: u16 }, // 9
    MethodRef { class_index: u16, name_and_type_index: u16 }, // 10,
    InterfaceMethodRef { class_index: u16, name_and_type_index: u16 }, // 11
    NameAndType { name_index: u16, descriptor_index: u16 }, // 12,
    MethodHandle { reference_kind: u8, reference_index: u16 }, // 15,
    MethodType { descriptor_index: u16 }, // 16,
    InvokeDynamic { bootstrap_method_attr_index: u16, name_and_type_index: u16 }, // 18,
    Placeholder,
    Unknown
}

impl ConstantType {

    pub fn is_long_entry(&self) -> bool {
        match *self {
            ConstantType::Long { high_bytes: _, low_bytes: _ } => true,
            ConstantType::Double { high_bytes: _, low_bytes: _ } => true,
            _ => false
        }
    }

    pub fn parse(stream: &mut ClassStream) -> Option<ConstantType> {
        let opt_tag = stream.read_u8();

        match opt_tag {
            Some(tag) => {
                match tag {
                    3 => stream.read_map_len(4, |bs| ConstantType::Integer { bytes: bs.get_u32() }),
                    4 => stream.read_map_len(4, |bs| ConstantType::Float { bytes: bs.get_u32() }),
                    5 => stream.read_map_len(8, |bs| ConstantType::Long { high_bytes: bs.get_u32(), low_bytes: bs.get_u32() }),
                    6 => stream.read_map_len(8, |bs| ConstantType::Double { high_bytes: bs.get_u32(), low_bytes: bs.get_u32() }),
                    7 => stream.read_map_len(2, |bs| ConstantType::Class { name_index: bs.get_u16() }),
                    8 => stream.read_map_len(2, |bs| ConstantType::String { string_index: bs.get_u16() }),
                    9 => stream.read_map_len(4, |bs| ConstantType::FieldRef { class_index: bs.get_u16(), name_and_type_index: bs.get_u16() }),
                    10 => stream.read_map_len(4, |bs| ConstantType::MethodRef { class_index: bs.get_u16(), name_and_type_index: bs.get_u16() }),
                    11 => stream.read_map_len(4, |bs| ConstantType::InterfaceMethodRef { class_index: bs.get_u16(), name_and_type_index: bs.get_u16() }),
                    12 => stream.read_map_len(4, |bs| ConstantType::NameAndType { name_index: bs.get_u16(), descriptor_index: bs.get_u16() }),
                    15 => stream.read_map_len(3, |bs| ConstantType::MethodHandle { reference_kind: bs.get_u8(), reference_index: bs.get_u16() }),
                    16 => stream.read_map_len(2, |bs| ConstantType::MethodType { descriptor_index: bs.get_u16() }),
                    18 => stream.read_map_len(4, |bs| ConstantType::InvokeDynamic { bootstrap_method_attr_index: bs.get_u16(), name_and_type_index: bs.get_u16() }),
                    1 => stream.read_map_len(2, |bs| {
                        let bytes_count = bs.get_u16();

                        match bs.read_n(bytes_count as usize) {
                            Some(bytes) => ConstantType::Utf8 { length: bytes_count, bytes: bytes },
                            None => ConstantType::Unknown
                        }
                    }),
                    _ => Some(ConstantType::Unknown)
                }
            },
            None => None
        }
    }
}

pub enum ClassAccessFlag {
    Public,
    Final,
    Super,
    Interface,
    Abstract,
    Synthetic,
    Annotation,
    Enum
}

pub enum FieldAccessFlag {
    Public,
    Private,
    Protected,
    Static,
    Final,
    Volatile,
    Transient,
    Synthetic,
    Enum
}

pub enum MethodAccessFlag {
    Public,
    Private,
    Protected,
    Static,
    Final,
    Synchronized,
    Bridge,
    Varargs,
    Native,
    Abstract,
    Strict,
    Synthetic,
}

pub struct AccessFlag {
    raw_flag: u16
}

impl AccessFlag {
    pub fn new() -> AccessFlag {
        AccessFlag::of(0)
    }

    pub fn of(value: u16) -> AccessFlag {
        AccessFlag { raw_flag: value }
    }

    pub fn get<T: FlagValue>(&self, other: T) -> bool {
        self.raw_flag & other.val() > 0
    }

    pub fn set<T: FlagValue>(&mut self, flag: T) {
        self.raw_flag |= flag.val();
    }

    pub fn clear<T: FlagValue>(&mut self, flag: T) {
        self.raw_flag &= 0xFFFF ^ flag.val();
    }
}

pub trait FlagValue {
    fn val(&self) -> u16;
}

impl FlagValue for ClassAccessFlag {
    fn val(&self) -> u16 {
        match self {
            &ClassAccessFlag::Public => 0x0001,
            &ClassAccessFlag::Final => 0x0010,
            &ClassAccessFlag::Super => 0x0020,
            &ClassAccessFlag::Interface => 0x0200,
            &ClassAccessFlag::Abstract => 0x0400,
            &ClassAccessFlag::Synthetic => 0x1000,
            &ClassAccessFlag::Annotation => 0x2000,
            &ClassAccessFlag::Enum => 0x4000
        }
    }
}

impl FlagValue for FieldAccessFlag {
    fn val(&self) -> u16 {
        match self {
            &FieldAccessFlag::Public => 0x0001,
            &FieldAccessFlag::Private => 0x0002,
            &FieldAccessFlag::Protected => 0x0004,
            &FieldAccessFlag::Static => 0x0008,
            &FieldAccessFlag::Final => 0x0010,
            &FieldAccessFlag::Volatile => 0x0040,
            &FieldAccessFlag::Transient => 0x0080,
            &FieldAccessFlag::Synthetic => 0x1000,
            &FieldAccessFlag::Enum => 0x4000
        }
    }
}

impl FlagValue for MethodAccessFlag {
    fn val(&self) -> u16 {
        match self {
            &MethodAccessFlag::Public => 0x0001,
            &MethodAccessFlag::Private => 0x0002,
            &MethodAccessFlag::Protected => 0x0004,
            &MethodAccessFlag::Static => 0x0008,
            &MethodAccessFlag::Final => 0x0010,
            &MethodAccessFlag::Synchronized => 0x0020,
            &MethodAccessFlag::Bridge => 0x0040,
            &MethodAccessFlag::Varargs => 0x0080,
            &MethodAccessFlag::Native => 0x0100,
            &MethodAccessFlag::Abstract => 0x0400,
            &MethodAccessFlag::Strict => 0x0800,
            &MethodAccessFlag::Synthetic => 0x1000
        }
    }
}

pub struct Attribute {
    /// attribute_name_index must be a CONSTANT_Utf8_info structure (§4.4.7) representing the name of the attribute.
    pub attribute_name_index: ConstantReference,
    pub info: Vec<u8>
}

impl Attribute {
    pub fn from_stream(stream: &mut ClassStream) -> Option<Attribute> {
        match stream.available() >= 6 {
            true => {

            },
            _ => None
        }
    }
}

pub enum AttributeType {
    // JVM attributes
    ConstantValue,
    Code,
    StackMapTable,
    Exceptions,
    BootstrapMethods,
    // Java SE Attributes
    InnerClasses,
    EnclosingMethod,
    Synthetic,
    Signature,
    RuntimeVisibleAnnotations,
    RuntimeInvisibleAnnotations,
    RuntimeVisibleParameterAnnotations,
    RuntimeInvisibleParameterAnnotations,
    RuntimeVisibleTypeAnnotations,
    RuntimeInvisibleTypeAnnotations,
    AnnotationDefault,
    MethodParameters,
    // Extra attributes
    SourceFile,
    SourceDebugExtension,
    LineNumberTable,
    LocalVariableTable,
    LocalVariableTypeTable,
    Deprecated,
    Unknown(String)
}

impl AttributeType {
    pub fn to_string(&self) -> String {
        match self {
            &AttributeType::ConstantValue => "ConstantValue",
            &AttributeType::Code => "Code",
            &AttributeType::StackMapTable => "StackMapTable",
            &AttributeType::Exceptions => "Exceptions",
            &AttributeType::BootstrapMethods => "BootstrapMethods",
            &AttributeType::InnerClasses => "InnerClasses",
            &AttributeType::EnclosingMethod => "EnclosingMethod",
            &AttributeType::Synthetic => "Synthetic",
            &AttributeType::Signature => "Signature",
            &AttributeType::RuntimeVisibleAnnotations => "RuntimeVisibleAnnotations",
            &AttributeType::RuntimeInvisibleAnnotations => "RuntimeInvisibleAnnotations",
            &AttributeType::RuntimeVisibleParameterAnnotations => "RuntimeVisibleParameterAnnotations",
            &AttributeType::RuntimeInvisibleParameterAnnotations => "RuntimeInvisibleParameterAnnotations",
            &AttributeType::RuntimeVisibleTypeAnnotations => "RuntimeVisibleTypeAnnotations",
            &AttributeType::RuntimeInvisibleTypeAnnotations => "RuntimeInvisibleTypeAnnotations",
            &AttributeType::AnnotationDefault => "AnnotationDefault",
            &AttributeType::MethodParameters => "MethodParameters",
            &AttributeType::SourceFile => "SourceFile",
            &AttributeType::SourceDebugExtension => "SourceDebugExtension",
            &AttributeType::LineNumberTable => "LineNumberTable",
            &AttributeType::LocalVariableTable => "LocalVariableTable",
            &AttributeType::LocalVariableTypeTable => "LocalVariableTypeTable",
            &AttributeType::Deprecated => "Deprecated",
            &AttributeType::Unknown(_) => "Unknown",
        }.to_string()
    }

    pub fn from_string(string: &str) -> AttributeType {
        match string {
            "ConstantValue" => AttributeType::ConstantValue,
            "Code" => AttributeType::Code,
            "StackMapTable" => AttributeType::StackMapTable,
            "Exceptions" => AttributeType::Exceptions,
            "BootstrapMethods" => AttributeType::BootstrapMethods,
            "InnerClasses" => AttributeType::InnerClasses,
            "EnclosingMethod" => AttributeType::EnclosingMethod,
            "Synthetic" => AttributeType::Synthetic,
            "Signature" => AttributeType::Signature,
            "RuntimeVisibleAnnotations" => AttributeType::RuntimeVisibleAnnotations,
            "RuntimeInvisibleAnnotations" => AttributeType::RuntimeInvisibleAnnotations,
            "RuntimeVisibleParameterAnnotations" => AttributeType::RuntimeVisibleParameterAnnotations,
            "RuntimeInvisibleParameterAnnotations" => AttributeType::RuntimeInvisibleParameterAnnotations,
            "RuntimeVisibleTypeAnnotations" => AttributeType::RuntimeVisibleTypeAnnotations,
            "RuntimeInvisibleTypeAnnotations" => AttributeType::RuntimeInvisibleTypeAnnotations,
            "AnnotationDefault" => AttributeType::AnnotationDefault,
            "MethodParameters" => AttributeType::MethodParameters,
            "SourceFile" => AttributeType::SourceFile,
            "SourceDebugExtension" => AttributeType::SourceDebugExtension,
            "LineNumberTable" => AttributeType::LineNumberTable,
            "LocalVariableTable" => AttributeType::LocalVariableTable,
            "LocalVariableTypeTable" => AttributeType::LocalVariableTypeTable,
            "Deprecated" => AttributeType::Deprecated,
            s@_ => AttributeType::Unknown(s.to_string())
        }
    }
}
<commit_msg>fixed build a bit<commit_after>use super::class_stream::ClassStream;
use super::class_stream::ReadChunks;
use super::class_stream::ReadMapper;

/// Represents a reference to an entry in the class file's constant pool
pub struct ConstantReference {
    pub constant_idx: u16
}

impl ConstantReference {
    ///
    /// Create a new constant reference pointing to the idx-th constant in the pool.
    /// Note that the value of 0 is deemed invalid by the specification and is used to indicate
    /// an unknown reference here.
    pub fn new(idx: u16) -> ConstantReference {
        ConstantReference { constant_idx: idx }
    }

    /// Return a new constant reference pointing to an unknown constant in the pool
    pub fn unknown() -> ConstantReference {
        ConstantReference { constant_idx: 0 }
    }
}

pub enum ConstantType {
    Utf8 { length: u16, bytes: Vec<u8> }, // 1,
    Integer { bytes: u32 }, // 3,
    Float { bytes: u32 }, // 4,
    Long { high_bytes: u32, low_bytes: u32 }, // 5,
    Double { high_bytes: u32, low_bytes: u32 }, // 6,
    Class { name_index: u16 }, // 7
    String { string_index: u16 }, // 8,
    FieldRef { class_index: u16, name_and_type_index: u16 }, // 9
    MethodRef { class_index: u16, name_and_type_index: u16 }, // 10,
    InterfaceMethodRef { class_index: u16, name_and_type_index: u16 }, // 11
    NameAndType { name_index: u16, descriptor_index: u16 }, // 12,
    MethodHandle { reference_kind: u8, reference_index: u16 }, // 15,
    MethodType { descriptor_index: u16 }, // 16,
    InvokeDynamic { bootstrap_method_attr_index: u16, name_and_type_index: u16 }, // 18,
    Placeholder,
    Unknown
}

impl ConstantType {

    pub fn is_long_entry(&self) -> bool {
        match *self {
            ConstantType::Long { high_bytes: _, low_bytes: _ } => true,
            ConstantType::Double { high_bytes: _, low_bytes: _ } => true,
            _ => false
        }
    }

    pub fn parse(stream: &mut ClassStream) -> Option<ConstantType> {
        let opt_tag = stream.read_u8();

        match opt_tag {
            Some(tag) => {
                match tag {
                    3 => stream.read_map_len(4, |bs| ConstantType::Integer { bytes: bs.get_u32() }),
                    4 => stream.read_map_len(4, |bs| ConstantType::Float { bytes: bs.get_u32() }),
                    5 => stream.read_map_len(8, |bs| ConstantType::Long { high_bytes: bs.get_u32(), low_bytes: bs.get_u32() }),
                    6 => stream.read_map_len(8, |bs| ConstantType::Double { high_bytes: bs.get_u32(), low_bytes: bs.get_u32() }),
                    7 => stream.read_map_len(2, |bs| ConstantType::Class { name_index: bs.get_u16() }),
                    8 => stream.read_map_len(2, |bs| ConstantType::String { string_index: bs.get_u16() }),
                    9 => stream.read_map_len(4, |bs| ConstantType::FieldRef { class_index: bs.get_u16(), name_and_type_index: bs.get_u16() }),
                    10 => stream.read_map_len(4, |bs| ConstantType::MethodRef { class_index: bs.get_u16(), name_and_type_index: bs.get_u16() }),
                    11 => stream.read_map_len(4, |bs| ConstantType::InterfaceMethodRef { class_index: bs.get_u16(), name_and_type_index: bs.get_u16() }),
                    12 => stream.read_map_len(4, |bs| ConstantType::NameAndType { name_index: bs.get_u16(), descriptor_index: bs.get_u16() }),
                    15 => stream.read_map_len(3, |bs| ConstantType::MethodHandle { reference_kind: bs.get_u8(), reference_index: bs.get_u16() }),
                    16 => stream.read_map_len(2, |bs| ConstantType::MethodType { descriptor_index: bs.get_u16() }),
                    18 => stream.read_map_len(4, |bs| ConstantType::InvokeDynamic { bootstrap_method_attr_index: bs.get_u16(), name_and_type_index: bs.get_u16() }),
                    1 => stream.read_map_len(2, |bs| {
                        let bytes_count = bs.get_u16();

                        match bs.read_n(bytes_count as usize) {
                            Some(bytes) => ConstantType::Utf8 { length: bytes_count, bytes: bytes },
                            None => ConstantType::Unknown
                        }
                    }),
                    _ => Some(ConstantType::Unknown)
                }
            },
            None => None
        }
    }
}

pub enum ClassAccessFlag {
    Public,
    Final,
    Super,
    Interface,
    Abstract,
    Synthetic,
    Annotation,
    Enum
}

pub enum FieldAccessFlag {
    Public,
    Private,
    Protected,
    Static,
    Final,
    Volatile,
    Transient,
    Synthetic,
    Enum
}

pub enum MethodAccessFlag {
    Public,
    Private,
    Protected,
    Static,
    Final,
    Synchronized,
    Bridge,
    Varargs,
    Native,
    Abstract,
    Strict,
    Synthetic,
}

pub struct AccessFlag {
    raw_flag: u16
}

impl AccessFlag {
    pub fn new() -> AccessFlag {
        AccessFlag::of(0)
    }

    pub fn of(value: u16) -> AccessFlag {
        AccessFlag { raw_flag: value }
    }

    pub fn get<T: FlagValue>(&self, other: T) -> bool {
        self.raw_flag & other.val() > 0
    }

    pub fn set<T: FlagValue>(&mut self, flag: T) {
        self.raw_flag |= flag.val();
    }

    pub fn clear<T: FlagValue>(&mut self, flag: T) {
        self.raw_flag &= 0xFFFF ^ flag.val();
    }
}

pub trait FlagValue {
    fn val(&self) -> u16;
}

impl FlagValue for ClassAccessFlag {
    fn val(&self) -> u16 {
        match self {
            &ClassAccessFlag::Public => 0x0001,
            &ClassAccessFlag::Final => 0x0010,
            &ClassAccessFlag::Super => 0x0020,
            &ClassAccessFlag::Interface => 0x0200,
            &ClassAccessFlag::Abstract => 0x0400,
            &ClassAccessFlag::Synthetic => 0x1000,
            &ClassAccessFlag::Annotation => 0x2000,
            &ClassAccessFlag::Enum => 0x4000
        }
    }
}

impl FlagValue for FieldAccessFlag {
    fn val(&self) -> u16 {
        match self {
            &FieldAccessFlag::Public => 0x0001,
            &FieldAccessFlag::Private => 0x0002,
            &FieldAccessFlag::Protected => 0x0004,
            &FieldAccessFlag::Static => 0x0008,
            &FieldAccessFlag::Final => 0x0010,
            &FieldAccessFlag::Volatile => 0x0040,
            &FieldAccessFlag::Transient => 0x0080,
            &FieldAccessFlag::Synthetic => 0x1000,
            &FieldAccessFlag::Enum => 0x4000
        }
    }
}

impl FlagValue for MethodAccessFlag {
    fn val(&self) -> u16 {
        match self {
            &MethodAccessFlag::Public => 0x0001,
            &MethodAccessFlag::Private => 0x0002,
            &MethodAccessFlag::Protected => 0x0004,
            &MethodAccessFlag::Static => 0x0008,
            &MethodAccessFlag::Final => 0x0010,
            &MethodAccessFlag::Synchronized => 0x0020,
            &MethodAccessFlag::Bridge => 0x0040,
            &MethodAccessFlag::Varargs => 0x0080,
            &MethodAccessFlag::Native => 0x0100,
            &MethodAccessFlag::Abstract => 0x0400,
            &MethodAccessFlag::Strict => 0x0800,
            &MethodAccessFlag::Synthetic => 0x1000
        }
    }
}

pub struct Attribute {
    /// attribute_name_index must be a CONSTANT_Utf8_info structure (§4.4.7) representing the name of the attribute.
    pub attribute_name_index: ConstantReference,
    pub info: Vec<u8>
}

impl Attribute {
    pub fn from_stream(stream: &mut ClassStream) -> Option<Attribute> {
        match stream.available() >= 6 {
            true => {
                let name_index = ConstantReference::new(stream.get_u16());
                let att_length = stream.get_u32() as usize;

                match stream.read_n(att_length) {
                    Some(bytes) => Some(Attribute {
                        attribute_name_index: name_index,
                        info: bytes
                    }),
                    _ => None
                }
            },
            _ => None
        }
    }
}

pub enum AttributeType {
    // JVM attributes
    ConstantValue,
    Code,
    StackMapTable,
    Exceptions,
    BootstrapMethods,
    // Java SE Attributes
    InnerClasses,
    EnclosingMethod,
    Synthetic,
    Signature,
    RuntimeVisibleAnnotations,
    RuntimeInvisibleAnnotations,
    RuntimeVisibleParameterAnnotations,
    RuntimeInvisibleParameterAnnotations,
    RuntimeVisibleTypeAnnotations,
    RuntimeInvisibleTypeAnnotations,
    AnnotationDefault,
    MethodParameters,
    // Extra attributes
    SourceFile,
    SourceDebugExtension,
    LineNumberTable,
    LocalVariableTable,
    LocalVariableTypeTable,
    Deprecated,
    Unknown(String)
}

impl AttributeType {
    pub fn to_string(&self) -> String {
        match self {
            &AttributeType::ConstantValue => "ConstantValue",
            &AttributeType::Code => "Code",
            &AttributeType::StackMapTable => "StackMapTable",
            &AttributeType::Exceptions => "Exceptions",
            &AttributeType::BootstrapMethods => "BootstrapMethods",
            &AttributeType::InnerClasses => "InnerClasses",
            &AttributeType::EnclosingMethod => "EnclosingMethod",
            &AttributeType::Synthetic => "Synthetic",
            &AttributeType::Signature => "Signature",
            &AttributeType::RuntimeVisibleAnnotations => "RuntimeVisibleAnnotations",
            &AttributeType::RuntimeInvisibleAnnotations => "RuntimeInvisibleAnnotations",
            &AttributeType::RuntimeVisibleParameterAnnotations => "RuntimeVisibleParameterAnnotations",
            &AttributeType::RuntimeInvisibleParameterAnnotations => "RuntimeInvisibleParameterAnnotations",
            &AttributeType::RuntimeVisibleTypeAnnotations => "RuntimeVisibleTypeAnnotations",
            &AttributeType::RuntimeInvisibleTypeAnnotations => "RuntimeInvisibleTypeAnnotations",
            &AttributeType::AnnotationDefault => "AnnotationDefault",
            &AttributeType::MethodParameters => "MethodParameters",
            &AttributeType::SourceFile => "SourceFile",
            &AttributeType::SourceDebugExtension => "SourceDebugExtension",
            &AttributeType::LineNumberTable => "LineNumberTable",
            &AttributeType::LocalVariableTable => "LocalVariableTable",
            &AttributeType::LocalVariableTypeTable => "LocalVariableTypeTable",
            &AttributeType::Deprecated => "Deprecated",
            &AttributeType::Unknown(_) => "Unknown",
        }.to_string()
    }

    pub fn from_string(string: &str) -> AttributeType {
        match string {
            "ConstantValue" => AttributeType::ConstantValue,
            "Code" => AttributeType::Code,
            "StackMapTable" => AttributeType::StackMapTable,
            "Exceptions" => AttributeType::Exceptions,
            "BootstrapMethods" => AttributeType::BootstrapMethods,
            "InnerClasses" => AttributeType::InnerClasses,
            "EnclosingMethod" => AttributeType::EnclosingMethod,
            "Synthetic" => AttributeType::Synthetic,
            "Signature" => AttributeType::Signature,
            "RuntimeVisibleAnnotations" => AttributeType::RuntimeVisibleAnnotations,
            "RuntimeInvisibleAnnotations" => AttributeType::RuntimeInvisibleAnnotations,
            "RuntimeVisibleParameterAnnotations" => AttributeType::RuntimeVisibleParameterAnnotations,
            "RuntimeInvisibleParameterAnnotations" => AttributeType::RuntimeInvisibleParameterAnnotations,
            "RuntimeVisibleTypeAnnotations" => AttributeType::RuntimeVisibleTypeAnnotations,
            "RuntimeInvisibleTypeAnnotations" => AttributeType::RuntimeInvisibleTypeAnnotations,
            "AnnotationDefault" => AttributeType::AnnotationDefault,
            "MethodParameters" => AttributeType::MethodParameters,
            "SourceFile" => AttributeType::SourceFile,
            "SourceDebugExtension" => AttributeType::SourceDebugExtension,
            "LineNumberTable" => AttributeType::LineNumberTable,
            "LocalVariableTable" => AttributeType::LocalVariableTable,
            "LocalVariableTypeTable" => AttributeType::LocalVariableTypeTable,
            "Deprecated" => AttributeType::Deprecated,
            s@_ => AttributeType::Unknown(s.to_string())
        }
    }
}
<|endoftext|>