<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Off-screen windows.
//!
//! This is used for off-screen rendering mode only; on-screen windows (the default embedding mode)
//! are managed by a platform toolkit (Glutin).

use eutil::Downcast;
use interfaces::CefBrowser;
use render_handler::CefRenderHandlerExtensions;
use types::{cef_cursor_handle_t, cef_rect_t};
use unicode::str::Utf16Encoder;

use compositing::compositor_task::{self, CompositorProxy, CompositorReceiver};
use compositing::windowing::{WindowEvent, WindowMethods};
use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use gleam::gl;
use layers::geometry::DevicePixel;
use layers::platform::surface::NativeGraphicsMetadata;
use libc::{c_char, c_void};
use msg::constellation_msg::{Key, KeyModifiers};
use msg::compositor_msg::{ReadyState, PaintState};
use msg::constellation_msg::LoadData;
use util::cursor::Cursor;
use util::geometry::ScreenPx;
use std::cell::RefCell;
use std::ffi::CString;
use std::rc::Rc;
use std::sync::mpsc::{Sender, channel};

/// The type of an off-screen window.
#[derive(Clone)]
pub struct Window {
    cef_browser: RefCell<Option<CefBrowser>>,
}

#[cfg(target_os="macos")]
fn load_gl() {
    const RTLD_DEFAULT: *mut c_void = (-2) as *mut c_void;

    extern {
        fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
    }

    gl::load_with(|s| {
        unsafe {
            let c_str = CString::from_slice(s.as_bytes());
            dlsym(RTLD_DEFAULT, c_str.as_ptr()) as *const c_void
        }
    });
}

#[cfg(target_os="linux")]
fn load_gl() {
    extern {
        fn glXGetProcAddress(symbol: *const c_char) -> *mut c_void;
    }

    gl::load_with(|s| {
        unsafe {
            let c_str = CString::from_slice(s.as_bytes());
            glXGetProcAddress(c_str.as_ptr()) as *const c_void
        }
    });
}

impl Window {
    /// Creates a new window.
    pub fn new() -> Rc<Window> {
        load_gl();

        Rc::new(Window {
            cef_browser: RefCell::new(None),
        })
    }

    /// Sets the current browser.
    pub fn set_browser(&self, browser: CefBrowser) {
        *self.cef_browser.borrow_mut() = Some(browser)
    }

    /// Currently unimplemented.
    pub fn wait_events(&self) -> WindowEvent {
        WindowEvent::Idle
    }

    /// Returns the Cocoa cursor for a CSS cursor. These match Firefox, except where Firefox
    /// bundles custom resources (which we don't yet do).
    #[cfg(target_os="macos")]
    fn cursor_handle_for_cursor(&self, cursor: Cursor) -> cef_cursor_handle_t {
        use cocoa::base::{class, msg_send, selector};

        let cocoa_name = match cursor {
            Cursor::NoCursor => return 0 as cef_cursor_handle_t,
            Cursor::ContextMenuCursor => "contextualMenuCursor",
            Cursor::GrabbingCursor => "closedHandCursor",
            Cursor::CrosshairCursor => "crosshairCursor",
            Cursor::CopyCursor => "dragCopyCursor",
            Cursor::AliasCursor => "dragLinkCursor",
            Cursor::TextCursor => "IBeamCursor",
            Cursor::GrabCursor | Cursor::AllScrollCursor => "openHandCursor",
            Cursor::NoDropCursor | Cursor::NotAllowedCursor => "operationNotAllowedCursor",
            Cursor::PointerCursor => "pointingHandCursor",
            Cursor::SResizeCursor => "resizeDownCursor",
            Cursor::WResizeCursor => "resizeLeftCursor",
            Cursor::EwResizeCursor | Cursor::ColResizeCursor => "resizeLeftRightCursor",
            Cursor::EResizeCursor => "resizeRightCursor",
            Cursor::NResizeCursor => "resizeUpCursor",
            Cursor::NsResizeCursor | Cursor::RowResizeCursor => "resizeUpDownCursor",
            Cursor::VerticalTextCursor => "IBeamCursorForVerticalLayout",
            _ => "arrowCursor",
        };
        unsafe {
            msg_send()(class("NSCursor"), selector(cocoa_name))
        }
    }

    #[cfg(not(target_os="macos"))]
    fn cursor_handle_for_cursor(&self, _: Cursor) -> cef_cursor_handle_t {
        0
    }
}

impl WindowMethods for Window {
    fn framebuffer_size(&self) -> TypedSize2D<DevicePixel,u32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => TypedSize2D(400, 300),
            Some(ref browser) => {
                let mut rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_backing_rect((*browser).clone(), &mut rect);
                TypedSize2D(rect.width as u32, rect.height as u32)
            }
        }
    }

    fn size(&self) -> TypedSize2D<ScreenPx,f32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => TypedSize2D(400.0, 300.0),
            Some(ref browser) => {
                let mut rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_view_rect((*browser).clone(), &mut rect);
                TypedSize2D(rect.width as f32, rect.height as f32)
            }
        }
    }

    fn present(&self) {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                browser.get_host().get_client().get_render_handler().on_present(browser.clone());
            }
        }
    }

    fn set_ready_state(&self, ready_state: ReadyState) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let is_loading = match ready_state {
            ReadyState::Blank | ReadyState::FinishedLoading => 0,
            ReadyState::Loading | ReadyState::PerformingLayout => 1,
        };
        browser.get_host()
               .get_client()
               .get_load_handler()
               .on_loading_state_change(browser.clone(), is_loading, 1, 1);
    }

    fn set_paint_state(&self, _: PaintState) {
        // TODO(pcwalton)
    }

    fn hidpi_factor(&self) -> ScaleFactor<ScreenPx,DevicePixel,f32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => ScaleFactor(1.0),
            Some(ref browser) => {
                let mut view_rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_view_rect((*browser).clone(), &mut view_rect);
                let mut backing_rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_backing_rect((*browser).clone(), &mut backing_rect);
                ScaleFactor(backing_rect.width as f32 / view_rect.width as f32)
            }
        }
    }

    #[cfg(target_os="macos")]
    fn native_metadata(&self) -> NativeGraphicsMetadata {
        use cgl::{CGLGetCurrentContext, CGLGetPixelFormat};

        // FIXME(pcwalton)
        unsafe {
            NativeGraphicsMetadata {
                pixel_format: CGLGetPixelFormat(CGLGetCurrentContext()),
            }
        }
    }

    #[cfg(target_os="linux")]
    fn native_metadata(&self) -> NativeGraphicsMetadata {
        extern {
            fn cef_get_xdisplay() -> *mut c_void;
        }

        unsafe {
            NativeGraphicsMetadata {
                display: cef_get_xdisplay()
            }
        }
    }

    fn create_compositor_channel(_: &Option<Rc<Window>>)
                                 -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) {
        let (sender, receiver) = channel();
        (box CefCompositorProxy {
             sender: sender,
         } as Box<CompositorProxy+Send>,
         box receiver as Box<CompositorReceiver>)
    }

    fn prepare_for_composite(&self) -> bool {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                browser.get_host().get_client().get_render_handler().paint(browser.clone());
            }
        }
        true
    }

    fn load_end(&self) {
        // FIXME(pcwalton): The status code 200 is a lie.
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        browser.get_host()
               .get_client()
               .get_load_handler()
               .on_load_end((*browser).clone(), browser.get_main_frame(), 200);
    }

    fn set_page_title(&self, string: Option<String>) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let frame = browser.get_main_frame();
        let frame = frame.downcast();
        let mut title_visitor = frame.title_visitor.borrow_mut();
        match &mut *title_visitor {
            &mut None => {}
            &mut Some(ref mut visitor) => {
                match string {
                    None => visitor.visit(&[]),
                    Some(string) => {
                        let utf16_chars: Vec<u16> = Utf16Encoder::new(string.chars()).collect();
                        visitor.visit(utf16_chars.as_slice())
                    }
                }
            }
        }
    }

    fn set_page_load_data(&self, load_data: LoadData) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let frame = browser.get_main_frame();
        let frame = frame.downcast();
        *frame.url.borrow_mut() = load_data.url.to_string()
    }

    fn handle_key(&self, _: Key, _: KeyModifiers) {
        // TODO(negge)
    }

    fn set_cursor(&self, cursor: Cursor) {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                let cursor_handle = self.cursor_handle_for_cursor(cursor);
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .on_cursor_change(browser.clone(), cursor_handle)
            }
        }
    }
}

struct CefCompositorProxy {
    sender: Sender<compositor_task::Msg>,
}

impl CompositorProxy for CefCompositorProxy {
    #[cfg(target_os="macos")]
    fn send(&mut self, msg: compositor_task::Msg) {
        use cocoa::appkit::{NSApp, NSApplication, NSApplicationDefined, NSAutoreleasePool};
        use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventSubtype, NSPoint};
        use cocoa::base::nil;

