<commit_before>//! EGL context related structs
use std::os::raw::c_int;
use std::ptr;
use std::sync::atomic::Ordering;

use super::{ffi, wrap_egl_call, Error, MakeCurrentError};
use crate::backend::egl::display::{EGLDisplay, PixelFormat};
use crate::backend::egl::native::EGLNativeSurface;
use crate::backend::egl::EGLSurface;

/// EGL context for rendering
#[derive(Debug)]
pub struct EGLContext {
    context: ffi::egl::types::EGLContext,
    pub(crate) display: EGLDisplay,
    config_id: ffi::egl::types::EGLConfig,
    pixel_format: Option<PixelFormat>,
}
// EGLContexts can be moved between threads safely
unsafe impl Send for EGLContext {}
unsafe impl Sync for EGLContext {}

impl EGLContext {
    pub fn new<L>(
        display: &EGLDisplay,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, None, log)
    }

    /// Create a new [`EGLContext`] from a given [`NativeDisplay`](native::NativeDisplay)
    pub fn new_with_config<L>(
        display: &EGLDisplay,
        attributes: GlAttributes,
        reqs: PixelFormatRequirements,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, Some((attributes, reqs)), log)
    }

    fn new_internal<L>(
        display: &EGLDisplay,
        config: Option<(GlAttributes, PixelFormatRequirements)>,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let log = crate::slog_or_fallback(log.into()).new(o!("smithay_module" => "backend_egl"));

        let (pixel_format, config_id) = match config {
            Some((attributes, reqs)) => {
                let (format, config_id) = display.choose_config(attributes, reqs)?;
                (Some(format), config_id)
            },
            None => {
                if !display.extensions.iter().any(|x| x == "EGL_KHR_no_config_context") &&
                   !display.extensions.iter().any(|x| x == "EGL_MESA_configless_context") &&
                   !display.extensions.iter().any(|x| x == "EGL_KHR_surfaceless_context")
                {
                    return Err(Error::EglExtensionNotSupported(&["EGL_KHR_no_config_context", "EGL_MESA_configless_context", "EGL_KHR_surfaceless_context"]));
                }
                (None, ffi::egl::NO_CONFIG_KHR)
            }
        };

        let mut context_attributes = Vec::with_capacity(10);

        if let Some((attributes, _)) = config {
            let version = attributes.version;

            if display.egl_version >= (1, 5) || display.extensions.iter().any(|s| s == "EGL_KHR_create_context") {
                trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0);
                context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
                context_attributes.push(version.0 as i32);
                trace!(log, "Setting CONTEXT_MINOR_VERSION to {}", version.1);
                context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
                context_attributes.push(version.1 as i32);

                if attributes.debug && display.egl_version >= (1, 5) {
                    trace!(log, "Setting CONTEXT_OPENGL_DEBUG to TRUE");
                    context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
                    context_attributes.push(ffi::egl::TRUE as i32);
                }

                context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
                context_attributes.push(0);
            } else if display.egl_version >= (1, 3) {
                trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0);
                context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
                context_attributes.push(version.0 as i32);
            }
        } else {
            trace!(log, "Setting CONTEXT_CLIENT_VERSION to 2");
            context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
            context_attributes.push(2);
        }

        context_attributes.push(ffi::egl::NONE as i32);

        trace!(log, "Creating EGL context...");
        // TODO: Support shared contexts
        let context = wrap_egl_call(|| unsafe {
            ffi::egl::CreateContext(
                **display.display,
                config_id,
                ptr::null(),
                context_attributes.as_ptr(),
            )
        })
        .map_err(Error::CreationFailed)?;

        info!(log, "EGL context created");

        Ok(EGLContext {
            context,
            display: display.clone(),
            config_id,
            pixel_format,
        })
    }

    /// Makes the OpenGL context the current context in the current thread with a surface to
    /// read/write to.
    ///
    /// # Safety
    ///
    /// This function is marked unsafe, because the context cannot be made current
    /// on multiple threads.
    pub unsafe fn make_current_with_surface(&self, surface: &EGLSurface) -> Result<(), MakeCurrentError>
    {
        let surface_ptr = surface.surface.load(Ordering::SeqCst);
        wrap_egl_call(|| ffi::egl::MakeCurrent(**self.display.display, surface_ptr, surface_ptr, self.context))
            .map(|_| ())
            .map_err(Into::into)
    }

    /// Makes the OpenGL context the current context in the current thread with no surface bound.
    ///
    /// # Safety
    ///
    /// This function is marked unsafe, because the context cannot be made current
    /// on multiple threads without being unbound again (see `unbind`)
    pub unsafe fn make_current(&self) -> Result<(), MakeCurrentError> {
        wrap_egl_call(|| {
            ffi::egl::MakeCurrent(
                **self.display.display,
                ffi::egl::NO_SURFACE,
                ffi::egl::NO_SURFACE,
                self.context,
            )
        })
        .map(|_| ())
        .map_err(Into::into)
    }

    /// Returns true if the OpenGL context is the current one in the thread.
    pub fn is_current(&self) -> bool {
        unsafe { ffi::egl::GetCurrentContext() == self.context as *const _ }
    }

    /// Returns the egl config for this context
    pub fn config_id(&self) -> ffi::egl::types::EGLConfig {
        self.config_id
    }

    /// Returns the pixel format of the main framebuffer of the context.
    pub fn pixel_format(&self) -> Option<PixelFormat> {
        self.pixel_format
    }

    /// Unbinds this context from the current thread, if set.
    ///
    /// This does nothing if this context is not the current context
    pub fn unbind(&self) -> Result<(), MakeCurrentError> {
        if self.is_current() {
            wrap_egl_call(|| unsafe {
                ffi::egl::MakeCurrent(
                    **self.display.display,
                    ffi::egl::NO_SURFACE,
                    ffi::egl::NO_SURFACE,
                    ffi::egl::NO_CONTEXT,
                )
            })?;
        }
        Ok(())
    }
}

impl Drop for EGLContext {
    fn drop(&mut self) {
        unsafe {
            // We need to ensure the context is unbound, otherwise it egl stalls the destroy call
            // ignore failures at this point
            let _ = self.unbind();
            ffi::egl::DestroyContext(**self.display.display, self.context);
        }
    }
}

/// Attributes to use when creating an OpenGL context.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GlAttributes {
    /// Describes the OpenGL API and version that are being requested when a context is created.
    ///
    /// `(3, 0)` will request a OpenGL ES 3.0 context for example.
    /// `(2, 0)` is the minimum.
    pub version: (u8, u8),
    /// OpenGL profile to use
    pub profile: Option<GlProfile>,
    /// Whether to enable the debug flag of the context.
    ///
    /// Debug contexts are usually slower but give better error reporting.
    pub debug: bool,
    /// Whether to use vsync. If vsync is enabled, calling `swap_buffers` will block until the screen refreshes.
    /// This is typically used to prevent screen tearing.
    pub vsync: bool,
}

/// Describes the requested OpenGL context profiles.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GlProfile {
    /// Include all the immediate more functions and definitions.
    Compatibility,
    /// Include all the future-compatible functions and definitions.
    Core,
}

/// Describes how the backend should choose a pixel format.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelFormatRequirements {
    /// If `true`, only hardware-accelerated formats will be considered. If `false`, only software renderers.
    /// `None` means "don't care". Default is `None`.
    pub hardware_accelerated: Option<bool>,
    /// Minimum number of bits for the color buffer, excluding alpha. `None` means "don't care". The default is `None`.
    pub color_bits: Option<u8>,
    /// If `true`, the color buffer must be in a floating point format. Default is `false`.
    ///
    /// Using floating points allows you to write values outside of the `[0.0, 1.0]` range.
    pub float_color_buffer: bool,
    /// Minimum number of bits for the alpha in the color buffer. `None` means "don't care". The default is `None`.
    pub alpha_bits: Option<u8>,
    /// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`.
    pub depth_bits: Option<u8>,
    /// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`.
    pub stencil_bits: Option<u8>,
    /// If `true`, only double-buffered formats will be considered. If `false`, only single-buffer formats.
    /// `None` means "don't care". The default is `None`.
    pub double_buffer: Option<bool>,
    /// Contains the minimum number of samples per pixel in the color, depth and stencil buffers.
    /// `None` means "don't care". Default is `None`. A value of `Some(0)` indicates that multisampling must not be enabled.
    pub multisampling: Option<u16>,
    /// If `true`, only stereoscopic formats will be considered. If `false`, only non-stereoscopic formats.
    /// The default is `false`.
    pub stereoscopy: bool,
}

impl Default for PixelFormatRequirements {
    fn default() -> Self {
        PixelFormatRequirements {
            hardware_accelerated: Some(true),
            color_bits: Some(24),
            float_color_buffer: false,
            alpha_bits: Some(8),
            depth_bits: Some(24),
            stencil_bits: Some(8),
            double_buffer: Some(true),
            multisampling: None,
            stereoscopy: false,
        }
    }
}

impl PixelFormatRequirements {
    /// Append the  requirements to the given attribute list
    pub fn create_attributes(&self, out: &mut Vec<c_int>, logger: &slog::Logger) -> Result<(), ()> {
        if let Some(hardware_accelerated) = self.hardware_accelerated {
            out.push(ffi::egl::CONFIG_CAVEAT as c_int);
            out.push(if hardware_accelerated {
                trace!(logger, "Setting CONFIG_CAVEAT to NONE");
                ffi::egl::NONE as c_int
            } else {
                trace!(logger, "Setting CONFIG_CAVEAT to SLOW_CONFIG");
                ffi::egl::SLOW_CONFIG as c_int
            });
        }

        if let Some(color) = self.color_bits {
            trace!(logger, "Setting RED_SIZE to {}", color / 3);
            out.push(ffi::egl::RED_SIZE as c_int);
            out.push((color / 3) as c_int);
            trace!(
                logger,
                "Setting GREEN_SIZE to {}",
                color / 3 + if color % 3 != 0 { 1 } else { 0 }
            );
            out.push(ffi::egl::GREEN_SIZE as c_int);
            out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
            trace!(
                logger,
                "Setting BLUE_SIZE to {}",
                color / 3 + if color % 3 == 2 { 1 } else { 0 }
            );
            out.push(ffi::egl::BLUE_SIZE as c_int);
            out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
        }

        if let Some(alpha) = self.alpha_bits {
            trace!(logger, "Setting ALPHA_SIZE to {}", alpha);
            out.push(ffi::egl::ALPHA_SIZE as c_int);
            out.push(alpha as c_int);
        }

        if let Some(depth) = self.depth_bits {
            trace!(logger, "Setting DEPTH_SIZE to {}", depth);
            out.push(ffi::egl::DEPTH_SIZE as c_int);
            out.push(depth as c_int);
        }

        if let Some(stencil) = self.stencil_bits {
            trace!(logger, "Setting STENCIL_SIZE to {}", stencil);
            out.push(ffi::egl::STENCIL_SIZE as c_int);
            out.push(stencil as c_int);
        }

        if let Some(multisampling) = self.multisampling {
            trace!(logger, "Setting SAMPLES to {}", multisampling);
            out.push(ffi::egl::SAMPLES as c_int);
            out.push(multisampling as c_int);
        }

        if self.stereoscopy {
            error!(logger, "Stereoscopy is currently unsupported (sorry!)");
            return Err(());
        }

        Ok(())
    }
}
<commit_msg>egl: Allow creation of shared contexts<commit_after>//! EGL context related structs
use std::os::raw::c_int;
use std::ptr;
use std::sync::atomic::Ordering;

use super::{ffi, wrap_egl_call, Error, MakeCurrentError};
use crate::backend::egl::display::{EGLDisplay, PixelFormat};
use crate::backend::egl::native::EGLNativeSurface;
use crate::backend::egl::EGLSurface;

/// EGL context for rendering
#[derive(Debug)]
pub struct EGLContext {
    context: ffi::egl::types::EGLContext,
    pub(crate) display: EGLDisplay,
    config_id: ffi::egl::types::EGLConfig,
    pixel_format: Option<PixelFormat>,
}
// EGLContexts can be moved between threads safely
unsafe impl Send for EGLContext {}
unsafe impl Sync for EGLContext {}

