<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.

#![allow(non_snake_case)]

use texturegl::Texture;

use geom::size::Size2D;
use libc::{c_char, c_int, c_uint, c_void};
use glx;
use gleam::gl;
use skia::{SkiaSkNativeSharedGLContextRef, SkiaSkNativeSharedGLContextStealSurface};
use std::ascii::OwnedAsciiExt;
use std::ffi::{CString, CStr};
use std::mem;
use std::ptr;
use std::str;
use x11::xlib::{Display, Pixmap, XCreateGC, XCreateImage, XCreatePixmap, XDefaultScreen};
use x11::xlib::{XDisplayString, XFree, XFreePixmap, XGetGeometry, XOpenDisplay, XPutImage};
use x11::xlib::{XRootWindow, 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.
#[allow(missing_copy_implementations)]
pub struct NativePaintingGraphicsContext {
    pub 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.
#[derive(Copy, Clone)]
pub struct NativeCompositingGraphicsContext {
    display: *mut Display,
    framebuffer_configuration: Option<glx::types::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<glx::types::GLXFBConfig>) {
        // If display is null, we'll assume we are going to be rendering
        // in headless mode without X running.
        if display == ptr::null_mut() {
            return (ptr::null_mut(), None);
        }

        unsafe {
            let fbconfig_attributes = [
                glx::DOUBLEBUFFER as i32, 0,
                glx::DRAWABLE_TYPE as i32, glx::PIXMAP_BIT as i32 | glx::WINDOW_BIT as i32,
                glx::BIND_TO_TEXTURE_RGBA_EXT as i32, 1,
                glx::RENDER_TYPE as i32, glx::RGBA_BIT as i32,
                glx::ALPHA_SIZE as i32, 8,
                0
            ];

            let screen = XDefaultScreen(display);
            let mut number_of_configs = 0;
            let configs = glx::ChooseFBConfig(mem::transmute(display),
                                              screen,
                                              fbconfig_attributes.as_ptr(),
                                              &mut number_of_configs);
            NativeCompositingGraphicsContext::get_compatible_configuration(display,
                                                                           configs,
                                                                           number_of_configs)
        }
    }

    fn get_compatible_configuration(display: *mut Display,
                                    configs: *mut glx::types::GLXFBConfig,
                                    number_of_configs: i32)
                                    -> (*mut XVisualInfo, Option<glx::types::GLXFBConfig>) {
        unsafe {
            if number_of_configs == 0 {
                panic!("glx::ChooseFBConfig returned no configurations.");
            }

            if !NativeCompositingGraphicsContext::need_to_find_32_bit_depth_visual(display) {
                let config = *configs.offset(0);
                let visual = glx::GetVisualFromFBConfig(mem::transmute(display), config);
                return (mem::transmute(visual), Some(config));
            }

            // 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 0..number_of_configs as isize {
                let config = *configs.offset(i);
                let visual: *mut XVisualInfo =
                    mem::transmute(glx::GetVisualFromFBConfig(mem::transmute(display), config));
                if (*visual).depth == 32 {
                    return (mem::transmute(visual), Some(config));
                }
                XFree(mem::transmute(visual));
            }

            panic!("Could not find 32-bit visual.");
        }
    }

    fn need_to_find_32_bit_depth_visual(display: *mut Display) -> bool {
        unsafe {
            let glXGetClientString: extern "C" fn(*mut Display, c_int) -> *const c_char =
                mem::transmute(glx::GetProcAddress(mem::transmute(&"glXGetClientString\x00".as_bytes()[0])));
            assert!(glXGetClientString as *mut c_void != ptr::null_mut());

            let glx_vendor = glx::GetClientString(mem::transmute(display), glx::VENDOR as i32);
            if glx_vendor == ptr::null() {
                panic!("Could not determine GLX vendor.");
            }
            let glx_vendor =
                str::from_utf8(CStr::from_ptr(glx_vendor).to_bytes())
                    .ok()
                    .expect("GLX client vendor string not in UTF-8 format.");
            let glx_vendor = String::from_str(glx_vendor).into_ascii_lowercase();
            glx_vendor.contains("nvidia") || glx_vendor.contains("ati")
        }
    }

    /// 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.
#[derive(Clone, Copy)]
pub struct NativeGraphicsMetadata {
    pub display: *mut Display,
}
unsafe impl Send for NativeGraphicsMetadata {}

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 c_str = CString::new(descriptor.display.as_bytes()).unwrap();
            let display = XOpenDisplay(c_str.as_ptr() as *mut _);
            if display.is_null() {
                panic!("XOpenDisplay() failed!");
            }
            NativeGraphicsMetadata {
                display: display,
            }
        }
    }
}

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

impl NativeGraphicsMetadataDescriptor {
    /// Creates a metadata descriptor from metadata.
    pub fn from_metadata(metadata: NativeGraphicsMetadata) -> NativeGraphicsMetadataDescriptor {
        unsafe {
            let c_str = XDisplayString(metadata.display) as *const _;
            let bytes = CStr::from_ptr(c_str).to_bytes();
            NativeGraphicsMetadataDescriptor {
                display: str::from_utf8(bytes).unwrap().to_string(),
            }
        }
    }
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct PixmapNativeSurface {
    /// The pixmap.
    pixmap: Pixmap,

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

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

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

    pub fn from_skia_shared_gl_context(context: SkiaSkNativeSharedGLContextRef)
                                       -> PixmapNativeSurface {
        unsafe {
            let surface = SkiaSkNativeSharedGLContextStealSurface(context);
            PixmapNativeSurface::from_pixmap(mem::transmute(surface))
        }
    }

    pub fn new(native_context: &NativePaintingGraphicsContext, size: Size2D<i32>, _stride: i32)
           -> PixmapNativeSurface {
        unsafe {
            // Create the pixmap.
            let screen = XDefaultScreen(native_context.display);
            let window = XRootWindow(native_context.display, screen);
            // The X server we use for testing on build machines always returns
            // visuals that report 24 bit depth. But creating a 32 bit pixmap does work, so
            // hard code the depth here.
            let pixmap = XCreatePixmap(native_context.display,
                                       window,
                                       size.width as c_uint,
                                       size.height as c_uint,
                                       32);
            PixmapNativeSurface::from_pixmap(pixmap)
        }
    }

    /// This may only be called on the compositor side.
    pub fn bind_to_texture(&self,
                           native_context: &NativeCompositingGraphicsContext,
                           texture: &Texture,
                           _: Size2D<isize>) {
        // Create the GLX pixmap.
        //
        // FIXME(pcwalton): RAII for exception safety?
        unsafe {
            let pixmap_attributes = [
                glx::TEXTURE_TARGET_EXT as i32, glx::TEXTURE_2D_EXT as i32,
                glx::TEXTURE_FORMAT_EXT as i32, glx::TEXTURE_FORMAT_RGBA_EXT as i32,
                0
            ];

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

            let glx_pixmap = glx::CreatePixmap(glx_display,
                                             native_context.framebuffer_configuration.expect(
                                                 "GLX 1.3 should have a framebuffer_configuration"),
                                             self.pixmap,
                                             pixmap_attributes.as_ptr());

            let glXBindTexImageEXT: extern "C" fn(*mut Display, glx::types::GLXDrawable, c_int, *mut c_int) =
                mem::transmute(glx::GetProcAddress(mem::transmute(&"glXBindTexImageEXT\x00".as_bytes()[0])));
            assert!(glXBindTexImageEXT as *mut c_void != ptr::null_mut());
            let _bound = texture.bind();
            glXBindTexImageEXT(native_context.display,
                               mem::transmute(glx_pixmap),
                               glx::FRONT_EXT  as i32,
                               ptr::null_mut());
            assert_eq!(gl::GetError(), gl::NO_ERROR);

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

    /// This may only be called on the painting side.
    pub 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::null_mut());

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

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

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

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

    pub fn mark_wont_leak(&mut self) {
        self.will_leak = false;
    }
}
<commit_msg>remove glGetError call from linux surface<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.

#![allow(non_snake_case)]

use texturegl::Texture;

use geom::size::Size2D;
use libc::{c_char, c_int, c_uint, c_void};
use glx;
use gleam::gl;
use skia::{SkiaSkNativeSharedGLContextRef, SkiaSkNativeSharedGLContextStealSurface};
use std::ascii::OwnedAsciiExt;
use std::ffi::{CString, CStr};
use std::mem;
use std::ptr;
use std::str;
use x11::xlib::{Display, Pixmap, XCreateGC, XCreateImage, XCreatePixmap, XDefaultScreen};
use x11::xlib::{XDisplayString, XFree, XFreePixmap, XGetGeometry, XOpenDisplay, XPutImage};
use x11::xlib::{XRootWindow, 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.
#[allow(missing_copy_implementations)]
pub struct NativePaintingGraphicsContext {
    pub 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.
#[derive(Copy, Clone)]
pub struct NativeCompositingGraphicsContext {
    display: *mut Display,
    framebuffer_configuration: Option<glx::types::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<glx::types::GLXFBConfig>) {
        // If display is null, we'll assume we are going to be rendering
        // in headless mode without X running.
        if display == ptr::null_mut() {
            return (ptr::null_mut(), None);
        }

        unsafe {
            let fbconfig_attributes = [
                glx::DOUBLEBUFFER as i32, 0,
                glx::DRAWABLE_TYPE as i32, glx::PIXMAP_BIT as i32 | glx::WINDOW_BIT as i32,
                glx::BIND_TO_TEXTURE_RGBA_EXT as i32, 1,
                glx::RENDER_TYPE as i32, glx::RGBA_BIT as i32,
                glx::ALPHA_SIZE as i32, 8,
                0
            ];

            let screen = XDefaultScreen(display);
            let mut number_of_configs = 0;
            let configs = glx::ChooseFBConfig(mem::transmute(display),
                                              screen,
                                              fbconfig_attributes.as_ptr(),
                                              &mut number_of_configs);
            NativeCompositingGraphicsContext::get_compatible_configuration(display,
                                                                           configs,
                                                                           number_of_configs)
        }
    }

    fn get_compatible_configuration(display: *mut Display,
                                    configs: *mut glx::types::GLXFBConfig,
                                    number_of_configs: i32)
                                    -> (*mut XVisualInfo, Option<glx::types::GLXFBConfig>) {
        unsafe {
            if number_of_configs == 0 {
                panic!("glx::ChooseFBConfig returned no configurations.");
            }

            if !NativeCompositingGraphicsContext::need_to_find_32_bit_depth_visual(display) {
                let config = *configs.offset(0);
                let visual = glx::GetVisualFromFBConfig(mem::transmute(display), config);
                return (mem::transmute(visual), Some(config));
            }

            // 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 0..number_of_configs as isize {
                let config = *configs.offset(i);
                let visual: *mut XVisualInfo =
                    mem::transmute(glx::GetVisualFromFBConfig(mem::transmute(display), config));
                if (*visual).depth == 32 {
                    return (mem::transmute(visual), Some(config));
                }
                XFree(mem::transmute(visual));
            }

            panic!("Could not find 32-bit visual.");
        }
    }

    fn need_to_find_32_bit_depth_visual(display: *mut Display) -> bool {
        unsafe {
            let glXGetClientString: extern "C" fn(*mut Display, c_int) -> *const c_char =
                mem::transmute(glx::GetProcAddress(mem::transmute(&"glXGetClientString\x00".as_bytes()[0])));
            assert!(glXGetClientString as *mut c_void != ptr::null_mut());

            let glx_vendor = glx::GetClientString(mem::transmute(display), glx::VENDOR as i32);
            if glx_vendor == ptr::null() {
                panic!("Could not determine GLX vendor.");
            }
            let glx_vendor =
                str::from_utf8(CStr::from_ptr(glx_vendor).to_bytes())
                    .ok()
                    .expect("GLX client vendor string not in UTF-8 format.");
            let glx_vendor = String::from_str(glx_vendor).into_ascii_lowercase();
            glx_vendor.contains("nvidia") || glx_vendor.contains("ati")
        }
    }

    /// 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.
#[derive(Clone, Copy)]
pub struct NativeGraphicsMetadata {
    pub display: *mut Display,
}
unsafe impl Send for NativeGraphicsMetadata {}

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 c_str = CString::new(descriptor.display.as_bytes()).unwrap();
            let display = XOpenDisplay(c_str.as_ptr() as *mut _);
            if display.is_null() {
                panic!("XOpenDisplay() failed!");
            }
            NativeGraphicsMetadata {
                display: display,
            }
        }
    }
}

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