        // Send a message and kick the OS event loop awake.
        self.sender.send(msg);

        unsafe {
            let pool = NSAutoreleasePool::new(nil);
            let event =
                NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
                nil,
                NSApplicationDefined,
                NSPoint::new(0.0, 0.0),
                NSEventModifierFlags::empty(),
                0.0,
                0,
                0,
                NSEventSubtype::NSWindowExposedEventType,
                0,
                0);
            NSApp().postEvent_atStart_(event, 0);
            pool.drain();
        }
    }

    #[cfg(target_os="linux")]
    fn send(&mut self, msg: compositor_task::Msg) {
        // FIXME(pcwalton): Kick the GTK event loop awake?
        self.sender.send(msg).unwrap();
    }

    fn clone_compositor_proxy(&self) -> Box<CompositorProxy+Send> {
        box CefCompositorProxy {
            sender: self.sender.clone(),
        } as Box<CompositorProxy+Send>
    }
}

<commit_msg>auto merge of #4932 : Ms2ger/servo/warning-cef-mac, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Off-screen windows.
//!
//! This is used for off-screen rendering mode only; on-screen windows (the default embedding mode)
//! are managed by a platform toolkit (Glutin).

use eutil::Downcast;
use interfaces::CefBrowser;
use render_handler::CefRenderHandlerExtensions;
use types::{cef_cursor_handle_t, cef_rect_t};
use unicode::str::Utf16Encoder;

use compositing::compositor_task::{self, CompositorProxy, CompositorReceiver};
use compositing::windowing::{WindowEvent, WindowMethods};
use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use gleam::gl;
use layers::geometry::DevicePixel;
use layers::platform::surface::NativeGraphicsMetadata;
use libc::{c_char, c_void};
use msg::constellation_msg::{Key, KeyModifiers};
use msg::compositor_msg::{ReadyState, PaintState};
use msg::constellation_msg::LoadData;
use util::cursor::Cursor;
use util::geometry::ScreenPx;
use std::cell::RefCell;
use std::ffi::CString;
use std::rc::Rc;
use std::sync::mpsc::{Sender, channel};

/// The type of an off-screen window.
#[derive(Clone)]
pub struct Window {
    cef_browser: RefCell<Option<CefBrowser>>,
}

#[cfg(target_os="macos")]
fn load_gl() {
    const RTLD_DEFAULT: *mut c_void = (-2) as *mut c_void;

    extern {
        fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
    }

    gl::load_with(|s| {
        unsafe {
            let c_str = CString::from_slice(s.as_bytes());
            dlsym(RTLD_DEFAULT, c_str.as_ptr()) as *const c_void
        }
    });
}

#[cfg(target_os="linux")]
fn load_gl() {
    extern {
        fn glXGetProcAddress(symbol: *const c_char) -> *mut c_void;
    }

    gl::load_with(|s| {
        unsafe {
            let c_str = CString::from_slice(s.as_bytes());
            glXGetProcAddress(c_str.as_ptr()) as *const c_void
        }
    });
}

impl Window {
    /// Creates a new window.
    pub fn new() -> Rc<Window> {
        load_gl();

        Rc::new(Window {
            cef_browser: RefCell::new(None),
        })
    }

    /// Sets the current browser.
    pub fn set_browser(&self, browser: CefBrowser) {
        *self.cef_browser.borrow_mut() = Some(browser)
    }

    /// Currently unimplemented.
    pub fn wait_events(&self) -> WindowEvent {
        WindowEvent::Idle
    }

    /// Returns the Cocoa cursor for a CSS cursor. These match Firefox, except where Firefox
    /// bundles custom resources (which we don't yet do).
    #[cfg(target_os="macos")]
    fn cursor_handle_for_cursor(&self, cursor: Cursor) -> cef_cursor_handle_t {
        use cocoa::base::{class, msg_send, selector};

        let cocoa_name = match cursor {
            Cursor::NoCursor => return 0 as cef_cursor_handle_t,
            Cursor::ContextMenuCursor => "contextualMenuCursor",
            Cursor::GrabbingCursor => "closedHandCursor",
            Cursor::CrosshairCursor => "crosshairCursor",
            Cursor::CopyCursor => "dragCopyCursor",
            Cursor::AliasCursor => "dragLinkCursor",
            Cursor::TextCursor => "IBeamCursor",
            Cursor::GrabCursor | Cursor::AllScrollCursor => "openHandCursor",
            Cursor::NoDropCursor | Cursor::NotAllowedCursor => "operationNotAllowedCursor",
            Cursor::PointerCursor => "pointingHandCursor",
            Cursor::SResizeCursor => "resizeDownCursor",
            Cursor::WResizeCursor => "resizeLeftCursor",
            Cursor::EwResizeCursor | Cursor::ColResizeCursor => "resizeLeftRightCursor",
            Cursor::EResizeCursor => "resizeRightCursor",
            Cursor::NResizeCursor => "resizeUpCursor",
            Cursor::NsResizeCursor | Cursor::RowResizeCursor => "resizeUpDownCursor",
            Cursor::VerticalTextCursor => "IBeamCursorForVerticalLayout",
            _ => "arrowCursor",
        };
        unsafe {
            msg_send()(class("NSCursor"), selector(cocoa_name))
        }
    }

    #[cfg(not(target_os="macos"))]
    fn cursor_handle_for_cursor(&self, _: Cursor) -> cef_cursor_handle_t {
        0
    }
}

impl WindowMethods for Window {
    fn framebuffer_size(&self) -> TypedSize2D<DevicePixel,u32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => TypedSize2D(400, 300),
            Some(ref browser) => {
                let mut rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_backing_rect((*browser).clone(), &mut rect);
                TypedSize2D(rect.width as u32, rect.height as u32)
            }
        }
    }

    fn size(&self) -> TypedSize2D<ScreenPx,f32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => TypedSize2D(400.0, 300.0),
            Some(ref browser) => {
                let mut rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_view_rect((*browser).clone(), &mut rect);
                TypedSize2D(rect.width as f32, rect.height as f32)
            }
        }
    }

    fn present(&self) {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                browser.get_host().get_client().get_render_handler().on_present(browser.clone());
            }
        }
    }

    fn set_ready_state(&self, ready_state: ReadyState) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let is_loading = match ready_state {
            ReadyState::Blank | ReadyState::FinishedLoading => 0,
            ReadyState::Loading | ReadyState::PerformingLayout => 1,
        };
        browser.get_host()
               .get_client()
               .get_load_handler()
               .on_loading_state_change(browser.clone(), is_loading, 1, 1);
    }

    fn set_paint_state(&self, _: PaintState) {
        // TODO(pcwalton)
    }

    fn hidpi_factor(&self) -> ScaleFactor<ScreenPx,DevicePixel,f32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => ScaleFactor(1.0),
            Some(ref browser) => {
                let mut view_rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_view_rect((*browser).clone(), &mut view_rect);
                let mut backing_rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_backing_rect((*browser).clone(), &mut backing_rect);
                ScaleFactor(backing_rect.width as f32 / view_rect.width as f32)
            }
        }
    }

    #[cfg(target_os="macos")]
    fn native_metadata(&self) -> NativeGraphicsMetadata {
        use cgl::{CGLGetCurrentContext, CGLGetPixelFormat};

        // FIXME(pcwalton)
        unsafe {
            NativeGraphicsMetadata {
                pixel_format: CGLGetPixelFormat(CGLGetCurrentContext()),
            }
        }
    }

    #[cfg(target_os="linux")]
    fn native_metadata(&self) -> NativeGraphicsMetadata {
        extern {
            fn cef_get_xdisplay() -> *mut c_void;
        }

        unsafe {
            NativeGraphicsMetadata {
                display: cef_get_xdisplay()
            }
        }
    }

    fn create_compositor_channel(_: &Option<Rc<Window>>)
                                 -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) {
        let (sender, receiver) = channel();
        (box CefCompositorProxy {
             sender: sender,
         } as Box<CompositorProxy+Send>,
         box receiver as Box<CompositorReceiver>)
    }

    fn prepare_for_composite(&self) -> bool {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                browser.get_host().get_client().get_render_handler().paint(browser.clone());
            }
        }
        true
    }

    fn load_end(&self) {
        // FIXME(pcwalton): The status code 200 is a lie.
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        browser.get_host()
               .get_client()
               .get_load_handler()
               .on_load_end((*browser).clone(), browser.get_main_frame(), 200);
    }

    fn set_page_title(&self, string: Option<String>) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let frame = browser.get_main_frame();
        let frame = frame.downcast();
        let mut title_visitor = frame.title_visitor.borrow_mut();
        match &mut *title_visitor {
            &mut None => {}
            &mut Some(ref mut visitor) => {
                match string {
                    None => visitor.visit(&[]),
                    Some(string) => {
                        let utf16_chars: Vec<u16> = Utf16Encoder::new(string.chars()).collect();
                        visitor.visit(utf16_chars.as_slice())
                    }
                }
            }
        }
    }

    fn set_page_load_data(&self, load_data: LoadData) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let frame = browser.get_main_frame();
        let frame = frame.downcast();
        *frame.url.borrow_mut() = load_data.url.to_string()
    }

    fn handle_key(&self, _: Key, _: KeyModifiers) {
        // TODO(negge)
    }

    fn set_cursor(&self, cursor: Cursor) {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                let cursor_handle = self.cursor_handle_for_cursor(cursor);
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .on_cursor_change(browser.clone(), cursor_handle)
            }
        }
    }
}

