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

//! Implementation of cross-process surfaces for Linux. This uses X pixmaps.

use platform::surface::NativeSurfaceMethods;
use texturegl::Texture;

use geom::size::Size2D;
use libc::{c_char, c_int, c_uint, c_void};
use opengles::glx::{GLXFBConfig, GLXDrawable};
use opengles::glx::{GLX_BIND_TO_TEXTURE_RGBA_EXT};
use opengles::glx::{GLX_DRAWABLE_TYPE, GLX_FRONT_EXT, GLX_PIXMAP_BIT};
use opengles::glx::{GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT};
use opengles::glx::{GLX_TEXTURE_TARGET_EXT, glXCreatePixmap, glXDestroyPixmap};
use opengles::glx::{glXGetProcAddress, glXChooseFBConfig};
use opengles::glx::{glXGetVisualFromFBConfig};
use opengles::glx::{GLX_RGBA_BIT, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_DOUBLEBUFFER};
use opengles::glx::{GLX_VENDOR};
use opengles::gl2::NO_ERROR;
use opengles::gl2;
use std::c_str::CString;
use std::mem;
use std::ptr;
use xlib::xlib::{Display, Pixmap, XCreateGC, XCreateImage, XCreatePixmap, XDefaultScreen};
use xlib::xlib::{XDisplayString, XFreePixmap, XGetGeometry, XOpenDisplay, XPutImage, XRootWindow};
use xlib::xlib::{XVisualInfo, ZPixmap};

/// The display and visual info. This is needed in order to upload on the painting side. This
/// holds a weak reference to the display and will not close it when done.
///
/// FIXME(pcwalton): Mark nonsendable and noncopyable.
pub struct NativePaintingGraphicsContext {
    display: *mut Display,
    visual_info: *mut XVisualInfo,
}

impl NativePaintingGraphicsContext {
    pub fn from_metadata(metadata: &NativeGraphicsMetadata) -> NativePaintingGraphicsContext {
        // FIXME(pcwalton): It would be more robust to actually have the compositor pass the
        // visual.
        let (compositor_visual_info, _) =
            NativeCompositingGraphicsContext::compositor_visual_info(metadata.display);

        NativePaintingGraphicsContext {
            display: metadata.display,
            visual_info: compositor_visual_info,
        }
    }
}

/// The display, visual info, and framebuffer configuration. This is needed in order to bind to a
/// texture on the compositor side. This holds only a *weak* reference to the display and does not
/// close it.
///
/// FIXME(pcwalton): Unchecked weak references are bad and can violate memory safety. This is hard
/// to fix because the Display is given to us by the native windowing system, but we should fix it
/// someday.
///
/// FIXME(pcwalton): Mark nonsendable.
pub struct NativeCompositingGraphicsContext {
    display: *mut Display,
    framebuffer_configuration: Option<GLXFBConfig>,
}

impl NativeCompositingGraphicsContext {
    /// Chooses the compositor visual info using the same algorithm that the compositor uses.
    ///
    /// FIXME(pcwalton): It would be more robust to actually have the compositor pass the visual.
    fn compositor_visual_info(display: *mut Display) -> (*mut XVisualInfo, Option<GLXFBConfig>) {
        unsafe {
            let glx_display = mem::transmute(display);

            // CONSIDER:
            // In skia, they compute the GLX_ALPHA_SIZE minimum and request
            // that as well.

            let mut fbconfig_attributes = [
                GLX_DOUBLEBUFFER, 0,
                GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT,
                GLX_BIND_TO_TEXTURE_RGBA_EXT, 1,
                GLX_RENDER_TYPE, GLX_RGBA_BIT,
                0
            ];

            let screen = XDefaultScreen(display);
            let mut number_of_configs = 0;
            let configs = glXChooseFBConfig(glx_display, screen,
                                            &mut fbconfig_attributes[0], &mut number_of_configs);
            let glXGetClientString: extern "C" fn(*mut Display, c_int) -> *const c_char =
                mem::transmute(glXGetProcAddress(mem::transmute(&"glXGetClientString\x00".as_bytes()[0])));
            assert!(glXGetClientString as *mut c_void != ptr::mut_null());
            let glx_cli_vendor_c_str = CString::new(glXGetClientString(display, GLX_VENDOR), false);
            let glx_cli_vendor = match glx_cli_vendor_c_str.as_str() { Some(s) => s,
                                                                       None => fail!("Can't get glx client vendor.") };
            if glx_cli_vendor.to_ascii().eq_ignore_case("NVIDIA".to_ascii()) ||
               glx_cli_vendor.to_ascii().eq_ignore_case("ATI".to_ascii()) {
                // NVidia (and AMD/ATI) drivers have RGBA configurations that use 24-bit XVisual, not capable of
                // representing an alpha-channel in Pixmap form, so we look for the configuration
                // with a full set of 32 bits.
                for i in range(0, number_of_configs as int) {
                    let config = *configs.offset(i);
                    let visual_info : *mut XVisualInfo = mem::transmute(glXGetVisualFromFBConfig(glx_display, config));
                    if (*visual_info).depth == 32 {
                        return (visual_info, Some(config))
                    }
                }
            } else if number_of_configs != 0 {
                let fbconfig = *configs.offset(0);
                let vi = glXGetVisualFromFBConfig(glx_display, fbconfig);
                return (mem::transmute(vi), Some(fbconfig));
            }
            fail!("Unable to locate a GLX FB configuration that supports RGBA.");
        }
    }

    /// Creates a native graphics context from the given X display connection. This uses GLX. Only
    /// the compositor is allowed to call this.
    pub fn from_display(display: *mut Display) -> NativeCompositingGraphicsContext {
        let (_, fbconfig) = NativeCompositingGraphicsContext::compositor_visual_info(display);

        NativeCompositingGraphicsContext {
            display: display,
            framebuffer_configuration: fbconfig,
        }
    }
}

/// The X display.
#[deriving(Clone)]
pub struct NativeGraphicsMetadata {
    pub display: *mut Display,
}

impl NativeGraphicsMetadata {
    /// Creates graphics metadata from a metadata descriptor.
    pub fn from_descriptor(descriptor: &NativeGraphicsMetadataDescriptor)
                           -> NativeGraphicsMetadata {
        // WARNING: We currently rely on the X display connection being the
        // same in both the Painting and Compositing contexts, as otherwise
        // the X Pixmap will not be sharable across them. Using this
        // method breaks that assumption.
        unsafe {
            let display = XOpenDisplay(descriptor.display.to_c_str().as_mut_ptr());
            if display.is_null() {
                fail!("XOpenDisplay() failed!");
            }
            NativeGraphicsMetadata {
                display: display,
            }
        }
    }
}

/// A sendable form of the X display string.
#[deriving(Clone, Decodable, Encodable)]
pub struct NativeGraphicsMetadataDescriptor {
    display: String,
}

impl NativeGraphicsMetadataDescriptor {
    /// Creates a metadata descriptor from metadata.
    pub fn from_metadata(metadata: NativeGraphicsMetadata) -> NativeGraphicsMetadataDescriptor {
        unsafe {
            let c_str = CString::new(XDisplayString(metadata.display) as *const i8, false);
            NativeGraphicsMetadataDescriptor {
                display: c_str.as_str().unwrap().to_string(),
            }
        }
    }
}

#[deriving(PartialEq)]
pub enum NativeSurfaceTransientData {
    NoTransientData,
    RenderTaskTransientData(*mut Display, *mut XVisualInfo),
}

#[deriving(Decodable, Encodable)]
pub struct NativeSurface {
    /// The pixmap.
    pixmap: Pixmap,

    /// Whether this pixmap will leak if the destructor runs. This is for debugging purposes.
    will_leak: bool,
}

impl Drop for NativeSurface {
    fn drop(&mut self) {
        if self.will_leak {
            fail!("You should have disposed of the pixmap properly with destroy()! This pixmap \
                   will leak!");
        }
    }
}

impl NativeSurface {
    pub fn from_pixmap(pixmap: Pixmap) -> NativeSurface {
        NativeSurface {
            pixmap: pixmap,
            will_leak: true,
        }
    }
}

impl NativeSurfaceMethods for NativeSurface {
    fn new(native_context: &NativePaintingGraphicsContext, size: Size2D<i32>, _stride: i32)
           -> NativeSurface {
        unsafe {
            // Create the pixmap.
            let screen = XDefaultScreen(native_context.display);
            let window = XRootWindow(native_context.display, screen);
            let pixmap = XCreatePixmap(native_context.display,
                                       window,
                                       size.width as c_uint,
                                       size.height as c_uint,
                                       (*native_context.visual_info).depth as c_uint);
            NativeSurface::from_pixmap(pixmap)
        }
    }

    /// This may only be called on the compositor side.
    fn bind_to_texture(&self,
                       native_context: &NativeCompositingGraphicsContext,
                       texture: &Texture,
                       _size: Size2D<int>) {
        unsafe {
            // Create the GLX pixmap.
            //
            // FIXME(pcwalton): RAII for exception safety?
            let mut pixmap_attributes = [
                GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
                GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
                0
            ];

            let glx_display = mem::transmute(native_context.display);

            let glx_pixmap = glXCreatePixmap(glx_display,
                                             native_context.framebuffer_configuration.expect(
                                                 "GLX 1.3 should have a framebuffer_configuration"),
                                             self.pixmap,
                                             &mut pixmap_attributes[0]);

            let glXBindTexImageEXT: extern "C" fn(*mut Display, GLXDrawable, c_int, *mut c_int) =
                mem::transmute(glXGetProcAddress(mem::transmute(&"glXBindTexImageEXT\x00".as_bytes()[0])));
            assert!(glXBindTexImageEXT as *mut c_void != ptr::mut_null());
            let _bound = texture.bind();
            glXBindTexImageEXT(native_context.display,
                               mem::transmute(glx_pixmap),
                               GLX_FRONT_EXT,
                               ptr::mut_null());
            assert_eq!(gl2::get_error(), NO_ERROR);

            // FIXME(pcwalton): Recycle these for speed?
            glXDestroyPixmap(glx_display, glx_pixmap);
        }
    }