impl NativeGraphicsMetadataDescriptor {
    /// Creates a metadata descriptor from metadata.
    pub fn from_metadata(metadata: NativeGraphicsMetadata) -> NativeGraphicsMetadataDescriptor {
        unsafe {
            let c_str = XDisplayString(metadata.display) as *const _;
            let bytes = CStr::from_ptr(c_str).to_bytes();
            NativeGraphicsMetadataDescriptor {
                display: str::from_utf8(bytes).unwrap().to_string(),
            }
        }
    }
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct PixmapNativeSurface {
    /// The pixmap.
    pixmap: Pixmap,

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

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

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

    pub fn from_skia_shared_gl_context(context: SkiaSkNativeSharedGLContextRef)
                                       -> PixmapNativeSurface {
        unsafe {
            let surface = SkiaSkNativeSharedGLContextStealSurface(context);
            PixmapNativeSurface::from_pixmap(mem::transmute(surface))
        }
    }

    pub fn new(native_context: &NativePaintingGraphicsContext, size: Size2D<i32>, _stride: i32)
           -> PixmapNativeSurface {
        unsafe {
            // Create the pixmap.
            let screen = XDefaultScreen(native_context.display);
            let window = XRootWindow(native_context.display, screen);
            // The X server we use for testing on build machines always returns
            // visuals that report 24 bit depth. But creating a 32 bit pixmap does work, so
            // hard code the depth here.
            let pixmap = XCreatePixmap(native_context.display,
                                       window,
                                       size.width as c_uint,
                                       size.height as c_uint,
                                       32);
            PixmapNativeSurface::from_pixmap(pixmap)
        }
    }

    /// This may only be called on the compositor side.
    pub fn bind_to_texture(&self,
                           native_context: &NativeCompositingGraphicsContext,
                           texture: &Texture,
                           _: Size2D<isize>) {
        // Create the GLX pixmap.
        //
        // FIXME(pcwalton): RAII for exception safety?
        unsafe {
            let pixmap_attributes = [
                glx::TEXTURE_TARGET_EXT as i32, glx::TEXTURE_2D_EXT as i32,
                glx::TEXTURE_FORMAT_EXT as i32, glx::TEXTURE_FORMAT_RGBA_EXT as i32,
                0
            ];

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

            let glx_pixmap = glx::CreatePixmap(glx_display,
                                             native_context.framebuffer_configuration.expect(
                                                 "GLX 1.3 should have a framebuffer_configuration"),
                                             self.pixmap,
                                             pixmap_attributes.as_ptr());

            let glXBindTexImageEXT: extern "C" fn(*mut Display, glx::types::GLXDrawable, c_int, *mut c_int) =
                mem::transmute(glx::GetProcAddress(mem::transmute(&"glXBindTexImageEXT\x00".as_bytes()[0])));
            assert!(glXBindTexImageEXT as *mut c_void != ptr::null_mut());
            let _bound = texture.bind();
            glXBindTexImageEXT(native_context.display,
                               mem::transmute(glx_pixmap),
                               glx::FRONT_EXT  as i32,
                               ptr::null_mut());

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

    /// This may only be called on the painting side.
    pub 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::null_mut());

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

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

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

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

    pub fn mark_wont_leak(&mut self) {
        self.will_leak = false;
    }
}
<|endoftext|>


<commit_before>use std::cmp;
use slsr_core::puzzle::Side;
use slsr_core::geom::{CellId, Geom};

use ::{State, SolverResult};
use ::model::connect_map::ConnectMap;
use ::model::side_map::SideMap;

fn create_conn_graph(conn_map: &mut ConnectMap, exclude_side: Side)
                     -> (Vec<CellId>, Vec<Vec<usize>>)
{
    let mut pts = (0..conn_map.cell_len())
        .map(CellId::new)
        .filter_map(|p| {
            let a = conn_map.get(p);
            if a.coord() == p && a.side() != State::Fixed(exclude_side) {
                Some(p)
            } else {
                None
            }
        }).collect::<Vec<_>>();
    pts.sort();

    let mut verts = vec![None; conn_map.cell_len()];
    for (i, &p) in pts.iter().enumerate() {
        verts[p.id()] = Some(i);
    }

    let graph = pts.iter().map(|&p| {
        conn_map.get(p).unknown_edge().iter()
            .filter_map(|p2| verts[p2.id()])
            .collect::<Vec<_>>()
    }).collect();

    (pts, graph)
}

fn get_articulation(graph: &[Vec<usize>], v: usize) -> (Vec<usize>, Vec<bool>) {
    if graph.is_empty() { return (vec![], vec![]) }

    let mut visited = vec![false; graph.len()];
    let mut ord = vec![0; graph.len()];
    let mut low = vec![0; graph.len()];
    let mut arts = vec![];
    let mut ord_cnt = 0;
    dfs(graph, v, &mut visited, &mut ord, &mut low, &mut ord_cnt, &mut arts);
    return (arts, visited);

    fn dfs(graph: &[Vec<usize>],
           v: usize, visited: &mut [bool], ord: &mut [usize], low: &mut [usize],
           ord_cnt: &mut usize, arts: &mut Vec<usize>) {
        debug_assert!(!visited[v]);

        *ord_cnt += 1;
        visited[v] = true;
        ord[v] = *ord_cnt;
        low[v] = ord[v];

        let mut is_articulation = false;
        let mut num_child = 0;

        for &u in &graph[v] {
            if u == v { continue }

            if !visited[u] {
                dfs(graph, u, visited, ord, low, ord_cnt, arts);

                num_child += 1;
                low[v] = cmp::min(low[v], low[u]);
                if ord[v] != 1 && ord[v] <= low[u] {
                    is_articulation = true;
                }
            } else {
                low[v] = cmp::min(low[v], ord[u]);
            }
        }

        if ord[v] == 1 && num_child > 1 {
            is_articulation = true;
        }

        if is_articulation {
            arts.push(v);
        }
    }
}

fn find_disconn_area(conn_map: &mut ConnectMap, pts: &[CellId], visited: &[bool])
                     -> SolverResult<Vec<usize>>
{
    let mut disconn = vec![];
    let mut disconn_sum = 0;
    for (u, &vis) in visited.iter().enumerate() {
        if !vis {
            disconn.push(u);
            disconn_sum += conn_map.get(pts[u]).sum_of_hint();
        }
    }
    if disconn.is_empty() {
        //  All area is connected
        return Ok(vec![])
    }
    if disconn_sum == 0 {
        // Disconnected components does not contain any edges. It is a hole in
        // the exclude_side area.
        return Ok(disconn)
    }

    let mut conn = vec![];
    let mut conn_sum = 0;
    for (u, &vis) in visited.iter().enumerate() {
        if vis {
            conn.push(u);
            conn_sum += conn_map.get(pts[u]).sum_of_hint();
        }
    }
    if conn_sum == 0 {
        // Conencted area does not contain any edges. It is a hole in the
        // exclude_side area.
        return Ok(conn)
    }

    // Graph is splitted into more than two parts, but both parts contain edges.
    // This may be valid in some situation, so, return empty.
    Ok(vec![])
}

fn splits(graph: &[Vec<usize>], v: usize,
          conn_map: &mut ConnectMap, pts: &[CellId], set_side: Side) -> bool {
    if graph.is_empty() { return false }

    let mut contain_cnt = 0;
    let mut visited = vec![false; graph.len()];

    visited[v] = true;

    for &u in &graph[v] {
        if u == v || visited[u] { continue }

        if dfs(graph, u, &mut visited, conn_map, pts, set_side) {
            contain_cnt += 1;
        }
    }

    return contain_cnt > 1;

    fn dfs(graph: &[Vec<usize>], v: usize, visited: &mut [bool],
           conn_map: &mut ConnectMap, pts: &[CellId], set_side: Side) -> bool {
        let mut contains = conn_map.get(pts[v]).side() == State::Fixed(set_side);
        visited[v] = true;

        for &u in &graph[v] {
            if u == v || visited[u] { continue }
            contains |= dfs(graph, u, visited, conn_map, pts, set_side);
        }
        contains
    }
}

pub fn run(side_map: &mut SideMap, conn_map: &mut ConnectMap)
    -> SolverResult<()>
{
    try!(conn_map.sync(side_map));

    let sides = &[(Side::In, Side::Out),
                  (Side::Out, Side::In)];

    for &(set_side, exclude_side) in sides {
        let (pts, graph) = create_conn_graph(conn_map, exclude_side);
        let (arts, visited) = get_articulation(&graph, 0);

        if set_side == Side::Out || conn_map.sum_of_hint() != 0 {
            // If there is no edge in puzzle (sum_of_hint == 0) and set_side ==
            // Side::In, any disconnected area can be loop.
            let disconn = try!(find_disconn_area(conn_map, &pts, &visited));
            for v in disconn {
                side_map.set_side(pts[v], exclude_side);
            }
        }

        for v in arts {
            let p = pts[v];
            if conn_map.get(p).side() == State::Fixed(set_side) {
                continue
            }
            if splits(&graph, v, conn_map, &pts, set_side) {
                side_map.set_side(p, set_side);
            }
        }
    }

    Ok(())
}
<commit_msg>solver: small performance tuning for connect_map<commit_after>use std::cmp;
use slsr_core::puzzle::Side;
use slsr_core::geom::{CellId, Geom};

use ::{State, SolverResult};
use ::model::connect_map::ConnectMap;
use ::model::side_map::SideMap;

fn create_conn_graph(conn_map: &mut ConnectMap, exclude_side: Side)
                     -> (Vec<CellId>, Vec<State<Side>>, Vec<Vec<usize>>)
{
    let mut pts = vec![];
    let mut sides = vec![];
    for i in 0..conn_map.cell_len() {
        let p = CellId::new(i);
        let a = conn_map.get(p);
        if a.coord() != p || a.side() == State::Fixed(exclude_side) {
            continue
        }
        pts.push(p);
        sides.push(a.side());
    }

    let mut verts = vec![None; conn_map.cell_len()];
    for (i, &p) in pts.iter().enumerate() {
        verts[p.id()] = Some(i);
    }

    let graph = pts.iter().map(|&p| {
        conn_map.get(p).unknown_edge().iter()
            .filter_map(|p2| verts[p2.id()])
            .collect::<Vec<_>>()
    }).collect();

    (pts, sides, graph)
}

fn get_articulation(graph: &[Vec<usize>], v: usize) -> (Vec<usize>, Vec<bool>) {
    if graph.is_empty() { return (vec![], vec![]) }

    let mut arts = vec![];
    let mut visited = vec![false; graph.len()];
    let mut ord = vec![0; graph.len()];
    let mut low = vec![0; graph.len()];
    let mut ord_cnt = 0;
    dfs(graph, v, &mut arts, &mut visited, &mut ord, &mut low, &mut ord_cnt);
    return (arts, visited);

    fn dfs(graph: &[Vec<usize>],
           v: usize,
           arts: &mut Vec<usize>,
           visited: &mut [bool],
           ord: &mut [usize],
           low: &mut [usize],
           ord_cnt: &mut usize) {
        debug_assert!(!visited[v]);

        *ord_cnt += 1;
        visited[v] = true;
        ord[v] = *ord_cnt;
        low[v] = ord[v];

        let mut is_articulation = false;
        let mut num_child = 0;

        for &u in &graph[v] {
            if u == v { continue }

            if !visited[u] {
                dfs(graph, u, arts, visited, ord, low, ord_cnt);

                num_child += 1;
                low[v] = cmp::min(low[v], low[u]);
                if ord[v] != 1 && ord[v] <= low[u] {
                    is_articulation = true;
                }
            } else {
                low[v] = cmp::min(low[v], ord[u]);
            }
        }

        if ord[v] == 1 && num_child > 1 {
            is_articulation = true;
        }

        if is_articulation {
            arts.push(v);
        }
    }
}

fn find_disconn_area(conn_map: &mut ConnectMap, pts: &[CellId], visited: &[bool])
                     -> SolverResult<Vec<usize>>
{
    let mut disconn = vec![];
    let mut disconn_sum = 0;
    for (u, &vis) in visited.iter().enumerate() {
        if !vis {
            disconn.push(u);
            disconn_sum += conn_map.get(pts[u]).sum_of_hint();
        }
    }
    if disconn.is_empty() {
        //  All area is connected
        return Ok(vec![])
    }
    if disconn_sum == 0 {
        // Disconnected components does not contain any edges. It is a hole in
        // the exclude_side area.
        return Ok(disconn)
    }

    let mut conn = vec![];
    let mut conn_sum = 0;
    for (u, &vis) in visited.iter().enumerate() {
        if vis {
            conn.push(u);
            conn_sum += conn_map.get(pts[u]).sum_of_hint();
        }
    }
    if conn_sum == 0 {
        // Conencted area does not contain any edges. It is a hole in the
        // exclude_side area.
        return Ok(conn)
    }

    // Graph is splitted into more than two parts, but both parts contain edges.
    // This may be valid in some situation, so, return empty.
    Ok(vec![])
}

fn splits(graph: &[Vec<usize>],
          v: usize,
          sides: &[State<Side>],
          set_side: Side)
          -> bool
{
    if graph.is_empty() { return false }

    let mut contain_cnt = 0;
    let mut visited = vec![false; graph.len()];

    visited[v] = true;

    for &u in &graph[v] {
        if visited[u] { continue }

        if dfs(graph, u, &mut visited, sides, set_side) {
            contain_cnt += 1;
            if contain_cnt > 1 {
                return true
            }
        }
    }

    return false;

    fn dfs(graph: &[Vec<usize>],
           v: usize,
           visited: &mut [bool],
           sides: &[State<Side>],
           set_side: Side)
           -> bool
    {
        let mut contains = sides[v] == State::Fixed(set_side);
        visited[v] = true;

        for &u in &graph[v] {
            if visited[u] { continue }
            contains |= dfs(graph, u, visited, sides, set_side);
        }
        contains
    }
}

pub fn run(side_map: &mut SideMap, conn_map: &mut ConnectMap)
    -> SolverResult<()>
{
    try!(conn_map.sync(side_map));

    let sides = &[(Side::In, Side::Out),
                  (Side::Out, Side::In)];

    for &(set_side, exclude_side) in sides {
        let (pts, sides, graph) = create_conn_graph(conn_map, exclude_side);
        let (arts, visited) = get_articulation(&graph, 0);

        if set_side == Side::Out || conn_map.sum_of_hint() != 0 {
            // If there is no edge in puzzle (sum_of_hint == 0) and set_side ==
            // Side::In, any disconnected area can be loop.
            let disconn = try!(find_disconn_area(conn_map, &pts, &visited));
            for v in disconn {
                side_map.set_side(pts[v], exclude_side);
            }
        }

        for v in arts {
            if sides[v] == State::Fixed(set_side) {
                continue
            }

            if splits(&graph, v, &sides, set_side) {
                side_map.set_side(pts[v], set_side);
            }
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>use graphics::*;

// Local crate.
use Vector2d = vector2d::Vector2d;

static EPSILON: f64 = 0.0000000001;

pub fn area(contour: &[Vector2d]) -> f64 {
    let n = contour.len();
    let mut A = 0.0_f64;

    for i in range(0, n) {
        let q = i;
        let p = (i + n - 1) % n;
        A += contour.get(p).unwrap().get_x() * contour.get(q).unwrap().get_y()
            - contour.get(q).unwrap().get_x() * contour.get(p).unwrap().get_y();
    }

    A * 0.5_f64
}

/// InsideTriangle decides if a point P is Inside of the triangle
/// defined by A, B, C.
pub fn inside_triangle(
    Ax: f64, Ay: f64,
    Bx: f64, By: f64,
    Cx: f64, Cy: f64,
    Px: f64, Py: f64
) -> bool {

    let ax = Cx - Bx;
    let ay = Cy - By;
    let bx = Ax - Cx;
    let by = Ay - Cy;
    let cx = Bx - Ax;
    let cy = By - Ay;
    let apx = Px - Ax;
    let apy = Py - Ay;
    let bpx = Px - Bx;
    let bpy = Py - By;
    let cpx = Px - Cx;
    let cpy = Py - Cy;

    let aCROSSbp = ax*bpy - ay*bpx;
    let cCROSSap = cx*apy - cy*apx;
    let bCROSScp = bx*cpy - by*cpx;

    (
        (aCROSSbp >= 0.0_f64)
        && (bCROSScp >= 0.0_f64)
        && (cCROSSap >= 0.0_f64)
    )
}

pub fn snip(
    contour: &[Vector2d],
    u: uint,
    v: uint,
    w: uint,
    n: uint,
    V: &mut Vec<uint>
) -> bool {

    let Ax = contour.get(*V.get(u)).unwrap().get_x();
    let Ay = contour.get(*V.get(u)).unwrap().get_y();

    let Bx = contour.get(*V.get(v)).unwrap().get_x();
    let By = contour.get(*V.get(v)).unwrap().get_y();

    let Cx = contour.get(*V.get(w)).unwrap().get_x();
    let Cy = contour.get(*V.get(w)).unwrap().get_y();

    if EPSILON > (((Bx-Ax)*(Cy-Ay)) - ((By-Ay)*(Cx-Ax))) {
        return false;
    }

    for p in range(0, n) {
        if (p == u) || (p == v) || (p == w) { continue; }

        let Px = contour.get(*V.get(p)).unwrap().get_x();
        let Py = contour.get(*V.get(p)).unwrap().get_y();

        if inside_triangle(Ax,Ay,Bx,By,Cx,Cy,Px,Py) { return false; }
    }

    return true;
}

pub fn process(
    contour: &[Vector2d]
) -> Option<Vec<Vector2d>> {
    /* allocate and initialize list of Vertices in polygon */

    let n = contour.len();
    if n < 3 { return None; }

    let mut result: Vec<Vector2d> = Vec::new();
    let mut V: Vec<uint> = Vec::with_capacity(n);

    /* we want a counter-clockwise polygon in V */

    // Computes the area which has opposite signs
    // whether the polygon is clock-wise or counter-clockwise.
    if 0.0_f64 < area(contour) {
        for v in range(0, n) { V.push(v); }
    } else {
        for v in range(0, n) { V.push((n-1)-v); }
    }

    let mut number_of_vertices = n;

    /*  remove nv-2 Vertices, creating 1 triangle every time */
    let mut count = 2 * number_of_vertices;   /* error detection */

    let mut m = 0;
    let mut v = number_of_vertices - 1;
    while number_of_vertices > 2 {
        /* if we loop, it is probably a non-simple polygon */
        if count <= 0 {
            //** Triangulate: ERROR - probable bad polygon!
            return None;
        }

        count -= 1;

        /* three consecutive vertices in current polygon, <u,v,w> */
        let triangle = [
            v,
            vecmath::modular_offset_index(number_of_vertices, v, 1),
            vecmath::modular_offset_index(number_of_vertices, v, 2)
        ];
        v = triangle[1];

        if snip(contour, triangle[0], triangle[1], triangle[2], number_of_vertices, &mut V) {
            for &i in triangle.iter() {
                result.push(*contour.get(*V.get(i)).unwrap());
            }

            m += 1;

            /* remove v from remaining polygon */
            for i in range(0, number_of_vertices - 3) {
                let s = v + i;
                let t = v + i + 1;

                *V.get_mut(s) = *V.get(t);
            }

            // "Removed" a vertex.
            number_of_vertices -= 1;

            /* resest error detection counter */
            count = 2 * number_of_vertices;
        }
    }

    Some(result)
}

<commit_msg>Simplified argument<commit_after>use graphics::*;

// Local crate.
use Vector2d = vector2d::Vector2d;

static EPSILON: f64 = 0.0000000001;

pub fn area(contour: &[Vector2d]) -> f64 {
    let n = contour.len();
    let mut A = 0.0_f64;

    for i in range(0, n) {
        let q = i;
        let p = (i + n - 1) % n;
        A += contour.get(p).unwrap().get_x() * contour.get(q).unwrap().get_y()
            - contour.get(q).unwrap().get_x() * contour.get(p).unwrap().get_y();
    }

    A * 0.5_f64
}

/// InsideTriangle decides if a point P is Inside of the triangle
/// defined by A, B, C.
pub fn inside_triangle(
    Ax: f64, Ay: f64,
    Bx: f64, By: f64,
    Cx: f64, Cy: f64,
    Px: f64, Py: f64
) -> bool {

    let ax = Cx - Bx;
    let ay = Cy - By;
    let bx = Ax - Cx;
    let by = Ay - Cy;
    let cx = Bx - Ax;
    let cy = By - Ay;
    let apx = Px - Ax;
    let apy = Py - Ay;
    let bpx = Px - Bx;
    let bpy = Py - By;
    let cpx = Px - Cx;
    let cpy = Py - Cy;

    let aCROSSbp = ax*bpy - ay*bpx;
    let cCROSSap = cx*apy - cy*apx;
    let bCROSScp = bx*cpy - by*cpx;

    (
        (aCROSSbp >= 0.0_f64)
        && (bCROSScp >= 0.0_f64)
        && (cCROSSap >= 0.0_f64)
    )
}

pub fn snip(
    contour: &[Vector2d],
    triangle: [uint, ..3],
    n: uint,
    V: &mut Vec<uint>
) -> bool {
    let u = triangle[0];
    let v = triangle[1];
    let w = triangle[2];

    let Ax = contour.get(*V.get(u)).unwrap().get_x();
    let Ay = contour.get(*V.get(u)).unwrap().get_y();

    let Bx = contour.get(*V.get(v)).unwrap().get_x();
    let By = contour.get(*V.get(v)).unwrap().get_y();

    let Cx = contour.get(*V.get(w)).unwrap().get_x();
    let Cy = contour.get(*V.get(w)).unwrap().get_y();

    if EPSILON > (((Bx-Ax)*(Cy-Ay)) - ((By-Ay)*(Cx-Ax))) {
        return false;
    }

    for p in range(0, n) {
        if (p == u) || (p == v) || (p == w) { continue; }

        let Px = contour.get(*V.get(p)).unwrap().get_x();
        let Py = contour.get(*V.get(p)).unwrap().get_y();

        if inside_triangle(Ax,Ay,Bx,By,Cx,Cy,Px,Py) { return false; }
    }

    return true;
}

pub fn process(
    contour: &[Vector2d]
) -> Option<Vec<Vector2d>> {
    /* allocate and initialize list of Vertices in polygon */

    let n = contour.len();
    if n < 3 { return None; }

    let mut result: Vec<Vector2d> = Vec::new();
    let mut V: Vec<uint> = Vec::with_capacity(n);

    /* we want a counter-clockwise polygon in V */

    // Computes the area which has opposite signs
    // whether the polygon is clock-wise or counter-clockwise.
    if 0.0_f64 < area(contour) {
        for v in range(0, n) { V.push(v); }
    } else {
        for v in range(0, n) { V.push((n-1)-v); }
    }

    let mut number_of_vertices = n;

    /*  remove nv-2 Vertices, creating 1 triangle every time */
    let mut count = 2 * number_of_vertices;   /* error detection */

    let mut m = 0;
    let mut v = number_of_vertices - 1;
    while number_of_vertices > 2 {
        /* if we loop, it is probably a non-simple polygon */
        if count <= 0 {
            //** Triangulate: ERROR - probable bad polygon!
            return None;
        }

        count -= 1;

        /* three consecutive vertices in current polygon, <u,v,w> */
        let triangle = [
            v,
            vecmath::modular_offset_index(number_of_vertices, v, 1),
            vecmath::modular_offset_index(number_of_vertices, v, 2)
        ];
        v = triangle[1];

        if snip(contour, triangle, number_of_vertices, &mut V) {
            for &i in triangle.iter() {
                result.push(*contour.get(*V.get(i)).unwrap());
            }

            m += 1;

            /* remove v from remaining polygon */
            for i in range(0, number_of_vertices - 3) {
                let s = v + i;
                let t = v + i + 1;

                *V.get_mut(s) = *V.get(t);
            }

            // "Removed" a vertex.
            number_of_vertices -= 1;

            /* resest error detection counter */
            count = 2 * number_of_vertices;
        }
    }

    Some(result)
}

<|endoftext|>


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

//! Script to check the validity of `href` links in our HTML documentation.
//!
//! In the past we've been quite error prone to writing in broken links as most
//! of them are manually rather than automatically added. As files move over
//! time or apis change old links become stale or broken. The purpose of this
//! script is to check all relative links in our documentation to make sure they
//! actually point to a valid place.
//!
//! Currently this doesn't actually do any HTML parsing or anything fancy like
//! that, it just has a simple "regex" to search for `href` and `id` tags.
//! These values are then translated to file URLs if possible and then the
//! destination is asserted to exist.
//!
//! A few whitelisted exceptions are allowed as there's known bugs in rustdoc,
//! but this should catch the majority of "broken link" cases.

use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf, Component};
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;

use Redirect::*;

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

fn main() {
    let docs = env::args().nth(1).unwrap();
    let docs = env::current_dir().unwrap().join(docs);
    let mut errors = false;
    walk(&mut HashMap::new(), &docs, &docs, &mut errors);
    if errors {
        panic!("found some broken links");
    }
}

#[derive(Debug)]
pub enum LoadError {
    IOError(std::io::Error),
    BrokenRedirect(PathBuf, std::io::Error),
    IsRedirect,
}

enum Redirect {
    SkipRedirect,
    FromRedirect(bool),
}

struct FileEntry {
    source: String,
    ids: HashSet<String>,
}

type Cache = HashMap<PathBuf, FileEntry>;

impl FileEntry {
    fn parse_ids(&mut self, file: &Path, contents: &str, errors: &mut bool) {
        if self.ids.is_empty() {
            with_attrs_in_source(contents, " id", |fragment, i| {
                let frag = fragment.trim_left_matches("#").to_owned();
                if !self.ids.insert(frag) {
                    *errors = true;
                    println!("{}:{}: id is not unique: `{}`", file.display(), i, fragment);
                }
            });
        }
    }
}

fn walk(cache: &mut Cache, root: &Path, dir: &Path, errors: &mut bool) {
    for entry in t!(dir.read_dir()).map(|e| t!(e)) {
        let path = entry.path();
        let kind = t!(entry.file_type());
        if kind.is_dir() {
            walk(cache, root, &path, errors);
        } else {
            let pretty_path = check(cache, root, &path, errors);
            if let Some(pretty_path) = pretty_path {
                let entry = cache.get_mut(&pretty_path).unwrap();
                // we don't need the source anymore,
                // so drop to reduce memory-usage
                entry.source = String::new();
            }
        }
    }
}

fn check(cache: &mut Cache,
         root: &Path,
         file: &Path,
         errors: &mut bool)
         -> Option<PathBuf> {
    // ignore js files as they are not prone to errors as the rest of the
    // documentation is and they otherwise bring up false positives.
    if file.extension().and_then(|s| s.to_str()) == Some("js") {
        return None;
    }

    // Unfortunately we're not 100% full of valid links today to we need a few
    // whitelists to get this past `make check` today.
    // FIXME(#32129)
    if file.ends_with("std/string/struct.String.html") {
        return None;
    }
    // FIXME(#32553)
    if file.ends_with("collections/string/struct.String.html") {
        return None;
    }
    // FIXME(#32130)
    if file.ends_with("btree_set/struct.BTreeSet.html") ||
       file.ends_with("collections/struct.BTreeSet.html") ||
       file.ends_with("collections/btree_map/struct.BTreeMap.html") ||
       file.ends_with("collections/hash_map/struct.HashMap.html") {
        return None;
    }

    let res = load_file(cache, root, PathBuf::from(file), SkipRedirect);
    let (pretty_file, contents) = match res {
        Ok(res) => res,
        Err(_) => return None,
    };
    {
        cache.get_mut(&pretty_file)
             .unwrap()
             .parse_ids(&pretty_file, &contents, errors);
    }

    // Search for anything that's the regex 'href[ ]*=[ ]*".*?"'
    with_attrs_in_source(&contents, " href", |url, i| {
        // Ignore external URLs
        if url.starts_with("http:") || url.starts_with("https:") ||
           url.starts_with("javascript:") || url.starts_with("ftp:") ||
           url.starts_with("irc:") || url.starts_with("data:") {
            return;
        }
        let mut parts = url.splitn(2, "#");
        let url = parts.next().unwrap();
        let fragment = parts.next();
        let mut parts = url.splitn(2, "?");
        let url = parts.next().unwrap();

        // Once we've plucked out the URL, parse it using our base url and
        // then try to extract a file path.
        let mut path = file.to_path_buf();
        if !url.is_empty() {
            path.pop();
            for part in Path::new(url).components() {
                match part {
                    Component::Prefix(_) |
                    Component::RootDir => panic!(),
                    Component::CurDir => {}
                    Component::ParentDir => { path.pop(); }
                    Component::Normal(s) => { path.push(s); }
                }
            }
        }

        if let Some(extension) = path.extension() {
            // don't check these files
            if extension == "png" {
                return;
            }
        }

        // Alright, if we've found a file name then this file had better
        // exist! If it doesn't then we register and print an error.
        if path.exists() {
            if path.is_dir() {
                // Links to directories show as directory listings when viewing
                // the docs offline so it's best to avoid them.
                *errors = true;
                let pretty_path = path.strip_prefix(root).unwrap_or(&path);
                println!("{}:{}: directory link - {}",
                         pretty_file.display(),
                         i + 1,
                         pretty_path.display());
                return;
            }
            let res = load_file(cache, root, path.clone(), FromRedirect(false));
            let (pretty_path, contents) = match res {
                Ok(res) => res,
                Err(LoadError::IOError(err)) => {
                    panic!(format!("error loading {}: {}", path.display(), err));
                }
                Err(LoadError::BrokenRedirect(target, _)) => {
                    *errors = true;
                    println!("{}:{}: broken redirect to {}",
                             pretty_file.display(),
                             i + 1,
                             target.display());
                    return;
                }
                Err(LoadError::IsRedirect) => unreachable!(),
            };

            // we don't check the book for fragments because they're added via JS
            for book in ["book/", "nomicon/"].iter() {
                if !pretty_path.to_str().unwrap().starts_with(book) {
                    return;
                }
            }

            if let Some(ref fragment) = fragment {
                // Fragments like `#1-6` are most likely line numbers to be
                // interpreted by javascript, so we're ignoring these
                if fragment.splitn(2, '-')
                           .all(|f| f.chars().all(|c| c.is_numeric())) {
                    return;
                }

                let entry = &mut cache.get_mut(&pretty_path).unwrap();
                entry.parse_ids(&pretty_path, &contents, errors);

                if !entry.ids.contains(*fragment) {
                    *errors = true;
                    print!("{}:{}: broken link fragment  ",
                           pretty_file.display(),
                           i + 1);
                    println!("`#{}` pointing to `{}`", fragment, pretty_path.display());
                };
            }
        } else {
            *errors = true;
            print!("{}:{}: broken link - ", pretty_file.display(), i + 1);
            let pretty_path = path.strip_prefix(root).unwrap_or(&path);
            println!("{}", pretty_path.display());
        }
    });
    Some(pretty_file)
}

fn load_file(cache: &mut Cache,
             root: &Path,
             mut file: PathBuf,
             redirect: Redirect)
             -> Result<(PathBuf, String), LoadError> {
    let mut contents = String::new();
    let pretty_file = PathBuf::from(file.strip_prefix(root).unwrap_or(&file));

    let maybe_redirect = match cache.entry(pretty_file.clone()) {
        Entry::Occupied(entry) => {
            contents = entry.get().source.clone();
            None
        }
        Entry::Vacant(entry) => {
            let mut fp = File::open(file.clone()).map_err(|err| {
                if let FromRedirect(true) = redirect {
                    LoadError::BrokenRedirect(file.clone(), err)
                } else {
                    LoadError::IOError(err)
                }
            })?;
            fp.read_to_string(&mut contents).map_err(|err| LoadError::IOError(err))?;

            let maybe = maybe_redirect(&contents);
            if maybe.is_some() {
                if let SkipRedirect = redirect {
                    return Err(LoadError::IsRedirect);
                }
            } else {
                entry.insert(FileEntry {
                    source: contents.clone(),
                    ids: HashSet::new(),
                });
            }
            maybe
        }
    };
    file.pop();
    match maybe_redirect.map(|url| file.join(url)) {
        Some(redirect_file) => {
            let path = PathBuf::from(redirect_file);
            load_file(cache, root, path, FromRedirect(true))
        }
        None => Ok((pretty_file, contents)),
    }
}

fn maybe_redirect(source: &str) -> Option<String> {
    const REDIRECT: &'static str = "
Redirecting to  l,
        None => return None,
    };

    redirect_line.find(REDIRECT).map(|i| {
        let rest = &redirect_line[(i + REDIRECT.len() + 1)..];
        let pos_quote = rest.find('"').unwrap();
        rest[..pos_quote].to_owned()
    })
}

fn with_attrs_in_source<F: FnMut(&str, usize)>(contents: &str, attr: &str, mut f: F) {
    for (i, mut line) in contents.lines().enumerate() {
        while let Some(j) = line.find(attr) {
            let rest = &line[j + attr.len()..];
            line = rest;
            let pos_equals = match rest.find("=") {
                Some(i) => i,
                None => continue,
            };
            if rest[..pos_equals].trim_left_matches(" ") != "" {
                continue;
            }

            let rest = &rest[pos_equals + 1..];

            let pos_quote = match rest.find(&['"', '\''][..]) {
                Some(i) => i,
                None => continue,
            };
            let quote_delim = rest.as_bytes()[pos_quote] as char;

            if rest[..pos_quote].trim_left_matches(" ") != "" {
                continue;
            }
            let rest = &rest[pos_quote + 1..];
            let url = match rest.find(quote_delim) {
                Some(i) => &rest[..i],
                None => continue,
            };
            f(url, i)
        }
    }
}
<commit_msg>Enable linkchecker on books<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Script to check the validity of `href` links in our HTML documentation.
//!
//! In the past we've been quite error prone to writing in broken links as most
//! of them are manually rather than automatically added. As files move over
//! time or apis change old links become stale or broken. The purpose of this
//! script is to check all relative links in our documentation to make sure they
//! actually point to a valid place.
//!
//! Currently this doesn't actually do any HTML parsing or anything fancy like
//! that, it just has a simple "regex" to search for `href` and `id` tags.
//! These values are then translated to file URLs if possible and then the
//! destination is asserted to exist.
//!
//! A few whitelisted exceptions are allowed as there's known bugs in rustdoc,
//! but this should catch the majority of "broken link" cases.

use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf, Component};
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;

use Redirect::*;

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

fn main() {
    let docs = env::args().nth(1).unwrap();
    let docs = env::current_dir().unwrap().join(docs);
    let mut errors = false;
    walk(&mut HashMap::new(), &docs, &docs, &mut errors);
    if errors {
        panic!("found some broken links");
    }
}

#[derive(Debug)]
pub enum LoadError {
    IOError(std::io::Error),
    BrokenRedirect(PathBuf, std::io::Error),
    IsRedirect,
}

enum Redirect {
    SkipRedirect,
    FromRedirect(bool),
}

struct FileEntry {
    source: String,
    ids: HashSet<String>,
    names: HashSet<String>,
}

type Cache = HashMap<PathBuf, FileEntry>;

impl FileEntry {
    fn parse_ids(&mut self, file: &Path, contents: &str, errors: &mut bool) {
        if self.ids.is_empty() {
            with_attrs_in_source(contents, " id", |fragment, i| {
                let frag = fragment.trim_left_matches("#").to_owned();
                if !self.ids.insert(frag) {
                    *errors = true;
                    println!("{}:{}: id is not unique: `{}`", file.display(), i, fragment);
                }
            });
        }
    }

    fn parse_names(&mut self, contents: &str) {
        if self.names.is_empty() {
            with_attrs_in_source(contents, " name", |fragment, _| {
                let frag = fragment.trim_left_matches("#").to_owned();
                self.names.insert(frag);
            });
        }
    }
}

fn walk(cache: &mut Cache, root: &Path, dir: &Path, errors: &mut bool) {
    for entry in t!(dir.read_dir()).map(|e| t!(e)) {
        let path = entry.path();
        let kind = t!(entry.file_type());
        if kind.is_dir() {
            walk(cache, root, &path, errors);
        } else {
            let pretty_path = check(cache, root, &path, errors);
            if let Some(pretty_path) = pretty_path {
                let entry = cache.get_mut(&pretty_path).unwrap();
                // we don't need the source anymore,
                // so drop to reduce memory-usage
                entry.source = String::new();
            }
        }
    }
}

fn check(cache: &mut Cache,
         root: &Path,
         file: &Path,
         errors: &mut bool)
         -> Option<PathBuf> {
    // ignore js files as they are not prone to errors as the rest of the
    // documentation is and they otherwise bring up false positives.
    if file.extension().and_then(|s| s.to_str()) == Some("js") {
        return None;
    }

    // Unfortunately we're not 100% full of valid links today to we need a few
    // whitelists to get this past `make check` today.
    // FIXME(#32129)
    if file.ends_with("std/string/struct.String.html") {
        return None;
    }
    // FIXME(#32553)
    if file.ends_with("collections/string/struct.String.html") {
        return None;
    }
    // FIXME(#32130)
    if file.ends_with("btree_set/struct.BTreeSet.html") ||
       file.ends_with("collections/struct.BTreeSet.html") ||
       file.ends_with("collections/btree_map/struct.BTreeMap.html") ||
       file.ends_with("collections/hash_map/struct.HashMap.html") {
        return None;
    }

    let res = load_file(cache, root, PathBuf::from(file), SkipRedirect);
    let (pretty_file, contents) = match res {
        Ok(res) => res,
        Err(_) => return None,
    };
    {
        cache.get_mut(&pretty_file)
             .unwrap()
             .parse_ids(&pretty_file, &contents, errors);
        cache.get_mut(&pretty_file)
             .unwrap()
             .parse_names(&contents);
    }

    // Search for anything that's the regex 'href[ ]*=[ ]*".*?"'
    with_attrs_in_source(&contents, " href", |url, i| {
        // Ignore external URLs
        if url.starts_with("http:") || url.starts_with("https:") ||
           url.starts_with("javascript:") || url.starts_with("ftp:") ||
           url.starts_with("irc:") || url.starts_with("data:") {
            return;
        }
        let mut parts = url.splitn(2, "#");
        let url = parts.next().unwrap();
        let fragment = parts.next();
        let mut parts = url.splitn(2, "?");
        let url = parts.next().unwrap();

        // Once we've plucked out the URL, parse it using our base url and
        // then try to extract a file path.
        let mut path = file.to_path_buf();
        if !url.is_empty() {
            path.pop();
            for part in Path::new(url).components() {
                match part {
                    Component::Prefix(_) |
                    Component::RootDir => panic!(),
                    Component::CurDir => {}
                    Component::ParentDir => { path.pop(); }
                    Component::Normal(s) => { path.push(s); }
                }
            }
        }

        if let Some(extension) = path.extension() {
            // don't check these files
            if extension == "png" {
                return;
            }
        }

        // Alright, if we've found a file name then this file had better
        // exist! If it doesn't then we register and print an error.
        if path.exists() {
            if path.is_dir() {
                // Links to directories show as directory listings when viewing
                // the docs offline so it's best to avoid them.
                *errors = true;
                let pretty_path = path.strip_prefix(root).unwrap_or(&path);
                println!("{}:{}: directory link - {}",
                         pretty_file.display(),
                         i + 1,
                         pretty_path.display());
                return;
            }
            let res = load_file(cache, root, path.clone(), FromRedirect(false));
            let (pretty_path, contents) = match res {
                Ok(res) => res,
                Err(LoadError::IOError(err)) => {
                    panic!(format!("error loading {}: {}", path.display(), err));
                }
                Err(LoadError::BrokenRedirect(target, _)) => {
                    *errors = true;
                    println!("{}:{}: broken redirect to {}",
                             pretty_file.display(),
                             i + 1,
                             target.display());
                    return;
                }
                Err(LoadError::IsRedirect) => unreachable!(),
            };

            if let Some(ref fragment) = fragment {
                // Fragments like `#1-6` are most likely line numbers to be
                // interpreted by javascript, so we're ignoring these
                if fragment.splitn(2, '-')
                           .all(|f| f.chars().all(|c| c.is_numeric())) {
                    return;
                }

                let entry = &mut cache.get_mut(&pretty_path).unwrap();
                entry.parse_ids(&pretty_path, &contents, errors);
                entry.parse_names(&contents);

                if !(entry.ids.contains(*fragment) || entry.names.contains(*fragment)) {
                    *errors = true;
                    print!("{}:{}: broken link fragment  ",
                           pretty_file.display(),
                           i + 1);
                    println!("`#{}` pointing to `{}`", fragment, pretty_path.display());
                };
            }
        } else {
            *errors = true;
            print!("{}:{}: broken link - ", pretty_file.display(), i + 1);
            let pretty_path = path.strip_prefix(root).unwrap_or(&path);
            println!("{}", pretty_path.display());
        }
    });
    Some(pretty_file)
}

fn load_file(cache: &mut Cache,
             root: &Path,
             mut file: PathBuf,
             redirect: Redirect)
             -> Result<(PathBuf, String), LoadError> {
    let mut contents = String::new();
    let pretty_file = PathBuf::from(file.strip_prefix(root).unwrap_or(&file));

    let maybe_redirect = match cache.entry(pretty_file.clone()) {
        Entry::Occupied(entry) => {
            contents = entry.get().source.clone();
            None
        }
        Entry::Vacant(entry) => {
            let mut fp = File::open(file.clone()).map_err(|err| {
                if let FromRedirect(true) = redirect {
                    LoadError::BrokenRedirect(file.clone(), err)
                } else {
                    LoadError::IOError(err)
                }
            })?;
            fp.read_to_string(&mut contents).map_err(|err| LoadError::IOError(err))?;

            let maybe = maybe_redirect(&contents);
            if maybe.is_some() {
                if let SkipRedirect = redirect {
                    return Err(LoadError::IsRedirect);
                }
            } else {
                entry.insert(FileEntry {
                    source: contents.clone(),
                    ids: HashSet::new(),
                    names: HashSet::new(),
                });
            }
            maybe
        }
    };
    file.pop();
    match maybe_redirect.map(|url| file.join(url)) {
        Some(redirect_file) => {
            let path = PathBuf::from(redirect_file);
            load_file(cache, root, path, FromRedirect(true))
        }
        None => Ok((pretty_file, contents)),
    }
}

fn maybe_redirect(source: &str) -> Option<String> {
    const REDIRECT: &'static str = "
Redirecting to  l,
        None => return None,
    };

    redirect_line.find(REDIRECT).map(|i| {
        let rest = &redirect_line[(i + REDIRECT.len() + 1)..];
        let pos_quote = rest.find('"').unwrap();
        rest[..pos_quote].to_owned()
    })
}

fn with_attrs_in_source<F: FnMut(&str, usize)>(contents: &str, attr: &str, mut f: F) {
    for (i, mut line) in contents.lines().enumerate() {
        while let Some(j) = line.find(attr) {
            let rest = &line[j + attr.len()..];
            line = rest;
            let pos_equals = match rest.find("=") {
                Some(i) => i,
                None => continue,
            };
            if rest[..pos_equals].trim_left_matches(" ") != "" {
                continue;
            }

            let rest = &rest[pos_equals + 1..];

            let pos_quote = match rest.find(&['"', '\''][..]) {
                Some(i) => i,
                None => continue,
            };
            let quote_delim = rest.as_bytes()[pos_quote] as char;

            if rest[..pos_quote].trim_left_matches(" ") != "" {
                continue;
            }
            let rest = &rest[pos_quote + 1..];
            let url = match rest.find(quote_delim) {
                Some(i) => &rest[..i],
                None => continue,
            };
            f(url, i)
        }
    }
}
<|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 https://mozilla.org/MPL/2.0/.

use errors::Result;
use errors::ResultExt;
use zip::Zip;
use zip_library::ZipLibrary;
use zip_command::ZipCommand;

use std::path::Path;
use std::io::Read;
use std::io::Write;

/// Wrapper around either a ZipCommand or a ZipLibrary
///
/// Allows to create an EpubBuilder that can decide at runtime which to use.
pub enum ZipCommandOrLibrary {
    /// Command variant
    Command(ZipCommand),
    /// Library variant
    Library(ZipLibrary)
}

impl Zip for ZipCommandOrLibrary {
    fn write_file<P: AsRef<Path>, R: Read>(&mut self, path: P, content: R) -> Result<()> {
        match self {
            ZipCommandOrLibrary::Command(ref mut command) => command.write_file(path, content),
            ZipCommandOrLibrary::Library(ref mut library) => library.write_file(path, content)
        }
    }