struct CefCompositorProxy {
    sender: Sender<compositor_task::Msg>,
}

impl CompositorProxy for CefCompositorProxy {
    #[cfg(target_os="macos")]
    fn send(&mut self, msg: compositor_task::Msg) {
        use cocoa::appkit::{NSApp, NSApplication, NSApplicationDefined, NSAutoreleasePool};
        use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventSubtype, NSPoint};
        use cocoa::base::nil;

        // Send a message and kick the OS event loop awake.
        self.sender.send(msg).unwrap();

        unsafe {
            let pool = NSAutoreleasePool::new(nil);
            let event =
                NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
                nil,
                NSApplicationDefined,
                NSPoint::new(0.0, 0.0),
                NSEventModifierFlags::empty(),
                0.0,
                0,
                0,
                NSEventSubtype::NSWindowExposedEventType,
                0,
                0);
            NSApp().postEvent_atStart_(event, 0);
            pool.drain();
        }
    }

    #[cfg(target_os="linux")]
    fn send(&mut self, msg: compositor_task::Msg) {
        // FIXME(pcwalton): Kick the GTK event loop awake?
        self.sender.send(msg).unwrap();
    }

    fn clone_compositor_proxy(&self) -> Box<CompositorProxy+Send> {
        box CefCompositorProxy {
            sender: self.sender.clone(),
        } as Box<CompositorProxy+Send>
    }
}

<|endoftext|>


<commit_before>//! This crate defines a
//! [Wadler-style](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)
//! pretty-printing API.
//!
//! See `Doc` and
extern crate typed_arena;

use doc::Doc::{Append, Group, Nest, Newline, Nil, Space, Text};
use std::borrow::Cow;
use std::fmt;
use std::ops::Deref;

mod doc;

#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct BoxDoc<'a>(Box<doc::Doc<'a, BoxDoc<'a>>>);

impl<'a> fmt::Debug for BoxDoc<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl<'a> BoxDoc<'a> {
    fn new(doc: doc::Doc<'a, BoxDoc<'a>>) -> BoxDoc<'a> {
        BoxDoc(Box::new(doc))
    }
}

impl<'a> Deref for BoxDoc<'a> {
    type Target = doc::Doc<'a, BoxDoc<'a>>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

/// The `DocBuilder` type allows for convenient appending of documents even for arena allocated
/// documents by storing the arena inline.
#[derive(Eq, Ord, PartialEq, PartialOrd)]
pub struct DocBuilder<'a, A: ?Sized>(pub &'a A, pub doc::Doc<'a, A::Doc>)
    where A: DocAllocator<'a> + 'a;

impl<'a, A: DocAllocator<'a> + 'a> Clone for DocBuilder<'a, A> {
    fn clone(&self) -> Self {
        DocBuilder(self.0, self.1.clone())
    }
}

impl<'a, A: ?Sized> Into<doc::Doc<'a, A::Doc>> for DocBuilder<'a, A>
    where A: DocAllocator<'a>
{
    fn into(self) -> doc::Doc<'a, A::Doc> {
        self.1
    }
}

/// The `DocAllocator` trait abstracts over a type which can allocate (pointers to) `Doc`.
pub trait DocAllocator<'a> {
    type Doc: Deref<Target = doc::Doc<'a, Self::Doc>> + Clone;
    fn alloc(&'a self, doc::Doc<'a, Self::Doc>) -> Self::Doc;

    #[inline]
    fn nil(&'a self) -> DocBuilder<'a, Self> {
        DocBuilder(self, Nil)
    }

    #[inline]
    fn newline(&'a self) -> DocBuilder<'a, Self> {
        DocBuilder(self, Newline)
    }

    #[inline]
    fn space(&'a self) -> DocBuilder<'a, Self> {
        DocBuilder(self, Space)
    }

    #[inline]
    fn as_string<T: ToString>(&'a self, t: T) -> DocBuilder<'a, Self> {
        self.text(t.to_string())
    }

    #[inline]
    fn text<T: Into<Cow<'a, str>>>(&'a self, data: T) -> DocBuilder<'a, Self> {
        let text = data.into();
        debug_assert!(!text.contains(|c: char| c == '\n' || c == '\r'));
        DocBuilder(self, Text(text))
    }

    #[inline]
    fn concat<I>(&'a self, docs: I) -> DocBuilder<'a, Self>
        where I: IntoIterator,
              I::Item: Into<doc::Doc<'a, Self::Doc>>
    {
        docs.into_iter().fold(self.nil(), |a, b| a.append(b))
    }
}

impl<'a, 's, A: ?Sized> DocBuilder<'a, A>
    where A: DocAllocator<'a>
{
    #[inline]
    pub fn append<B>(self, that: B) -> DocBuilder<'a, A>
        where B: Into<doc::Doc<'a, A::Doc>>
    {
        let DocBuilder(allocator, this) = self;
        let that = that.into();
        let doc = match this {
            Nil => that,
            _ => {
                match that {
                    Nil => this,
                    _ => Append(allocator.alloc(this), allocator.alloc(that)),
                }
            }
        };
        DocBuilder(allocator, doc)
    }

    #[inline]
    pub fn group(self) -> DocBuilder<'a, A> {
        let DocBuilder(allocator, this) = self;
        DocBuilder(allocator, Group(allocator.alloc(this)))
    }

    #[inline]
    pub fn nest(self, offset: usize) -> DocBuilder<'a, A> {
        let DocBuilder(allocator, this) = self;
        DocBuilder(allocator, Nest(offset, allocator.alloc(this)))
    }
}

/// Newtype wrapper for `&doc::Doc`
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct RefDoc<'a>(&'a doc::Doc<'a, RefDoc<'a>>);

impl<'a> fmt::Debug for RefDoc<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl<'a> Deref for RefDoc<'a> {
    type Target = doc::Doc<'a, RefDoc<'a>>;

    fn deref(&self) -> &doc::Doc<'a, RefDoc<'a>> {
        &self.0
    }
}

/// An arena which can be used to allocate `Doc` values.
pub type Arena<'a> = typed_arena::Arena<doc::Doc<'a, RefDoc<'a>>>;

impl<'a> DocAllocator<'a> for Arena<'a> {
    type Doc = RefDoc<'a>;

    fn alloc(&'a self, doc: doc::Doc<'a, Self::Doc>) -> Self::Doc {
        RefDoc(Arena::alloc(self, doc))
    }
}

pub struct BoxAllocator;

static BOX_ALLOCATOR: BoxAllocator = BoxAllocator;

impl<'a> DocAllocator<'a> for BoxAllocator {
    type Doc = BoxDoc<'a>;

    fn alloc(&'a self, doc: doc::Doc<'a, Self::Doc>) -> Self::Doc {
        BoxDoc::new(doc)
    }
}

pub use doc::Doc;

impl<'a, B> Doc<'a, B> {
    #[inline]
    pub fn nil() -> Doc<'a, B> {
        Nil
    }

    #[inline]
    pub fn as_string<T: ToString>(t: T) -> Doc<'a, B> {
        Doc::text(t.to_string())
    }

    #[inline]
    pub fn newline() -> Doc<'a, B> {
        Newline
    }

    #[inline]
    pub fn text<T: Into<Cow<'a, str>>>(data: T) -> Doc<'a, B> {
        let text = data.into();
        debug_assert!(!text.contains(|c: char| c == '\n' || c == '\r'));
        Text(text)
    }

    #[inline]
    pub fn space() -> Doc<'a, B> {
        Space
    }
}