    /// This may only be called on the painting side.
    fn upload(&mut self, graphics_context: &NativePaintingGraphicsContext, data: &[u8]) {
        unsafe {
            // Ensure that we're running on the render task. Take the display.
            let pixmap = self.pixmap;

            // Figure out the width, height, and depth of the pixmap.
            let mut root_window = 0;
            let mut x = 0;
            let mut y = 0;
            let mut width = 0;
            let mut height = 0;
            let mut border_width = 0;
            let mut depth = 0;
            let _ = XGetGeometry(graphics_context.display,
                                 mem::transmute(pixmap),
                                 &mut root_window,
                                 &mut x,
                                 &mut y,
                                 &mut width,
                                 &mut height,
                                 &mut border_width,
                                 &mut depth);

            // Create the image.
            let image = XCreateImage(graphics_context.display,
                                     (*graphics_context.visual_info).visual,
                                     depth,
                                     ZPixmap,
                                     0,
                                     mem::transmute(&data[0]),
                                     width as c_uint,
                                     height as c_uint,
                                     32,
                                     0);

            // Create the X graphics context.
            let gc = XCreateGC(graphics_context.display, pixmap, 0, ptr::mut_null());

            // Draw the image.
            let _ = XPutImage(graphics_context.display,
                              pixmap,
                              gc,
                              image,
                              0,
                              0,
                              0,
                              0,
                              width,
                              height);
        }
    }

    fn get_id(&self) -> int {
        self.pixmap as int
    }

    fn destroy(&mut self, graphics_context: &NativePaintingGraphicsContext) {
        unsafe {
            assert!(self.pixmap != 0);
            XFreePixmap(graphics_context.display, self.pixmap);
            self.mark_wont_leak()
        }
    }

    fn mark_will_leak(&mut self) {
        self.will_leak = true
    }

    fn mark_wont_leak(&mut self) {
        self.will_leak = false
    }
}

<commit_msg>Specify GLX_ALPHA_SIZE when creating Linux framebuffers<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of cross-process surfaces for Linux. This uses X pixmaps.

use platform::surface::NativeSurfaceMethods;
use texturegl::Texture;

use geom::size::Size2D;
use libc::{c_char, c_int, c_uint, c_void};
use opengles::glx::{GLXFBConfig, GLXDrawable};
use opengles::glx::{GLX_ALPHA_SIZE, GLX_BIND_TO_TEXTURE_RGBA_EXT};
use opengles::glx::{GLX_DRAWABLE_TYPE, GLX_FRONT_EXT, GLX_PIXMAP_BIT};
use opengles::glx::{GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT};
use opengles::glx::{GLX_TEXTURE_TARGET_EXT, glXCreatePixmap, glXDestroyPixmap};
use opengles::glx::{glXGetProcAddress, glXChooseFBConfig};
use opengles::glx::{glXGetVisualFromFBConfig};
use opengles::glx::{GLX_RGBA_BIT, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_DOUBLEBUFFER};
use opengles::glx::{GLX_VENDOR};
use opengles::gl2::NO_ERROR;
use opengles::gl2;
use std::c_str::CString;
use std::mem;
use std::ptr;
use xlib::xlib::{Display, Pixmap, XCreateGC, XCreateImage, XCreatePixmap, XDefaultScreen};
use xlib::xlib::{XDisplayString, XFreePixmap, XGetGeometry, XOpenDisplay, XPutImage, XRootWindow};
use xlib::xlib::{XVisualInfo, ZPixmap};

/// The display and visual info. This is needed in order to upload on the painting side. This
/// holds a weak reference to the display and will not close it when done.
///
/// FIXME(pcwalton): Mark nonsendable and noncopyable.
pub struct NativePaintingGraphicsContext {
    display: *mut Display,
    visual_info: *mut XVisualInfo,
}

impl NativePaintingGraphicsContext {
    pub fn from_metadata(metadata: &NativeGraphicsMetadata) -> NativePaintingGraphicsContext {
        // FIXME(pcwalton): It would be more robust to actually have the compositor pass the
        // visual.
        let (compositor_visual_info, _) =
            NativeCompositingGraphicsContext::compositor_visual_info(metadata.display);

        NativePaintingGraphicsContext {
            display: metadata.display,
            visual_info: compositor_visual_info,
        }
    }
}

/// The display, visual info, and framebuffer configuration. This is needed in order to bind to a
/// texture on the compositor side. This holds only a *weak* reference to the display and does not
/// close it.
///
/// FIXME(pcwalton): Unchecked weak references are bad and can violate memory safety. This is hard
/// to fix because the Display is given to us by the native windowing system, but we should fix it
/// someday.
///
/// FIXME(pcwalton): Mark nonsendable.
pub struct NativeCompositingGraphicsContext {
    display: *mut Display,
    framebuffer_configuration: Option<GLXFBConfig>,
}

impl NativeCompositingGraphicsContext {
    /// Chooses the compositor visual info using the same algorithm that the compositor uses.
    ///
    /// FIXME(pcwalton): It would be more robust to actually have the compositor pass the visual.
    fn compositor_visual_info(display: *mut Display) -> (*mut XVisualInfo, Option<GLXFBConfig>) {
        unsafe {
            let glx_display = mem::transmute(display);
            let mut fbconfig_attributes = [
                GLX_DOUBLEBUFFER, 0,
                GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT,
                GLX_BIND_TO_TEXTURE_RGBA_EXT, 1,
                GLX_RENDER_TYPE, GLX_RGBA_BIT,
                GLX_ALPHA_SIZE, 8,
                0
            ];

            let screen = XDefaultScreen(display);
            let mut number_of_configs = 0;
            let configs = glXChooseFBConfig(glx_display, screen,
                                            &mut fbconfig_attributes[0], &mut number_of_configs);
            let glXGetClientString: extern "C" fn(*mut Display, c_int) -> *const c_char =
                mem::transmute(glXGetProcAddress(mem::transmute(&"glXGetClientString\x00".as_bytes()[0])));
            assert!(glXGetClientString as *mut c_void != ptr::mut_null());
            let glx_cli_vendor_c_str = CString::new(glXGetClientString(display, GLX_VENDOR), false);
            let glx_cli_vendor = match glx_cli_vendor_c_str.as_str() { Some(s) => s,
                                                                       None => fail!("Can't get glx client vendor.") };
            if glx_cli_vendor.to_ascii().eq_ignore_case("NVIDIA".to_ascii()) ||
               glx_cli_vendor.to_ascii().eq_ignore_case("ATI".to_ascii()) {
                // NVidia (and AMD/ATI) drivers have RGBA configurations that use 24-bit XVisual, not capable of
                // representing an alpha-channel in Pixmap form, so we look for the configuration
                // with a full set of 32 bits.
                for i in range(0, number_of_configs as int) {
                    let config = *configs.offset(i);
                    let visual_info : *mut XVisualInfo = mem::transmute(glXGetVisualFromFBConfig(glx_display, config));
                    if (*visual_info).depth == 32 {
                        return (visual_info, Some(config))
                    }
                }
            } else if number_of_configs != 0 {
                let fbconfig = *configs.offset(0);
                let vi = glXGetVisualFromFBConfig(glx_display, fbconfig);
                return (mem::transmute(vi), Some(fbconfig));
            }
            fail!("Unable to locate a GLX FB configuration that supports RGBA.");
        }
    }

    /// Creates a native graphics context from the given X display connection. This uses GLX. Only
    /// the compositor is allowed to call this.
    pub fn from_display(display: *mut Display) -> NativeCompositingGraphicsContext {
        let (_, fbconfig) = NativeCompositingGraphicsContext::compositor_visual_info(display);

        NativeCompositingGraphicsContext {
            display: display,
            framebuffer_configuration: fbconfig,
        }
    }
}

/// The X display.
#[deriving(Clone)]
pub struct NativeGraphicsMetadata {
    pub display: *mut Display,
}

impl NativeGraphicsMetadata {
    /// Creates graphics metadata from a metadata descriptor.
    pub fn from_descriptor(descriptor: &NativeGraphicsMetadataDescriptor)
                           -> NativeGraphicsMetadata {
        // WARNING: We currently rely on the X display connection being the
        // same in both the Painting and Compositing contexts, as otherwise
        // the X Pixmap will not be sharable across them. Using this
        // method breaks that assumption.
        unsafe {
            let display = XOpenDisplay(descriptor.display.to_c_str().as_mut_ptr());
            if display.is_null() {
                fail!("XOpenDisplay() failed!");
            }
            NativeGraphicsMetadata {
                display: display,
            }
        }
    }
}

/// A sendable form of the X display string.
#[deriving(Clone, Decodable, Encodable)]
pub struct NativeGraphicsMetadataDescriptor {
    display: String,
}

impl NativeGraphicsMetadataDescriptor {
    /// Creates a metadata descriptor from metadata.
    pub fn from_metadata(metadata: NativeGraphicsMetadata) -> NativeGraphicsMetadataDescriptor {
        unsafe {
            let c_str = CString::new(XDisplayString(metadata.display) as *const i8, false);
            NativeGraphicsMetadataDescriptor {
                display: c_str.as_str().unwrap().to_string(),
            }
        }
    }
}

#[deriving(PartialEq)]
pub enum NativeSurfaceTransientData {
    NoTransientData,
    RenderTaskTransientData(*mut Display, *mut XVisualInfo),
}

#[deriving(Decodable, Encodable)]
pub struct NativeSurface {
    /// The pixmap.
    pixmap: Pixmap,

    /// Whether this pixmap will leak if the destructor runs. This is for debugging purposes.
    will_leak: bool,
}

impl Drop for NativeSurface {
    fn drop(&mut self) {
        if self.will_leak {
            fail!("You should have disposed of the pixmap properly with destroy()! This pixmap \
                   will leak!");
        }
    }
}

impl NativeSurface {
    pub fn from_pixmap(pixmap: Pixmap) -> NativeSurface {
        NativeSurface {
            pixmap: pixmap,
            will_leak: true,
        }
    }
}

impl NativeSurfaceMethods for NativeSurface {
    fn new(native_context: &NativePaintingGraphicsContext, size: Size2D<i32>, _stride: i32)
           -> NativeSurface {
        unsafe {
            // Create the pixmap.
            let screen = XDefaultScreen(native_context.display);
            let window = XRootWindow(native_context.display, screen);
            let pixmap = XCreatePixmap(native_context.display,
                                       window,
                                       size.width as c_uint,
                                       size.height as c_uint,
                                       (*native_context.visual_info).depth as c_uint);
            NativeSurface::from_pixmap(pixmap)
        }
    }

    /// This may only be called on the compositor side.
    fn bind_to_texture(&self,
                       native_context: &NativeCompositingGraphicsContext,
                       texture: &Texture,
                       _size: Size2D<int>) {
        unsafe {
            // Create the GLX pixmap.
            //
            // FIXME(pcwalton): RAII for exception safety?
            let mut pixmap_attributes = [
                GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
                GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
                0
            ];

            let glx_display = mem::transmute(native_context.display);

            let glx_pixmap = glXCreatePixmap(glx_display,
                                             native_context.framebuffer_configuration.expect(
                                                 "GLX 1.3 should have a framebuffer_configuration"),
                                             self.pixmap,
                                             &mut pixmap_attributes[0]);

            let glXBindTexImageEXT: extern "C" fn(*mut Display, GLXDrawable, c_int, *mut c_int) =
                mem::transmute(glXGetProcAddress(mem::transmute(&"glXBindTexImageEXT\x00".as_bytes()[0])));
            assert!(glXBindTexImageEXT as *mut c_void != ptr::mut_null());
            let _bound = texture.bind();
            glXBindTexImageEXT(native_context.display,
                               mem::transmute(glx_pixmap),
                               GLX_FRONT_EXT,
                               ptr::mut_null());
            assert_eq!(gl2::get_error(), NO_ERROR);

            // FIXME(pcwalton): Recycle these for speed?
            glXDestroyPixmap(glx_display, glx_pixmap);
        }
    }