    fn generate<W: Write>(&mut self, to: W) -> Result<()> {
        match self {
            ZipCommandOrLibrary::Command(ref mut command) => command.generate(to),
            ZipCommandOrLibrary::Library(ref mut library) => library.generate(to),
        }
    }
}

impl ZipCommandOrLibrary {
    /// Try to create a ZipCommand using `command`. If running `command` fails on the system,
    /// fall back to `ZipLibrary`.
    pub fn new(command: &str) -> Result<ZipCommandOrLibrary> {
        ZipCommand::new()
            .map(|mut z| {
            z.command(command);
            z})
            .and_then(|z| z.test().map(|_| z))
            .map(|z| ZipCommandOrLibrary::Command(z))
            .or_else(|_| ZipLibrary::new()
                     .map(|l| ZipCommandOrLibrary::Library(l)))
    }
}
<commit_msg>Remove warning at compile time<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 https://mozilla.org/MPL/2.0/.

use errors::Result;
use zip::Zip;
use zip_library::ZipLibrary;
use zip_command::ZipCommand;

use std::path::Path;
use std::io::Read;
use std::io::Write;

/// Wrapper around either a ZipCommand or a ZipLibrary
///
/// Allows to create an EpubBuilder that can decide at runtime which to use.
pub enum ZipCommandOrLibrary {
    /// Command variant
    Command(ZipCommand),
    /// Library variant
    Library(ZipLibrary)
}

impl Zip for ZipCommandOrLibrary {
    fn write_file<P: AsRef<Path>, R: Read>(&mut self, path: P, content: R) -> Result<()> {
        match self {
            ZipCommandOrLibrary::Command(ref mut command) => command.write_file(path, content),
            ZipCommandOrLibrary::Library(ref mut library) => library.write_file(path, content)
        }
    }