impl<'a> Doc<'a, BoxDoc<'a>> {
    #[inline]
    pub fn append(self, that: Doc<'a, BoxDoc<'a>>) -> Doc<'a, BoxDoc<'a>> {
        DocBuilder(&BOX_ALLOCATOR, self).append(that).into()
    }

    #[inline]
    pub fn concat<I>(&'a self, docs: I) -> Doc<'a, BoxDoc<'a>>
        where I: IntoIterator<Item = Doc<'a, BoxDoc<'a>>>
    {
        docs.into_iter().fold(Doc::nil(), |a, b| a.append(b))
    }

    #[inline]
    pub fn group(self) -> Doc<'a, BoxDoc<'a>> {
        DocBuilder(&BOX_ALLOCATOR, self).group().into()
    }

    #[inline]
    pub fn nest(self, offset: usize) -> Doc<'a, BoxDoc<'a>> {
        DocBuilder(&BOX_ALLOCATOR, self).nest(offset).into()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::str::from_utf8;

    macro_rules! test {
        ($size: expr, $actual: expr, $expected: expr) => {
            let mut vec = Vec::new();
            $actual.render($size, &mut vec).unwrap();
            assert_eq!(from_utf8(&vec).unwrap(), $expected);
        };
        ($actual: expr, $expected: expr) => {
            test!(70, $actual, $expected)
        }
    }

    #[test]
    fn box_doc_inference() {
        let doc = Doc::group(Doc::text("test").append(Doc::space()).append(Doc::text("test")));
        test!(doc, "test test");
    }

    #[test]
    fn forced_newline() {
        let doc = Doc::group(Doc::text("test").append(Doc::newline()).append(Doc::text("test")));
        test!(doc, "test\ntest");
    }

    #[test]
    fn block() {
        let doc = Doc::group(Doc::text("{")
            .append(Doc::space()
                .append(Doc::text("test"))
                .append(Doc::space())
                .append(Doc::text("test"))
                .nest(2))
            .append(Doc::space())
            .append(Doc::text("}")));
        test!(5, doc, "{\n  test\n  test\n}");
    }
}
<commit_msg>perf: Avoid allocating newline and space documents<commit_after>//! This crate defines a
//! [Wadler-style](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)
//! pretty-printing API.
//!
//! See `Doc` and
extern crate typed_arena;

use doc::Doc::{Append, Group, Nest, Newline, Nil, Space, Text};
use std::borrow::Cow;
use std::fmt;
use std::ops::Deref;

mod doc;

#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct BoxDoc<'a>(Box<doc::Doc<'a, BoxDoc<'a>>>);

impl<'a> fmt::Debug for BoxDoc<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl<'a> BoxDoc<'a> {
    fn new(doc: doc::Doc<'a, BoxDoc<'a>>) -> BoxDoc<'a> {
        BoxDoc(Box::new(doc))
    }
}

impl<'a> Deref for BoxDoc<'a> {
    type Target = doc::Doc<'a, BoxDoc<'a>>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

/// The `DocBuilder` type allows for convenient appending of documents even for arena allocated
/// documents by storing the arena inline.
#[derive(Eq, Ord, PartialEq, PartialOrd)]
pub struct DocBuilder<'a, A: ?Sized>(pub &'a A, pub doc::Doc<'a, A::Doc>)
    where A: DocAllocator<'a> + 'a;

impl<'a, A: DocAllocator<'a> + 'a> Clone for DocBuilder<'a, A> {
    fn clone(&self) -> Self {
        DocBuilder(self.0, self.1.clone())
    }
}

impl<'a, A: ?Sized> Into<doc::Doc<'a, A::Doc>> for DocBuilder<'a, A>
    where A: DocAllocator<'a>
{
    fn into(self) -> doc::Doc<'a, A::Doc> {
        self.1
    }
}

/// The `DocAllocator` trait abstracts over a type which can allocate (pointers to) `Doc`.
pub trait DocAllocator<'a> {
    type Doc: Deref<Target = doc::Doc<'a, Self::Doc>> + Clone;
    fn alloc(&'a self, doc::Doc<'a, Self::Doc>) -> Self::Doc;

    #[inline]
    fn nil(&'a self) -> DocBuilder<'a, Self> {
        DocBuilder(self, Nil)
    }

    #[inline]
    fn newline(&'a self) -> DocBuilder<'a, Self> {
        DocBuilder(self, Newline)
    }

    #[inline]
    fn space(&'a self) -> DocBuilder<'a, Self> {
        DocBuilder(self, Space)
    }

    #[inline]
    fn as_string<T: ToString>(&'a self, t: T) -> DocBuilder<'a, Self> {
        self.text(t.to_string())
    }

    #[inline]
    fn text<T: Into<Cow<'a, str>>>(&'a self, data: T) -> DocBuilder<'a, Self> {
        let text = data.into();
        debug_assert!(!text.contains(|c: char| c == '\n' || c == '\r'));
        DocBuilder(self, Text(text))
    }

    #[inline]
    fn concat<I>(&'a self, docs: I) -> DocBuilder<'a, Self>
        where I: IntoIterator,
              I::Item: Into<doc::Doc<'a, Self::Doc>>
    {
        docs.into_iter().fold(self.nil(), |a, b| a.append(b))
    }
}

impl<'a, 's, A: ?Sized> DocBuilder<'a, A>
    where A: DocAllocator<'a>
{
    #[inline]
    pub fn append<B>(self, that: B) -> DocBuilder<'a, A>
        where B: Into<doc::Doc<'a, A::Doc>>
    {
        let DocBuilder(allocator, this) = self;
        let that = that.into();
        let doc = match this {
            Nil => that,
            _ => {
                match that {
                    Nil => this,
                    _ => Append(allocator.alloc(this), allocator.alloc(that)),
                }
            }
        };
        DocBuilder(allocator, doc)
    }

    #[inline]
    pub fn group(self) -> DocBuilder<'a, A> {
        let DocBuilder(allocator, this) = self;
        DocBuilder(allocator, Group(allocator.alloc(this)))
    }

    #[inline]
    pub fn nest(self, offset: usize) -> DocBuilder<'a, A> {
        let DocBuilder(allocator, this) = self;
        DocBuilder(allocator, Nest(offset, allocator.alloc(this)))
    }
}

/// Newtype wrapper for `&doc::Doc`
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct RefDoc<'a>(&'a doc::Doc<'a, RefDoc<'a>>);

impl<'a> fmt::Debug for RefDoc<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl<'a> Deref for RefDoc<'a> {
    type Target = doc::Doc<'a, RefDoc<'a>>;

    fn deref(&self) -> &doc::Doc<'a, RefDoc<'a>> {
        &self.0
    }
}

/// An arena which can be used to allocate `Doc` values.
pub type Arena<'a> = typed_arena::Arena<doc::Doc<'a, RefDoc<'a>>>;

impl<'a> DocAllocator<'a> for Arena<'a> {
    type Doc = RefDoc<'a>;

    #[inline]
    fn alloc(&'a self, doc: doc::Doc<'a, Self::Doc>) -> Self::Doc {
        static SPACE: doc::Doc<'static, RefDoc<'static>> = Doc::Space;
        static NEWLINE: doc::Doc<'static, RefDoc<'static>> = Doc::Newline;

        RefDoc(match doc {
            Space => &SPACE,
            Newline => &NEWLINE,
            _ => Arena::alloc(self, doc),
        })
    }
}

pub struct BoxAllocator;

static BOX_ALLOCATOR: BoxAllocator = BoxAllocator;

impl<'a> DocAllocator<'a> for BoxAllocator {
    type Doc = BoxDoc<'a>;

    #[inline]
    fn alloc(&'a self, doc: doc::Doc<'a, Self::Doc>) -> Self::Doc {
        BoxDoc::new(doc)
    }
}

pub use doc::Doc;

impl<'a, B> Doc<'a, B> {
    #[inline]
    pub fn nil() -> Doc<'a, B> {
        Nil
    }

    #[inline]
    pub fn as_string<T: ToString>(t: T) -> Doc<'a, B> {
        Doc::text(t.to_string())
    }

    #[inline]
    pub fn newline() -> Doc<'a, B> {
        Newline
    }

    #[inline]
    pub fn text<T: Into<Cow<'a, str>>>(data: T) -> Doc<'a, B> {
        let text = data.into();
        debug_assert!(!text.contains(|c: char| c == '\n' || c == '\r'));
        Text(text)
    }

    #[inline]
    pub fn space() -> Doc<'a, B> {
        Space
    }
}