    /// This may only be called on the painting side.
    fn upload(&mut self, graphics_context: &NativePaintingGraphicsContext, data: &[u8]) {
        unsafe {
            // Ensure that we're running on the render task. Take the display.
            let pixmap = self.pixmap;

            // Figure out the width, height, and depth of the pixmap.
            let mut root_window = 0;
            let mut x = 0;
            let mut y = 0;
            let mut width = 0;
            let mut height = 0;
            let mut border_width = 0;
            let mut depth = 0;
            let _ = XGetGeometry(graphics_context.display,
                                 mem::transmute(pixmap),
                                 &mut root_window,
                                 &mut x,
                                 &mut y,
                                 &mut width,
                                 &mut height,
                                 &mut border_width,
                                 &mut depth);

            // Create the image.
            let image = XCreateImage(graphics_context.display,
                                     (*graphics_context.visual_info).visual,
                                     depth,
                                     ZPixmap,
                                     0,
                                     mem::transmute(&data[0]),
                                     width as c_uint,
                                     height as c_uint,
                                     32,
                                     0);

            // Create the X graphics context.
            let gc = XCreateGC(graphics_context.display, pixmap, 0, ptr::mut_null());

            // Draw the image.
            let _ = XPutImage(graphics_context.display,
                              pixmap,
                              gc,
                              image,
                              0,
                              0,
                              0,
                              0,
                              width,
                              height);
        }
    }

    fn get_id(&self) -> int {
        self.pixmap as int
    }

    fn destroy(&mut self, graphics_context: &NativePaintingGraphicsContext) {
        unsafe {
            assert!(self.pixmap != 0);
            XFreePixmap(graphics_context.display, self.pixmap);
            self.mark_wont_leak()
        }
    }

    fn mark_will_leak(&mut self) {
        self.will_leak = true
    }

    fn mark_wont_leak(&mut self) {
        self.will_leak = false
    }
}

<|endoftext|>


<commit_before>use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::channel;
use std::sync::{Arc, TryLockError};
use std::thread;

use crossbeam_utils::sync::ShardedLock;
use rand::Rng;

#[derive(Eq, PartialEq, Debug)]
struct NonCopy(i32);

#[test]
fn smoke() {
    let l = ShardedLock::new(());
    drop(l.read().unwrap());
    drop(l.write().unwrap());
    drop((l.read().unwrap(), l.read().unwrap()));
    drop(l.write().unwrap());
}

#[test]
fn frob() {
    const N: u32 = 10;
    const M: usize = 1000;

    let r = Arc::new(ShardedLock::new(()));

    let (tx, rx) = channel::<()>();
    for _ in 0..N {
        let tx = tx.clone();
        let r = r.clone();
        thread::spawn(move || {
            let mut rng = rand::thread_rng();
            for _ in 0..M {
                if rng.gen_bool(1.0 / (N as f64)) {
                    drop(r.write().unwrap());
                } else {
                    drop(r.read().unwrap());
                }
            }
            drop(tx);
        });
    }
    drop(tx);
    let _ = rx.recv();
}

#[test]
fn arc_poison_wr() {
    let arc = Arc::new(ShardedLock::new(1));
    let arc2 = arc.clone();
    let _: Result<(), _> = thread::spawn(move || {
        let _lock = arc2.write().unwrap();
        panic!();
    })
    .join();
    assert!(arc.read().is_err());
}

#[test]
fn arc_poison_ww() {
    let arc = Arc::new(ShardedLock::new(1));
    assert!(!arc.is_poisoned());
    let arc2 = arc.clone();
    let _: Result<(), _> = thread::spawn(move || {
        let _lock = arc2.write().unwrap();
        panic!();
    })
    .join();
    assert!(arc.write().is_err());
    assert!(arc.is_poisoned());
}

#[test]
fn arc_no_poison_rr() {
    let arc = Arc::new(ShardedLock::new(1));
    let arc2 = arc.clone();
    let _: Result<(), _> = thread::spawn(move || {
        let _lock = arc2.read().unwrap();
        panic!();
    })
    .join();
    let lock = arc.read().unwrap();
    assert_eq!(*lock, 1);
}
#[test]
fn arc_no_poison_sl() {
    let arc = Arc::new(ShardedLock::new(1));
    let arc2 = arc.clone();
    let _: Result<(), _> = thread::spawn(move || {
        let _lock = arc2.read().unwrap();
        panic!()
    })
    .join();
    let lock = arc.write().unwrap();
    assert_eq!(*lock, 1);
}

#[test]
fn arc() {
    let arc = Arc::new(ShardedLock::new(0));
    let arc2 = arc.clone();
    let (tx, rx) = channel();

    thread::spawn(move || {
        let mut lock = arc2.write().unwrap();
        for _ in 0..10 {
            let tmp = *lock;
            *lock = -1;
            thread::yield_now();
            *lock = tmp + 1;
        }
        tx.send(()).unwrap();
    });

    // Readers try to catch the writer in the act
    let mut children = Vec::new();
    for _ in 0..5 {
        let arc3 = arc.clone();
        children.push(thread::spawn(move || {
            let lock = arc3.read().unwrap();
            assert!(*lock >= 0);
        }));
    }

    // Wait for children to pass their asserts
    for r in children {
        assert!(r.join().is_ok());
    }

    // Wait for writer to finish
    rx.recv().unwrap();
    let lock = arc.read().unwrap();
    assert_eq!(*lock, 10);
}

#[test]
fn arc_access_in_unwind() {
    let arc = Arc::new(ShardedLock::new(1));
    let arc2 = arc.clone();
    let _ = thread::spawn(move || -> () {
        struct Unwinder {
            i: Arc<ShardedLock<isize>>,
        }
        impl Drop for Unwinder {
            fn drop(&mut self) {
                let mut lock = self.i.write().unwrap();
                *lock += 1;
            }
        }
        let _u = Unwinder { i: arc2 };
        panic!();
    })
    .join();
    let lock = arc.read().unwrap();
    assert_eq!(*lock, 2);
}

#[test]
fn unsized_type() {
    let sl: &ShardedLock<[i32]> = &ShardedLock::new([1, 2, 3]);
    {
        let b = &mut *sl.write().unwrap();
        b[0] = 4;
        b[2] = 5;
    }
    let comp: &[i32] = &[4, 2, 5];
    assert_eq!(&*sl.read().unwrap(), comp);
}

#[test]
fn try_write() {
    let lock = ShardedLock::new(0isize);
    let read_guard = lock.read().unwrap();

    let write_result = lock.try_write();
    match write_result {
        Err(TryLockError::WouldBlock) => (),
        Ok(_) => assert!(
            false,
            "try_write should not succeed while read_guard is in scope"
        ),
        Err(_) => assert!(false, "unexpected error"),
    }

    drop(read_guard);
}

#[test]
fn test_into_inner() {
    let m = ShardedLock::new(NonCopy(10));
    assert_eq!(m.into_inner().unwrap(), NonCopy(10));
}

#[test]
fn test_into_inner_drop() {
    struct Foo(Arc<AtomicUsize>);
    impl Drop for Foo {
        fn drop(&mut self) {
            self.0.fetch_add(1, Ordering::SeqCst);
        }
    }
    let num_drops = Arc::new(AtomicUsize::new(0));
    let m = ShardedLock::new(Foo(num_drops.clone()));
    assert_eq!(num_drops.load(Ordering::SeqCst), 0);
    {
        let _inner = m.into_inner().unwrap();
        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
    }
    assert_eq!(num_drops.load(Ordering::SeqCst), 1);
}

#[test]
fn test_into_inner_poison() {
    let m = Arc::new(ShardedLock::new(NonCopy(10)));
    let m2 = m.clone();
    let _ = thread::spawn(move || {
        let _lock = m2.write().unwrap();
        panic!("test panic in inner thread to poison ShardedLock");
    })
    .join();

    assert!(m.is_poisoned());
    match Arc::try_unwrap(m).unwrap().into_inner() {
        Err(e) => assert_eq!(e.into_inner(), NonCopy(10)),
        Ok(x) => panic!("into_inner of poisoned ShardedLock is Ok: {:?}", x),
    }
}

#[test]
fn test_get_mut() {
    let mut m = ShardedLock::new(NonCopy(10));
    *m.get_mut().unwrap() = NonCopy(20);
    assert_eq!(m.into_inner().unwrap(), NonCopy(20));
}

#[test]
fn test_get_mut_poison() {
    let m = Arc::new(ShardedLock::new(NonCopy(10)));
    let m2 = m.clone();
    let _ = thread::spawn(move || {
        let _lock = m2.write().unwrap();
        panic!("test panic in inner thread to poison ShardedLock");
    })
    .join();

    assert!(m.is_poisoned());
    match Arc::try_unwrap(m).unwrap().get_mut() {
        Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)),
        Ok(x) => panic!("get_mut of poisoned ShardedLock is Ok: {:?}", x),
    }
}
<commit_msg>Replace `assert!(false)` with `panic!`<commit_after>use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::channel;
use std::sync::{Arc, TryLockError};
use std::thread;

use crossbeam_utils::sync::ShardedLock;
use rand::Rng;

#[derive(Eq, PartialEq, Debug)]
struct NonCopy(i32);

#[test]
fn smoke() {
    let l = ShardedLock::new(());
    drop(l.read().unwrap());
    drop(l.write().unwrap());
    drop((l.read().unwrap(), l.read().unwrap()));
    drop(l.write().unwrap());
}

#[test]
fn frob() {
    const N: u32 = 10;
    const M: usize = 1000;

    let r = Arc::new(ShardedLock::new(()));

    let (tx, rx) = channel::<()>();
    for _ in 0..N {
        let tx = tx.clone();
        let r = r.clone();
        thread::spawn(move || {
            let mut rng = rand::thread_rng();
            for _ in 0..M {
                if rng.gen_bool(1.0 / (N as f64)) {
                    drop(r.write().unwrap());
                } else {
                    drop(r.read().unwrap());
                }
            }
            drop(tx);
        });
    }
    drop(tx);
    let _ = rx.recv();
}