    fn generate<W: Write>(&mut self, to: W) -> Result<()> {
        match self {
            ZipCommandOrLibrary::Command(ref mut command) => command.generate(to),
            ZipCommandOrLibrary::Library(ref mut library) => library.generate(to),
        }
    }
}

impl ZipCommandOrLibrary {
    /// Try to create a ZipCommand using `command`. If running `command` fails on the system,
    /// fall back to `ZipLibrary`.
    pub fn new(command: &str) -> Result<ZipCommandOrLibrary> {
        ZipCommand::new()
            .map(|mut z| {
            z.command(command);
            z})
            .and_then(|z| z.test().map(|_| z))
            .map(|z| ZipCommandOrLibrary::Command(z))
            .or_else(|_| ZipLibrary::new()
                     .map(|l| ZipCommandOrLibrary::Library(l)))
    }
}
<|endoftext|>


<commit_before>//! Various traits that are implemented by ast nodes.
//!
//! The implementations are usually trivial, and live in generated.rs

use itertools::Itertools;

use crate::{
    ast::{self, child_opt, children, AstChildren, AstNode, AstToken},
    syntax_node::{SyntaxElementChildren, SyntaxNodeChildren},
};

pub trait TypeAscriptionOwner: AstNode {
    fn ascribed_type(&self) -> Option<ast::TypeRef> {
        child_opt(self)
    }
}

pub trait NameOwner: AstNode {
    fn name(&self) -> Option<ast::Name> {
        child_opt(self)
    }
}

pub trait VisibilityOwner: AstNode {
    fn visibility(&self) -> Option<ast::Visibility> {
        child_opt(self)
    }
}

pub trait LoopBodyOwner: AstNode {
    fn loop_body(&self) -> Option<ast::BlockExpr> {
        child_opt(self)
    }
}

pub trait ArgListOwner: AstNode {
    fn arg_list(&self) -> Option<ast::ArgList> {
        child_opt(self)
    }
}

pub trait FnDefOwner: AstNode {
    fn functions(&self) -> AstChildren<ast::FnDef> {
        children(self)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ItemOrMacro {
    Item(ast::ModuleItem),
    Macro(ast::MacroCall),
}

pub trait ModuleItemOwner: AstNode {
    fn items(&self) -> AstChildren<ast::ModuleItem> {
        children(self)
    }
    fn items_with_macros(&self) -> ItemOrMacroIter {
        ItemOrMacroIter(self.syntax().children())
    }
}

#[derive(Debug)]
pub struct ItemOrMacroIter(SyntaxNodeChildren);

impl Iterator for ItemOrMacroIter {
    type Item = ItemOrMacro;
    fn next(&mut self) -> Option<ItemOrMacro> {
        loop {
            let n = self.0.next()?;
            if let Some(item) = ast::ModuleItem::cast(n.clone()) {
                return Some(ItemOrMacro::Item(item));
            }
            if let Some(call) = ast::MacroCall::cast(n) {
                return Some(ItemOrMacro::Macro(call));
            }
        }
    }
}

pub trait TypeParamsOwner: AstNode {
    fn type_param_list(&self) -> Option<ast::TypeParamList> {
        child_opt(self)
    }

    fn where_clause(&self) -> Option<ast::WhereClause> {
        child_opt(self)
    }
}

pub trait TypeBoundsOwner: AstNode {
    fn type_bound_list(&self) -> Option<ast::TypeBoundList> {
        child_opt(self)
    }
}

pub trait AttrsOwner: AstNode {
    fn attrs(&self) -> AstChildren<ast::Attr> {
        children(self)
    }
    fn has_atom_attr(&self, atom: &str) -> bool {
        self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom)
    }
}

pub trait DocCommentsOwner: AstNode {
    fn doc_comments(&self) -> CommentIter {
        CommentIter { iter: self.syntax().children_with_tokens() }
    }