impl<'a> Doc<'a, BoxDoc<'a>> {
    #[inline]
    pub fn append(self, that: Doc<'a, BoxDoc<'a>>) -> Doc<'a, BoxDoc<'a>> {
        DocBuilder(&BOX_ALLOCATOR, self).append(that).into()
    }

    #[inline]
    pub fn concat<I>(&'a self, docs: I) -> Doc<'a, BoxDoc<'a>>
        where I: IntoIterator<Item = Doc<'a, BoxDoc<'a>>>
    {
        docs.into_iter().fold(Doc::nil(), |a, b| a.append(b))
    }

    #[inline]
    pub fn group(self) -> Doc<'a, BoxDoc<'a>> {
        DocBuilder(&BOX_ALLOCATOR, self).group().into()
    }

    #[inline]
    pub fn nest(self, offset: usize) -> Doc<'a, BoxDoc<'a>> {
        DocBuilder(&BOX_ALLOCATOR, self).nest(offset).into()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::str::from_utf8;

    macro_rules! test {
        ($size: expr, $actual: expr, $expected: expr) => {
            let mut vec = Vec::new();
            $actual.render($size, &mut vec).unwrap();
            assert_eq!(from_utf8(&vec).unwrap(), $expected);
        };
        ($actual: expr, $expected: expr) => {
            test!(70, $actual, $expected)
        }
    }

    #[test]
    fn box_doc_inference() {
        let doc = Doc::group(Doc::text("test").append(Doc::space()).append(Doc::text("test")));
        test!(doc, "test test");
    }

    #[test]
    fn forced_newline() {
        let doc = Doc::group(Doc::text("test").append(Doc::newline()).append(Doc::text("test")));
        test!(doc, "test\ntest");
    }

    #[test]
    fn block() {
        let doc = Doc::group(Doc::text("{")
            .append(Doc::space()
                .append(Doc::text("test"))
                .append(Doc::space())
                .append(Doc::text("test"))
                .nest(2))
            .append(Doc::space())
            .append(Doc::text("}")));
        test!(5, doc, "{\n  test\n  test\n}");
    }
}
<|endoftext|>


<commit_before>extern crate protoc_grpcio;

fn main() {
    let proto_root = &format!("{}{}", "src", std::path::MAIN_SEPARATOR);
    println!("cargo:rerun-if-changed={}", proto_root);
    protoc_grpcio::compile_grpc_protos(&["services.proto"], &[proto_root], &proto_root)
        .expect("Failed to compile gRPC definitions!");
}
<commit_msg>Change build root folder String to a Path<commit_after>extern crate protoc_grpcio;

use std::path::Path;

fn main() {
    let proto_root = Path::new("src");
    println!("cargo:rerun-if-changed={}", proto_root.display());
    protoc_grpcio::compile_grpc_protos(&["services.proto"], &[proto_root], &proto_root)
        .expect("Failed to compile gRPC definitions!");
}
<|endoftext|>


<commit_before>// Copyright 2020 The Evcxr Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::scan::{validate_source_fragment, FragmentValidity};
use colored::*;
use rustyline::{
    completion::Completer,
    error::ReadlineError,
    highlight::Highlighter,
    hint::Hinter,
    validate::{ValidationContext, ValidationResult, Validator},
    Helper,
};
use std::borrow::Cow;

pub struct EvcxrRustylineHelper {
    _priv: (),
}

impl Default for EvcxrRustylineHelper {
    fn default() -> Self {
        Self { _priv: () }
    }
}

// Have to implement a bunch of traits as mostly noop...

impl Hinter for EvcxrRustylineHelper {}

impl Completer for EvcxrRustylineHelper {
    type Candidate = String;
}

impl Highlighter for EvcxrRustylineHelper {
    fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
        &'s self,
        prompt: &'p str,
        _default: bool,
    ) -> Cow<'b, str> {
        prompt.yellow().to_string().into()
    }
}

impl Validator for EvcxrRustylineHelper {
    fn validate(&self, ctx: &mut ValidationContext<'_>) -> Result<ValidationResult, ReadlineError> {
        let input = ctx.input();
        // If a user is hammering on the enter key, lets pass things along to
        // rustc. This is an escape hatch for the case where *we* know (well,
        // think) the source is incomplete, but the user doesn't. It also makes
        // bugs in our code less disasterous.
        if input.ends_with("\n\n") {
            return Ok(ValidationResult::Valid(None));
        }
        match validate_source_fragment(input) {
            FragmentValidity::Incomplete => Ok(ValidationResult::Incomplete),
            FragmentValidity::Invalid => {
                // Hrm... AFAICT if we return Invalid here, we don't get to run
                // it. `rustc` is likely to be able to provide a better error
                // message than us, so...
                Ok(ValidationResult::Valid(None))
            }
            FragmentValidity::Valid => Ok(ValidationResult::Valid(None)),
        }
    }

    // We actually work with this on for the most part, but it seems incomplete
    // in rustyline, so disable it (explicitly). It's unclear how desirable
    // it is without the ability to e.g. highlight the mismatched bracket or
    // whatever.
    fn validate_while_typing(&self) -> bool {
        false
    }
}

impl Helper for EvcxrRustylineHelper {}
<commit_msg>naive-tab-completion<commit_after>// Copyright 2020 The Evcxr Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::scan::{validate_source_fragment, FragmentValidity};
use colored::*;
use rustyline::{
    completion::Completer,
    error::ReadlineError,
    highlight::Highlighter,
    hint::Hinter,
    validate::{ValidationContext, ValidationResult, Validator},
    Context,
    Helper,
};
use std::borrow::Cow;
use std::collections::HashSet;

pub struct EvcxrRustylineHelper {
    _priv: (),
}

impl Default for EvcxrRustylineHelper {
    fn default() -> Self {
        Self { _priv: () }
    }
}

// Have to implement a bunch of traits as mostly noop...

impl Hinter for EvcxrRustylineHelper {}

impl Completer for EvcxrRustylineHelper {
    type Candidate = String;

    fn complete(
        &self,
        line: &str,
        pos: usize,
        ctx: &Context<'_>,
    ) -> rustyline::Result<(usize, Vec<String>)> {
        // find the left side of cursor
        let (left, _) = line.split_at(pos);
        // remove the longest alphanumeric text before the cursor
        let new_pos = left.trim_end_matches(|c: char| c.is_alphanumeric()).len();
        // search for the longest alphanumeric text before the cursor
        let (_, search_prefix) = left.split_at(new_pos);

        // go through history and find all the matching 'words'
        let res: HashSet<_> = ctx.history()
            .iter()
            .flat_map(|h| h.split(|c:char| !c.is_alphanumeric()))
            .filter(|s| !s.is_empty() && s.starts_with(search_prefix))
            .collect();
        // HashSet here is used to make them unique
        let res : Vec<String> = res.iter()
            .map(|s| (*s).into())
            .collect();

        Ok((new_pos, res))
    }
}

impl Highlighter for EvcxrRustylineHelper {
    fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
        &'s self,
        prompt: &'p str,
        _default: bool,
    ) -> Cow<'b, str> {
        prompt.yellow().to_string().into()
    }
}

impl Validator for EvcxrRustylineHelper {
    fn validate(&self, ctx: &mut ValidationContext<'_>) -> Result<ValidationResult, ReadlineError> {
        let input = ctx.input();
        // If a user is hammering on the enter key, lets pass things along to
        // rustc. This is an escape hatch for the case where *we* know (well,
        // think) the source is incomplete, but the user doesn't. It also makes
        // bugs in our code less disasterous.
        if input.ends_with("\n\n") {
            return Ok(ValidationResult::Valid(None));
        }
        match validate_source_fragment(input) {
            FragmentValidity::Incomplete => Ok(ValidationResult::Incomplete),
            FragmentValidity::Invalid => {
                // Hrm... AFAICT if we return Invalid here, we don't get to run
                // it. `rustc` is likely to be able to provide a better error
                // message than us, so...
                Ok(ValidationResult::Valid(None))
            }
            FragmentValidity::Valid => Ok(ValidationResult::Valid(None)),
        }
    }

    // We actually work with this on for the most part, but it seems incomplete
    // in rustyline, so disable it (explicitly). It's unclear how desirable
    // it is without the ability to e.g. highlight the mismatched bracket or
    // whatever.
    fn validate_while_typing(&self) -> bool {
        false
    }
}

impl Helper for EvcxrRustylineHelper {}
<|endoftext|>


<commit_before>extern crate flate2;
extern crate std;
extern crate tar;

use std::io::Read;

#[derive(Debug, Default, Clone)]
pub struct Desc {
    groups: Vec<String>,
    license: Vec<String>,
    replaces: Vec<String>,
    filename: String,
    name: String,
    base: String,
    version: String,
    desc: String,
    csize: u64,
    isize: u64,
    md5sum: String,
    sha256sum: String,
    pgpsig: String,
    url: String,
    arch: String,
    builddate: u64,
    packager: String,

    // These fields are once stored in depends file
    // https://git.archlinux.org/pacman.git/commit/?id=b520c6312ff0ffec864576b5218f1638fae1e18b
    conflicts: Vec<String>,
    provides: Vec<String>,
    depends: Vec<String>,
    makedepends: Vec<String>,
    checkdepends: Vec<String>,
    optdepends: Vec<String>,
}

#[derive(Clone)]
pub struct PackageEntry {
    pub desc: Desc,
    pub files: Vec<String>,
}

#[derive(Clone)]
pub struct Repository {
    path: std::path::PathBuf,
    signer: Option<super::signer::Signer>,
    entries: std::collections::HashMap<String, PackageEntry>,
}

impl Repository {
    pub fn new(path: std::path::PathBuf, signer: Option<super::signer::Signer>) -> Repository {
        return Repository {
            path: path,
            signer: signer,
            entries: std::collections::HashMap::new(),
        };
    }