#[test]
fn arc_poison_wr() {
    let arc = Arc::new(ShardedLock::new(1));
    let arc2 = arc.clone();
    let _: Result<(), _> = thread::spawn(move || {
        let _lock = arc2.write().unwrap();
        panic!();
    })
    .join();
    assert!(arc.read().is_err());
}

#[test]
fn arc_poison_ww() {
    let arc = Arc::new(ShardedLock::new(1));
    assert!(!arc.is_poisoned());
    let arc2 = arc.clone();
    let _: Result<(), _> = thread::spawn(move || {
        let _lock = arc2.write().unwrap();
        panic!();
    })
    .join();
    assert!(arc.write().is_err());
    assert!(arc.is_poisoned());
}

#[test]
fn arc_no_poison_rr() {
    let arc = Arc::new(ShardedLock::new(1));
    let arc2 = arc.clone();
    let _: Result<(), _> = thread::spawn(move || {
        let _lock = arc2.read().unwrap();
        panic!();
    })
    .join();
    let lock = arc.read().unwrap();
    assert_eq!(*lock, 1);
}
#[test]
fn arc_no_poison_sl() {
    let arc = Arc::new(ShardedLock::new(1));
    let arc2 = arc.clone();
    let _: Result<(), _> = thread::spawn(move || {
        let _lock = arc2.read().unwrap();
        panic!()
    })
    .join();
    let lock = arc.write().unwrap();
    assert_eq!(*lock, 1);
}

#[test]
fn arc() {
    let arc = Arc::new(ShardedLock::new(0));
    let arc2 = arc.clone();
    let (tx, rx) = channel();

    thread::spawn(move || {
        let mut lock = arc2.write().unwrap();
        for _ in 0..10 {
            let tmp = *lock;
            *lock = -1;
            thread::yield_now();
            *lock = tmp + 1;
        }
        tx.send(()).unwrap();
    });

    // Readers try to catch the writer in the act
    let mut children = Vec::new();
    for _ in 0..5 {
        let arc3 = arc.clone();
        children.push(thread::spawn(move || {
            let lock = arc3.read().unwrap();
            assert!(*lock >= 0);
        }));
    }

    // Wait for children to pass their asserts
    for r in children {
        assert!(r.join().is_ok());
    }

    // Wait for writer to finish
    rx.recv().unwrap();
    let lock = arc.read().unwrap();
    assert_eq!(*lock, 10);
}

#[test]
fn arc_access_in_unwind() {
    let arc = Arc::new(ShardedLock::new(1));
    let arc2 = arc.clone();
    let _ = thread::spawn(move || -> () {
        struct Unwinder {
            i: Arc<ShardedLock<isize>>,
        }
        impl Drop for Unwinder {
            fn drop(&mut self) {
                let mut lock = self.i.write().unwrap();
                *lock += 1;
            }
        }
        let _u = Unwinder { i: arc2 };
        panic!();
    })
    .join();
    let lock = arc.read().unwrap();
    assert_eq!(*lock, 2);
}

#[test]
fn unsized_type() {
    let sl: &ShardedLock<[i32]> = &ShardedLock::new([1, 2, 3]);
    {
        let b = &mut *sl.write().unwrap();
        b[0] = 4;
        b[2] = 5;
    }
    let comp: &[i32] = &[4, 2, 5];
    assert_eq!(&*sl.read().unwrap(), comp);
}

#[test]
fn try_write() {
    let lock = ShardedLock::new(0isize);
    let read_guard = lock.read().unwrap();

    let write_result = lock.try_write();
    match write_result {
        Err(TryLockError::WouldBlock) => (),
        Ok(_) => panic!("try_write should not succeed while read_guard is in scope"),
        Err(_) => panic!("unexpected error"),
    }

    drop(read_guard);
}

#[test]
fn test_into_inner() {
    let m = ShardedLock::new(NonCopy(10));
    assert_eq!(m.into_inner().unwrap(), NonCopy(10));
}

#[test]
fn test_into_inner_drop() {
    struct Foo(Arc<AtomicUsize>);
    impl Drop for Foo {
        fn drop(&mut self) {
            self.0.fetch_add(1, Ordering::SeqCst);
        }
    }
    let num_drops = Arc::new(AtomicUsize::new(0));
    let m = ShardedLock::new(Foo(num_drops.clone()));
    assert_eq!(num_drops.load(Ordering::SeqCst), 0);
    {
        let _inner = m.into_inner().unwrap();
        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
    }
    assert_eq!(num_drops.load(Ordering::SeqCst), 1);
}

#[test]
fn test_into_inner_poison() {
    let m = Arc::new(ShardedLock::new(NonCopy(10)));
    let m2 = m.clone();
    let _ = thread::spawn(move || {
        let _lock = m2.write().unwrap();
        panic!("test panic in inner thread to poison ShardedLock");
    })
    .join();

    assert!(m.is_poisoned());
    match Arc::try_unwrap(m).unwrap().into_inner() {
        Err(e) => assert_eq!(e.into_inner(), NonCopy(10)),
        Ok(x) => panic!("into_inner of poisoned ShardedLock is Ok: {:?}", x),
    }
}

#[test]
fn test_get_mut() {
    let mut m = ShardedLock::new(NonCopy(10));
    *m.get_mut().unwrap() = NonCopy(20);
    assert_eq!(m.into_inner().unwrap(), NonCopy(20));
}

#[test]
fn test_get_mut_poison() {
    let m = Arc::new(ShardedLock::new(NonCopy(10)));
    let m2 = m.clone();
    let _ = thread::spawn(move || {
        let _lock = m2.write().unwrap();
        panic!("test panic in inner thread to poison ShardedLock");
    })
    .join();

    assert!(m.is_poisoned());
    match Arc::try_unwrap(m).unwrap().get_mut() {
        Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)),
        Ok(x) => panic!("get_mut of poisoned ShardedLock is Ok: {:?}", x),
    }
}
<|endoftext|>


<commit_before>//! The simplest possible implementation of the TimeSteward API.
//!
//! This implementation is unusably slow on large simulations. Its main use is to cross-check with other TimeSteward implementations to make sure they are implementing the API correctly.
//!
//!

use super::{DeterministicRandomId, SiphashIdGenerator, RowId, FieldId, Column, ExtendedTime,
            EventRng, Basics, TimeSteward, FiatEventOperationResult, ValidSince,
            TimeStewardLifetimedMethods};
use std::collections::{HashMap, BTreeMap};
use std::hash::Hash;
// use std::collections::Bound::{Included, Excluded, Unbounded};
use std::any::Any;
use std::borrow::Borrow;
use std::rc::Rc;
use rand::Rng;

#[derive (Clone)]
struct Field<B: Basics> {
  last_change: ExtendedTime<B>,
  data: Rc<Any>,
}

#[derive (Clone)]
struct StewardState<B: Basics> {
  last_change: Option<ExtendedTime<B>>,
  field_states: HashMap<FieldId, Field<B>>,
  fiat_events: BTreeMap<ExtendedTime<B>, Event<B>>,
}

struct StewardSettings<B: Basics> {
  predictors: Vec<Predictor<B>>,
  constants: B::Constants,
}
#[derive (Clone)]
pub struct Steward<B: Basics> {
  state: StewardState<B>,
  settings: Rc<StewardSettings<B>>,
}
type StewardImpl<B> = Steward<B>;
pub struct Snapshot<'a, B: Basics> {
  now: B::Time,
  state: StewardState<B>,
  settings: &'a StewardSettings<B>,
}
pub struct Mutator<'a, B: Basics> {
  now: ExtendedTime<B>,
  steward: &'a mut StewardImpl<B>,
  generator: EventRng,
}
pub struct PredictorAccessor<'a, B: Basics> {
  steward: &'a StewardImpl<B>,
  soonest_prediction: Option<(B::Time, Event<B>)>,
  dependencies_hasher: SiphashIdGenerator,
}
pub type Event<B> = Rc<for<'d, 'e> Fn(&'d mut Mutator<'e, B>)>;
pub type Predictor<B> = super::Predictor<PredictorFn<B>>;
pub type PredictorFn<B> = Rc<for<'b, 'c> Fn(&'b mut PredictorAccessor<'c, B>, RowId)>;

impl<'a, B: Basics> super::Accessor<B> for Snapshot<'a, B> {
  fn get<C: Column>(&mut self, id: RowId) -> Option<&C::FieldType> {
    self.state.get::<C>(id).map(|p| p.0)
  }
  fn constants(&self) -> &B::Constants {
    &self.settings.constants
  }
}
impl<'a, B: Basics> super::Accessor<B> for Mutator<'a, B> {
  fn get<C: Column>(&mut self, id: RowId) -> Option<&C::FieldType> {
    self.steward.state.get::<C>(id).map(|p| p.0)
  }
  fn constants(&self) -> &B::Constants {
    &self.steward.settings.constants
  }
}
impl<'a, B: Basics> super::Accessor<B> for PredictorAccessor<'a, B> {
  fn get<C: Column>(&mut self, id: RowId) -> Option<&C::FieldType> {
    self.steward.state.get::<C>(id).map(|p| {
      p.1.id.hash(&mut self.dependencies_hasher);
      p.0
    })
  }
  fn constants(&self) -> &B::Constants {
    &self.steward.settings.constants
  }
}

impl<'a, B: Basics> super::MomentaryAccessor<B> for Snapshot<'a, B> {
  fn now(&self) -> &B::Time {
    &self.now
  }
}
impl<'a, B: Basics> super::MomentaryAccessor<B> for Mutator<'a, B> {
  fn now(&self) -> &B::Time {
    &self.now.base
  }
}
impl<'a, B: Basics> PredictorAccessor<'a, B> {
  fn internal_now<'b>(&'b self) -> &'a ExtendedTime<B> {
    self.steward
        .state
        .last_change
        .as_ref()
        .expect("how can we be calling a predictor when there are no fields \
                yet?")
  }
}
impl<'a, B: Basics> super::PredictorAccessor<B> for PredictorAccessor<'a, B> {
  type Event = Event<B>;
  fn predict_immediately(&mut self, event: Event<B>) {
    let t = &self.internal_now().base;
    self.predict_at_time(t, event);
  }
  fn predict_at_time(&mut self, time: &B::Time, event: Event<B>) {
    if time < &self.internal_now().base {
      return;
    }
    if let Some((ref old_time, _)) = self.soonest_prediction {
      if old_time <= time {
        return;
      }
    }
    self.soonest_prediction = Some((time.clone(), event));
  }
}
impl<'a, B: Basics> super::Snapshot<B> for Snapshot<'a, B> {}