    /// Returns the textual content of a doc comment block as a single string.
    /// That is, strips leading `///` (+ optional 1 character of whitespace),
    /// trailing `*/`, trailing whitespace and then joins the lines.
    fn doc_comment_text(&self) -> Option<String> {
        let mut has_comments = false;
        let docs = self
            .doc_comments()
            .filter(|comment| comment.kind().doc.is_some())
            .map(|comment| {
                has_comments = true;
                let prefix_len = comment.prefix().len();

                let line: &str = comment.text().as_str();

                // Determine if the prefix or prefix + 1 char is stripped
                let pos =
                    if line.chars().nth(prefix_len).map(|c| c.is_whitespace()).unwrap_or(false) {
                        prefix_len + 1
                    } else {
                        prefix_len
                    };

                let end = if comment.kind().shape.is_block() && line.ends_with("*/") {
                    line.len() - 2
                } else {
                    line.len()
                };

                // Note that we do not trim the end of the line here
                // since whitespace can have special meaning at the end
                // of a line in markdown.
                line[pos..end].to_owned()
            })
            .join("\n");

        if has_comments {
            Some(docs)
        } else {
            None
        }
    }
}

pub struct CommentIter {
    iter: SyntaxElementChildren,
}

impl Iterator for CommentIter {
    type Item = ast::Comment;
    fn next(&mut self) -> Option<ast::Comment> {
        self.iter.by_ref().find_map(|el| el.into_token().and_then(ast::Comment::cast))
    }
}

pub trait DefaultTypeParamOwner: AstNode {
    fn default_type(&self) -> Option<ast::PathType> {
        child_opt(self)
    }
}
<commit_msg>traits => match_ast!<commit_after>//! Various traits that are implemented by ast nodes.
//!
//! The implementations are usually trivial, and live in generated.rs

use itertools::Itertools;

use crate::{
    ast::{self, child_opt, children, AstChildren, AstNode, AstToken},
    match_ast,
    syntax_node::{SyntaxElementChildren, SyntaxNodeChildren},
};

pub trait TypeAscriptionOwner: AstNode {
    fn ascribed_type(&self) -> Option<ast::TypeRef> {
        child_opt(self)
    }
}

pub trait NameOwner: AstNode {
    fn name(&self) -> Option<ast::Name> {
        child_opt(self)
    }
}

pub trait VisibilityOwner: AstNode {
    fn visibility(&self) -> Option<ast::Visibility> {
        child_opt(self)
    }
}

pub trait LoopBodyOwner: AstNode {
    fn loop_body(&self) -> Option<ast::BlockExpr> {
        child_opt(self)
    }
}

pub trait ArgListOwner: AstNode {
    fn arg_list(&self) -> Option<ast::ArgList> {
        child_opt(self)
    }
}

pub trait FnDefOwner: AstNode {
    fn functions(&self) -> AstChildren<ast::FnDef> {
        children(self)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ItemOrMacro {
    Item(ast::ModuleItem),
    Macro(ast::MacroCall),
}

pub trait ModuleItemOwner: AstNode {
    fn items(&self) -> AstChildren<ast::ModuleItem> {
        children(self)
    }
    fn items_with_macros(&self) -> ItemOrMacroIter {
        ItemOrMacroIter(self.syntax().children())
    }
}

#[derive(Debug)]
pub struct ItemOrMacroIter(SyntaxNodeChildren);

impl Iterator for ItemOrMacroIter {
    type Item = ItemOrMacro;
    fn next(&mut self) -> Option<ItemOrMacro> {
        loop {
            let n = self.0.next()?;
            match_ast! {
                match n {
                    ast::ModuleItem(it) => { return Some(ItemOrMacro::Item(it)) },
                    ast::MacroCall(it) => { return Some(ItemOrMacro::Macro(it)) },
                    _ => {},
                }
            }
        }
    }
}

pub trait TypeParamsOwner: AstNode {
    fn type_param_list(&self) -> Option<ast::TypeParamList> {
        child_opt(self)
    }

    fn where_clause(&self) -> Option<ast::WhereClause> {
        child_opt(self)
    }
}

pub trait TypeBoundsOwner: AstNode {
    fn type_bound_list(&self) -> Option<ast::TypeBoundList> {
        child_opt(self)
    }
}

pub trait AttrsOwner: AstNode {
    fn attrs(&self) -> AstChildren<ast::Attr> {
        children(self)
    }
    fn has_atom_attr(&self, atom: &str) -> bool {
        self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom)
    }
}

pub trait DocCommentsOwner: AstNode {
    fn doc_comments(&self) -> CommentIter {
        CommentIter { iter: self.syntax().children_with_tokens() }
    }