    pub fn load(&mut self) {
        let file = std::fs::File::open(&self.path).unwrap();
        let gz_reader = flate2::read::GzDecoder::new(file).unwrap();
        let mut tar_reader = tar::Archive::new(gz_reader);
        let mut desc_entries = std::collections::HashMap::new();
        let mut files_entries = std::collections::HashMap::new();
        for entry_result in tar_reader.entries().unwrap() {
            let mut entry = entry_result.unwrap();
            let pathbuf = entry.path().unwrap().into_owned();
            let pathname = pathbuf.to_str().unwrap();
            match entry.header().entry_type() {
                tar::EntryType::Regular => {
                    let splitn: Vec<&str> = pathname.splitn(2, '/').collect();
                    if splitn.len() == 2 {
                        let mut body = String::new();
                        entry.read_to_string(&mut body).unwrap();
                        match splitn[1] {
                            "desc" => {
                                desc_entries.insert(splitn[0].to_owned(), parse_desc(&body));
                            }
                            "depends" => {
                                // old format
                            }
                            "files" => {
                                files_entries.insert(splitn[0].to_owned(), parse_files(&body));
                            }
                            _ => {
                                panic!("Unknown pathname: {}", pathname);
                            }
                        }
                    } else {
                        panic!("Invalid pathname entry: {}", pathname);
                    }
                }
                tar::EntryType::Directory => {}
                _ => {
                    panic!("Unknown file type: {}", pathname);
                }
            }
        }

        for (_, desc) in desc_entries.into_iter() {
            let files = files_entries.remove(&desc.name).unwrap_or(vec![]);
            self.entries.insert(desc.name.to_owned(),
                                PackageEntry {
                                    desc: desc,
                                    files: files,
                                });
        }
    }

    pub fn add(&mut self, package: &super::package::Package) {
        let desc = Desc {
            groups: package.groups().to_owned(),
            license: package.license().to_owned(),
            replaces: package.replaces().to_owned(),
            filename: package.filename().to_owned(),
            name: package.pkgname().to_owned(),
            base: package.pkgbase().to_owned(),
            version: package.pkgver().to_owned(),
            desc: package.pkgdesc().to_owned(),
            csize: package.csize(),
            isize: package.isize(),
            md5sum: package.md5sum().to_owned(),
            sha256sum: package.sha256sum().to_owned(),
            pgpsig: package.pgpsig().to_owned(),
            url: package.url().to_owned(),
            arch: package.arch().to_owned(),
            builddate: package.builddate(),
            packager: package.packager().to_owned(),
            conflicts: package.conflicts().to_owned(),
            provides: package.provides().to_owned(),
            depends: package.depends().to_owned(),
            makedepends: package.makedepends().to_owned(),
            checkdepends: package.checkdepends().to_owned(),
            optdepends: package.optdepends().to_owned(),
        };
        self.entries.insert(desc.name.to_owned(),
                            PackageEntry {
                                desc: desc,
                                files: package.files().to_owned(),
                            });
    }

    pub fn remove(&mut self, package_name: &str) {
        self.entries.remove(package_name);
    }

    pub fn save(&self, include_files: bool) {
        let mut tmp_path = self.path.clone().into_os_string();
        tmp_path.push(".progress");
        let file = std::fs::File::create(&tmp_path).unwrap();
        let gz_writer = flate2::write::GzEncoder::new(file, flate2::Compression::Default);
        let mut builder = tar::Builder::new(gz_writer);
        for (_, package_entry) in self.entries.iter() {
            let pathbuf = std::path::PathBuf::from(format!("{}-{}/",
                                                           package_entry.desc.name,
                                                           package_entry.desc.version));
            {
                let mut dir_header = tar::Header::new_gnu();
                dir_header.set_entry_type(tar::EntryType::Directory);
                dir_header.set_path(&pathbuf).unwrap();
                dir_header.set_mode(0o755);
                dir_header.set_size(0);
                dir_header.set_cksum();
                builder.append(&dir_header, std::io::empty()).unwrap();
            }
            {
                let mut desc_header = tar::Header::new_gnu();
                desc_header.set_entry_type(tar::EntryType::Regular);
                desc_header.set_path(pathbuf.join("desc")).unwrap();
                desc_header.set_mode(0o644);
                let desc_str = into_desc_file(package_entry);
                let desc_bytes = desc_str.as_bytes();
                desc_header.set_size(desc_bytes.len() as u64);
                desc_header.set_cksum();
                builder.append(&desc_header, desc_bytes).unwrap();
            }
            if include_files {
                let mut files_header = tar::Header::new_gnu();
                files_header.set_entry_type(tar::EntryType::Regular);
                files_header.set_path(pathbuf.join("files")).unwrap();
                files_header.set_mode(0o644);
                let files_str = into_files_file(&package_entry.files);
                let files_bytes = files_str.as_bytes();
                files_header.set_size(files_bytes.len() as u64);
                files_header.set_cksum();
                builder.append(&files_header, files_bytes).unwrap();
            }
        }
        let gz_writer = builder.into_inner().unwrap();
        gz_writer.finish().unwrap();

        if let Some(ref signer) = self.signer {
            let mut sig_path = self.path.clone().into_os_string();
            sig_path.push(".sig");
            signer.sign(&tmp_path, sig_path);
        }

        std::fs::rename(&tmp_path, &self.path).unwrap();
    }
}

fn parse_desc(body: &str) -> Desc {
    let mut desc = Desc::default();
    for (key, val) in each_entry(body) {
        match key {
            "GROUPS" => {
                desc.groups.push(val.to_owned());
            }
            "REPLACES" => {
                desc.replaces.push(val.to_owned());
            }
            "FILENAME" => {
                desc.filename = val.to_owned();
            }
            "NAME" => {
                desc.name = val.to_owned();
            }
            "BASE" => {
                desc.base = val.to_owned();
            }
            "VERSION" => {
                desc.version = val.to_owned();
            }
            "DESC" => {
                desc.desc = val.to_owned();
            }
            "CSIZE" => {
                desc.csize = val.parse().unwrap();
            }
            "ISIZE" => {
                desc.isize = val.parse().unwrap();
            }
            "MD5SUM" => {
                desc.md5sum = val.to_owned();
            }
            "SHA256SUM" => {
                desc.sha256sum = val.to_owned();
            }
            "PGPSIG" => {
                desc.pgpsig = val.to_owned();
            }
            "URL" => {
                desc.url = val.to_owned();
            }
            "LICENSE" => {
                desc.license.push(val.to_owned());
            }
            "ARCH" => {
                desc.arch = val.to_owned();
            }
            "BUILDDATE" => {
                desc.builddate = val.parse().unwrap();
            }
            "PACKAGER" => {
                desc.packager = val.to_owned();
            }
            "CONFLICTS" => {
                desc.conflicts.push(val.to_owned());
            }
            "PROVIDES" => {
                desc.provides.push(val.to_owned());
            }
            "DEPENDS" => {
                desc.depends.push(val.to_owned());
            }
            "MAKEDEPENDS" => {
                desc.makedepends.push(val.to_owned());
            }
            "CHECKDEPENDS" => {
                desc.checkdepends.push(val.to_owned());
            }
            "OPTDEPENDS" => {
                desc.optdepends.push(val.to_owned());
            }
            _ => {
                panic!("Unknown desc entry: {}", key);
            }
        }
    }
    return desc;
}

struct EachEntry<'a> {
    key: &'a str,
    lines: std::str::Lines<'a>,
}

impl<'a> Iterator for EachEntry<'a> {
    type Item = (&'a str, &'a str);

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(line) = self.lines.next() {
            let line = line.trim();
            if line.starts_with('%') && line.ends_with('%') {
                self.key = &line[1..line.len() - 1];
            } else if line.is_empty() {
                self.key = "";
            } else {
                return Some((self.key, line));
            }
        }
        return None;
    }
}

fn each_entry(body: &str) -> EachEntry {
    EachEntry {
        key: "",
        lines: body.lines(),
    }
}