impl<'a, B: Basics> super::Mutator<B> for Mutator<'a, B> {
  fn set<C: Column>(&mut self, id: RowId, data: Option<C::FieldType>) {
    self.steward.state.set_opt::<C>(id, data, &self.now);
  }
  fn rng(&mut self) -> &mut EventRng {
    &mut self.generator
  }
  fn random_id(&mut self) -> RowId {
    RowId { data: [self.generator.gen::<u64>(), self.generator.gen::<u64>()] }
  }
}

// https://github.com/rust-lang/rfcs/issues/1485
trait Filter<T> {
  fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self;
}
impl<T> Filter<T> for Option<T> {
  fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self {
    self.and_then(|x| if predicate(&x) {
      Some(x)
    } else {
      None
    })
  }
}

impl<B: Basics> StewardState<B> {
  fn get<C: Column>(&self, id: RowId) -> Option<(&C::FieldType, &ExtendedTime<B>)> {
    self.field_states
      .get(&FieldId {
        row_id: id,
        column_id: C::column_id(),
      })
      .map(|something| {
        (something.data
          .downcast_ref::<C::FieldType>()
          .expect("a field had the wrong type for its column")
          .borrow(),
         &something.last_change)
      })
  }
  fn set<C: Column>(&mut self, id: RowId, value: C::FieldType, time: &ExtendedTime<B>) {
    self.field_states
      .insert(FieldId {
                row_id: id,
                column_id: C::column_id(),
              },
              Field {
                data: Rc::new(value),
                last_change: time.clone(),
              });
  }
  fn remove<C: Column>(&mut self, id: RowId) {
    self.field_states
      .remove(&FieldId {
        row_id: id,
        column_id: C::column_id(),
      });
  }
  fn set_opt<C: Column>(&mut self,
                        id: RowId,
                        value_opt: Option<C::FieldType>,
                        time: &ExtendedTime<B>) {
    if let Some(value) = value_opt {
      self.set::<C>(id, value, time);
    } else {
      self.remove::<C>(id);
    }
  }
}
impl<B: Basics> StewardImpl<B> {
  fn next_event(&self) -> Option<(ExtendedTime<B>, Event<B>)> {
    let first_fiat_event_iter =
    // range is unstable
    // https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.range
    // and cannot be enabled in stable rustc
    // https://stackoverflow.com/questions/30975088/use-of-unstable-library-feature-how-can-i-fix-those
    // so use something else...
    //  self.state.fiat_events.range(Excluded(&self.state.last_change), Unbounded).next();
      self.state.fiat_events.iter().map(|ev| (ev.0.clone(), ev.1.clone()));
    let predicted_events_iter = self.settings
      .predictors
      .iter()
      .flat_map(|predictor|
      // TODO change field_states
      // to separate by field type, for efficiency,
      // like the haskell does?
      self.state.field_states.keys().filter_map(move |field_id|
        if field_id.column_id != predictor.column_id {
          None
        } else {
          let mut pa = PredictorAccessor{
              steward: self,
              soonest_prediction: None,
              dependencies_hasher: SiphashIdGenerator::new(),
            };
          (predictor.function)(&mut pa, field_id.row_id);
          let dependencies_hash = pa.dependencies_hasher.generate();
          pa.soonest_prediction.and_then(|(event_base_time, event)|
            super::next_extended_time_of_predicted_event(
              predictor.predictor_id,
              field_id.row_id,
              dependencies_hash,
              event_base_time,
              &self.state.last_change.as_ref().expect ("how can we be calling a predictor when there are no fields yet?")
            ).map(|event_time| (event_time, event)))}));
    let events_iter = first_fiat_event_iter.chain(predicted_events_iter);
    events_iter.min_by_key(|ev| ev.0.clone())
  }

  fn execute_event(&mut self, event_time: ExtendedTime<B>, event: Event<B>) {
    event(&mut Mutator {
      now: event_time.clone(),
      steward: &mut *self,
      generator: super::generator_for_event(event_time.id),
    });
    // if it was a fiat event, clean it up:
    self.state.fiat_events.remove(&event_time);
    self.state.last_change = Some(event_time);
  }

  fn update_until_beginning_of(&mut self, target_time: &B::Time) {
    while let Some(ev) = self.next_event().filter(|ev| ev.0.base < *target_time) {
      let (event_time, event) = ev;
      self.execute_event(event_time, event);
    }
  }
}

impl<B: Basics> Steward<B> {}
impl<'a, B: Basics> TimeStewardLifetimedMethods<'a, B> for Steward<B> {
  type Snapshot = Snapshot<'a, B>;
  fn snapshot_before(&'a mut self, time: &B::Time) -> Option<Self::Snapshot> {
    if let Some(ref change) = self.state.last_change {
      if change.base >= *time {
        return None;
      }
    }
    self.update_until_beginning_of(time);
    Some(Snapshot {
      now: time.clone(),
      state: self.state.clone(),
      settings: self.settings.as_ref(),
    })
  }
}
impl<B: Basics> TimeSteward<B> for Steward<B> {
  type Event = Event<B>;
  type Predictor = Predictor<B>;

  fn valid_since(&self) -> ValidSince<B::Time> {
    match self.state.last_change {
      None => ValidSince::TheBeginning,
      Some(ref time) => ValidSince::After(time.base.clone()),
    }
  }
  fn new_empty(constants: B::Constants, predictors: Vec<Self::Predictor>) -> Self {
    StewardImpl {
      state: StewardState {
        last_change: None,
        field_states: HashMap::new(),
        fiat_events: BTreeMap::new(),
      },
      settings: Rc::new(StewardSettings {
        predictors: predictors,
        constants: constants,
      }),
    }
  }

  fn insert_fiat_event(&mut self,
                       time: B::Time,
                       id: DeterministicRandomId,
                       event: Self::Event)
                       -> FiatEventOperationResult {
    if let Some(ref change) = self.state.last_change {
      if change.base >= time {
        return FiatEventOperationResult::InvalidTime;
      }
    }
    match self.state.fiat_events.insert(super::extended_time_of_fiat_event(time, id), event) {
      None => FiatEventOperationResult::Success,
      Some(_) => FiatEventOperationResult::InvalidInput,
    }
  }
  fn erase_fiat_event(&mut self,
                      time: &B::Time,
                      id: DeterministicRandomId)
                      -> FiatEventOperationResult {
    if let Some(ref change) = self.state.last_change {
      if change.base >= *time {
        return FiatEventOperationResult::InvalidTime;
      }
    }
    match self.state.fiat_events.remove(&super::extended_time_of_fiat_event(time.clone(), id)) {
      None => FiatEventOperationResult::InvalidInput,
      Some(_) => FiatEventOperationResult::Success,
    }
  }
}
<commit_msg>start on memoized flat time steward code<commit_after>//! The simplest possible implementation of the TimeSteward API.
//!
//! This implementation is unusably slow on large simulations. Its main use is to cross-check with other TimeSteward implementations to make sure they are implementing the API correctly.
//!
//!

use super::{DeterministicRandomId, SiphashIdGenerator, RowId, FieldId, PredictorId, Column, ExtendedTime,
            EventRng, Basics, TimeSteward, FiatEventOperationResult, ValidSince,
            TimeStewardLifetimedMethods};
use std::collections::{HashMap, BTreeMap};
use std::hash::Hash;
// use std::collections::Bound::{Included, Excluded, Unbounded};
use std::any::Any;
use std::borrow::Borrow;
use std::rc::Rc;
use rand::Rng;

#[derive (Clone)]
struct Field<B: Basics> {
  last_change: ExtendedTime<B>,
  data: Rc<Any>,
}

struct Prediction<B: Basics> {
  predictor_idx: u32,
  predictor_id: PredictorId,
  prediction_is_about_row_id: RowId,
  predictor_accessed: Vec<FieldId>,
  what_will_happen: Option<(ExtendedTime<B>, Event<B>)>,
}

#[derive (Clone)]
struct StewardState<B: Basics> {
  last_change: Option<ExtendedTime<B>>,
  field_states: HashMap<FieldId, Field<B>>,
  fiat_events: BTreeMap<ExtendedTime<B>, Event<B>>,

//   predictions: BTreeMap<ExtendedTime<B>, Prediction<B>>,
//   prediction_dependencies: HashMap<FieldID, HashMap<ExtendedTime<B>>>,
}

struct StewardSettings<B: Basics> {
  predictors: Vec<Predictor<B>>,
  constants: B::Constants,
}
#[derive (Clone)]
pub struct Steward<B: Basics> {
  state: StewardState<B>,
  settings: Rc<StewardSettings<B>>,
}
type StewardImpl<B> = Steward<B>;
pub struct Snapshot<'a, B: Basics> {
  now: B::Time,
  state: StewardState<B>,
  settings: &'a StewardSettings<B>,
}
pub struct Mutator<'a, B: Basics> {
  now: ExtendedTime<B>,
  steward: &'a mut StewardImpl<B>,
  generator: EventRng,
}
pub struct PredictorAccessor<'a, B: Basics> {
  steward: &'a StewardImpl<B>,
  soonest_prediction: Option<(B::Time, Event<B>)>,
  dependencies_hasher: SiphashIdGenerator,
}
pub type Event<B> = Rc<for<'d, 'e> Fn(&'d mut Mutator<'e, B>)>;
pub type Predictor<B> = super::Predictor<PredictorFn<B>>;
pub type PredictorFn<B> = Rc<for<'b, 'c> Fn(&'b mut PredictorAccessor<'c, B>, RowId)>;

impl<'a, B: Basics> super::Accessor<B> for Snapshot<'a, B> {
  fn get<C: Column>(&mut self, id: RowId) -> Option<&C::FieldType> {
    self.state.get::<C>(id).map(|p| p.0)
  }
  fn constants(&self) -> &B::Constants {
    &self.settings.constants
  }
}
impl<'a, B: Basics> super::Accessor<B> for Mutator<'a, B> {
  fn get<C: Column>(&mut self, id: RowId) -> Option<&C::FieldType> {
    self.steward.state.get::<C>(id).map(|p| p.0)
  }
  fn constants(&self) -> &B::Constants {
    &self.steward.settings.constants
  }
}
impl<'a, B: Basics> super::Accessor<B> for PredictorAccessor<'a, B> {
  fn get<C: Column>(&mut self, id: RowId) -> Option<&C::FieldType> {
    self.steward.state.get::<C>(id).map(|p| {
      p.1.id.hash(&mut self.dependencies_hasher);
      p.0
    })
  }
  fn constants(&self) -> &B::Constants {
    &self.steward.settings.constants
  }
}