    /// Returns the textual content of a doc comment block as a single string.
    /// That is, strips leading `///` (+ optional 1 character of whitespace),
    /// trailing `*/`, trailing whitespace and then joins the lines.
    fn doc_comment_text(&self) -> Option<String> {
        let mut has_comments = false;
        let docs = self
            .doc_comments()
            .filter(|comment| comment.kind().doc.is_some())
            .map(|comment| {
                has_comments = true;
                let prefix_len = comment.prefix().len();

                let line: &str = comment.text().as_str();

                // Determine if the prefix or prefix + 1 char is stripped
                let pos =
                    if line.chars().nth(prefix_len).map(|c| c.is_whitespace()).unwrap_or(false) {
                        prefix_len + 1
                    } else {
                        prefix_len
                    };

                let end = if comment.kind().shape.is_block() && line.ends_with("*/") {
                    line.len() - 2
                } else {
                    line.len()
                };

                // Note that we do not trim the end of the line here
                // since whitespace can have special meaning at the end
                // of a line in markdown.
                line[pos..end].to_owned()
            })
            .join("\n");

        if has_comments {
            Some(docs)
        } else {
            None
        }
    }
}

pub struct CommentIter {
    iter: SyntaxElementChildren,
}

impl Iterator for CommentIter {
    type Item = ast::Comment;
    fn next(&mut self) -> Option<ast::Comment> {
        self.iter.by_ref().find_map(|el| el.into_token().and_then(ast::Comment::cast))
    }
}

pub trait DefaultTypeParamOwner: AstNode {
    fn default_type(&self) -> Option<ast::PathType> {
        child_opt(self)
    }
}
<|endoftext|>