fn into_desc_file(package_entry: &PackageEntry) -> String {
    let mut buf = String::new();
    let ref desc = package_entry.desc;
    desc_write_array(&mut buf, "GROUPS", &desc.groups);
    desc_write_array(&mut buf, "REPLACES", &desc.replaces);
    desc_write_string(&mut buf, "FILENAME", &desc.filename);
    desc_write_string(&mut buf, "NAME", &desc.name);
    desc_write_string(&mut buf, "BASE", &desc.base);
    desc_write_string(&mut buf, "VERSION", &desc.version);
    desc_write_string(&mut buf, "DESC", &desc.desc);
    desc_write_u64(&mut buf, "CSIZE", desc.csize);
    desc_write_u64(&mut buf, "ISIZE", desc.isize);
    desc_write_string(&mut buf, "MD5SUM", &desc.md5sum);
    desc_write_string(&mut buf, "SHA256SUM", &desc.sha256sum);
    desc_write_string(&mut buf, "PGPSIG", &desc.pgpsig);
    desc_write_string(&mut buf, "URL", &desc.url);
    desc_write_array(&mut buf, "LICENSE", &desc.license);
    desc_write_string(&mut buf, "ARCH", &desc.arch);
    desc_write_u64(&mut buf, "BUILDDATE", desc.builddate);
    desc_write_string(&mut buf, "PACKAGER", &desc.packager);
    desc_write_array(&mut buf, "CONFLICTS", &desc.conflicts);
    desc_write_array(&mut buf, "PROVIDES", &desc.provides);
    desc_write_array(&mut buf, "DEPENDS", &desc.depends);
    desc_write_array(&mut buf, "MAKEDEPENDS", &desc.makedepends);
    desc_write_array(&mut buf, "CHECKDEPENDS", &desc.checkdepends);
    desc_write_array(&mut buf, "OPTDEPENDS", &desc.optdepends);
    return buf;
}

fn desc_write_array(buf: &mut String, key: &str, xs: &Vec<String>) {
    if !xs.is_empty() {
        buf.push_str("%");
        buf.push_str(key);
        buf.push_str("%\n");
        for x in xs {
            buf.push_str(&x);
            buf.push_str("\n");
        }
        buf.push_str("\n");
    }
}

fn desc_write_string(buf: &mut String, key: &str, val: &str) {
    if !val.is_empty() {
        buf.push_str("%");
        buf.push_str(key);
        buf.push_str("%\n");
        buf.push_str(val);
        buf.push_str("\n\n");
    }
}

fn desc_write_u64(buf: &mut String, key: &str, val: u64) {
    if val != 0 {
        buf.push_str("%");
        buf.push_str(key);
        buf.push_str("%\n");
        buf.push_str(&format!("{}", val));
        buf.push_str("\n\n");
    }
}

fn parse_files(body: &str) -> Vec<String> {
    let mut iter = body.lines();

    if let Some("%FILES%") = iter.next() {
        let mut files = vec![];
        for line in iter {
            files.push(line.to_owned());
        }
        return files;
    } else {
        panic!("Empty files file");
    }
}

fn into_files_file(files: &Vec<String>) -> String {
    let mut buf = String::new();
    buf.push_str("%FILES%\n");
    for file in files {
        buf.push_str(file);
        buf.push_str("\n");
    }
    return buf;
}
<commit_msg>Ignore not-found error<commit_after>extern crate flate2;
extern crate std;
extern crate tar;

use std::io::Read;

#[derive(Debug, Default, Clone)]
pub struct Desc {
    groups: Vec<String>,
    license: Vec<String>,
    replaces: Vec<String>,
    filename: String,
    name: String,
    base: String,
    version: String,
    desc: String,
    csize: u64,
    isize: u64,
    md5sum: String,
    sha256sum: String,
    pgpsig: String,
    url: String,
    arch: String,
    builddate: u64,
    packager: String,

    // These fields are once stored in depends file
    // https://git.archlinux.org/pacman.git/commit/?id=b520c6312ff0ffec864576b5218f1638fae1e18b
    conflicts: Vec<String>,
    provides: Vec<String>,
    depends: Vec<String>,
    makedepends: Vec<String>,
    checkdepends: Vec<String>,
    optdepends: Vec<String>,
}

#[derive(Clone)]
pub struct PackageEntry {
    pub desc: Desc,
    pub files: Vec<String>,
}

#[derive(Clone)]
pub struct Repository {
    path: std::path::PathBuf,
    signer: Option<super::signer::Signer>,
    entries: std::collections::HashMap<String, PackageEntry>,
}

impl Repository {
    pub fn new(path: std::path::PathBuf, signer: Option<super::signer::Signer>) -> Repository {
        return Repository {
            path: path,
            signer: signer,
            entries: std::collections::HashMap::new(),
        };
    }

    pub fn load(&mut self) {
        match std::fs::File::open(&self.path) {
            Ok(file) => {
                let gz_reader = flate2::read::GzDecoder::new(file).unwrap();
                let mut tar_reader = tar::Archive::new(gz_reader);
                let mut desc_entries = std::collections::HashMap::new();
                let mut files_entries = std::collections::HashMap::new();
                for entry_result in tar_reader.entries().unwrap() {
                    let mut entry = entry_result.unwrap();
                    let pathbuf = entry.path().unwrap().into_owned();
                    let pathname = pathbuf.to_str().unwrap();
                    match entry.header().entry_type() {
                        tar::EntryType::Regular => {
                            let splitn: Vec<&str> = pathname.splitn(2, '/').collect();
                            if splitn.len() == 2 {
                                let mut body = String::new();
                                entry.read_to_string(&mut body).unwrap();
                                match splitn[1] {
                                    "desc" => {
                                        desc_entries.insert(splitn[0].to_owned(), parse_desc(&body));
                                    }
                                    "depends" => {
                                        // old format
                                    }
                                    "files" => {
                                        files_entries.insert(splitn[0].to_owned(), parse_files(&body));
                                    }
                                    _ => {
                                        panic!("Unknown pathname: {}", pathname);
                                    }
                                }
                            } else {
                                panic!("Invalid pathname entry: {}", pathname);
                            }
                        }
                        tar::EntryType::Directory => {}
                        _ => {
                            panic!("Unknown file type: {}", pathname);
                        }
                    }
                }

                for (_, desc) in desc_entries.into_iter() {
                    let files = files_entries.remove(&desc.name).unwrap_or(vec![]);
                    self.entries.insert(desc.name.to_owned(),
                                        PackageEntry {
                                            desc: desc,
                                            files: files,
                                        });
                }
            }
            Err(e) => {
                if e.kind() != std::io::ErrorKind::NotFound {
                    panic!("{:?}", e);
                }
            }
        }
    }

    pub fn add(&mut self, package: &super::package::Package) {
        let desc = Desc {
            groups: package.groups().to_owned(),
            license: package.license().to_owned(),
            replaces: package.replaces().to_owned(),
            filename: package.filename().to_owned(),
            name: package.pkgname().to_owned(),
            base: package.pkgbase().to_owned(),
            version: package.pkgver().to_owned(),
            desc: package.pkgdesc().to_owned(),
            csize: package.csize(),
            isize: package.isize(),
            md5sum: package.md5sum().to_owned(),
            sha256sum: package.sha256sum().to_owned(),
            pgpsig: package.pgpsig().to_owned(),
            url: package.url().to_owned(),
            arch: package.arch().to_owned(),
            builddate: package.builddate(),
            packager: package.packager().to_owned(),
            conflicts: package.conflicts().to_owned(),
            provides: package.provides().to_owned(),
            depends: package.depends().to_owned(),
            makedepends: package.makedepends().to_owned(),
            checkdepends: package.checkdepends().to_owned(),
            optdepends: package.optdepends().to_owned(),
        };
        self.entries.insert(desc.name.to_owned(),
                            PackageEntry {
                                desc: desc,
                                files: package.files().to_owned(),
                            });
    }

    pub fn remove(&mut self, package_name: &str) {
        self.entries.remove(package_name);
    }

    pub fn save(&self, include_files: bool) {
        let mut tmp_path = self.path.clone().into_os_string();
        tmp_path.push(".progress");
        let file = std::fs::File::create(&tmp_path).unwrap();
        let gz_writer = flate2::write::GzEncoder::new(file, flate2::Compression::Default);
        let mut builder = tar::Builder::new(gz_writer);
        for (_, package_entry) in self.entries.iter() {
            let pathbuf = std::path::PathBuf::from(format!("{}-{}/",
                                                           package_entry.desc.name,
                                                           package_entry.desc.version));
            {
                let mut dir_header = tar::Header::new_gnu();
                dir_header.set_entry_type(tar::EntryType::Directory);
                dir_header.set_path(&pathbuf).unwrap();
                dir_header.set_mode(0o755);
                dir_header.set_size(0);
                dir_header.set_cksum();
                builder.append(&dir_header, std::io::empty()).unwrap();
            }
            {
                let mut desc_header = tar::Header::new_gnu();
                desc_header.set_entry_type(tar::EntryType::Regular);
                desc_header.set_path(pathbuf.join("desc")).unwrap();
                desc_header.set_mode(0o644);
                let desc_str = into_desc_file(package_entry);
                let desc_bytes = desc_str.as_bytes();
                desc_header.set_size(desc_bytes.len() as u64);
                desc_header.set_cksum();
                builder.append(&desc_header, desc_bytes).unwrap();
            }
            if include_files {
                let mut files_header = tar::Header::new_gnu();
                files_header.set_entry_type(tar::EntryType::Regular);
                files_header.set_path(pathbuf.join("files")).unwrap();
                files_header.set_mode(0o644);
                let files_str = into_files_file(&package_entry.files);
                let files_bytes = files_str.as_bytes();
                files_header.set_size(files_bytes.len() as u64);
                files_header.set_cksum();
                builder.append(&files_header, files_bytes).unwrap();
            }
        }
        let gz_writer = builder.into_inner().unwrap();
        gz_writer.finish().unwrap();

        if let Some(ref signer) = self.signer {
            let mut sig_path = self.path.clone().into_os_string();
            sig_path.push(".sig");
            signer.sign(&tmp_path, sig_path);
        }

        std::fs::rename(&tmp_path, &self.path).unwrap();
    }
}