impl<'a, B: Basics> super::MomentaryAccessor<B> for Snapshot<'a, B> {
  fn now(&self) -> &B::Time {
    &self.now
  }
}
impl<'a, B: Basics> super::MomentaryAccessor<B> for Mutator<'a, B> {
  fn now(&self) -> &B::Time {
    &self.now.base
  }
}
impl<'a, B: Basics> PredictorAccessor<'a, B> {
  fn internal_now<'b>(&'b self) -> &'a ExtendedTime<B> {
    self.steward
        .state
        .last_change
        .as_ref()
        .expect("how can we be calling a predictor when there are no fields \
                yet?")
  }
}
impl<'a, B: Basics> super::PredictorAccessor<B> for PredictorAccessor<'a, B> {
  type Event = Event<B>;
  fn predict_immediately(&mut self, event: Event<B>) {
    let t = &self.internal_now().base;
    self.predict_at_time(t, event);
  }
  fn predict_at_time(&mut self, time: &B::Time, event: Event<B>) {
    if time < &self.internal_now().base {
      return;
    }
    if let Some((ref old_time, _)) = self.soonest_prediction {
      if old_time <= time {
        return;
      }
    }
    self.soonest_prediction = Some((time.clone(), event));
  }
}
impl<'a, B: Basics> super::Snapshot<B> for Snapshot<'a, B> {}

impl<'a, B: Basics> super::Mutator<B> for Mutator<'a, B> {
  fn set<C: Column>(&mut self, id: RowId, data: Option<C::FieldType>) {
    self.steward.state.set_opt::<C>(id, data, &self.now);
  }
  fn rng(&mut self) -> &mut EventRng {
    &mut self.generator
  }
  fn random_id(&mut self) -> RowId {
    RowId { data: [self.generator.gen::<u64>(), self.generator.gen::<u64>()] }
  }
}

// https://github.com/rust-lang/rfcs/issues/1485
trait Filter<T> {
  fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self;
}
impl<T> Filter<T> for Option<T> {
  fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self {
    self.and_then(|x| if predicate(&x) {
      Some(x)
    } else {
      None
    })
  }
}

impl<B: Basics> StewardState<B> {
  fn get<C: Column>(&self, id: RowId) -> Option<(&C::FieldType, &ExtendedTime<B>)> {
    self.field_states
      .get(&FieldId {
        row_id: id,
        column_id: C::column_id(),
      })
      .map(|something| {
        (something.data
          .downcast_ref::<C::FieldType>()
          .expect("a field had the wrong type for its column")
          .borrow(),
         &something.last_change)
      })
  }
  fn set<C: Column>(&mut self, id: RowId, value: C::FieldType, time: &ExtendedTime<B>) {
    self.field_states
      .insert(FieldId {
                row_id: id,
                column_id: C::column_id(),
              },
              Field {
                data: Rc::new(value),
                last_change: time.clone(),
              });
  }
  fn remove<C: Column>(&mut self, id: RowId) {
    self.field_states
      .remove(&FieldId {
        row_id: id,
        column_id: C::column_id(),
      });
  }
  fn set_opt<C: Column>(&mut self,
                        id: RowId,
                        value_opt: Option<C::FieldType>,
                        time: &ExtendedTime<B>) {
    if let Some(value) = value_opt {
      self.set::<C>(id, value, time);
    } else {
      self.remove::<C>(id);
    }
  }
}
impl<B: Basics> StewardImpl<B> {
  fn next_event(&self) -> Option<(ExtendedTime<B>, Event<B>)> {
    let first_fiat_event_iter =
    // range is unstable
    // https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.range
    // and cannot be enabled in stable rustc
    // https://stackoverflow.com/questions/30975088/use-of-unstable-library-feature-how-can-i-fix-those
    // so use something else...
    //  self.state.fiat_events.range(Excluded(&self.state.last_change), Unbounded).next();
      self.state.fiat_events.iter().map(|ev| (ev.0.clone(), ev.1.clone()));
    let predicted_events_iter = self.settings
      .predictors
      .iter()
      .flat_map(|predictor|
      // TODO change field_states
      // to separate by field type, for efficiency,
      // like the haskell does?
      self.state.field_states.keys().filter_map(move |field_id|
        if field_id.column_id != predictor.column_id {
          None
        } else {
          let mut pa = PredictorAccessor{
              steward: self,
              soonest_prediction: None,
              dependencies_hasher: SiphashIdGenerator::new(),
            };
          (predictor.function)(&mut pa, field_id.row_id);
          let dependencies_hash = pa.dependencies_hasher.generate();
          pa.soonest_prediction.and_then(|(event_base_time, event)|
            super::next_extended_time_of_predicted_event(
              predictor.predictor_id,
              field_id.row_id,
              dependencies_hash,
              event_base_time,
              &self.state.last_change.as_ref().expect ("how can we be calling a predictor when there are no fields yet?")
            ).map(|event_time| (event_time, event)))}));
    let events_iter = first_fiat_event_iter.chain(predicted_events_iter);
    events_iter.min_by_key(|ev| ev.0.clone())
  }

  fn execute_event(&mut self, event_time: ExtendedTime<B>, event: Event<B>) {
    event(&mut Mutator {
      now: event_time.clone(),
      steward: &mut *self,
      generator: super::generator_for_event(event_time.id),
    });
    // if it was a fiat event, clean it up:
    self.state.fiat_events.remove(&event_time);
    self.state.last_change = Some(event_time);
  }

  fn update_until_beginning_of(&mut self, target_time: &B::Time) {
    while let Some(ev) = self.next_event().filter(|ev| ev.0.base < *target_time) {
      let (event_time, event) = ev;
      self.execute_event(event_time, event);
    }
  }
}

impl<B: Basics> Steward<B> {}
impl<'a, B: Basics> TimeStewardLifetimedMethods<'a, B> for Steward<B> {
  type Snapshot = Snapshot<'a, B>;
  fn snapshot_before(&'a mut self, time: &B::Time) -> Option<Self::Snapshot> {
    if let Some(ref change) = self.state.last_change {
      if change.base >= *time {
        return None;
      }
    }
    self.update_until_beginning_of(time);
    Some(Snapshot {
      now: time.clone(),
      state: self.state.clone(),
      settings: self.settings.as_ref(),
    })
  }
}
impl<B: Basics> TimeSteward<B> for Steward<B> {
  type Event = Event<B>;
  type Predictor = Predictor<B>;

  fn valid_since(&self) -> ValidSince<B::Time> {
    match self.state.last_change {
      None => ValidSince::TheBeginning,
      Some(ref time) => ValidSince::After(time.base.clone()),
    }
  }
  fn new_empty(constants: B::Constants, predictors: Vec<Self::Predictor>) -> Self {
    StewardImpl {
      state: StewardState {
        last_change: None,
        field_states: HashMap::new(),
        fiat_events: BTreeMap::new(),
      },
      settings: Rc::new(StewardSettings {
        predictors: predictors,
        constants: constants,
      }),
    }
  }

  fn insert_fiat_event(&mut self,
                       time: B::Time,
                       id: DeterministicRandomId,
                       event: Self::Event)
                       -> FiatEventOperationResult {
    if let Some(ref change) = self.state.last_change {
      if change.base >= time {
        return FiatEventOperationResult::InvalidTime;
      }
    }
    match self.state.fiat_events.insert(super::extended_time_of_fiat_event(time, id), event) {
      None => FiatEventOperationResult::Success,
      Some(_) => FiatEventOperationResult::InvalidInput,
    }
  }
  fn erase_fiat_event(&mut self,
                      time: &B::Time,
                      id: DeterministicRandomId)
                      -> FiatEventOperationResult {
    if let Some(ref change) = self.state.last_change {
      if change.base >= *time {
        return FiatEventOperationResult::InvalidTime;
      }
    }
    match self.state.fiat_events.remove(&super::extended_time_of_fiat_event(time.clone(), id)) {
      None => FiatEventOperationResult::InvalidInput,
      Some(_) => FiatEventOperationResult::Success,
    }
  }
}
<|endoftext|>


<commit_before>extern crate piston;
extern crate opengl_graphics;
extern crate graphics;
#[cfg(feature = "include_sdl2")]
extern crate sdl2_window;
#[cfg(feature = "include_glfw")]
extern crate glfw_window;
#[cfg(feature = "include_glutin")]
extern crate glutin_window;

use opengl_graphics::{ GlGraphics, OpenGL };
use graphics::Graphics;
use std::cell::RefCell;
use piston::window::WindowSettings;
use piston::input::Button;
use piston::input::keyboard::Key;
#[cfg(feature = "include_sdl2")]
use sdl2_window::Sdl2Window as Window;
#[cfg(feature = "include_glfw")]
use glfw_window::GlfwWindow as Window;
#[cfg(feature = "include_glutin")]
use glutin_window::GlutinWindow as Window;

fn main() {
    let opengl = OpenGL::_3_2;
    let window = Window::new(
        opengl,
        WindowSettings {
            title: "piston-examples/user_input".to_string(),
            size: [600, 600],
            fullscreen: false,
            exit_on_esc: true,
            samples: 0,
        }
    );

    println!("Press C to turn capture cursor on/off");

    let mut capture_cursor = false;
    let ref window = RefCell::new(window);
    let ref mut gl = GlGraphics::new(opengl);
    let mut cursor = [0.0, 0.0];
    for e in piston::events(window) {
        use piston::event::*;

        if let Some(Button::Mouse(button)) = e.press_args() {
            println!("Pressed mouse button '{:?}'", button);
        }
        if let Some(Button::Keyboard(key)) = e.press_args() {
            if key == Key::C {
                println!("Turned capture cursor on");
                capture_cursor = !capture_cursor;
                // window.set(CaptureCursor(capture_cursor));
            }

            println!("Pressed keyboard key '{:?}'", key);
        };
        if let Some(button) = e.release_args() {
            match button {
                Button::Keyboard(key) => println!("Released keyboard key '{:?}'", key),
                Button::Mouse(button) => println!("Released mouse button '{:?}'", button),
            }
        };
        e.mouse_cursor(|x, y| {
            cursor = [x, y];
            println!("Mouse moved '{} {}'", x, y);
        });
        e.mouse_scroll(|dx, dy| println!("Scrolled mouse '{}, {}'", dx, dy));
        e.mouse_relative(|dx, dy| println!("Relative mouse moved '{} {}'", dx, dy));
        e.text(|text| println!("Typed '{}'", text));
        e.resize(|w, h| println!("Resized '{}, {}'", w, h));
        if let Some(focused) = e.focus_args() {
            if focused { println!("Gained focus"); }
            else { println!("Lost focus"); }
        };
        if let Some(args) = e.render_args() {
            gl.draw(
                [0, 0, args.width as i32, args.height as i32],
                |_, g| {
                    graphics::clear([1.0; 4], g);
                    draw_rectangles(cursor, &window, g);
                }
            );
        }
        e.update(|_| {});
    }
}