impl EGLContext {
    pub fn new<L>(
        display: &EGLDisplay,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, None, None, log)
    }

    /// Create a new [`EGLContext`] from a given [`NativeDisplay`](native::NativeDisplay)
    pub fn new_with_config<L>(
        display: &EGLDisplay,
        attributes: GlAttributes,
        reqs: PixelFormatRequirements,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, None, Some((attributes, reqs)), log)
    }

    pub fn new_shared<L>(
        display: &EGLDisplay,
        share: &EGLContext,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, Some(share), None, log)
    }

    pub fn new_shared_with_config<L>(
        display: &EGLDisplay,
        share: &EGLContext,
        attributes: GlAttributes,
        reqs: PixelFormatRequirements,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        Self::new_internal(display, Some(share), Some((attributes, reqs)), log)
    }

    fn new_internal<L>(
        display: &EGLDisplay,
        shared: Option<&EGLContext>,
        config: Option<(GlAttributes, PixelFormatRequirements)>,
        log: L,
    ) -> Result<EGLContext, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let log = crate::slog_or_fallback(log.into()).new(o!("smithay_module" => "backend_egl"));

        let (pixel_format, config_id) = match config {
            Some((attributes, reqs)) => {
                let (format, config_id) = display.choose_config(attributes, reqs)?;
                (Some(format), config_id)
            },
            None => {
                if !display.extensions.iter().any(|x| x == "EGL_KHR_no_config_context") &&
                   !display.extensions.iter().any(|x| x == "EGL_MESA_configless_context") &&
                   !display.extensions.iter().any(|x| x == "EGL_KHR_surfaceless_context")
                {
                    return Err(Error::EglExtensionNotSupported(&["EGL_KHR_no_config_context", "EGL_MESA_configless_context", "EGL_KHR_surfaceless_context"]));
                }
                (None, ffi::egl::NO_CONFIG_KHR)
            }
        };

        let mut context_attributes = Vec::with_capacity(10);

        if let Some((attributes, _)) = config {
            let version = attributes.version;

            if display.egl_version >= (1, 5) || display.extensions.iter().any(|s| s == "EGL_KHR_create_context") {
                trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0);
                context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
                context_attributes.push(version.0 as i32);
                trace!(log, "Setting CONTEXT_MINOR_VERSION to {}", version.1);
                context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
                context_attributes.push(version.1 as i32);

                if attributes.debug && display.egl_version >= (1, 5) {
                    trace!(log, "Setting CONTEXT_OPENGL_DEBUG to TRUE");
                    context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
                    context_attributes.push(ffi::egl::TRUE as i32);
                }

                context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
                context_attributes.push(0);
            } else if display.egl_version >= (1, 3) {
                trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0);
                context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
                context_attributes.push(version.0 as i32);
            }
        } else {
            trace!(log, "Setting CONTEXT_CLIENT_VERSION to 2");
            context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
            context_attributes.push(2);
        }

        context_attributes.push(ffi::egl::NONE as i32);

        trace!(log, "Creating EGL context...");
        // TODO: Support shared contexts
        let context = wrap_egl_call(|| unsafe {
            ffi::egl::CreateContext(
                **display.display,
                config_id,
                shared.map(|context| context.context).unwrap_or(ffi::egl::NO_CONTEXT),
                context_attributes.as_ptr(),
            )
        })
        .map_err(Error::CreationFailed)?;

        info!(log, "EGL context created");

        Ok(EGLContext {
            context,
            display: display.clone(),
            config_id,
            pixel_format,
        })
    }

    /// Makes the OpenGL context the current context in the current thread with a surface to
    /// read/write to.
    ///
    /// # Safety
    ///
    /// This function is marked unsafe, because the context cannot be made current
    /// on multiple threads.
    pub unsafe fn make_current_with_surface(&self, surface: &EGLSurface) -> Result<(), MakeCurrentError>
    {
        let surface_ptr = surface.surface.load(Ordering::SeqCst);
        wrap_egl_call(|| ffi::egl::MakeCurrent(**self.display.display, surface_ptr, surface_ptr, self.context))
            .map(|_| ())
            .map_err(Into::into)
    }

    /// Makes the OpenGL context the current context in the current thread with no surface bound.
    ///
    /// # Safety
    ///
    /// This function is marked unsafe, because the context cannot be made current
    /// on multiple threads without being unbound again (see `unbind`)
    pub unsafe fn make_current(&self) -> Result<(), MakeCurrentError> {
        wrap_egl_call(|| {
            ffi::egl::MakeCurrent(
                **self.display.display,
                ffi::egl::NO_SURFACE,
                ffi::egl::NO_SURFACE,
                self.context,
            )
        })
        .map(|_| ())
        .map_err(Into::into)
    }

    /// Returns true if the OpenGL context is the current one in the thread.
    pub fn is_current(&self) -> bool {
        unsafe { ffi::egl::GetCurrentContext() == self.context as *const _ }
    }

    /// Returns the egl config for this context
    pub fn config_id(&self) -> ffi::egl::types::EGLConfig {
        self.config_id
    }

    /// Returns the pixel format of the main framebuffer of the context.
    pub fn pixel_format(&self) -> Option<PixelFormat> {
        self.pixel_format
    }

    /// Unbinds this context from the current thread, if set.
    ///
    /// This does nothing if this context is not the current context
    pub fn unbind(&self) -> Result<(), MakeCurrentError> {
        if self.is_current() {
            wrap_egl_call(|| unsafe {
                ffi::egl::MakeCurrent(
                    **self.display.display,
                    ffi::egl::NO_SURFACE,
                    ffi::egl::NO_SURFACE,
                    ffi::egl::NO_CONTEXT,
                )
            })?;
        }
        Ok(())
    }
}

impl Drop for EGLContext {
    fn drop(&mut self) {
        unsafe {
            // We need to ensure the context is unbound, otherwise it egl stalls the destroy call
            // ignore failures at this point
            let _ = self.unbind();
            ffi::egl::DestroyContext(**self.display.display, self.context);
        }
    }
}

/// Attributes to use when creating an OpenGL context.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GlAttributes {
    /// Describes the OpenGL API and version that are being requested when a context is created.
    ///
    /// `(3, 0)` will request a OpenGL ES 3.0 context for example.
    /// `(2, 0)` is the minimum.
    pub version: (u8, u8),
    /// OpenGL profile to use
    pub profile: Option<GlProfile>,
    /// Whether to enable the debug flag of the context.
    ///
    /// Debug contexts are usually slower but give better error reporting.
    pub debug: bool,
    /// Whether to use vsync. If vsync is enabled, calling `swap_buffers` will block until the screen refreshes.
    /// This is typically used to prevent screen tearing.
    pub vsync: bool,
}

/// Describes the requested OpenGL context profiles.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GlProfile {
    /// Include all the immediate more functions and definitions.
    Compatibility,
    /// Include all the future-compatible functions and definitions.
    Core,
}

/// Describes how the backend should choose a pixel format.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelFormatRequirements {
    /// If `true`, only hardware-accelerated formats will be considered. If `false`, only software renderers.
    /// `None` means "don't care". Default is `None`.
    pub hardware_accelerated: Option<bool>,
    /// Minimum number of bits for the color buffer, excluding alpha. `None` means "don't care". The default is `None`.
    pub color_bits: Option<u8>,
    /// If `true`, the color buffer must be in a floating point format. Default is `false`.
    ///
    /// Using floating points allows you to write values outside of the `[0.0, 1.0]` range.
    pub float_color_buffer: bool,
    /// Minimum number of bits for the alpha in the color buffer. `None` means "don't care". The default is `None`.
    pub alpha_bits: Option<u8>,
    /// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`.
    pub depth_bits: Option<u8>,
    /// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`.
    pub stencil_bits: Option<u8>,
    /// If `true`, only double-buffered formats will be considered. If `false`, only single-buffer formats.
    /// `None` means "don't care". The default is `None`.
    pub double_buffer: Option<bool>,
    /// Contains the minimum number of samples per pixel in the color, depth and stencil buffers.
    /// `None` means "don't care". Default is `None`. A value of `Some(0)` indicates that multisampling must not be enabled.
    pub multisampling: Option<u16>,
    /// If `true`, only stereoscopic formats will be considered. If `false`, only non-stereoscopic formats.
    /// The default is `false`.
    pub stereoscopy: bool,
}

impl Default for PixelFormatRequirements {
    fn default() -> Self {
        PixelFormatRequirements {
            hardware_accelerated: Some(true),
            color_bits: Some(24),
            float_color_buffer: false,
            alpha_bits: Some(8),
            depth_bits: Some(24),
            stencil_bits: Some(8),
            double_buffer: Some(true),
            multisampling: None,
            stereoscopy: false,
        }
    }
}

impl PixelFormatRequirements {
    /// Append the  requirements to the given attribute list
    pub fn create_attributes(&self, out: &mut Vec<c_int>, logger: &slog::Logger) -> Result<(), ()> {
        if let Some(hardware_accelerated) = self.hardware_accelerated {
            out.push(ffi::egl::CONFIG_CAVEAT as c_int);
            out.push(if hardware_accelerated {
                trace!(logger, "Setting CONFIG_CAVEAT to NONE");
                ffi::egl::NONE as c_int
            } else {
                trace!(logger, "Setting CONFIG_CAVEAT to SLOW_CONFIG");
                ffi::egl::SLOW_CONFIG as c_int
            });
        }

        if let Some(color) = self.color_bits {
            trace!(logger, "Setting RED_SIZE to {}", color / 3);
            out.push(ffi::egl::RED_SIZE as c_int);
            out.push((color / 3) as c_int);
            trace!(
                logger,
                "Setting GREEN_SIZE to {}",
                color / 3 + if color % 3 != 0 { 1 } else { 0 }
            );
            out.push(ffi::egl::GREEN_SIZE as c_int);
            out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
            trace!(
                logger,
                "Setting BLUE_SIZE to {}",
                color / 3 + if color % 3 == 2 { 1 } else { 0 }
            );
            out.push(ffi::egl::BLUE_SIZE as c_int);
            out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
        }

        if let Some(alpha) = self.alpha_bits {
            trace!(logger, "Setting ALPHA_SIZE to {}", alpha);
            out.push(ffi::egl::ALPHA_SIZE as c_int);
            out.push(alpha as c_int);
        }

        if let Some(depth) = self.depth_bits {
            trace!(logger, "Setting DEPTH_SIZE to {}", depth);
            out.push(ffi::egl::DEPTH_SIZE as c_int);
            out.push(depth as c_int);
        }

        if let Some(stencil) = self.stencil_bits {
            trace!(logger, "Setting STENCIL_SIZE to {}", stencil);
            out.push(ffi::egl::STENCIL_SIZE as c_int);
            out.push(stencil as c_int);
        }

        if let Some(multisampling) = self.multisampling {
            trace!(logger, "Setting SAMPLES to {}", multisampling);
            out.push(ffi::egl::SAMPLES as c_int);
            out.push(multisampling as c_int);
        }

        if self.stereoscopy {
            error!(logger, "Stereoscopy is currently unsupported (sorry!)");
            return Err(());
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::path::Path;
use std::io::BufWriter;
use std::io::Write;

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest = Path::new(&out_dir);

    let base = Path::new("assets");
    let mut out = Vec::new();
    build_map(&mut out, &base);

    let mut file = BufWriter::new(fs::File::create(&dest.join("resources.rs")).unwrap());
    write!(file, "pub fn get_file(name: &str) -> Option<&'static [u8]> {{\n").unwrap();
    write!(file, "    match name {{\n").unwrap();
    for entry in &out {
    	let entry = entry.replace("\\", "/");
    	let short = &entry;
    	write!(file, "        {:?} => Some(include_bytes!(\"../{}\")),\n", short, entry).unwrap();
    }
    write!(file, "        _ => None\n    }}\n}}\n").unwrap();

}

fn build_map(out: &mut Vec<String>, path: &Path) {
    let files = fs::read_dir(path).unwrap();
    for entry in files {
    	let entry = entry.unwrap();
    	if fs::metadata(entry.path()).unwrap().is_dir() {
    		build_map(out, &entry.path());
    	} else {
    		out.push(entry.path().to_str().unwrap().to_owned());
    	}
    }
}
<commit_msg>Use absolute paths when including resources. (Fixes #53)<commit_after>use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::io::BufWriter;
use std::io::Write;

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest = Path::new(&out_dir);

    let base = Path::new("assets");
    let mut out = Vec::new();
    build_map(&mut out, &base);

    let mut file = BufWriter::new(fs::File::create(&dest.join("resources.rs")).unwrap());
    write!(file, "pub fn get_file(name: &str) -> Option<&'static [u8]> {{\n").unwrap();
    write!(file, "    match name {{\n").unwrap();
    for path in &out {
        let mut absolute_path = std::env::current_dir().unwrap();
        absolute_path.push(path);

        let absolute = absolute_path.to_str().unwrap().replace("\\", "/");
        let relative = path.to_str().unwrap().replace("\\", "/");

        write!(file, "        {:?} => Some(include_bytes!(\"{}\")),\n", relative, absolute).unwrap();
    }
    write!(file, "        _ => None\n    }}\n}}\n").unwrap();

}

fn build_map(out: &mut Vec<PathBuf>, path: &Path) {
    let files = fs::read_dir(path).unwrap();
    for entry in files {
        let entry = entry.unwrap();
        if fs::metadata(entry.path()).unwrap().is_dir() {
            build_map(out, &entry.path());
        } else {
            out.push(entry.path());
        }
    }
}
<|endoftext|>


<commit_before>use postgres::Connection;
use postgres::rows::Row;
use postgres::error::{Error as PgError};
use error::ScurryError;
use models::ScurryMetadata;
use versions::{Version, DesiredVersion};
use util::{self, HistoryDifferences};
use connection::ScurryConnection;
use postgres::transaction::Transaction;

const METADATA_EXISTS: &'static str = "
SELECT EXISTS (
    SELECT 1 FROM pg_catalog.pg_class c
    where c.relname = '_scurry'
    and c.relkind = 'r');";

const CREATE_METADATA_TABLE: &'static str = "
CREATE TABLE _scurry (
    id serial,
    migration_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE 'utc'),
    script_hash TEXT NOT NULL,
    script_name TEXT NOT NULL,
    script_version TEXT NOT NULL
);";

const INSERT_HISTORY_LINE: &'static str = "INSERT INTO _scurry(script_hash, script_name, script_version) values($1, $2, $3);";

const GET_ALL_REVISIONS: &'static str = "SELECT id, migration_date, script_hash, script_name, script_version FROM _scurry ORDER BY script_version ASC;";

const ACQUIRE_LOCK: &'static str = "LOCK TABLE _scurry IN ACCESS EXCLUSIVE MODE;";

const DELETE_HISTORY: &'static str = "DELETE FROM _scurry;";

pub struct Postgres {
    conn: Connection,
    migrations_dir: String
}
pub fn establish(conn: Connection, migrations_dir: &str) -> Postgres {
    Postgres {
        conn: conn,
        migrations_dir: migrations_dir.into(),
    }
}

fn history_table_exists(xact: &Transaction) -> Result<bool, ScurryError> {
    let exists = try!(xact.query(METADATA_EXISTS, &[]));
    for row in &exists {
        let res: bool = row.get(0);
        return Ok(res);
    }
    Ok(false)
}

fn write_history_line(xact: &Transaction, version: &Version) -> Result<(), ScurryError> {
    try!(xact.execute(INSERT_HISTORY_LINE,
                           &[&version.hash, &version.name, &version.version]));
    Ok(())
}

fn lock_table(xact: &Transaction) -> Result<(), ScurryError> {
    let exists = try!(history_table_exists(xact));
    if !exists {
        try!(create_metadata_table(xact));
    }
    try!(xact.execute(ACQUIRE_LOCK, &[]));
    info!("Locked table for updating");
    Ok(())
}

fn create_metadata_table(xact: &Transaction) -> Result<(), ScurryError> {
    try!(xact.batch_execute(CREATE_METADATA_TABLE));
    info!("Metadata table created");
    Ok(())
}

fn clear_history_table(xact: &Transaction) -> Result<(), ScurryError> {
    try!(xact.execute(DELETE_HISTORY, &[]));
    Ok(())
}

fn apply_migration(xact: &Transaction, version: &Version) -> Result<(), ScurryError> {
    let sub_xact = try!(xact.transaction());
    let contents = try!(util::get_file_contents(&version.path));
    try!(sub_xact.batch_execute(&contents));
    try!(write_history_line(&sub_xact, &version));
    try!(sub_xact.commit());
    Ok(())
}

impl ScurryConnection for Postgres {
    type DbConnection = Connection;