fn parse_desc(body: &str) -> Desc {
    let mut desc = Desc::default();
    for (key, val) in each_entry(body) {
        match key {
            "GROUPS" => {
                desc.groups.push(val.to_owned());
            }
            "REPLACES" => {
                desc.replaces.push(val.to_owned());
            }
            "FILENAME" => {
                desc.filename = val.to_owned();
            }
            "NAME" => {
                desc.name = val.to_owned();
            }
            "BASE" => {
                desc.base = val.to_owned();
            }
            "VERSION" => {
                desc.version = val.to_owned();
            }
            "DESC" => {
                desc.desc = val.to_owned();
            }
            "CSIZE" => {
                desc.csize = val.parse().unwrap();
            }
            "ISIZE" => {
                desc.isize = val.parse().unwrap();
            }
            "MD5SUM" => {
                desc.md5sum = val.to_owned();
            }
            "SHA256SUM" => {
                desc.sha256sum = val.to_owned();
            }
            "PGPSIG" => {
                desc.pgpsig = val.to_owned();
            }
            "URL" => {
                desc.url = val.to_owned();
            }
            "LICENSE" => {
                desc.license.push(val.to_owned());
            }
            "ARCH" => {
                desc.arch = val.to_owned();
            }
            "BUILDDATE" => {
                desc.builddate = val.parse().unwrap();
            }
            "PACKAGER" => {
                desc.packager = val.to_owned();
            }
            "CONFLICTS" => {
                desc.conflicts.push(val.to_owned());
            }
            "PROVIDES" => {
                desc.provides.push(val.to_owned());
            }
            "DEPENDS" => {
                desc.depends.push(val.to_owned());
            }
            "MAKEDEPENDS" => {
                desc.makedepends.push(val.to_owned());
            }
            "CHECKDEPENDS" => {
                desc.checkdepends.push(val.to_owned());
            }
            "OPTDEPENDS" => {
                desc.optdepends.push(val.to_owned());
            }
            _ => {
                panic!("Unknown desc entry: {}", key);
            }
        }
    }
    return desc;
}

struct EachEntry<'a> {
    key: &'a str,
    lines: std::str::Lines<'a>,
}

impl<'a> Iterator for EachEntry<'a> {
    type Item = (&'a str, &'a str);

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(line) = self.lines.next() {
            let line = line.trim();
            if line.starts_with('%') && line.ends_with('%') {
                self.key = &line[1..line.len() - 1];
            } else if line.is_empty() {
                self.key = "";
            } else {
                return Some((self.key, line));
            }
        }
        return None;
    }
}

fn each_entry(body: &str) -> EachEntry {
    EachEntry {
        key: "",
        lines: body.lines(),
    }
}

fn into_desc_file(package_entry: &PackageEntry) -> String {
    let mut buf = String::new();
    let ref desc = package_entry.desc;
    desc_write_array(&mut buf, "GROUPS", &desc.groups);
    desc_write_array(&mut buf, "REPLACES", &desc.replaces);
    desc_write_string(&mut buf, "FILENAME", &desc.filename);
    desc_write_string(&mut buf, "NAME", &desc.name);
    desc_write_string(&mut buf, "BASE", &desc.base);
    desc_write_string(&mut buf, "VERSION", &desc.version);
    desc_write_string(&mut buf, "DESC", &desc.desc);
    desc_write_u64(&mut buf, "CSIZE", desc.csize);
    desc_write_u64(&mut buf, "ISIZE", desc.isize);
    desc_write_string(&mut buf, "MD5SUM", &desc.md5sum);
    desc_write_string(&mut buf, "SHA256SUM", &desc.sha256sum);
    desc_write_string(&mut buf, "PGPSIG", &desc.pgpsig);
    desc_write_string(&mut buf, "URL", &desc.url);
    desc_write_array(&mut buf, "LICENSE", &desc.license);
    desc_write_string(&mut buf, "ARCH", &desc.arch);
    desc_write_u64(&mut buf, "BUILDDATE", desc.builddate);
    desc_write_string(&mut buf, "PACKAGER", &desc.packager);
    desc_write_array(&mut buf, "CONFLICTS", &desc.conflicts);
    desc_write_array(&mut buf, "PROVIDES", &desc.provides);
    desc_write_array(&mut buf, "DEPENDS", &desc.depends);
    desc_write_array(&mut buf, "MAKEDEPENDS", &desc.makedepends);
    desc_write_array(&mut buf, "CHECKDEPENDS", &desc.checkdepends);
    desc_write_array(&mut buf, "OPTDEPENDS", &desc.optdepends);
    return buf;
}

fn desc_write_array(buf: &mut String, key: &str, xs: &Vec<String>) {
    if !xs.is_empty() {
        buf.push_str("%");
        buf.push_str(key);
        buf.push_str("%\n");
        for x in xs {
            buf.push_str(&x);
            buf.push_str("\n");
        }
        buf.push_str("\n");
    }
}

fn desc_write_string(buf: &mut String, key: &str, val: &str) {
    if !val.is_empty() {
        buf.push_str("%");
        buf.push_str(key);
        buf.push_str("%\n");
        buf.push_str(val);
        buf.push_str("\n\n");
    }
}

fn desc_write_u64(buf: &mut String, key: &str, val: u64) {
    if val != 0 {
        buf.push_str("%");
        buf.push_str(key);
        buf.push_str("%\n");
        buf.push_str(&format!("{}", val));
        buf.push_str("\n\n");
    }
}

fn parse_files(body: &str) -> Vec<String> {
    let mut iter = body.lines();

    if let Some("%FILES%") = iter.next() {
        let mut files = vec![];
        for line in iter {
            files.push(line.to_owned());
        }
        return files;
    } else {
        panic!("Empty files file");
    }
}

fn into_files_file(files: &Vec<String>) -> String {
    let mut buf = String::new();
    buf.push_str("%FILES%\n");
    for file in files {
        buf.push_str(file);
        buf.push_str("\n");
    }
    return buf;
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate quote;
#[macro_use] extern crate syn;
#[macro_use] extern crate synstructure;

decl_derive!([JSTraceable] => js_traceable_derive);

fn js_traceable_derive(s: synstructure::Structure) -> quote::Tokens {
    let match_body = s.each(|binding| {
        Some(quote!(#binding.trace(tracer);))
    });

    let ast = s.ast();
    let name = ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
    let mut where_clause = where_clause.unwrap_or(&parse_quote!(where)).clone();
    for param in ast.generics.type_params() {
        let ident = param.ident;
        where_clause.predicates.push(parse_quote!(#ident: ::dom::bindings::trace::JSTraceable))
    }

    let tokens = quote! {
        #[allow(unsafe_code)]
        unsafe impl #impl_generics ::dom::bindings::trace::JSTraceable for #name #ty_generics #where_clause {
            #[inline]
            #[allow(unused_variables, unused_imports)]
            unsafe fn trace(&self, tracer: *mut ::js::jsapi::JSTracer) {
                use ::dom::bindings::trace::JSTraceable;
                match *self {
                    #match_body
                }
            }
        }
    };

    tokens
}
<commit_msg>Format jstraceable_derive #21373<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate quote;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate synstructure;

decl_derive!([JSTraceable] => js_traceable_derive);

fn js_traceable_derive(s: synstructure::Structure) -> quote::Tokens {
    let match_body = s.each(|binding| Some(quote!(#binding.trace(tracer);)));

    let ast = s.ast();
    let name = ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
    let mut where_clause = where_clause.unwrap_or(&parse_quote!(where)).clone();
    for param in ast.generics.type_params() {
        let ident = param.ident;
        where_clause
            .predicates
            .push(parse_quote!(#ident: ::dom::bindings::trace::JSTraceable))
    }

    let tokens = quote! {
        #[allow(unsafe_code)]
        unsafe impl #impl_generics ::dom::bindings::trace::JSTraceable for #name #ty_generics #where_clause {
            #[inline]
            #[allow(unused_variables, unused_imports)]
            unsafe fn trace(&self, tracer: *mut ::js::jsapi::JSTracer) {
                use ::dom::bindings::trace::JSTraceable;
                match *self {
                    #match_body
                }
            }
        }
    };

    tokens
}
<|endoftext|>