fn draw_rectangles<G: Graphics>(
    cursor: [f64; 2],
    window: &RefCell<Window>,
    g: &mut G,
) {
    use piston::window::{ Size, DrawSize };
    use piston::quack::Get;

    let Size([w, h]) = window.get();
    let DrawSize([dw, dh]) = window.get();
    let zoom = 0.2;
    let offset = 30.0;

    let draw_state = graphics::default_draw_state();
    let transform = graphics::abs_transform(w as f64, h as f64);
    let rect_border = graphics::Rectangle::border([1.0, 0.0, 0.0, 1.0], 1.0);

    // Cursor.
    let cursor_color = [0.0, 0.0, 0.0, 1.0];
    let zoomed_cursor = [offset + cursor[0] * zoom, offset + cursor[1] * zoom];
    graphics::ellipse(
        cursor_color,
        graphics::ellipse::circle(zoomed_cursor[0], zoomed_cursor[1], 4.0),
        transform,
        g
    );

    // User coordinates.
    rect_border.draw([offset, offset, w as f64 * zoom, h as f64 * zoom],
        draw_state, transform, g);
    let rect_border = graphics::Rectangle::border([0.0, 0.0, 1.0, 1.0], 1.0);
    rect_border.draw([offset + w as f64 * zoom, offset, dw as f64 * zoom, dh as f64 * zoom],
        draw_state, transform, g);
}
<commit_msg>Change title in user_input example<commit_after>extern crate piston;
extern crate opengl_graphics;
extern crate graphics;
#[cfg(feature = "include_sdl2")]
extern crate sdl2_window;
#[cfg(feature = "include_glfw")]
extern crate glfw_window;
#[cfg(feature = "include_glutin")]
extern crate glutin_window;

use opengl_graphics::{ GlGraphics, OpenGL };
use graphics::Graphics;
use std::cell::RefCell;
use piston::window::WindowSettings;
use piston::input::Button;
use piston::input::keyboard::Key;
#[cfg(feature = "include_sdl2")]
use sdl2_window::Sdl2Window as Window;
#[cfg(feature = "include_glfw")]
use glfw_window::GlfwWindow as Window;
#[cfg(feature = "include_glutin")]
use glutin_window::GlutinWindow as Window;

fn main() {
    let opengl = OpenGL::_3_2;
    let window = Window::new(
        opengl,
        WindowSettings {
            title: "piston-example-user_input".to_string(),
            size: [600, 600],
            fullscreen: false,
            exit_on_esc: true,
            samples: 0,
        }
    );

    println!("Press C to turn capture cursor on/off");

    let mut capture_cursor = false;
    let ref window = RefCell::new(window);
    let ref mut gl = GlGraphics::new(opengl);
    let mut cursor = [0.0, 0.0];
    for e in piston::events(window) {
        use piston::event::*;

        if let Some(Button::Mouse(button)) = e.press_args() {
            println!("Pressed mouse button '{:?}'", button);
        }
        if let Some(Button::Keyboard(key)) = e.press_args() {
            if key == Key::C {
                println!("Turned capture cursor on");
                capture_cursor = !capture_cursor;
                // window.set(CaptureCursor(capture_cursor));
            }

            println!("Pressed keyboard key '{:?}'", key);
        };
        if let Some(button) = e.release_args() {
            match button {
                Button::Keyboard(key) => println!("Released keyboard key '{:?}'", key),
                Button::Mouse(button) => println!("Released mouse button '{:?}'", button),
            }
        };
        e.mouse_cursor(|x, y| {
            cursor = [x, y];
            println!("Mouse moved '{} {}'", x, y);
        });
        e.mouse_scroll(|dx, dy| println!("Scrolled mouse '{}, {}'", dx, dy));
        e.mouse_relative(|dx, dy| println!("Relative mouse moved '{} {}'", dx, dy));
        e.text(|text| println!("Typed '{}'", text));
        e.resize(|w, h| println!("Resized '{}, {}'", w, h));
        if let Some(focused) = e.focus_args() {
            if focused { println!("Gained focus"); }
            else { println!("Lost focus"); }
        };
        if let Some(args) = e.render_args() {
            gl.draw(
                [0, 0, args.width as i32, args.height as i32],
                |_, g| {
                    graphics::clear([1.0; 4], g);
                    draw_rectangles(cursor, &window, g);
                }
            );
        }
        e.update(|_| {});
    }
}

fn draw_rectangles<G: Graphics>(
    cursor: [f64; 2],
    window: &RefCell<Window>,
    g: &mut G,
) {
    use piston::window::{ Size, DrawSize };
    use piston::quack::Get;

    let Size([w, h]) = window.get();
    let DrawSize([dw, dh]) = window.get();
    let zoom = 0.2;
    let offset = 30.0;

    let draw_state = graphics::default_draw_state();
    let transform = graphics::abs_transform(w as f64, h as f64);
    let rect_border = graphics::Rectangle::border([1.0, 0.0, 0.0, 1.0], 1.0);

    // Cursor.
    let cursor_color = [0.0, 0.0, 0.0, 1.0];
    let zoomed_cursor = [offset + cursor[0] * zoom, offset + cursor[1] * zoom];
    graphics::ellipse(
        cursor_color,
        graphics::ellipse::circle(zoomed_cursor[0], zoomed_cursor[1], 4.0),
        transform,
        g
    );

    // User coordinates.
    rect_border.draw([offset, offset, w as f64 * zoom, h as f64 * zoom],
        draw_state, transform, g);
    let rect_border = graphics::Rectangle::border([0.0, 0.0, 1.0, 1.0], 1.0);
    rect_border.draw([offset + w as f64 * zoom, offset, dw as f64 * zoom, dh as f64 * zoom],
        draw_state, transform, g);
}
<|endoftext|>


<commit_before>use std::vec;

use naulang::interpreter::frame::Frame;
use naulang::objectspace::primitives::Object;

pub struct MethodObject {
	literals: vec::Vec<Object>,
	bytecodes: vec::Vec<u32>,
	enclosing_frame: Option<Frame>,
	arg_count: u32,
	stack_depth: usize,
	// TODO: SourceMaps
}

impl MethodObject {
	pub fn new(bytecodes: vec::Vec<u32>) -> MethodObject {
		MethodObject {
			bytecodes: bytecodes.clone(),
			literals: vec::Vec::new(),
			enclosing_frame: Option::None,
			arg_count: 0,
			stack_depth: bytecodes.len(),
		}
	}

	pub fn get_bytecode(&self, at_point: usize) -> u32 {
		self.bytecodes[at_point] as u32
	}
}

#[cfg(test)]
mod tests {
	use super::MethodObject;
	use naulang::interpreter::bytecode::ByteCode;

	#[test]
	fn test_get_bytecode() {
		let method: MethodObject = MethodObject::new(vec![
			ByteCode.LOAD_CONST, 0,
		]);

		assert!(method.get_bytecode(0) == (ByteCode.LOAD_CONST as u32));
	}
}
<commit_msg>Unnecessary casts<commit_after>use std::vec;

use naulang::interpreter::frame::Frame;
use naulang::objectspace::primitives::Object;

pub struct MethodObject {
	literals: vec::Vec<Object>,
	bytecodes: vec::Vec<u32>,
	enclosing_frame: Option<Frame>,
	arg_count: u32,
	stack_depth: usize,
	// TODO: SourceMaps
}

impl MethodObject {
	pub fn new(bytecodes: vec::Vec<u32>) -> MethodObject {
		MethodObject {
			bytecodes: bytecodes.clone(),
			literals: vec::Vec::new(),
			enclosing_frame: Option::None,
			arg_count: 0,
			stack_depth: bytecodes.len(),
		}
	}

	pub fn get_bytecode(&self, at_point: usize) -> u32 {
		self.bytecodes[at_point]
	}
}

#[cfg(test)]
mod tests {
	use super::MethodObject;
	use naulang::interpreter::bytecode::ByteCode;

	#[test]
	fn test_get_bytecode() {
		let method: MethodObject = MethodObject::new(vec![
			ByteCode.LOAD_CONST, 0,
		]);

		assert!(method.get_bytecode(0) == ByteCode.LOAD_CONST);
	}
}
<|endoftext|>


<commit_before>//! Plugin which converts all paths in a `dc::File` to absolute.

use compose_yml::v2 as dc;
use std::env;
use std::marker::PhantomData;

use errors::*;
use plugins;
use plugins::{Operation, PluginNew, PluginTransform};
use project::Project;
use util::{ConductorPathExt, err};

/// Plugin which converts all paths in a `dc::File` to absolute.
#[derive(Debug)]
#[allow(missing_copy_implementations)]
pub struct Plugin {
    /// Placeholder field for future hidden fields, to keep this from being
    /// directly constructable.
    _placeholder: PhantomData<()>,
}

impl plugins::Plugin for Plugin {
    fn name(&self) -> &'static str {
        Self::plugin_name()
    }
}

impl PluginNew for Plugin {
    fn plugin_name() -> &'static str {
        "abs_path"
    }

    fn new(_project: &Project) -> Result<Self> {
        Ok(Plugin { _placeholder: PhantomData })
    }
}

impl PluginTransform for Plugin {
    fn transform(&self,
                 op: Operation,
                 ctx: &plugins::Context,
                 file: &mut dc::File)
                 -> Result<()> {
        // Give up immediately if we're not doing this for local output.
        // It's not yet clear what we should do with relative paths in
        // exported output, anyway.
        if op != Operation::Output {
            return Ok(());
        }

        for service in file.services.values_mut() {
            // Handle relative paths in `build:`.
            if let Some(ref mut build) = service.build {
                let context: &mut _ = try!(build.context.value_mut());
                if let dc::Context::Dir(ref mut path) = *context {
                    let new_path = try!(ctx.project
                        .pods_dir()
                        .join(&path)
                        .to_absolute());
                    *path = new_path;
                }
            }

            for volume in &mut service.volumes {
                let volume = try!(volume.value_mut());
                // TODO LOW: Move to `dc` library.
                let new_host = match volume.host {
                    Some(dc::HostVolume::Path(ref path)) if path.is_relative() => {
                        let new_path = ctx.project.pods_dir().join(path);
                        Some(dc::HostVolume::Path(try!(new_path.to_absolute())))
                    }
                    Some(dc::HostVolume::UserRelativePath(ref path))
                        if path.is_relative() => {

                        let home = try!(env::home_dir()
                            .ok_or_else(|| err("Cannot find HOME directory")));
                        let new_path = home.join(path);
                        Some(dc::HostVolume::Path(try!(new_path.to_absolute())))
                    }
                    ref other => other.to_owned(),
                };
                volume.host = new_host;
            }
        }

        Ok(())
    }
}