    fn migrate(&mut self, desired_version: DesiredVersion) -> Result<usize, ScurryError> {
        let versions = try!(util::calculate_available_versions(&self.migrations_dir));
        info!("Found {} migrations", versions.len());
        let history = try!(self.get_history());
        try!(util::verify_common_history(&versions, &history));
        let latest_version = history.iter().last();
        match latest_version {
            None => {
                info!("Schema at version 0");
            }
            Some(rev) => {
                info!("Schema at version {}", rev.script_version);
            }
        }
        let xact = try!(self.conn.transaction());
        try!(lock_table(&xact));
        let upgrade_path = util::choose_upgrade_path(&versions, &latest_version, &desired_version);
        let upgrade_len = upgrade_path.len();
        info!("Applying {} migrations", upgrade_len);
        for v in upgrade_path {
            info!("Applying version {}...", &v.version);
            try!(apply_migration(&xact, &v));
        }
        try!(xact.commit());
        Ok(upgrade_len)
    }

    fn get_differences(&self) -> Result<Vec<HistoryDifferences>, ScurryError> {
        let available = try!(util::calculate_available_versions(&self.migrations_dir));
        let installed = try!(self.get_history());
        Ok(util::get_history_differences(&available, &installed))
    }

    fn set_schema_level(&self, desired_version: DesiredVersion) -> Result<(), ScurryError> {
        let versions = try!(util::calculate_available_versions(&self.migrations_dir));
        let upgrade_path = util::choose_upgrade_path(&versions, &None, &desired_version);
        try!(self.override_versions(&upgrade_path));
        Ok(())
    }

    fn get_history(&self) -> Result<Vec<ScurryMetadata>, ScurryError> {
        let revisions_query = try!(self.conn.query(GET_ALL_REVISIONS, &[]));
        Ok(revisions_query.iter().map(ScurryMetadata::from).collect::<Vec<_>>())
    }

    fn override_versions(&self, versions: &[&Version]) -> Result<(), ScurryError> {
        let xact = try!(self.conn.transaction());
        try!(clear_history_table(&xact));
        for v in versions {
            try!(write_history_line(&xact, v));
        }
        try!(xact.commit());
        Ok(())
    }

    fn take_connection(self) -> Connection {
        self.conn
    }
}

impl<'a> From<Row<'a>> for ScurryMetadata {
    fn from(row: Row<'a>) -> ScurryMetadata {
        ScurryMetadata {
            id: row.get(0),
            migration_date: row.get(1),
            script_hash: row.get(2),
            script_name: row.get(3),
            script_version: row.get(4),
        }
    }
}

impl From<PgError> for ScurryError {
    fn from(e: PgError) -> ScurryError {
        ScurryError::Sql(Box::new(e))
    }
}
<commit_msg>Fix execution order so metadata table will exist on postgres<commit_after>use postgres::Connection;
use postgres::rows::Row;
use postgres::error::Error as PgError;
use error::ScurryError;
use models::ScurryMetadata;
use versions::{Version, DesiredVersion};
use util::{self, HistoryDifferences};
use connection::ScurryConnection;
use postgres::transaction::Transaction;

const METADATA_EXISTS: &'static str = "
SELECT EXISTS (
    SELECT 1 FROM pg_catalog.pg_class c
    where c.relname = '_scurry'
    and c.relkind = 'r');";

const CREATE_METADATA_TABLE: &'static str = "
CREATE TABLE _scurry (
    id serial,
    migration_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE 'utc'),
    script_hash TEXT NOT NULL,
    script_name TEXT NOT NULL,
    script_version TEXT NOT NULL
);";

const INSERT_HISTORY_LINE: &'static str = "INSERT INTO _scurry(script_hash, script_name, \
                                           script_version) values($1, $2, $3);";

const GET_ALL_REVISIONS: &'static str = "SELECT id, migration_date, script_hash, script_name, \
                                         script_version FROM _scurry ORDER BY script_version ASC;";

const ACQUIRE_LOCK: &'static str = "LOCK TABLE _scurry IN ACCESS EXCLUSIVE MODE;";

const DELETE_HISTORY: &'static str = "DELETE FROM _scurry;";

pub struct Postgres {
    conn: Connection,
    migrations_dir: String,
}
pub fn establish(conn: Connection, migrations_dir: &str) -> Postgres {
    Postgres {
        conn: conn,
        migrations_dir: migrations_dir.into(),
    }
}

fn history_table_exists(xact: &Transaction) -> Result<bool, ScurryError> {
    let exists = try!(xact.query(METADATA_EXISTS, &[]));
    for row in &exists {
        let res: bool = row.get(0);
        return Ok(res);
    }
    Ok(false)
}

fn write_history_line(xact: &Transaction, version: &Version) -> Result<(), ScurryError> {
    try!(xact.execute(INSERT_HISTORY_LINE,
                      &[&version.hash, &version.name, &version.version]));
    Ok(())
}

fn lock_table(xact: &Transaction) -> Result<(), ScurryError> {
    try!(xact.execute(ACQUIRE_LOCK, &[]));
    info!("Locked table for updating");
    Ok(())
}

fn create_metadata_table(xact: &Transaction) -> Result<(), ScurryError> {
    let exists = try!(history_table_exists(xact));
    if !exists {
        try!(xact.batch_execute(CREATE_METADATA_TABLE));
        info!("Metadata table created");
    }
    Ok(())
}

fn get_history(xact: &Transaction) -> Result<Vec<ScurryMetadata>, ScurryError> {
    let revisions_query = try!(xact.query(GET_ALL_REVISIONS, &[]));
    Ok(revisions_query.iter().map(ScurryMetadata::from).collect::<Vec<_>>())
}

fn clear_history_table(xact: &Transaction) -> Result<(), ScurryError> {
    try!(xact.execute(DELETE_HISTORY, &[]));
    Ok(())
}

fn apply_migration(xact: &Transaction, version: &Version) -> Result<(), ScurryError> {
    let sub_xact = try!(xact.transaction());
    let contents = try!(util::get_file_contents(&version.path));
    try!(sub_xact.batch_execute(&contents));
    try!(write_history_line(&sub_xact, &version));
    try!(sub_xact.commit());
    Ok(())
}

impl ScurryConnection for Postgres {
    type DbConnection = Connection;

    fn migrate(&mut self, desired_version: DesiredVersion) -> Result<usize, ScurryError> {
        let versions = try!(util::calculate_available_versions(&self.migrations_dir));
        info!("Found {} migrations", versions.len());
        let xact = try!(self.conn.transaction());
        try!(create_metadata_table(&xact));
        try!(lock_table(&xact));
        let history = try!(get_history(&xact));
        try!(util::verify_common_history(&versions, &history));
        let latest_version = history.iter().last();
        match latest_version {
            None => {
                info!("Schema at version 0");
            }
            Some(rev) => {
                info!("Schema at version {}", rev.script_version);
            }
        }

        let upgrade_path = util::choose_upgrade_path(&versions, &latest_version, &desired_version);
        let upgrade_len = upgrade_path.len();
        info!("Applying {} migrations", upgrade_len);
        for v in upgrade_path {
            info!("Applying version {}...", &v.version);
            try!(apply_migration(&xact, &v));
        }
        try!(xact.commit());
        Ok(upgrade_len)
    }

    fn get_differences(&self) -> Result<Vec<HistoryDifferences>, ScurryError> {
        let available = try!(util::calculate_available_versions(&self.migrations_dir));
        let installed = try!(self.get_history());
        Ok(util::get_history_differences(&available, &installed))
    }

    fn set_schema_level(&self, desired_version: DesiredVersion) -> Result<(), ScurryError> {
        let versions = try!(util::calculate_available_versions(&self.migrations_dir));
        let upgrade_path = util::choose_upgrade_path(&versions, &None, &desired_version);
        try!(self.override_versions(&upgrade_path));
        Ok(())
    }

    fn get_history(&self) -> Result<Vec<ScurryMetadata>, ScurryError> {
        let revisions_query = try!(self.conn.query(GET_ALL_REVISIONS, &[]));
        Ok(revisions_query.iter().map(ScurryMetadata::from).collect::<Vec<_>>())
    }

    fn override_versions(&self, versions: &[&Version]) -> Result<(), ScurryError> {
        let xact = try!(self.conn.transaction());
        try!(clear_history_table(&xact));
        for v in versions {
            try!(write_history_line(&xact, v));
        }
        try!(xact.commit());
        Ok(())
    }

    fn take_connection(self) -> Connection {
        self.conn
    }
}