#[test]
fn converts_relative_paths_to_absolute() {
    use env_logger;
    let _ = env_logger::init();
    let proj = Project::from_example("vault_integration").unwrap();
    proj.output().unwrap();

    // Load the generated file and look at the `db` service we cloned.
    let db_file = proj.output_dir().join("pods/db.yml");
    let file = dc::File::read_from_path(db_file).unwrap();
    let db = file.services.get("db").unwrap();

    assert_eq!(db.volumes.len(), 1);
    let expected = proj.pods_dir().join("../data/db").to_absolute().unwrap();
    assert_eq!(db.volumes[0].value().unwrap().host.as_ref().unwrap(),
               &dc::HostVolume::Path(expected));
}
<commit_msg>Test fix: Disable a test for a non-recommended configuration<commit_after>//! Plugin which converts all paths in a `dc::File` to absolute.

use compose_yml::v2 as dc;
use std::env;
use std::marker::PhantomData;

use errors::*;
use plugins;
use plugins::{Operation, PluginNew, PluginTransform};
use project::Project;
use util::{ConductorPathExt, err};

/// Plugin which converts all paths in a `dc::File` to absolute.
#[derive(Debug)]
#[allow(missing_copy_implementations)]
pub struct Plugin {
    /// Placeholder field for future hidden fields, to keep this from being
    /// directly constructable.
    _placeholder: PhantomData<()>,
}

impl plugins::Plugin for Plugin {
    fn name(&self) -> &'static str {
        Self::plugin_name()
    }
}

impl PluginNew for Plugin {
    fn plugin_name() -> &'static str {
        "abs_path"
    }

    fn new(_project: &Project) -> Result<Self> {
        Ok(Plugin { _placeholder: PhantomData })
    }
}

impl PluginTransform for Plugin {
    fn transform(&self,
                 op: Operation,
                 ctx: &plugins::Context,
                 file: &mut dc::File)
                 -> Result<()> {
        // Give up immediately if we're not doing this for local output.
        // It's not yet clear what we should do with relative paths in
        // exported output, anyway.
        if op != Operation::Output {
            return Ok(());
        }

        for service in file.services.values_mut() {
            // Handle relative paths in `build:`.
            if let Some(ref mut build) = service.build {
                let context: &mut _ = try!(build.context.value_mut());
                if let dc::Context::Dir(ref mut path) = *context {
                    let new_path = try!(ctx.project
                        .pods_dir()
                        .join(&path)
                        .to_absolute());
                    *path = new_path;
                }
            }

            for volume in &mut service.volumes {
                let volume = try!(volume.value_mut());
                // TODO LOW: Move to `dc` library.
                let new_host = match volume.host {
                    Some(dc::HostVolume::Path(ref path)) if path.is_relative() => {
                        let new_path = ctx.project.pods_dir().join(path);
                        Some(dc::HostVolume::Path(try!(new_path.to_absolute())))
                    }
                    Some(dc::HostVolume::UserRelativePath(ref path))
                        if path.is_relative() => {

                        let home = try!(env::home_dir()
                            .ok_or_else(|| err("Cannot find HOME directory")));
                        let new_path = home.join(path);
                        Some(dc::HostVolume::Path(try!(new_path.to_absolute())))
                    }
                    ref other => other.to_owned(),
                };
                volume.host = new_host;
            }
        }

        Ok(())
    }
}

// TODO MED: This is no longer a recommended configuration, so we need to
// figure out a good way to test it without using it in one of our main
// example programs.
//
//#[test]
//fn converts_relative_paths_to_absolute() {
//    use env_logger;
//    let _ = env_logger::init();
//    let proj = Project::from_example("rails_hello").unwrap();
//    proj.output().unwrap();
//
//    // Load the generated file and look at the `db` service we cloned.
//    let db_file = proj.output_dir().join("pods/db.yml");
//    let file = dc::File::read_from_path(db_file).unwrap();
//    let db = file.services.get("db").unwrap();
//
//    assert_eq!(db.volumes.len(), 1);
//    let expected = proj.pods_dir().join("../data/db").to_absolute().unwrap();
//    assert_eq!(db.volumes[0].value().unwrap().host.as_ref().unwrap(),
//               &dc::HostVolume::Path(expected));
//}
<|endoftext|>


<commit_before>#[cfg(test)]
mod new_github_repository_should {
    use super::*;

    #[test]
    fn return_github_repository_with_app_name_hello_world_and_language_rust_and_program_type_library_and_user_intrepion(
    ) {
        let expected = GitHubRepository {
            app_name: "intrepion-hello-world-library".to_owned(),
            client_type: None,
            language: Language::Rust,
            program_type: ProgramType::Library,
            server_type: None,
            user: "intrepion".to_owned(),
        };

        let actual = new_github_repository(
            "intrepion",
            "hello-world",
            Language::Rust,
            ProgramType::Library,
        );

        assert_eq!(actual, expected);
    }

    #[test]
    fn return_github_repository_with_app_name_fizz_buzz_and_language_rust_and_program_type_library_and_user_intrepion(
    ) {
        let expected = GitHubRepository {
            app_name: "intrepion-fizz-buzz-library".to_owned(),
            client_type: None,
            language: Language::Rust,
            program_type: ProgramType::Library,
            server_type: None,
            user: "intrepion".to_owned(),
        };

        let actual = new_github_repository(
            "intrepion",
            "fizz-buzz",
            Language::Rust,
            ProgramType::Library,
        );

        assert_eq!(actual, expected);
    }

    #[test]
    fn return_github_repository_with_app_name_hello_world_and_language_typescript_and_program_type_library_and_user_intrepion(
    ) {
        let expected = GitHubRepository {
            app_name: "intrepion-hello-world-library".to_owned(),
            client_type: None,
            language: Language::TypeScript,
            program_type: ProgramType::Library,
            server_type: None,
            user: "intrepion".to_owned(),
        };

        let actual = new_github_repository(
            "intrepion",
            "hello-world",
            Language::TypeScript,
            ProgramType::Library,
        );

        assert_eq!(actual, expected);
    }

    #[test]
    fn return_github_repository_with_app_name_hello_world_and_language_rust_and_program_type_console_and_user_intrepion(
    ) {
        let expected = GitHubRepository {
            app_name: "intrepion-hello-world-console".to_owned(),
            client_type: None,
            language: Language::Rust,
            program_type: ProgramType::Console,
            server_type: None,
            user: "intrepion".to_owned(),
        };

        let actual = new_github_repository(
            "intrepion",
            "hello-world",
            Language::Rust,
            ProgramType::Console,
        );

        assert_eq!(actual, expected);
    }
}

#[derive(Debug, PartialEq)]
enum Language {
    Rust,
    TypeScript,
}

#[derive(Debug, PartialEq)]
enum ProgramType {
    Console,
    Library,
}

#[derive(Debug, PartialEq)]
struct GitHubRepository {
    app_name: String,
    client_type: Option<String>,
    language: Language,
    program_type: ProgramType,
    server_type: Option<String>,
    user: String,
}

fn new_github_repository(
    user: &str,
    app_name: &str,
    language: Language,
    program_type: ProgramType,
) -> GitHubRepository {
    GitHubRepository {
        app_name: match program_type {
            ProgramType::Library => format!("{app_name}-library"),
            _ => app_name.to_owned(),
        },
        client_type: None,
        language,
        program_type,
        server_type: None,
        user: "intrepion".to_owned(),
    }
}
<commit_msg>green<commit_after>#[cfg(test)]
mod new_github_repository_should {
    use super::*;

    #[test]
    fn return_github_repository_with_app_name_hello_world_and_language_rust_and_program_type_library_and_user_intrepion(
    ) {
        let expected = GitHubRepository {
            app_name: "intrepion-hello-world-library".to_owned(),
            client_type: None,
            language: Language::Rust,
            program_type: ProgramType::Library,
            server_type: None,
            user: "intrepion".to_owned(),
        };

        let actual = new_github_repository(
            "intrepion",
            "hello-world",
            Language::Rust,
            ProgramType::Library,
        );

        assert_eq!(actual, expected);
    }

    #[test]
    fn return_github_repository_with_app_name_fizz_buzz_and_language_rust_and_program_type_library_and_user_intrepion(
    ) {
        let expected = GitHubRepository {
            app_name: "intrepion-fizz-buzz-library".to_owned(),
            client_type: None,
            language: Language::Rust,
            program_type: ProgramType::Library,
            server_type: None,
            user: "intrepion".to_owned(),
        };

        let actual = new_github_repository(
            "intrepion",
            "fizz-buzz",
            Language::Rust,
            ProgramType::Library,
        );

        assert_eq!(actual, expected);
    }

    #[test]
    fn return_github_repository_with_app_name_hello_world_and_language_typescript_and_program_type_library_and_user_intrepion(
    ) {
        let expected = GitHubRepository {
            app_name: "intrepion-hello-world-library".to_owned(),
            client_type: None,
            language: Language::TypeScript,
            program_type: ProgramType::Library,
            server_type: None,
            user: "intrepion".to_owned(),
        };

        let actual = new_github_repository(
            "intrepion",
            "hello-world",
            Language::TypeScript,
            ProgramType::Library,
        );

        assert_eq!(actual, expected);
    }

    #[test]
    fn return_github_repository_with_app_name_hello_world_and_language_rust_and_program_type_console_and_user_intrepion(
    ) {
        let expected = GitHubRepository {
            app_name: "intrepion-hello-world-console".to_owned(),
            client_type: None,
            language: Language::Rust,
            program_type: ProgramType::Console,
            server_type: None,
            user: "intrepion".to_owned(),
        };

        let actual = new_github_repository(
            "intrepion",
            "hello-world",
            Language::Rust,
            ProgramType::Console,
        );

        assert_eq!(actual, expected);
    }
}

#[derive(Debug, PartialEq)]
enum Language {
    Rust,
    TypeScript,
}

#[derive(Debug, PartialEq)]
enum ProgramType {
    Console,
    Library,
}

#[derive(Debug, PartialEq)]
struct GitHubRepository {
    app_name: String,
    client_type: Option<String>,
    language: Language,
    program_type: ProgramType,
    server_type: Option<String>,
    user: String,
}

fn new_github_repository(
    user: &str,
    app_name: &str,
    language: Language,
    program_type: ProgramType,
) -> GitHubRepository {
    GitHubRepository {
        app_name: match program_type {
            ProgramType::Library => format!("{user}-{app_name}-library"),
            ProgramType::Console => format!("{user}-{app_name}-console"),
        },
        client_type: None,
        language,
        program_type,
        server_type: None,
        user: "intrepion".to_owned(),
    }
}
<|endoftext|>