impl<'a> From<Row<'a>> for ScurryMetadata {
    fn from(row: Row<'a>) -> ScurryMetadata {
        ScurryMetadata {
            id: row.get(0),
            migration_date: row.get(1),
            script_hash: row.get(2),
            script_name: row.get(3),
            script_version: row.get(4),
        }
    }
}

impl From<PgError> for ScurryError {
    fn from(e: PgError) -> ScurryError {
        ScurryError::Sql(Box::new(e))
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
extern crate libstratis;
extern crate devicemapper;
#[macro_use]
extern crate log;
#[macro_use]
mod util;

use libstratis::engine::Engine;
use libstratis::engine::strat_engine::StratEngine;
use libstratis::engine::strat_engine::engine::DevOwnership;

use std::path::Path;
use std::path::PathBuf;

use util::blockdev_utils::clean_blockdev_headers;
use util::blockdev_utils::get_ownership;
use util::test_config::TestConfig;
use util::test_consts::DEFAULT_CONFIG_FILE;
use util::test_result::TestResult;

// Check to make sure the disks in blockdev_paths are no longer "Owned" by
// Stratis
pub fn validate_disks_released(blockdev_paths: &Vec<PathBuf>) -> TestResult<()> {

    for path_name in blockdev_paths {
        let path = PathBuf::from(path_name);

        match get_ownership(&path) {
            Ok(ownership) => {
                match ownership {
                    DevOwnership::Unowned => {
                        debug!("Path confirmed {:?} Unowned | Theirs", path);
                    }
                    DevOwnership::Theirs => {
                        error!("Path set to Theirs after pool delete.  {:?}", path);
                        assert!(false);
                    }
                    DevOwnership::Ours(_) => {
                        error!("Failed to release {:?}", path);
                        assert!(false);
                    }
                }
            }
            Err(e) => {
                error!("Failed to determine ownership of {:?} {:?}", path, e);
                assert!(false);
            }
        }

    }

    Ok(())
}

// Validate that disks are "owned" by Stratis
pub fn validate_disks_init(blockdev_paths: &Vec<PathBuf>) -> TestResult<()> {

    for path_name in blockdev_paths {
        let path = PathBuf::from(path_name);

        match get_ownership(&path) {
            Ok(ownership) => {
                match ownership {
                    DevOwnership::Unowned | DevOwnership::Theirs => {
                        error!("Failed to initialize {:?}", path);
                        assert!(false)
                    }
                    DevOwnership::Ours(_) => {
                        debug!("Path {:?} is confirmed Ours.", path);
                    }
                }
            }
            Err(e) => {
                error!("Failed to determine ownership of {:?} {:?}", path, e);
                assert!(false)
            }
        }
    }

    Ok(())
}

pub fn test_create_and_delete(device_paths: &Vec<&Path>) -> TestResult<()> {

    let pool_name = "test_pool";

    let mut engine = StratEngine::new();

    let blockdevs = try!(engine.create_pool(pool_name, &device_paths, Some(1), true));

    try!(validate_disks_init(&blockdevs));

    try!(engine.destroy_pool(pool_name));

    try!(validate_disks_released(&blockdevs));

    Ok(())
}

#[test]
pub fn test_pools() {
    let mut test_config = TestConfig::new(DEFAULT_CONFIG_FILE);

    let _ = test_config.init();

    let safe_to_destroy_devs = match test_config.get_safe_to_destroy_devs() {
        Ok(devs) => {
            if devs.len() == 0 {
                warn!("No devs availabe for testing.  Test not run");
                return;
            }
            devs
        }
        Err(e) => {
            error!("Failed : get_safe_to_destroy_devs : {:?}", e);
            return;
        }
    };
    info!("safe_to_destroy_devs = {:?}", safe_to_destroy_devs);

    let device_paths = safe_to_destroy_devs.iter().map(|x| Path::new(x)).collect::<Vec<&Path>>();

    clean_blockdev_headers(&device_paths);

    assert!(match test_create_and_delete(&device_paths) {
        Ok(_) => true,
        Err(_) => false,
    });

}
<commit_msg>Change redundancy option to None, which means accept the default.<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
extern crate libstratis;
extern crate devicemapper;
#[macro_use]
extern crate log;
#[macro_use]
mod util;

use libstratis::engine::Engine;
use libstratis::engine::strat_engine::StratEngine;
use libstratis::engine::strat_engine::engine::DevOwnership;

use std::path::Path;
use std::path::PathBuf;

use util::blockdev_utils::clean_blockdev_headers;
use util::blockdev_utils::get_ownership;
use util::test_config::TestConfig;
use util::test_consts::DEFAULT_CONFIG_FILE;
use util::test_result::TestResult;

// Check to make sure the disks in blockdev_paths are no longer "Owned" by
// Stratis
pub fn validate_disks_released(blockdev_paths: &Vec<PathBuf>) -> TestResult<()> {

    for path_name in blockdev_paths {
        let path = PathBuf::from(path_name);

        match get_ownership(&path) {
            Ok(ownership) => {
                match ownership {
                    DevOwnership::Unowned => {
                        debug!("Path confirmed {:?} Unowned | Theirs", path);
                    }
                    DevOwnership::Theirs => {
                        error!("Path set to Theirs after pool delete.  {:?}", path);
                        assert!(false);
                    }
                    DevOwnership::Ours(_) => {
                        error!("Failed to release {:?}", path);
                        assert!(false);
                    }
                }
            }
            Err(e) => {
                error!("Failed to determine ownership of {:?} {:?}", path, e);
                assert!(false);
            }
        }

    }

    Ok(())
}

// Validate that disks are "owned" by Stratis
pub fn validate_disks_init(blockdev_paths: &Vec<PathBuf>) -> TestResult<()> {

    for path_name in blockdev_paths {
        let path = PathBuf::from(path_name);

        match get_ownership(&path) {
            Ok(ownership) => {
                match ownership {
                    DevOwnership::Unowned | DevOwnership::Theirs => {
                        error!("Failed to initialize {:?}", path);
                        assert!(false)
                    }
                    DevOwnership::Ours(_) => {
                        debug!("Path {:?} is confirmed Ours.", path);
                    }
                }
            }
            Err(e) => {
                error!("Failed to determine ownership of {:?} {:?}", path, e);
                assert!(false)
            }
        }
    }

    Ok(())
}

pub fn test_create_and_delete(device_paths: &Vec<&Path>) -> TestResult<()> {

    let pool_name = "test_pool";

    let mut engine = StratEngine::new();

    let blockdevs = try!(engine.create_pool(pool_name, &device_paths, None, true));

    try!(validate_disks_init(&blockdevs));

    try!(engine.destroy_pool(pool_name));

    try!(validate_disks_released(&blockdevs));

    Ok(())
}

#[test]
pub fn test_pools() {
    let mut test_config = TestConfig::new(DEFAULT_CONFIG_FILE);

    let _ = test_config.init();

    let safe_to_destroy_devs = match test_config.get_safe_to_destroy_devs() {
        Ok(devs) => {
            if devs.len() == 0 {
                warn!("No devs availabe for testing.  Test not run");
                return;
            }
            devs
        }
        Err(e) => {
            error!("Failed : get_safe_to_destroy_devs : {:?}", e);
            return;
        }
    };
    info!("safe_to_destroy_devs = {:?}", safe_to_destroy_devs);

    let device_paths = safe_to_destroy_devs.iter().map(|x| Path::new(x)).collect::<Vec<&Path>>();

    clean_blockdev_headers(&device_paths);

    assert!(match test_create_and_delete(&device_paths) {
        Ok(_) => true,
        Err(_) => false,
    });

}
<|endoftext|>


<commit_before>extern crate cargotest;
extern crate hamcrest;

use cargotest::is_nightly;
use cargotest::support::{project, execs};
use hamcrest::assert_that;

#[test]
#[ignore]
fn noop() {
    if !is_nightly() {
        return;
    }

    let client = project("client")
        .file("Cargo.toml", r#"
            [package]
            name = "client"
            version = "0.0.1"
            authors = []

            [dependencies.noop]
            path = "../noop"
        "#)
        .file("src/main.rs", r#"
            #![feature(proc_macro)]

            #[macro_use]
            extern crate noop;

            #[derive(Noop)]
            struct X;

            fn main() {}
        "#);
    let noop = project("noop")
        .file("Cargo.toml", r#"
            [package]
            name = "noop"
            version = "0.0.1"
            authors = []

            [lib]
            proc-macro = true
        "#)
        .file("src/lib.rs", r#"
            #![feature(proc_macro, proc_macro_lib)]

            extern crate proc_macro;
            use proc_macro::TokenStream;

            #[proc_macro_derive(Noop)]
            pub fn noop(input: TokenStream) -> TokenStream {
                input
            }
        "#);
    noop.build();

    assert_that(client.cargo_process("build"),
                execs().with_status(0));
    assert_that(client.cargo("build"),
                execs().with_status(0));
}

#[test]
#[ignore]
fn impl_and_derive() {
    if !is_nightly() {
        return;
    }

    let client = project("client")
        .file("Cargo.toml", r#"
            [package]
            name = "client"
            version = "0.0.1"
            authors = []

            [dependencies.transmogrify]
            path = "../transmogrify"
        "#)
        .file("src/main.rs", r#"
            #![feature(proc_macro)]

            #[macro_use]
            extern crate transmogrify;

            trait ImplByTransmogrify {
                fn impl_by_transmogrify(&self) -> bool;
            }

            #[derive(Transmogrify)]
            struct X;

            fn main() {
                let x = X::new();
                assert!(x.impl_by_transmogrify());
                println!("{:?}", x);
            }
        "#);
    let transmogrify = project("transmogrify")
        .file("Cargo.toml", r#"
            [package]
            name = "transmogrify"
            version = "0.0.1"
            authors = []

            [lib]
            proc-macro = true
        "#)
        .file("src/lib.rs", r#"
            #![feature(proc_macro, proc_macro_lib)]

            extern crate proc_macro;
            use proc_macro::TokenStream;

            #[proc_macro_derive(Transmogrify)]
            #[doc(hidden)]
            pub fn transmogrify(input: TokenStream) -> TokenStream {
                assert_eq!(input.to_string(), "struct X;\n");

                "
                    impl X {
                        fn new() -> Self {
                            X { success: true }
                        }
                    }

                    impl ImplByTransmogrify for X {
                        fn impl_by_transmogrify(&self) -> bool {
                            true
                        }
                    }

                    #[derive(Debug)]
                    struct X {
                        success: bool,
                    }
                ".parse().unwrap()
            }
        "#);
    transmogrify.build();

    assert_that(client.cargo_process("build"),
                execs().with_status(0));
    assert_that(client.cargo("run"),
                execs().with_status(0).with_stdout("X { success: true }"));
}

#[test]
#[ignore]
fn plugin_and_proc_macro() {
    if !is_nightly() {
        return;
    }

    let questionable = project("questionable")
        .file("Cargo.toml", r#"
            [package]
            name = "questionable"
            version = "0.0.1"
            authors = []

            [lib]
            plugin = true
            proc-macro = true
        "#)
        .file("src/lib.rs", r#"
            #![feature(plugin_registrar, rustc_private)]
            #![feature(proc_macro, proc_macro_lib)]

            extern crate rustc_plugin;
            use rustc_plugin::Registry;

            extern crate proc_macro;
            use proc_macro::TokenStream;

            #[plugin_registrar]
            pub fn plugin_registrar(reg: &mut Registry) {}

            #[proc_macro_derive(Questionable)]
            pub fn questionable(input: TokenStream) -> TokenStream {
                input
            }
        "#);

    let msg = "  lib.plugin and lib.proc-macro cannot both be true";
    assert_that(questionable.cargo_process("build"),
                execs().with_status(101).with_stderr_contains(msg));
}
<commit_msg>Un-ignore proc-macro tests<commit_after>extern crate cargotest;
extern crate hamcrest;

use cargotest::is_nightly;
use cargotest::support::{project, execs};
use hamcrest::assert_that;

#[test]
fn noop() {
    if !is_nightly() {
        return;
    }

    let client = project("client")
        .file("Cargo.toml", r#"
            [package]
            name = "client"
            version = "0.0.1"
            authors = []

            [dependencies.noop]
            path = "../noop"
        "#)
        .file("src/main.rs", r#"
            #![feature(proc_macro)]

            #[macro_use]
            extern crate noop;

            #[derive(Noop)]
            struct X;

            fn main() {}
        "#);
    let noop = project("noop")
        .file("Cargo.toml", r#"
            [package]
            name = "noop"
            version = "0.0.1"
            authors = []

            [lib]
            proc-macro = true
        "#)
        .file("src/lib.rs", r#"
            #![feature(proc_macro, proc_macro_lib)]

            extern crate proc_macro;
            use proc_macro::TokenStream;

            #[proc_macro_derive(Noop)]
            pub fn noop(input: TokenStream) -> TokenStream {
                input
            }
        "#);
    noop.build();

    assert_that(client.cargo_process("build"),
                execs().with_status(0));
    assert_that(client.cargo("build"),
                execs().with_status(0));
}

#[test]
fn impl_and_derive() {
    if !is_nightly() {
        return;
    }

    let client = project("client")
        .file("Cargo.toml", r#"
            [package]
            name = "client"
            version = "0.0.1"
            authors = []

            [dependencies.transmogrify]
            path = "../transmogrify"
        "#)
        .file("src/main.rs", r#"
            #![feature(proc_macro)]

            #[macro_use]
            extern crate transmogrify;

            trait ImplByTransmogrify {
                fn impl_by_transmogrify(&self) -> bool;
            }

            #[derive(Transmogrify)]
            struct X;

            fn main() {
                let x = X::new();
                assert!(x.impl_by_transmogrify());
                println!("{:?}", x);
            }
        "#);
    let transmogrify = project("transmogrify")
        .file("Cargo.toml", r#"
            [package]
            name = "transmogrify"
            version = "0.0.1"
            authors = []

            [lib]
            proc-macro = true
        "#)
        .file("src/lib.rs", r#"
            #![feature(proc_macro, proc_macro_lib)]

            extern crate proc_macro;
            use proc_macro::TokenStream;

            #[proc_macro_derive(Transmogrify)]
            #[doc(hidden)]
            pub fn transmogrify(input: TokenStream) -> TokenStream {
                assert_eq!(input.to_string(), "struct X;\n");

                "
                    impl X {
                        fn new() -> Self {
                            X { success: true }
                        }
                    }

                    impl ImplByTransmogrify for X {
                        fn impl_by_transmogrify(&self) -> bool {
                            true
                        }
                    }

                    #[derive(Debug)]
                    struct X {
                        success: bool,
                    }
                ".parse().unwrap()
            }
        "#);
    transmogrify.build();

    assert_that(client.cargo_process("build"),
                execs().with_status(0));
    assert_that(client.cargo("run"),
                execs().with_status(0).with_stdout("X { success: true }"));
}

#[test]
fn plugin_and_proc_macro() {
    if !is_nightly() {
        return;
    }

    let questionable = project("questionable")
        .file("Cargo.toml", r#"
            [package]
            name = "questionable"
            version = "0.0.1"
            authors = []

            [lib]
            plugin = true
            proc-macro = true
        "#)
        .file("src/lib.rs", r#"
            #![feature(plugin_registrar, rustc_private)]
            #![feature(proc_macro, proc_macro_lib)]

            extern crate rustc_plugin;
            use rustc_plugin::Registry;

            extern crate proc_macro;
            use proc_macro::TokenStream;

            #[plugin_registrar]
            pub fn plugin_registrar(reg: &mut Registry) {}

            #[proc_macro_derive(Questionable)]
            pub fn questionable(input: TokenStream) -> TokenStream {
                input
            }
        "#);

    let msg = "  lib.plugin and lib.proc-macro cannot both be true";
    assert_that(questionable.cargo_process("build"),
                execs().with_status(101).with_stderr_contains(msg));
}
<|endoftext|>


<commit_before>//! Project changelog

// TODO: for the next breaking release rename `Error::LoadLibraryW` to `Error::LoadLibraryExW`.
// TODO: for the next breaking release use `RTLD_LAZY | RTLD_LOCAL` by default  on unix.

/// Release 0.6.7 (2021-01-14)
///
/// * Added a [`os::windows::Library::open_already_loaded`] to obtain a handle to a library that
/// must already be loaded. There is no portable equivalent for all UNIX targets. Users who do not
/// care about portability across UNIX platforms may use [`os::unix::Library::open`] with
/// `libc::RTLD_NOLOAD`;
///
/// [`os::windows::Library::open_already_loaded`]: crate::os::windows::Library::open_already_loaded
pub mod r0_6_7 {}

/// Release 0.6.6 (2020-12-03)
///
/// * Fix a double-release of resources when [`Library::close`] or [`os::windows::Library::close`]
///   is used on Windows.
///
/// [`Library::close`]: crate::Library::close
/// [`os::windows::Library::close`]: crate::os::windows::Library::close
pub mod r0_6_6 {}

/// Release 0.6.5 (2020-10-23)
///
/// * Upgrade cfg-if 0.1 to 1.0
pub mod r0_6_5 {}

/// Release 0.6.4 (2020-10-10)
///
/// * Remove use of `build.rs` making it easier to build `libloading` without cargo. It also
///   almost halves the build time of this crate.
pub mod r0_6_4 {}

/// Release 0.6.3 (2020-08-22)
///
/// * Improve documentation, allowing to view all of the os-specific functionality from
/// documentation generated for any target;
/// * Add [`os::windows::Library::this`];
/// * Added constants to use with OS-specific `Library::open`;
/// * Add [`library_filename`].
///
/// [`os::windows::Library::this`]: crate::os::windows::Library::this
/// [`library_filename`]: crate::library_filename
pub mod r0_6_3 {}

/// Release 0.6.2 (2020-05-06)
///
/// * Fixed building of this library on Illumos.
pub mod r0_6_2 {}

/// Release 0.6.1 (2020-04-15)
///
/// * Introduced a new method [`os::windows::Library::load_with_flags`];
/// * Added support for the Illumos triple.
///
/// [`os::windows::Library::load_with_flags`]: crate::os::windows::Library::load_with_flags
pub mod r0_6_1 {}

/// Release 0.6.0 (2020-04-05)
///
/// * Introduced a new method [`os::unix::Library::get_singlethreaded`];
/// * Added (untested) support for building when targetting Redox and Fuchsia;
/// * The APIs exposed by this library no longer panic and instead return an `Err` when it used
///   to panic.
///
/// ## Breaking changes
///
/// * Minimum required (stable) version of Rust to build this library is now 1.40.0;
/// * This crate now implements a custom [`Error`] type and all APIs now return this type rather
///   than returning the `std::io::Error`;
/// * `libloading::Result` has been removed;
/// * Removed the dependency on the C compiler to build this library on UNIX-like platforms.
///   `libloading` used to utilize a snippet written in C to work-around the unlikely possibility
///   of the target having a thread-unsafe implementation of the `dlerror` function. The effect of
///   the work-around was very opportunistic: it would not work if the function was called by
///   forgoing `libloading`.
///
///   Starting with 0.6.0, [`Library::get`] on platforms where `dlerror` is not MT-safe (such as
///   FreeBSD, DragonflyBSD or NetBSD) will unconditionally return an error when the underlying
///   `dlsym` returns a null pointer. For the use-cases where loading null pointers is necessary
///   consider using [`os::unix::Library::get_singlethreaded`] instead.
///
/// [`Library::get`]: crate::Library::get
/// [`os::unix::Library::get_singlethreaded`]: crate::os::unix::Library::get_singlethreaded
/// [`Error`]: crate::Error
pub mod r0_6_0 {}

/// Release 0.5.2 (2019-07-07)
///
/// * Added API to convert OS-specific `Library` and `Symbol` conversion to underlying resources.
pub mod r0_5_2 {}

/// Release 0.5.1 (2019-06-01)
///
/// * Build on Haiku targets.
pub mod r0_5_1 {}

/// Release 0.5.0 (2018-01-11)
///
/// * Update to `winapi = ^0.3`;
///
/// ## Breaking changes
///
/// * libloading now requires a C compiler to build on UNIX;
///   * This is a temporary measure until the [`linkage`] attribute is stabilised;
///   * Necessary to resolve [#32].
///
/// [`linkage`]: https://github.com/rust-lang/rust/issues/29603
/// [#32]: https://github.com/nagisa/rust_libloading/issues/32
pub mod r0_5_0 {}

/// Release 0.4.3 (2017-12-07)
///
/// * Bump lazy-static dependency to `^1.0`;
/// * `cargo test --release` now works when testing libloading.
pub mod r0_4_3 {}

/// Release 0.4.2 (2017-09-24)
///
/// * Improved error and race-condition handling on Windows;
/// * Improved documentation about thread-safety of Library;
/// * Added `Symbol::<Option<T>::lift_option() -> Option<Symbol<T>>` convenience method.
pub mod r0_4_2 {}

/// Release 0.4.1 (2017-08-29)
///
/// * Solaris support
pub mod r0_4_1 {}

/// Release 0.4.0 (2017-05-01)
///
/// * Remove build-time dependency on target_build_utils (and by extension serde/phf);
/// * Require at least version 1.14.0 of rustc to build;
///   * Actually, it is cargo which has to be more recent here. The one shipped with rustc 1.14.0
///     is what’s being required from now on.
pub mod r0_4_0 {}

/// Release 0.3.4 (2017-03-25)
///
/// * Remove rogue println!
pub mod r0_3_4 {}

/// Release 0.3.3 (2017-03-25)
///
/// * Panics when `Library::get` is called for incompatibly sized type such as named function
///   types (which are zero-sized).
pub mod r0_3_3 {}

/// Release 0.3.2 (2017-02-10)
///
/// * Minimum version required is now rustc 1.12.0;
/// * Updated dependency versions (most notably target_build_utils to 0.3.0)
pub mod r0_3_2 {}

/// Release 0.3.1 (2016-10-01)
///
/// * `Symbol<T>` and `os::*::Symbol<T>` now implement `Send` where `T: Send`;
/// * `Symbol<T>` and `os::*::Symbol<T>` now implement `Sync` where `T: Sync`;
/// * `Library` and `os::*::Library` now implement `Sync` (they were `Send` in 0.3.0 already).
pub mod r0_3_1 {}

/// Release 0.3.0 (2016-07-27)
///
/// * Greatly improved documentation, especially around platform-specific behaviours;
/// * Improved test suite by building our own library to test against;
/// * All `Library`-ies now implement `Send`.
/// * Added `impl From<os::platform::Library> for Library` and `impl From<Library> for
/// os::platform::Library` allowing wrapping and extracting the platform-specific library handle;
/// * Added methods to wrap (`Symbol::from_raw`) and unwrap (`Symbol::into_raw`) the safe `Symbol`
/// wrapper into unsafe `os::platform::Symbol`.
///
/// The last two additions focus on not restricting potential usecases of this library, allowing
/// users of the library to circumvent safety checks if need be.
///
/// ## Breaking Changes
///
/// `Library::new` defaults to `RTLD_NOW` instead of `RTLD_LAZY` on UNIX for more consistent
/// cross-platform behaviour. If a library loaded with `Library::new` had any linking errors, but
/// unresolved references weren’t forced to be resolved, the library would’ve “just worked”,
/// whereas now the call to `Library::new` will return an error signifying presence of such error.
///
/// ## os::platform
/// * Added `os::unix::Library::open` which allows specifying arbitrary flags (e.g. `RTLD_LAZY`);
/// * Added `os::windows::Library::get_ordinal` which allows finding a function or variable by its
/// ordinal number;
pub mod r0_3_0 {}
<commit_msg>Add a missing link in changelog<commit_after>//! Project changelog

// TODO: for the next breaking release rename `Error::LoadLibraryW` to `Error::LoadLibraryExW`.
// TODO: for the next breaking release use `RTLD_LAZY | RTLD_LOCAL` by default  on unix.

/// Release 0.6.7 (2021-01-14)
///
/// * Added a [`os::windows::Library::open_already_loaded`] to obtain a handle to a library that
/// must already be loaded. There is no portable equivalent for all UNIX targets. Users who do not
/// care about portability across UNIX platforms may use [`os::unix::Library::open`] with
/// `libc::RTLD_NOLOAD`;
///
/// [`os::windows::Library::open_already_loaded`]: crate::os::windows::Library::open_already_loaded
/// [`os::unix::Library::open`]: crate::os::unix::Library::open
pub mod r0_6_7 {}

/// Release 0.6.6 (2020-12-03)
///
/// * Fix a double-release of resources when [`Library::close`] or [`os::windows::Library::close`]
///   is used on Windows.
///
/// [`Library::close`]: crate::Library::close
/// [`os::windows::Library::close`]: crate::os::windows::Library::close
pub mod r0_6_6 {}

/// Release 0.6.5 (2020-10-23)
///
/// * Upgrade cfg-if 0.1 to 1.0
pub mod r0_6_5 {}

/// Release 0.6.4 (2020-10-10)
///
/// * Remove use of `build.rs` making it easier to build `libloading` without cargo. It also
///   almost halves the build time of this crate.
pub mod r0_6_4 {}

/// Release 0.6.3 (2020-08-22)
///
/// * Improve documentation, allowing to view all of the os-specific functionality from
/// documentation generated for any target;
/// * Add [`os::windows::Library::this`];
/// * Added constants to use with OS-specific `Library::open`;
/// * Add [`library_filename`].
///
/// [`os::windows::Library::this`]: crate::os::windows::Library::this
/// [`library_filename`]: crate::library_filename
pub mod r0_6_3 {}

/// Release 0.6.2 (2020-05-06)
///
/// * Fixed building of this library on Illumos.
pub mod r0_6_2 {}

/// Release 0.6.1 (2020-04-15)
///
/// * Introduced a new method [`os::windows::Library::load_with_flags`];
/// * Added support for the Illumos triple.
///
/// [`os::windows::Library::load_with_flags`]: crate::os::windows::Library::load_with_flags
pub mod r0_6_1 {}

/// Release 0.6.0 (2020-04-05)
///
/// * Introduced a new method [`os::unix::Library::get_singlethreaded`];
/// * Added (untested) support for building when targetting Redox and Fuchsia;
/// * The APIs exposed by this library no longer panic and instead return an `Err` when it used
///   to panic.
///
/// ## Breaking changes
///
/// * Minimum required (stable) version of Rust to build this library is now 1.40.0;
/// * This crate now implements a custom [`Error`] type and all APIs now return this type rather
///   than returning the `std::io::Error`;
/// * `libloading::Result` has been removed;
/// * Removed the dependency on the C compiler to build this library on UNIX-like platforms.
///   `libloading` used to utilize a snippet written in C to work-around the unlikely possibility
///   of the target having a thread-unsafe implementation of the `dlerror` function. The effect of
///   the work-around was very opportunistic: it would not work if the function was called by
///   forgoing `libloading`.
///
///   Starting with 0.6.0, [`Library::get`] on platforms where `dlerror` is not MT-safe (such as
///   FreeBSD, DragonflyBSD or NetBSD) will unconditionally return an error when the underlying
///   `dlsym` returns a null pointer. For the use-cases where loading null pointers is necessary
///   consider using [`os::unix::Library::get_singlethreaded`] instead.
///
/// [`Library::get`]: crate::Library::get
/// [`os::unix::Library::get_singlethreaded`]: crate::os::unix::Library::get_singlethreaded
/// [`Error`]: crate::Error
pub mod r0_6_0 {}

/// Release 0.5.2 (2019-07-07)
///
/// * Added API to convert OS-specific `Library` and `Symbol` conversion to underlying resources.
pub mod r0_5_2 {}

/// Release 0.5.1 (2019-06-01)
///
/// * Build on Haiku targets.
pub mod r0_5_1 {}

/// Release 0.5.0 (2018-01-11)
///
/// * Update to `winapi = ^0.3`;
///
/// ## Breaking changes
///
/// * libloading now requires a C compiler to build on UNIX;
///   * This is a temporary measure until the [`linkage`] attribute is stabilised;
///   * Necessary to resolve [#32].
///
/// [`linkage`]: https://github.com/rust-lang/rust/issues/29603
/// [#32]: https://github.com/nagisa/rust_libloading/issues/32
pub mod r0_5_0 {}

/// Release 0.4.3 (2017-12-07)
///
/// * Bump lazy-static dependency to `^1.0`;
/// * `cargo test --release` now works when testing libloading.
pub mod r0_4_3 {}

/// Release 0.4.2 (2017-09-24)
///
/// * Improved error and race-condition handling on Windows;
/// * Improved documentation about thread-safety of Library;
/// * Added `Symbol::<Option<T>::lift_option() -> Option<Symbol<T>>` convenience method.
pub mod r0_4_2 {}

/// Release 0.4.1 (2017-08-29)
///
/// * Solaris support
pub mod r0_4_1 {}

/// Release 0.4.0 (2017-05-01)
///
/// * Remove build-time dependency on target_build_utils (and by extension serde/phf);
/// * Require at least version 1.14.0 of rustc to build;
///   * Actually, it is cargo which has to be more recent here. The one shipped with rustc 1.14.0
///     is what’s being required from now on.
pub mod r0_4_0 {}

/// Release 0.3.4 (2017-03-25)
///
/// * Remove rogue println!
pub mod r0_3_4 {}

/// Release 0.3.3 (2017-03-25)
///
/// * Panics when `Library::get` is called for incompatibly sized type such as named function
///   types (which are zero-sized).
pub mod r0_3_3 {}

/// Release 0.3.2 (2017-02-10)
///
/// * Minimum version required is now rustc 1.12.0;
/// * Updated dependency versions (most notably target_build_utils to 0.3.0)
pub mod r0_3_2 {}

/// Release 0.3.1 (2016-10-01)
///
/// * `Symbol<T>` and `os::*::Symbol<T>` now implement `Send` where `T: Send`;
/// * `Symbol<T>` and `os::*::Symbol<T>` now implement `Sync` where `T: Sync`;
/// * `Library` and `os::*::Library` now implement `Sync` (they were `Send` in 0.3.0 already).
pub mod r0_3_1 {}

/// Release 0.3.0 (2016-07-27)
///
/// * Greatly improved documentation, especially around platform-specific behaviours;
/// * Improved test suite by building our own library to test against;
/// * All `Library`-ies now implement `Send`.
/// * Added `impl From<os::platform::Library> for Library` and `impl From<Library> for
/// os::platform::Library` allowing wrapping and extracting the platform-specific library handle;
/// * Added methods to wrap (`Symbol::from_raw`) and unwrap (`Symbol::into_raw`) the safe `Symbol`
/// wrapper into unsafe `os::platform::Symbol`.
///
/// The last two additions focus on not restricting potential usecases of this library, allowing
/// users of the library to circumvent safety checks if need be.
///
/// ## Breaking Changes
///
/// `Library::new` defaults to `RTLD_NOW` instead of `RTLD_LAZY` on UNIX for more consistent
/// cross-platform behaviour. If a library loaded with `Library::new` had any linking errors, but
/// unresolved references weren’t forced to be resolved, the library would’ve “just worked”,
/// whereas now the call to `Library::new` will return an error signifying presence of such error.
///
/// ## os::platform
/// * Added `os::unix::Library::open` which allows specifying arbitrary flags (e.g. `RTLD_LAZY`);
/// * Added `os::windows::Library::get_ordinal` which allows finding a function or variable by its
/// ordinal number;
pub mod r0_3_0 {}
<|endoftext|>


<commit_before>extern crate bitflags;

use super::ffi::*;

pub enum SpecialKeyring {
    ThreadKeyring,
    ProcessKeyring,
    SessionKeyring,
    UserKeyring,
    UserSessionKeyring,
    GroupKeyring,
    RequestKeyAuthKey,
}

pub type KeyringSerial = i32;

impl SpecialKeyring {
    pub fn serial(self) -> KeyringSerial {
        match self {
            SpecialKeyring::ThreadKeyring       => KEY_SPEC_THREAD_KEYRING,
            SpecialKeyring::ProcessKeyring      => KEY_SPEC_PROCESS_KEYRING,
            SpecialKeyring::SessionKeyring      => KEY_SPEC_SESSION_KEYRING,
            SpecialKeyring::UserKeyring         => KEY_SPEC_USER_KEYRING,
            SpecialKeyring::UserSessionKeyring  => KEY_SPEC_USER_SESSION_KEYRING,
            SpecialKeyring::GroupKeyring        => KEY_SPEC_GROUP_KEYRING,
            SpecialKeyring::RequestKeyAuthKey   => KEY_SPEC_REQKEY_AUTH_KEY,
        }
    }
}

pub enum DefaultKeyring {
    NoChange,
    ThreadKeyring,
    ProcessKeyring,
    SessionKeyring,
    UserKeyring,
    UserSessionKeyring,
    GroupKeyring,
    DefaultKeyring,
}

pub type KeyringDefaultSerial = i32;

impl DefaultKeyring {
    pub fn serial(self) -> KeyringDefaultSerial {
        match self {
            DefaultKeyring::NoChange            => KEY_REQKEY_DEFL_NO_CHANGE,
            DefaultKeyring::ThreadKeyring       => KEY_REQKEY_DEFL_THREAD_KEYRING,
            DefaultKeyring::ProcessKeyring      => KEY_REQKEY_DEFL_PROCESS_KEYRING,
            DefaultKeyring::SessionKeyring      => KEY_REQKEY_DEFL_SESSION_KEYRING,
            DefaultKeyring::UserKeyring         => KEY_REQKEY_DEFL_USER_KEYRING,
            DefaultKeyring::UserSessionKeyring  => KEY_REQKEY_DEFL_USER_SESSION_KEYRING,
            DefaultKeyring::GroupKeyring        => KEY_REQKEY_DEFL_GROUP_KEYRING,
            DefaultKeyring::DefaultKeyring      => KEY_REQKEY_DEFL_DEFAULT,
        }
    }
}

pub type KeyPermissions = u32;

bitflags! {
    flags KeyringPermission: key_perm_t {
        const POSSESSOR_VIEW            = KEY_POS_VIEW,
        const POSSESSOR_READ            = KEY_POS_READ,
        const POSSESSOR_WRITE           = KEY_POS_WRITE,
        const POSSESSOR_SEARCH          = KEY_POS_SEARCH,
        const POSSESSOR_LINK            = KEY_POS_LINK,
        const POSSESSOR_SET_ATTRIBUTE   = KEY_POS_SETATTR,
        const POSSESSOR_ALL             = KEY_POS_ALL,

        const USER_VIEW             = KEY_USR_VIEW,
        const USER_READ             = KEY_USR_READ,
        const USER_WRITE            = KEY_USR_WRITE,
        const USER_SEARCH           = KEY_USR_SEARCH,
        const USER_LINK             = KEY_USR_LINK,
        const USER_SET_ATTRIBUTE    = KEY_USR_SETATTR,
        const USER_ALL              = KEY_USR_ALL,

        const GROUP_VIEW            = KEY_GRP_VIEW,
        const GROUP_READ            = KEY_GRP_READ,
        const GROUP_WRITE           = KEY_GRP_WRITE,
        const GROUP_SEARCH          = KEY_GRP_SEARCH,
        const GROUP_LINK            = KEY_GRP_LINK,
        const GROUP_SET_ATTRIBUTE   = KEY_GRP_SETATTR,
        const GROUP_ALL             = KEY_GRP_ALL,

        const OTHER_VIEW            = KEY_OTH_VIEW,
        const OTHER_READ            = KEY_OTH_READ,
        const OTHER_WRITE           = KEY_OTH_WRITE,
        const OTHER_SEARCH          = KEY_OTH_SEARCH,
        const OTHER_LINK            = KEY_OTH_LINK,
        const OTHER_SET_ATTRIBUTE   = KEY_OTH_SETATTR,
        const OTHER_ALL             = KEY_OTH_ALL,
    }
}

impl KeyringPermission {
    pub fn permissions(self) -> KeyPermissions {
        self.bits
    }
}

#[test]
fn test_keyring_ids() {
    assert_eq!(SpecialKeyring::ThreadKeyring.serial(), KEY_SPEC_THREAD_KEYRING);
    assert_eq!(SpecialKeyring::ProcessKeyring.serial(), KEY_SPEC_PROCESS_KEYRING);
    assert_eq!(SpecialKeyring::SessionKeyring.serial(), KEY_SPEC_SESSION_KEYRING);
    assert_eq!(SpecialKeyring::UserKeyring.serial(), KEY_SPEC_USER_KEYRING);
    assert_eq!(SpecialKeyring::UserSessionKeyring.serial(), KEY_SPEC_USER_SESSION_KEYRING);
    assert_eq!(SpecialKeyring::GroupKeyring.serial(), KEY_SPEC_GROUP_KEYRING);
    assert_eq!(SpecialKeyring::RequestKeyAuthKey.serial(), KEY_SPEC_REQKEY_AUTH_KEY);
}

#[test]
fn test_default_keyring_ids() {
    assert_eq!(DefaultKeyring::NoChange.serial(), KEY_REQKEY_DEFL_NO_CHANGE);
    assert_eq!(DefaultKeyring::ThreadKeyring.serial(), KEY_REQKEY_DEFL_THREAD_KEYRING);
    assert_eq!(DefaultKeyring::ProcessKeyring.serial(), KEY_REQKEY_DEFL_PROCESS_KEYRING);
    assert_eq!(DefaultKeyring::SessionKeyring.serial(), KEY_REQKEY_DEFL_SESSION_KEYRING);
    assert_eq!(DefaultKeyring::UserKeyring.serial(), KEY_REQKEY_DEFL_USER_KEYRING);
    assert_eq!(DefaultKeyring::UserSessionKeyring.serial(), KEY_REQKEY_DEFL_USER_SESSION_KEYRING);
    assert_eq!(DefaultKeyring::GroupKeyring.serial(), KEY_REQKEY_DEFL_GROUP_KEYRING);
    assert_eq!(DefaultKeyring::DefaultKeyring.serial(), KEY_REQKEY_DEFL_DEFAULT);
}

#[test]
fn test_permission_bits() {
    assert_eq!(POSSESSOR_VIEW.bits, KEY_POS_VIEW);
    assert_eq!(POSSESSOR_READ.bits, KEY_POS_READ);
    assert_eq!(POSSESSOR_WRITE.bits, KEY_POS_WRITE);
    assert_eq!(POSSESSOR_SEARCH.bits, KEY_POS_SEARCH);
    assert_eq!(POSSESSOR_LINK.bits, KEY_POS_LINK);
    assert_eq!(POSSESSOR_SET_ATTRIBUTE.bits, KEY_POS_SETATTR);
    assert_eq!(POSSESSOR_ALL.bits, KEY_POS_ALL);

    assert_eq!(USER_VIEW.bits, KEY_USR_VIEW);
    assert_eq!(USER_READ.bits, KEY_USR_READ);
    assert_eq!(USER_WRITE.bits, KEY_USR_WRITE);
    assert_eq!(USER_SEARCH.bits, KEY_USR_SEARCH);
    assert_eq!(USER_LINK.bits, KEY_USR_LINK);
    assert_eq!(USER_SET_ATTRIBUTE.bits, KEY_USR_SETATTR);
    assert_eq!(USER_ALL.bits, KEY_USR_ALL);

    assert_eq!(GROUP_VIEW.bits, KEY_GRP_VIEW);
    assert_eq!(GROUP_READ.bits, KEY_GRP_READ);
    assert_eq!(GROUP_WRITE.bits, KEY_GRP_WRITE);
    assert_eq!(GROUP_SEARCH.bits, KEY_GRP_SEARCH);
    assert_eq!(GROUP_LINK.bits, KEY_GRP_LINK);
    assert_eq!(GROUP_SET_ATTRIBUTE.bits, KEY_GRP_SETATTR);
    assert_eq!(GROUP_ALL.bits, KEY_GRP_ALL);

    assert_eq!(OTHER_VIEW.bits, KEY_OTH_VIEW);
    assert_eq!(OTHER_READ.bits, KEY_OTH_READ);
    assert_eq!(OTHER_WRITE.bits, KEY_OTH_WRITE);
    assert_eq!(OTHER_SEARCH.bits, KEY_OTH_SEARCH);
    assert_eq!(OTHER_LINK.bits, KEY_OTH_LINK);
    assert_eq!(OTHER_SET_ATTRIBUTE.bits, KEY_OTH_SETATTR);
    assert_eq!(OTHER_ALL.bits, KEY_OTH_ALL);
}
<commit_msg>constants: implement a From<i32> for DefaultKeyring<commit_after>extern crate bitflags;

use super::ffi::*;

pub enum SpecialKeyring {
    ThreadKeyring,
    ProcessKeyring,
    SessionKeyring,
    UserKeyring,
    UserSessionKeyring,
    GroupKeyring,
    RequestKeyAuthKey,
}

pub type KeyringSerial = i32;

impl SpecialKeyring {
    pub fn serial(self) -> KeyringSerial {
        match self {
            SpecialKeyring::ThreadKeyring       => KEY_SPEC_THREAD_KEYRING,
            SpecialKeyring::ProcessKeyring      => KEY_SPEC_PROCESS_KEYRING,
            SpecialKeyring::SessionKeyring      => KEY_SPEC_SESSION_KEYRING,
            SpecialKeyring::UserKeyring         => KEY_SPEC_USER_KEYRING,
            SpecialKeyring::UserSessionKeyring  => KEY_SPEC_USER_SESSION_KEYRING,
            SpecialKeyring::GroupKeyring        => KEY_SPEC_GROUP_KEYRING,
            SpecialKeyring::RequestKeyAuthKey   => KEY_SPEC_REQKEY_AUTH_KEY,
        }
    }
}

pub enum DefaultKeyring {
    NoChange,
    ThreadKeyring,
    ProcessKeyring,
    SessionKeyring,
    UserKeyring,
    UserSessionKeyring,
    GroupKeyring,
    DefaultKeyring,
}

pub type KeyringDefaultSerial = i32;

impl DefaultKeyring {
    pub fn serial(self) -> KeyringDefaultSerial {
        match self {
            DefaultKeyring::NoChange            => KEY_REQKEY_DEFL_NO_CHANGE,
            DefaultKeyring::ThreadKeyring       => KEY_REQKEY_DEFL_THREAD_KEYRING,
            DefaultKeyring::ProcessKeyring      => KEY_REQKEY_DEFL_PROCESS_KEYRING,
            DefaultKeyring::SessionKeyring      => KEY_REQKEY_DEFL_SESSION_KEYRING,
            DefaultKeyring::UserKeyring         => KEY_REQKEY_DEFL_USER_KEYRING,
            DefaultKeyring::UserSessionKeyring  => KEY_REQKEY_DEFL_USER_SESSION_KEYRING,
            DefaultKeyring::GroupKeyring        => KEY_REQKEY_DEFL_GROUP_KEYRING,
            DefaultKeyring::DefaultKeyring      => KEY_REQKEY_DEFL_DEFAULT,
        }
    }
}

impl From<i32> for DefaultKeyring {
    fn from(id: i32) -> DefaultKeyring {
        match id {
            KEY_REQKEY_DEFL_NO_CHANGE               => DefaultKeyring::NoChange,
            KEY_REQKEY_DEFL_THREAD_KEYRING          => DefaultKeyring::ThreadKeyring,
            KEY_REQKEY_DEFL_PROCESS_KEYRING         => DefaultKeyring::ProcessKeyring,
            KEY_REQKEY_DEFL_SESSION_KEYRING         => DefaultKeyring::SessionKeyring,
            KEY_REQKEY_DEFL_USER_KEYRING            => DefaultKeyring::UserKeyring,
            KEY_REQKEY_DEFL_USER_SESSION_KEYRING    => DefaultKeyring::UserSessionKeyring,
            KEY_REQKEY_DEFL_GROUP_KEYRING           => DefaultKeyring::GroupKeyring,
            KEY_REQKEY_DEFL_DEFAULT                 => DefaultKeyring::DefaultKeyring,
            _                                       => panic!("Invalid value for a default keyring: {}", id),
        }
    }
}

pub type KeyPermissions = u32;

bitflags! {
    flags KeyringPermission: key_perm_t {
        const POSSESSOR_VIEW            = KEY_POS_VIEW,
        const POSSESSOR_READ            = KEY_POS_READ,
        const POSSESSOR_WRITE           = KEY_POS_WRITE,
        const POSSESSOR_SEARCH          = KEY_POS_SEARCH,
        const POSSESSOR_LINK            = KEY_POS_LINK,
        const POSSESSOR_SET_ATTRIBUTE   = KEY_POS_SETATTR,
        const POSSESSOR_ALL             = KEY_POS_ALL,

        const USER_VIEW             = KEY_USR_VIEW,
        const USER_READ             = KEY_USR_READ,
        const USER_WRITE            = KEY_USR_WRITE,
        const USER_SEARCH           = KEY_USR_SEARCH,
        const USER_LINK             = KEY_USR_LINK,
        const USER_SET_ATTRIBUTE    = KEY_USR_SETATTR,
        const USER_ALL              = KEY_USR_ALL,

        const GROUP_VIEW            = KEY_GRP_VIEW,
        const GROUP_READ            = KEY_GRP_READ,
        const GROUP_WRITE           = KEY_GRP_WRITE,
        const GROUP_SEARCH          = KEY_GRP_SEARCH,
        const GROUP_LINK            = KEY_GRP_LINK,
        const GROUP_SET_ATTRIBUTE   = KEY_GRP_SETATTR,
        const GROUP_ALL             = KEY_GRP_ALL,

        const OTHER_VIEW            = KEY_OTH_VIEW,
        const OTHER_READ            = KEY_OTH_READ,
        const OTHER_WRITE           = KEY_OTH_WRITE,
        const OTHER_SEARCH          = KEY_OTH_SEARCH,
        const OTHER_LINK            = KEY_OTH_LINK,
        const OTHER_SET_ATTRIBUTE   = KEY_OTH_SETATTR,
        const OTHER_ALL             = KEY_OTH_ALL,
    }
}

impl KeyringPermission {
    pub fn permissions(self) -> KeyPermissions {
        self.bits
    }
}

#[test]
fn test_keyring_ids() {
    assert_eq!(SpecialKeyring::ThreadKeyring.serial(), KEY_SPEC_THREAD_KEYRING);
    assert_eq!(SpecialKeyring::ProcessKeyring.serial(), KEY_SPEC_PROCESS_KEYRING);
    assert_eq!(SpecialKeyring::SessionKeyring.serial(), KEY_SPEC_SESSION_KEYRING);
    assert_eq!(SpecialKeyring::UserKeyring.serial(), KEY_SPEC_USER_KEYRING);
    assert_eq!(SpecialKeyring::UserSessionKeyring.serial(), KEY_SPEC_USER_SESSION_KEYRING);
    assert_eq!(SpecialKeyring::GroupKeyring.serial(), KEY_SPEC_GROUP_KEYRING);
    assert_eq!(SpecialKeyring::RequestKeyAuthKey.serial(), KEY_SPEC_REQKEY_AUTH_KEY);
}

#[test]
fn test_default_keyring_ids() {
    assert_eq!(DefaultKeyring::NoChange.serial(), KEY_REQKEY_DEFL_NO_CHANGE);
    assert_eq!(DefaultKeyring::ThreadKeyring.serial(), KEY_REQKEY_DEFL_THREAD_KEYRING);
    assert_eq!(DefaultKeyring::ProcessKeyring.serial(), KEY_REQKEY_DEFL_PROCESS_KEYRING);
    assert_eq!(DefaultKeyring::SessionKeyring.serial(), KEY_REQKEY_DEFL_SESSION_KEYRING);
    assert_eq!(DefaultKeyring::UserKeyring.serial(), KEY_REQKEY_DEFL_USER_KEYRING);
    assert_eq!(DefaultKeyring::UserSessionKeyring.serial(), KEY_REQKEY_DEFL_USER_SESSION_KEYRING);
    assert_eq!(DefaultKeyring::GroupKeyring.serial(), KEY_REQKEY_DEFL_GROUP_KEYRING);
    assert_eq!(DefaultKeyring::DefaultKeyring.serial(), KEY_REQKEY_DEFL_DEFAULT);
}

#[test]
fn test_permission_bits() {
    assert_eq!(POSSESSOR_VIEW.bits, KEY_POS_VIEW);
    assert_eq!(POSSESSOR_READ.bits, KEY_POS_READ);
    assert_eq!(POSSESSOR_WRITE.bits, KEY_POS_WRITE);
    assert_eq!(POSSESSOR_SEARCH.bits, KEY_POS_SEARCH);
    assert_eq!(POSSESSOR_LINK.bits, KEY_POS_LINK);
    assert_eq!(POSSESSOR_SET_ATTRIBUTE.bits, KEY_POS_SETATTR);
    assert_eq!(POSSESSOR_ALL.bits, KEY_POS_ALL);

    assert_eq!(USER_VIEW.bits, KEY_USR_VIEW);
    assert_eq!(USER_READ.bits, KEY_USR_READ);
    assert_eq!(USER_WRITE.bits, KEY_USR_WRITE);
    assert_eq!(USER_SEARCH.bits, KEY_USR_SEARCH);
    assert_eq!(USER_LINK.bits, KEY_USR_LINK);
    assert_eq!(USER_SET_ATTRIBUTE.bits, KEY_USR_SETATTR);
    assert_eq!(USER_ALL.bits, KEY_USR_ALL);

    assert_eq!(GROUP_VIEW.bits, KEY_GRP_VIEW);
    assert_eq!(GROUP_READ.bits, KEY_GRP_READ);
    assert_eq!(GROUP_WRITE.bits, KEY_GRP_WRITE);
    assert_eq!(GROUP_SEARCH.bits, KEY_GRP_SEARCH);
    assert_eq!(GROUP_LINK.bits, KEY_GRP_LINK);
    assert_eq!(GROUP_SET_ATTRIBUTE.bits, KEY_GRP_SETATTR);
    assert_eq!(GROUP_ALL.bits, KEY_GRP_ALL);

    assert_eq!(OTHER_VIEW.bits, KEY_OTH_VIEW);
    assert_eq!(OTHER_READ.bits, KEY_OTH_READ);
    assert_eq!(OTHER_WRITE.bits, KEY_OTH_WRITE);
    assert_eq!(OTHER_SEARCH.bits, KEY_OTH_SEARCH);
    assert_eq!(OTHER_LINK.bits, KEY_OTH_LINK);
    assert_eq!(OTHER_SET_ATTRIBUTE.bits, KEY_OTH_SETATTR);
    assert_eq!(OTHER_ALL.bits, KEY_OTH_ALL);
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::rc::Rc;

use channel::Channel;
use config::Config;
use logger::log;
use logger::LogLevel::*;
use net::ConnectionState;
use plugin::IrcEvent;
use protocol::Protocol;
use plugin::{PluginApi, HookData};
use plugin_handler::LoadedPlugin;
use user::{BaseUser, User};
use server::Server;

pub trait Target {
    fn get_target(&self) -> Vec<u8>;
}

impl<P: Protocol> PluginApi for NeroData<P> {
    fn get_user_by_nick(&self, nick: &[u8]) -> Option<BaseUser> {
        for user in &self.users {
            let borrowed_user = user.borrow();
            if borrowed_user.base.nick == nick.to_vec() {
                return Some(borrowed_user.base.clone());
            }
        }

        None
    }

    fn get_user_by_numeric(&self, nick: &[u8]) -> Option<BaseUser> {
        let proto = &self.protocol;
        proto.find_user_by_numeric(&self.users, nick)
    }

    fn get_user_count(&self) -> usize {
        self.users.len()
    }

    fn get_channel_count(&self) -> usize {
        self.channels.len()
    }

    fn get_server_count(&self) -> usize {
        self.servers.len()
    }

    fn send_notice(&mut self, source: &BaseUser, target: &Target, message: &[u8]) {
        self.send_textmessage(source, target, message, false);
    }

    fn send_privmsg(&mut self, source: &BaseUser, target: &Target, message: &[u8]) {
        self.send_textmessage(source, target, message, true);
    }

    fn send_textmessage(&mut self, source: &BaseUser, target: &Target, message: &[u8], privmsg: bool) {
        let target_name = target.get_target();
        let proto = &self.protocol;
        let users = &self.users;

        if privmsg {
            proto.send_privmsg(users, &mut self.write_buffer, &source, &target_name, message);
        } else {
            proto.send_notice(users, &mut self.write_buffer, &source, &target_name, message);
        }
    }

    fn send_privmsg_raw_target(&mut self, source: &BaseUser, target: &[u8], message: &[u8]) {
        let proto = &self.protocol;
        let users = &self.users;
        proto.send_privmsg(users, &mut self.write_buffer, &source, target, message);
    }
}

#[derive(Debug)]
pub struct NeroData<P: Protocol> {
    pub state: ConnectionState,
    pub now: u64,
    pub uplink: Option<Rc<RefCell<Server<P>>>>,
    pub me: Rc<RefCell<Server<P>>>,
    pub channels: Vec<Rc<RefCell<Channel<P>>>>,
    pub unbursted_channels: Vec<Vec<u8>>,
    pub servers: Vec<Rc<RefCell<Server<P>>>>,
    pub users: Vec<Rc<RefCell<User<P>>>>,
    pub plugins: Vec<LoadedPlugin>,
    pub events: Vec<IrcEvent>,
    pub config: Config,
    pub write_buffer: Vec<Vec<u8>>,
    pub protocol: P,
}

impl<P: Protocol> NeroData<P> {
    pub fn new(config: Config) -> Self {
        let my_hostname = config.uplink.hostname.clone().into_bytes();
        let my_description = config.uplink.description.clone().into_bytes();

        Self {
            state: ConnectionState::Connecting,
            now: 0,
            uplink: None,
            me: Rc::new(RefCell::new(Server::<P>::new(&my_hostname, &my_description))),
            channels: Vec::new(),
            unbursted_channels: Vec::new(),
            servers: Vec::new(),
            users: Vec::new(),
            plugins: Vec::new(),
            events: Vec::new(),
            config: config,
            write_buffer: Vec::new(),
            protocol: P::new(),
        }
    }

    pub fn add_to_buffer(&mut self, data: &[u8]) {
        self.write_buffer.push(data.into());
    }

    pub fn setup(&mut self) {
        let config = &self.config;
        let mut me_borrow = self.me.borrow_mut();
        self.protocol.setup(&mut me_borrow, config);
    }

    pub fn load_plugins(&mut self) {
        if let Some(plugins) = self.config.plugins.take() {
            for data in &plugins {
                let dynload = LoadedPlugin::new(data.file.as_str());

                match dynload {
                    Ok(mut plugin) => {

                        if let Some(events) = plugin.register_hooks() {
                            for event in events {
                                log(Debug, "CORE_DATA", format!("Registered hook"));
                                self.events.push(event);
                            }
                        }

                        if let Some(bots) = plugin.register_bots() {
                            for bot in bots {
                                let protocol = ::std::mem::replace(&mut self.protocol, P::new());
                                protocol.add_local_bot(self, &bot);
                                self.protocol = protocol;
                            }
                        }

                        log(Debug, "CORE_DATA", format!("Loaded plugin {}", plugin.name()));
                        self.plugins.push(plugin);

                    }
                    Err(e) => {
                        log(Error, "CORE_DATA", format!("Failed to load {} shared object: {}", data.file, e));
                    }
                }
            }

            self.config.plugins = Some(plugins);
        }
    }

    pub fn fire_hook(&mut self, hook_data: &HookData) {
        use std::ptr;
        use std::mem;

        let mut events = mem::replace(&mut self.events, Vec::new());
        let mut plugins = mem::replace(&mut self.plugins, Vec::new());

        for event in &mut events {
            if event.event_type == hook_data.hook_type {
                let plugin = plugins.iter_mut().filter(|x| ptr::eq(&***x, event.plugin_ptr)).next().unwrap();
                match (event.f.0)(self, &mut **plugin, hook_data) {
                    Ok(_) => {},
                    Err(e) => {
                        log(Error, "PLUGIN", format!("Error from plugin: {}", e.message));
                    }
                }
            }
        }

        self.events = events;
        self.plugins = plugins;
    }
}
<commit_msg>core_data: Add ourself to the servers list<commit_after>use std::cell::RefCell;
use std::rc::Rc;

use channel::Channel;
use config::Config;
use logger::log;
use logger::LogLevel::*;
use net::ConnectionState;
use plugin::IrcEvent;
use protocol::Protocol;
use plugin::{PluginApi, HookData};
use plugin_handler::LoadedPlugin;
use user::{BaseUser, User};
use server::Server;

pub trait Target {
    fn get_target(&self) -> Vec<u8>;
}

impl<P: Protocol> PluginApi for NeroData<P> {
    fn get_user_by_nick(&self, nick: &[u8]) -> Option<BaseUser> {
        for user in &self.users {
            let borrowed_user = user.borrow();
            if borrowed_user.base.nick == nick.to_vec() {
                return Some(borrowed_user.base.clone());
            }
        }

        None
    }

    fn get_user_by_numeric(&self, nick: &[u8]) -> Option<BaseUser> {
        let proto = &self.protocol;
        proto.find_user_by_numeric(&self.users, nick)
    }

    fn get_user_count(&self) -> usize {
        self.users.len()
    }

    fn get_channel_count(&self) -> usize {
        self.channels.len()
    }

    fn get_server_count(&self) -> usize {
        self.servers.len()
    }

    fn send_notice(&mut self, source: &BaseUser, target: &Target, message: &[u8]) {
        self.send_textmessage(source, target, message, false);
    }

    fn send_privmsg(&mut self, source: &BaseUser, target: &Target, message: &[u8]) {
        self.send_textmessage(source, target, message, true);
    }

    fn send_textmessage(&mut self, source: &BaseUser, target: &Target, message: &[u8], privmsg: bool) {
        let target_name = target.get_target();
        let proto = &self.protocol;
        let users = &self.users;

        if privmsg {
            proto.send_privmsg(users, &mut self.write_buffer, &source, &target_name, message);
        } else {
            proto.send_notice(users, &mut self.write_buffer, &source, &target_name, message);
        }
    }

    fn send_privmsg_raw_target(&mut self, source: &BaseUser, target: &[u8], message: &[u8]) {
        let proto = &self.protocol;
        let users = &self.users;
        proto.send_privmsg(users, &mut self.write_buffer, &source, target, message);
    }
}

#[derive(Debug)]
pub struct NeroData<P: Protocol> {
    pub state: ConnectionState,
    pub now: u64,
    pub uplink: Option<Rc<RefCell<Server<P>>>>,
    pub me: Rc<RefCell<Server<P>>>,
    pub channels: Vec<Rc<RefCell<Channel<P>>>>,
    pub unbursted_channels: Vec<Vec<u8>>,
    pub servers: Vec<Rc<RefCell<Server<P>>>>,
    pub users: Vec<Rc<RefCell<User<P>>>>,
    pub plugins: Vec<LoadedPlugin>,
    pub events: Vec<IrcEvent>,
    pub config: Config,
    pub write_buffer: Vec<Vec<u8>>,
    pub protocol: P,
}

impl<P: Protocol> NeroData<P> {
    pub fn new(config: Config) -> Self {
        let my_hostname = config.uplink.hostname.clone().into_bytes();
        let my_description = config.uplink.description.clone().into_bytes();
        let me = Rc::new(RefCell::new(Server::<P>::new(&my_hostname, &my_description)));

        let mut s = Self {
            state: ConnectionState::Connecting,
            now: 0,
            uplink: None,
            me: me.clone(),
            channels: Vec::new(),
            unbursted_channels: Vec::new(),
            servers: Vec::new(),
            users: Vec::new(),
            plugins: Vec::new(),
            events: Vec::new(),
            config: config,
            write_buffer: Vec::new(),
            protocol: P::new(),
        };

        s.servers.push(me);
        s
    }

    pub fn add_to_buffer(&mut self, data: &[u8]) {
        self.write_buffer.push(data.into());
    }

    pub fn setup(&mut self) {
        let config = &self.config;
        let mut me_borrow = self.me.borrow_mut();
        self.protocol.setup(&mut me_borrow, config);
    }

    pub fn load_plugins(&mut self) {
        if let Some(plugins) = self.config.plugins.take() {
            for data in &plugins {
                let dynload = LoadedPlugin::new(data.file.as_str());

                match dynload {
                    Ok(mut plugin) => {

                        if let Some(events) = plugin.register_hooks() {
                            for event in events {
                                log(Debug, "CORE_DATA", format!("Registered hook"));
                                self.events.push(event);
                            }
                        }

                        if let Some(bots) = plugin.register_bots() {
                            for bot in bots {
                                let protocol = ::std::mem::replace(&mut self.protocol, P::new());
                                protocol.add_local_bot(self, &bot);
                                self.protocol = protocol;
                            }
                        }

                        log(Debug, "CORE_DATA", format!("Loaded plugin {}", plugin.name()));
                        self.plugins.push(plugin);

                    }
                    Err(e) => {
                        log(Error, "CORE_DATA", format!("Failed to load {} shared object: {}", data.file, e));
                    }
                }
            }

            self.config.plugins = Some(plugins);
        }
    }

    pub fn fire_hook(&mut self, hook_data: &HookData) {
        use std::ptr;
        use std::mem;

        let mut events = mem::replace(&mut self.events, Vec::new());
        let mut plugins = mem::replace(&mut self.plugins, Vec::new());

        for event in &mut events {
            if event.event_type == hook_data.hook_type {
                let plugin = plugins.iter_mut().filter(|x| ptr::eq(&***x, event.plugin_ptr)).next().unwrap();
                match (event.f.0)(self, &mut **plugin, hook_data) {
                    Ok(_) => {},
                    Err(e) => {
                        log(Error, "PLUGIN", format!("Error from plugin: {}", e.message));
                    }
                }
            }
        }

        self.events = events;
        self.plugins = plugins;
    }
}
<|endoftext|>