<commit_before>//! Window abstraction

use std::cell::RefCell;
use input::InputEvent;
use current::{ Get, Modifier, Set, Usage };

use GenericEvent;

/// Whether window should close or not.
pub struct ShouldClose(pub bool);

/// Work-around trait for `Get<ShouldClose>`.
/// Used to support generic constraints.
pub trait GetShouldClose: Get<ShouldClose> {
    /// Returns whether window should close.
    fn get_should_close(&self) -> ShouldClose {
        self.get()
    }
}

impl<T: Get<ShouldClose>> GetShouldClose for T {}

/// Work-around trait for `Set<ShouldClose>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetShouldClose: Set<ShouldClose> {
    /// Sets whether window should close.
    fn set_should_close(&mut self, val: ShouldClose);
}

/// The size of the window.
pub struct Size(pub [u32, ..2]);

/// Work-around trait for `Get<Size>`.
/// Used to support generic constraints.
pub trait GetSize: Get<Size> {
    /// Returns the size of window.
    fn get_size(&self) -> Size {
        self.get()
    }
}

impl<T: Get<Size>> GetSize for T {}

/// Work-around trait for `Set<Size>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetSize: Set<Size> {
    /// Sets size of window.
    fn set_size(&mut self, val: Size);
}

/// The title of the window.
pub struct Title(pub String);

/// Work-around trait for `Get<Title>`.
/// Used to support generic constraints.
pub trait GetTitle: Get<Title> {
    /// Returns the title of the window.
    fn get_title(&self) -> Title {
        self.get()
    }
}

impl<T: Get<Title>> GetTitle for T {}

/// Work-around trait for `Set<Title>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetTitle: Set<Title> {
    /// Sets title of window.
    fn set_title(&mut self, val: Title);
}

/// The anti-aliasing samples when rendering.
pub struct Samples(pub u8);

/// Work-around trait for `Get<Samples>`.
/// Used to support generic constraints.
pub trait GetSamples: Get<Samples> {
    /// Returns the antialiasing samples when rendering.
    fn get_samples(&self) -> Samples {
        self.get()
    }
}

impl<T: Get<Samples>> GetSamples for T {}

/// Work-around trait for `Set<Samples>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetSamples: Set<Samples> {
    /// Sets antialiasing samples of window.
    fn set_samples(&mut self, val: Samples);
}

/// Whether window is opened in full screen mode.
pub struct Fullscreen(pub bool);

/// Work-around trait for `Get<Fullscreen>`.
/// Used to support generic constraints.
pub trait GetFullscreen: Get<Fullscreen> {
    /// Returns whether window is in full screen mode.
    fn get_fullscreen(&self) -> Fullscreen {
        self.get()
    }
}

impl<T: Get<Fullscreen>> GetFullscreen for T {}

/// Work-around trait for `Set<Fullscreen>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetFullscreen: Set<Fullscreen> {
    /// Sets window to fullscreen mode.
    fn set_fullscreen(&mut self, val: Fullscreen);
}

/// Whether to exit when pressing the Esc keyboard button.
pub struct ExitOnEsc(pub bool);

/// Work-around trait for `Get<ExitOnEsc>`.
/// Used to support generic constraints.
pub trait GetExitOnEsc: Get<ExitOnEsc> {
    /// Returns whether window exits when pressing Esc.
    fn get_exit_on_esc(&self) -> ExitOnEsc {
        self.get()
    }
}

impl<T: Get<ExitOnEsc>> GetExitOnEsc for T {}

/// Work-around trait for `Set<ExitOnEsc>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetExitOnEsc: Set<ExitOnEsc> {
    /// Sets exit when pressing Esc.
    fn set_exit_on_esc(&mut self, val: ExitOnEsc);
}

/// Whether to capture the mouse cursor.
pub struct CaptureCursor(pub bool);

/// Work-around trait for `Get<CaptureCursor>`.
/// Used to support generic constraints.
pub trait GetCaptureCursor: Get<CaptureCursor> {
    /// Returns whether window captures cursor.
    fn get_capture_cursor(&self) -> CaptureCursor {
        self.get()
    }
}

impl<T: Get<CaptureCursor>> GetCaptureCursor for T {}

/// Work-around trait for `Set<CaptureCursor>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetCaptureCursor: Set<CaptureCursor> {
    /// Sets capture cursor.
    fn set_capture_cursor(&mut self, val: CaptureCursor);
}

/// The draw size of the window.
pub struct DrawSize(pub [u32, ..2]);

/// Work-around trait for `Get<DrawSize>`.
/// Used to support generic constraints.
pub trait GetDrawSize: Get<DrawSize> {
    /// Returns the draw size of window.
    fn get_draw_size(&self) -> DrawSize {
        self.get()
    }
}

impl<T: Get<DrawSize>> GetDrawSize for T {}

/// Work-around trait for `Set<DrawSize>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetDrawSize: Set<DrawSize> {
    /// Sets draw size.
    fn set_draw_size(&mut self, val: DrawSize);
}

#[test]
fn test_methods() {
    use current::Modifier;

    struct Obj;

    impl Get<ShouldClose> for Obj {
        fn get(&self) -> ShouldClose { ShouldClose(false) }
    }

    impl Modifier<Obj> for ShouldClose {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetShouldClose for Obj {
        fn set_should_close(&mut self, val: ShouldClose) {
            self.set_mut(val);
        }
    }

    impl Get<Size> for Obj {
        fn get(&self) -> Size { Size([0, 0]) }
    }

    impl Modifier<Obj> for Size {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetSize for Obj {
        fn set_size(&mut self, val: Size) {
            self.set_mut(val);
        }
    }

    impl Get<Title> for Obj {
        fn get(&self) -> Title { Title("hello".to_string()) }
    }

    impl Modifier<Obj> for Title {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetTitle for Obj {
        fn set_title(&mut self, val: Title) {
            self.set_mut(val);
        }
    }

    impl Get<Samples> for Obj {
        fn get(&self) -> Samples { Samples(0) }
    }

    impl Modifier<Obj> for Samples {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetSamples for Obj {
        fn set_samples(&mut self, val: Samples) {
            self.set_mut(val);
        }
    }

    impl Get<Fullscreen> for Obj {
        fn get(&self) -> Fullscreen { Fullscreen(false) }
    }

    impl Modifier<Obj> for Fullscreen {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetFullscreen for Obj {
        fn set_fullscreen(&mut self, val: Fullscreen) {
            self.set_mut(val);
        }
    }

    impl Get<ExitOnEsc> for Obj {
        fn get(&self) -> ExitOnEsc { ExitOnEsc(true) }
    }

    impl Modifier<Obj> for ExitOnEsc {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetExitOnEsc for Obj {
        fn set_exit_on_esc(&mut self, val: ExitOnEsc) {
            self.set_mut(val);
        }
    }

    impl Get<CaptureCursor> for Obj {
        fn get(&self) -> CaptureCursor { CaptureCursor(false) }
    }

    impl Modifier<Obj> for CaptureCursor {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetCaptureCursor for Obj {
        fn set_capture_cursor(&mut self, val: CaptureCursor) {
            self.set_mut(val);
        }
    }

    impl Get<DrawSize> for Obj {
        fn get(&self) -> DrawSize { DrawSize([0, 0]) }
    }

    impl Modifier<Obj> for DrawSize {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetDrawSize for Obj {
        fn set_draw_size(&mut self, val: DrawSize) {
            self.set_mut(val);
        }
    }

    fn foo<T: GetShouldClose + SetShouldClose
            + GetSize + SetSize
            + GetTitle + SetTitle
            + GetSamples + SetSamples
            + GetFullscreen + SetFullscreen
            + GetExitOnEsc + SetExitOnEsc
            + GetCaptureCursor + SetCaptureCursor
            + GetDrawSize + SetDrawSize>(_obj: T) {}

    foo(Obj);
}

/// Implemented by windows that can swap buffers.
pub trait SwapBuffers {
    /// Swaps the buffers.
    fn swap_buffers(&mut self);
}

impl<'a, W: 'a + SwapBuffers> SwapBuffers for Usage<'a, W> {
    #[inline(always)]
    fn swap_buffers(&mut self) {
        self.with_unwrap(|window: &RefCell<W>| {
            window.borrow_mut().deref_mut().swap_buffers()
        })
    }
}

impl<'a, W: 'a + SwapBuffers> SwapBuffers for &'a RefCell<W> {
    #[inline(always)]
    fn swap_buffers(&mut self) {
        self.borrow_mut().deref_mut().swap_buffers()
    }
}

/// Implemented by windows that can pull events.
pub trait PollEvent<E: GenericEvent> {
    /// Polls event from window.
    fn poll_event(&mut self) -> Option<E>;
}

impl<'a, W: 'a + PollEvent<I>, I: GenericEvent> PollEvent<I> for Usage<'a, W> {
    #[inline(always)]
    fn poll_event(&mut self) -> Option<I> {
        self.with_unwrap(|window: &RefCell<W>| {
            window.borrow_mut().deref_mut().poll_event()
        })
    }
}

impl<'a, W: 'a + PollEvent<I>, I: GenericEvent> PollEvent<I> for &'a RefCell<W> {
    #[inline(always)]
    fn poll_event(&mut self) -> Option<I> {
        self.borrow_mut().deref_mut().poll_event()
    }
}

/// Settings for window behavior.
pub struct WindowSettings {
    /// Title of the window.
    pub title: String,
    /// The size of the window.
    pub size: [u32, ..2],
    /// Number samples per pixel (anti-aliasing).
    pub samples: u8,
    /// If true, the window is fullscreen.
    pub fullscreen: bool,
    /// If true, exit when pressing Esc.
    pub exit_on_esc: bool,
}

impl WindowSettings {
    /// Gets default settings.
    ///
    /// This exits the window when pressing `Esc`.
    /// The background color is set to black.
    pub fn default() -> WindowSettings {
        WindowSettings {
            title: "Piston".to_string(),
            size: [640, 480],
            samples: 0,
            fullscreen: false,
            exit_on_esc: true,
        }
    }
}

/// Implemented by window back-end.
pub trait Window<E: GenericEvent = InputEvent>:
    SwapBuffers
  + PollEvent<E>
  + GetShouldClose
  + SetShouldClose
  + GetSize
  + SetCaptureCursor {
    /// Get the window's settings.
    fn get_settings<'a>(&'a self) -> &'a WindowSettings;

    /// Get the size in drawing coordinates.
    fn get_draw_size(&self) -> (u32, u32);
}

/// An implementation of Window that runs without a window at all.
pub struct NoWindow {
    settings: WindowSettings,
    should_close: bool
}

impl NoWindow {
    /// Returns a new `NoWindow`.
    pub fn new(settings: WindowSettings) -> NoWindow {
         NoWindow {
             settings: settings,
             should_close: false
         }
    }
}

impl SwapBuffers for NoWindow {
    fn swap_buffers(&mut self) {}
}

impl PollEvent<InputEvent> for NoWindow {
    fn poll_event(&mut self) -> Option<InputEvent> { None }
}

impl Get<ShouldClose> for NoWindow {
    fn get(&self) -> ShouldClose {
        ShouldClose(self.should_close)
    }
}

impl Get<Size> for NoWindow {
    fn get(&self) -> Size {
        Size([0, 0])
    }
}

impl Modifier<NoWindow> for CaptureCursor {
    fn modify(self, _window: &mut NoWindow) {}
}

impl SetCaptureCursor for NoWindow {
    fn set_capture_cursor(&mut self, val: CaptureCursor) {
        self.set_mut(val);
    }
}

impl Modifier<NoWindow> for ShouldClose {
    fn modify(self, window: &mut NoWindow) {
        let ShouldClose(val) = self;
        window.should_close = val;
    }
}

impl SetShouldClose for NoWindow {
    fn set_should_close(&mut self, val: ShouldClose) {
        self.set_mut(val);
    }
}

impl Window<InputEvent> for NoWindow {
     fn get_settings<'a>(&'a self) -> &'a WindowSettings {
        &self.settings
     }

    fn get_draw_size(&self) -> (u32, u32) {
        let Size([w, h]) = self.get_size();
        (w, h)
    }
}

<commit_msg>Separated `Get<DrawSize>` from Window trait<commit_after>//! Window abstraction

use std::cell::RefCell;
use input::InputEvent;
use current::{ Get, Modifier, Set, Usage };

use GenericEvent;

/// Whether window should close or not.
pub struct ShouldClose(pub bool);

/// Work-around trait for `Get<ShouldClose>`.
/// Used to support generic constraints.
pub trait GetShouldClose: Get<ShouldClose> {
    /// Returns whether window should close.
    fn get_should_close(&self) -> ShouldClose {
        self.get()
    }
}

impl<T: Get<ShouldClose>> GetShouldClose for T {}

/// Work-around trait for `Set<ShouldClose>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetShouldClose: Set<ShouldClose> {
    /// Sets whether window should close.
    fn set_should_close(&mut self, val: ShouldClose);
}

/// The size of the window.
pub struct Size(pub [u32, ..2]);

/// Work-around trait for `Get<Size>`.
/// Used to support generic constraints.
pub trait GetSize: Get<Size> {
    /// Returns the size of window.
    fn get_size(&self) -> Size {
        self.get()
    }
}

impl<T: Get<Size>> GetSize for T {}

/// Work-around trait for `Set<Size>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetSize: Set<Size> {
    /// Sets size of window.
    fn set_size(&mut self, val: Size);
}

/// The title of the window.
pub struct Title(pub String);

/// Work-around trait for `Get<Title>`.
/// Used to support generic constraints.
pub trait GetTitle: Get<Title> {
    /// Returns the title of the window.
    fn get_title(&self) -> Title {
        self.get()
    }
}

impl<T: Get<Title>> GetTitle for T {}

/// Work-around trait for `Set<Title>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetTitle: Set<Title> {
    /// Sets title of window.
    fn set_title(&mut self, val: Title);
}

/// The anti-aliasing samples when rendering.
pub struct Samples(pub u8);

/// Work-around trait for `Get<Samples>`.
/// Used to support generic constraints.
pub trait GetSamples: Get<Samples> {
    /// Returns the antialiasing samples when rendering.
    fn get_samples(&self) -> Samples {
        self.get()
    }
}

impl<T: Get<Samples>> GetSamples for T {}

/// Work-around trait for `Set<Samples>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetSamples: Set<Samples> {
    /// Sets antialiasing samples of window.
    fn set_samples(&mut self, val: Samples);
}

/// Whether window is opened in full screen mode.
pub struct Fullscreen(pub bool);

/// Work-around trait for `Get<Fullscreen>`.
/// Used to support generic constraints.
pub trait GetFullscreen: Get<Fullscreen> {
    /// Returns whether window is in full screen mode.
    fn get_fullscreen(&self) -> Fullscreen {
        self.get()
    }
}

impl<T: Get<Fullscreen>> GetFullscreen for T {}

/// Work-around trait for `Set<Fullscreen>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetFullscreen: Set<Fullscreen> {
    /// Sets window to fullscreen mode.
    fn set_fullscreen(&mut self, val: Fullscreen);
}

/// Whether to exit when pressing the Esc keyboard button.
pub struct ExitOnEsc(pub bool);

/// Work-around trait for `Get<ExitOnEsc>`.
/// Used to support generic constraints.
pub trait GetExitOnEsc: Get<ExitOnEsc> {
    /// Returns whether window exits when pressing Esc.
    fn get_exit_on_esc(&self) -> ExitOnEsc {
        self.get()
    }
}

impl<T: Get<ExitOnEsc>> GetExitOnEsc for T {}

/// Work-around trait for `Set<ExitOnEsc>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetExitOnEsc: Set<ExitOnEsc> {
    /// Sets exit when pressing Esc.
    fn set_exit_on_esc(&mut self, val: ExitOnEsc);
}

/// Whether to capture the mouse cursor.
pub struct CaptureCursor(pub bool);

/// Work-around trait for `Get<CaptureCursor>`.
/// Used to support generic constraints.
pub trait GetCaptureCursor: Get<CaptureCursor> {
    /// Returns whether window captures cursor.
    fn get_capture_cursor(&self) -> CaptureCursor {
        self.get()
    }
}

impl<T: Get<CaptureCursor>> GetCaptureCursor for T {}

/// Work-around trait for `Set<CaptureCursor>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetCaptureCursor: Set<CaptureCursor> {
    /// Sets capture cursor.
    fn set_capture_cursor(&mut self, val: CaptureCursor);
}

/// The draw size of the window.
pub struct DrawSize(pub [u32, ..2]);

/// Work-around trait for `Get<DrawSize>`.
/// Used to support generic constraints.
pub trait GetDrawSize: Get<DrawSize> {
    /// Returns the draw size of window.
    fn get_draw_size(&self) -> DrawSize {
        self.get()
    }
}

impl<T: Get<DrawSize>> GetDrawSize for T {}

/// Work-around trait for `Set<DrawSize>`.
/// Used to support generic constraints.
/// This must be implemented for every `Modifier` impl.
pub trait SetDrawSize: Set<DrawSize> {
    /// Sets draw size.
    fn set_draw_size(&mut self, val: DrawSize);
}

#[test]
fn test_methods() {
    use current::Modifier;

    struct Obj;

    impl Get<ShouldClose> for Obj {
        fn get(&self) -> ShouldClose { ShouldClose(false) }
    }

    impl Modifier<Obj> for ShouldClose {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetShouldClose for Obj {
        fn set_should_close(&mut self, val: ShouldClose) {
            self.set_mut(val);
        }
    }

    impl Get<Size> for Obj {
        fn get(&self) -> Size { Size([0, 0]) }
    }

    impl Modifier<Obj> for Size {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetSize for Obj {
        fn set_size(&mut self, val: Size) {
            self.set_mut(val);
        }
    }

    impl Get<Title> for Obj {
        fn get(&self) -> Title { Title("hello".to_string()) }
    }

    impl Modifier<Obj> for Title {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetTitle for Obj {
        fn set_title(&mut self, val: Title) {
            self.set_mut(val);
        }
    }

    impl Get<Samples> for Obj {
        fn get(&self) -> Samples { Samples(0) }
    }

    impl Modifier<Obj> for Samples {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetSamples for Obj {
        fn set_samples(&mut self, val: Samples) {
            self.set_mut(val);
        }
    }

    impl Get<Fullscreen> for Obj {
        fn get(&self) -> Fullscreen { Fullscreen(false) }
    }

    impl Modifier<Obj> for Fullscreen {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetFullscreen for Obj {
        fn set_fullscreen(&mut self, val: Fullscreen) {
            self.set_mut(val);
        }
    }

    impl Get<ExitOnEsc> for Obj {
        fn get(&self) -> ExitOnEsc { ExitOnEsc(true) }
    }

    impl Modifier<Obj> for ExitOnEsc {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetExitOnEsc for Obj {
        fn set_exit_on_esc(&mut self, val: ExitOnEsc) {
            self.set_mut(val);
        }
    }

    impl Get<CaptureCursor> for Obj {
        fn get(&self) -> CaptureCursor { CaptureCursor(false) }
    }

    impl Modifier<Obj> for CaptureCursor {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetCaptureCursor for Obj {
        fn set_capture_cursor(&mut self, val: CaptureCursor) {
            self.set_mut(val);
        }
    }

    impl Get<DrawSize> for Obj {
        fn get(&self) -> DrawSize { DrawSize([0, 0]) }
    }

    impl Modifier<Obj> for DrawSize {
        fn modify(self, _obj: &mut Obj) {}
    }

    impl SetDrawSize for Obj {
        fn set_draw_size(&mut self, val: DrawSize) {
            self.set_mut(val);
        }
    }

    fn foo<T: GetShouldClose + SetShouldClose
            + GetSize + SetSize
            + GetTitle + SetTitle
            + GetSamples + SetSamples
            + GetFullscreen + SetFullscreen
            + GetExitOnEsc + SetExitOnEsc
            + GetCaptureCursor + SetCaptureCursor
            + GetDrawSize + SetDrawSize>(_obj: T) {}

    foo(Obj);
}

/// Implemented by windows that can swap buffers.
pub trait SwapBuffers {
    /// Swaps the buffers.
    fn swap_buffers(&mut self);
}

impl<'a, W: 'a + SwapBuffers> SwapBuffers for Usage<'a, W> {
    #[inline(always)]
    fn swap_buffers(&mut self) {
        self.with_unwrap(|window: &RefCell<W>| {
            window.borrow_mut().deref_mut().swap_buffers()
        })
    }
}

impl<'a, W: 'a + SwapBuffers> SwapBuffers for &'a RefCell<W> {
    #[inline(always)]
    fn swap_buffers(&mut self) {
        self.borrow_mut().deref_mut().swap_buffers()
    }
}

/// Implemented by windows that can pull events.
pub trait PollEvent<E: GenericEvent> {
    /// Polls event from window.
    fn poll_event(&mut self) -> Option<E>;
}

impl<'a, W: 'a + PollEvent<I>, I: GenericEvent> PollEvent<I> for Usage<'a, W> {
    #[inline(always)]
    fn poll_event(&mut self) -> Option<I> {
        self.with_unwrap(|window: &RefCell<W>| {
            window.borrow_mut().deref_mut().poll_event()
        })
    }
}

impl<'a, W: 'a + PollEvent<I>, I: GenericEvent> PollEvent<I> for &'a RefCell<W> {
    #[inline(always)]
    fn poll_event(&mut self) -> Option<I> {
        self.borrow_mut().deref_mut().poll_event()
    }
}

/// Settings for window behavior.
pub struct WindowSettings {
    /// Title of the window.
    pub title: String,
    /// The size of the window.
    pub size: [u32, ..2],
    /// Number samples per pixel (anti-aliasing).
    pub samples: u8,
    /// If true, the window is fullscreen.
    pub fullscreen: bool,
    /// If true, exit when pressing Esc.
    pub exit_on_esc: bool,
}

impl WindowSettings {
    /// Gets default settings.
    ///
    /// This exits the window when pressing `Esc`.
    /// The background color is set to black.
    pub fn default() -> WindowSettings {
        WindowSettings {
            title: "Piston".to_string(),
            size: [640, 480],
            samples: 0,
            fullscreen: false,
            exit_on_esc: true,
        }
    }
}

/// Implemented by window back-end.
pub trait Window<E: GenericEvent = InputEvent>:
    SwapBuffers
  + PollEvent<E>
  + GetShouldClose
  + SetShouldClose
  + GetSize
  + SetCaptureCursor
  + GetDrawSize {
    /// Get the window's settings.
    fn get_settings<'a>(&'a self) -> &'a WindowSettings;
}

/// An implementation of Window that runs without a window at all.
pub struct NoWindow {
    settings: WindowSettings,
    should_close: bool
}

impl NoWindow {
    /// Returns a new `NoWindow`.
    pub fn new(settings: WindowSettings) -> NoWindow {
         NoWindow {
             settings: settings,
             should_close: false
         }
    }
}

impl SwapBuffers for NoWindow {
    fn swap_buffers(&mut self) {}
}

impl PollEvent<InputEvent> for NoWindow {
    fn poll_event(&mut self) -> Option<InputEvent> { None }
}

impl Get<ShouldClose> for NoWindow {
    fn get(&self) -> ShouldClose {
        ShouldClose(self.should_close)
    }
}

impl Get<Size> for NoWindow {
    fn get(&self) -> Size {
        Size([0, 0])
    }
}

impl Modifier<NoWindow> for CaptureCursor {
    fn modify(self, _window: &mut NoWindow) {}
}

impl SetCaptureCursor for NoWindow {
    fn set_capture_cursor(&mut self, val: CaptureCursor) {
        self.set_mut(val);
    }
}

impl Modifier<NoWindow> for ShouldClose {
    fn modify(self, window: &mut NoWindow) {
        let ShouldClose(val) = self;
        window.should_close = val;
    }
}

impl SetShouldClose for NoWindow {
    fn set_should_close(&mut self, val: ShouldClose) {
        self.set_mut(val);
    }
}

impl Get<DrawSize> for NoWindow {
    fn get(&self) -> DrawSize {
        let Size(val) = self.get();
        DrawSize(val)
    }
}

impl Window<InputEvent> for NoWindow {
    fn get_settings<'a>(&'a self) -> &'a WindowSettings {
        &self.settings
    }
}

<|endoftext|>


<commit_before>extern crate glutin_window;
extern crate piston;

use self::glutin_window::GlutinWindow;
use self::piston::event_loop::{EventLoop, EventSettings, Events};
use self::piston::input::Event;
use self::piston::window::{
    AdvancedWindow, BuildFromWindowSettings, OpenGLWindow, Position, Size, Window, WindowSettings,
};
use glium::backend::{Backend, Context, Facade};
use glium::{Frame, IncompatibleOpenGl, SwapBuffersError};
use std::cell::RefCell;
use std::error::Error;
use std::ops::Deref;
use std::os::raw::c_void;
use std::rc::Rc;
use std::time::Duration;

#[derive(Clone)]
struct Wrapper<W>(Rc<RefCell<W>>);

/// A window struct for glium.
pub struct GliumWindow<W = GlutinWindow> {
    /// Window.
    pub window: Rc<RefCell<W>>,
    /// Glium context.
    pub context: Rc<Context>,
    /// Event loop state.
    pub events: Events,
}

impl<W> Deref for GliumWindow<W> {
    type Target = Context;

    fn deref(&self) -> &Context {
        &self.context
    }
}

impl<W> Clone for GliumWindow<W> {
    fn clone(&self) -> GliumWindow<W> {
        GliumWindow {
            window: self.window.clone(),
            context: self.context.clone(),
            events: self.events.clone(),
        }
    }
}

impl<W> BuildFromWindowSettings for GliumWindow<W>
where
    W: 'static + Window + OpenGLWindow + BuildFromWindowSettings,
{
    fn build_from_window_settings(settings: &WindowSettings) -> Result<GliumWindow<W>, Box<dyn Error>> {
        // Turn on sRGB.
        let settings = settings.clone().srgb(true);
        let window: W = settings.build()?;
        GliumWindow::new(&Rc::new(RefCell::new(window))).map_err(|err| err.into())
    }
}

impl<W> GliumWindow<W>
where
    W: OpenGLWindow + 'static,
{
    /// Creates new GliumWindow.
    pub fn new(window: &Rc<RefCell<W>>) -> Result<Self, IncompatibleOpenGl> {
        unsafe {
            let check_current = cfg!(feature = "check_current_window");
            Context::new(Wrapper(window.clone()), check_current, Default::default())
        }
        .map(|context| GliumWindow {
            window: window.clone(),
            context: context,
            events: Events::new(EventSettings::new()).swap_buffers(false),
        })
    }

    /// Returns new frame.
    pub fn draw(&self) -> Frame {
        Frame::new(
            self.context.clone(),
            self.context.get_framebuffer_dimensions(),
        )
    }

    /// Returns next event.
    pub fn next(&mut self) -> Option<Event> {
        self.events.next(&mut *self.window.borrow_mut())
    }
}

impl<W> Facade for GliumWindow<W> {
    fn get_context(&self) -> &Rc<Context> {
        &self.context
    }
}

unsafe impl<W> Backend for Wrapper<W>
where
    W: OpenGLWindow,
{
    fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
        self.0.borrow_mut().swap_buffers();
        Ok(())
    }

    unsafe fn get_proc_address(&self, proc_name: &str) -> *const c_void {
        self.0.borrow_mut().get_proc_address(proc_name) as *const c_void
    }

    fn get_framebuffer_dimensions(&self) -> (u32, u32) {
        let size = self.0.borrow().draw_size();
        (size.width as u32, size.height as u32)
    }

    fn is_current(&self) -> bool {
        self.0.borrow().is_current()
    }

    unsafe fn make_current(&self) {
        self.0.borrow_mut().make_current()
    }
}

impl<W> Window for GliumWindow<W>
where
    W: Window,
{
    fn should_close(&self) -> bool {
        self.window.borrow().should_close()
    }
    fn set_should_close(&mut self, value: bool) {
        self.window.borrow_mut().set_should_close(value)
    }
    fn size(&self) -> Size {
        self.window.borrow().size()
    }
    fn draw_size(&self) -> Size {
        self.window.borrow().draw_size()
    }
    fn swap_buffers(&mut self) {
        self.window.borrow_mut().swap_buffers()
    }
    fn poll_event(&mut self) -> Option<Event> {
        Window::poll_event(&mut *self.window.borrow_mut())
    }
    fn wait_event(&mut self) -> Event {
        Window::wait_event(&mut *self.window.borrow_mut())
    }
    fn wait_event_timeout(&mut self, duration: Duration) -> Option<Event> {
        let mut window = self.window.borrow_mut();
        Window::wait_event_timeout(&mut *window, duration)
    }
}

impl<W> AdvancedWindow for GliumWindow<W>
where
    W: AdvancedWindow,
{
    fn get_title(&self) -> String {
        self.window.borrow().get_title()
    }
    fn set_title(&mut self, title: String) {
        self.window.borrow_mut().set_title(title)
    }
    fn get_automatic_close(&self) -> bool {
        self.window.borrow().get_automatic_close()
    }
    fn set_automatic_close(&mut self, value: bool) {
        self.window.borrow_mut().set_automatic_close(value);
    }
    fn get_exit_on_esc(&self) -> bool {
        self.window.borrow().get_exit_on_esc()
    }
    fn set_exit_on_esc(&mut self, value: bool) {
        self.window.borrow_mut().set_exit_on_esc(value)
    }
    fn set_capture_cursor(&mut self, value: bool) {
        self.window.borrow_mut().set_capture_cursor(value)
    }
    fn show(&mut self) {
        self.window.borrow_mut().show()
    }
    fn hide(&mut self) {
        self.window.borrow_mut().show()
    }
    fn get_position(&self) -> Option<Position> {
        self.window.borrow().get_position()
    }
    fn set_position<P: Into<Position>>(&mut self, pos: P) {
        self.window.borrow_mut().set_position(pos);
    }
    fn set_size<S: Into<Size>>(&mut self, size: S) {
        self.window.borrow_mut().set_size(size)
    }
}

impl<W> EventLoop for GliumWindow<W> {
    fn get_event_settings(&self) -> EventSettings {
        self.events.get_event_settings()
    }

    fn set_event_settings(&mut self, settings: EventSettings) {
        self.events.set_event_settings(settings);
    }
}
<commit_msg>fix: don't hardcode srgb<commit_after>extern crate glutin_window;
extern crate piston;

use self::glutin_window::GlutinWindow;
use self::piston::event_loop::{EventLoop, EventSettings, Events};
use self::piston::input::Event;
use self::piston::window::{
    AdvancedWindow, BuildFromWindowSettings, OpenGLWindow, Position, Size, Window, WindowSettings,
};
use glium::backend::{Backend, Context, Facade};
use glium::{Frame, IncompatibleOpenGl, SwapBuffersError};
use std::cell::RefCell;
use std::error::Error;
use std::ops::Deref;
use std::os::raw::c_void;
use std::rc::Rc;
use std::time::Duration;

#[derive(Clone)]
struct Wrapper<W>(Rc<RefCell<W>>);

/// A window struct for glium.
pub struct GliumWindow<W = GlutinWindow> {
    /// Window.
    pub window: Rc<RefCell<W>>,
    /// Glium context.
    pub context: Rc<Context>,
    /// Event loop state.
    pub events: Events,
}

impl<W> Deref for GliumWindow<W> {
    type Target = Context;

    fn deref(&self) -> &Context {
        &self.context
    }
}

impl<W> Clone for GliumWindow<W> {
    fn clone(&self) -> GliumWindow<W> {
        GliumWindow {
            window: self.window.clone(),
            context: self.context.clone(),
            events: self.events.clone(),
        }
    }
}

impl<W> BuildFromWindowSettings for GliumWindow<W>
where
    W: 'static + Window + OpenGLWindow + BuildFromWindowSettings,
{
    fn build_from_window_settings(settings: &WindowSettings) -> Result<GliumWindow<W>, Box<dyn Error>> {
        let window: W = settings.clone().build()?;
        GliumWindow::new(&Rc::new(RefCell::new(window))).map_err(|err| err.into())
    }
}

impl<W> GliumWindow<W>
where
    W: OpenGLWindow + 'static,
{
    /// Creates new GliumWindow.
    pub fn new(window: &Rc<RefCell<W>>) -> Result<Self, IncompatibleOpenGl> {
        unsafe {
            let check_current = cfg!(feature = "check_current_window");
            Context::new(Wrapper(window.clone()), check_current, Default::default())
        }
        .map(|context| GliumWindow {
            window: window.clone(),
            context: context,
            events: Events::new(EventSettings::new()).swap_buffers(false),
        })
    }

    /// Returns new frame.
    pub fn draw(&self) -> Frame {
        Frame::new(
            self.context.clone(),
            self.context.get_framebuffer_dimensions(),
        )
    }

    /// Returns next event.
    pub fn next(&mut self) -> Option<Event> {
        self.events.next(&mut *self.window.borrow_mut())
    }
}

impl<W> Facade for GliumWindow<W> {
    fn get_context(&self) -> &Rc<Context> {
        &self.context
    }
}

unsafe impl<W> Backend for Wrapper<W>
where
    W: OpenGLWindow,
{
    fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
        self.0.borrow_mut().swap_buffers();
        Ok(())
    }

    unsafe fn get_proc_address(&self, proc_name: &str) -> *const c_void {
        self.0.borrow_mut().get_proc_address(proc_name) as *const c_void
    }

    fn get_framebuffer_dimensions(&self) -> (u32, u32) {
        let size = self.0.borrow().draw_size();
        (size.width as u32, size.height as u32)
    }

    fn is_current(&self) -> bool {
        self.0.borrow().is_current()
    }

    unsafe fn make_current(&self) {
        self.0.borrow_mut().make_current()
    }
}

impl<W> Window for GliumWindow<W>
where
    W: Window,
{
    fn should_close(&self) -> bool {
        self.window.borrow().should_close()
    }
    fn set_should_close(&mut self, value: bool) {
        self.window.borrow_mut().set_should_close(value)
    }
    fn size(&self) -> Size {
        self.window.borrow().size()
    }
    fn draw_size(&self) -> Size {
        self.window.borrow().draw_size()
    }
    fn swap_buffers(&mut self) {
        self.window.borrow_mut().swap_buffers()
    }
    fn poll_event(&mut self) -> Option<Event> {
        Window::poll_event(&mut *self.window.borrow_mut())
    }
    fn wait_event(&mut self) -> Event {
        Window::wait_event(&mut *self.window.borrow_mut())
    }
    fn wait_event_timeout(&mut self, duration: Duration) -> Option<Event> {
        let mut window = self.window.borrow_mut();
        Window::wait_event_timeout(&mut *window, duration)
    }
}

impl<W> AdvancedWindow for GliumWindow<W>
where
    W: AdvancedWindow,
{
    fn get_title(&self) -> String {
        self.window.borrow().get_title()
    }
    fn set_title(&mut self, title: String) {
        self.window.borrow_mut().set_title(title)
    }
    fn get_automatic_close(&self) -> bool {
        self.window.borrow().get_automatic_close()
    }
    fn set_automatic_close(&mut self, value: bool) {
        self.window.borrow_mut().set_automatic_close(value);
    }
    fn get_exit_on_esc(&self) -> bool {
        self.window.borrow().get_exit_on_esc()
    }
    fn set_exit_on_esc(&mut self, value: bool) {
        self.window.borrow_mut().set_exit_on_esc(value)
    }
    fn set_capture_cursor(&mut self, value: bool) {
        self.window.borrow_mut().set_capture_cursor(value)
    }
    fn show(&mut self) {
        self.window.borrow_mut().show()
    }
    fn hide(&mut self) {
        self.window.borrow_mut().show()
    }
    fn get_position(&self) -> Option<Position> {
        self.window.borrow().get_position()
    }
    fn set_position<P: Into<Position>>(&mut self, pos: P) {
        self.window.borrow_mut().set_position(pos);
    }
    fn set_size<S: Into<Size>>(&mut self, size: S) {
        self.window.borrow_mut().set_size(size)
    }
}

impl<W> EventLoop for GliumWindow<W> {
    fn get_event_settings(&self) -> EventSettings {
        self.events.get_event_settings()
    }

    fn set_event_settings(&mut self, settings: EventSettings) {
        self.events.set_event_settings(settings);
    }
}
<|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 cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    build.no_default_flags(true);
    if build.get_compiler().is_like_msvc() {
        build.flag("/EP").flag("/clang:-undef");
    } else {
        build.flag("-xc").flag("-P").flag("-undef");
    }
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    // SWGL relies heavily on inlining for performance so override -Oz with -O2
    if build.get_compiler().args().contains(&std::ffi::OsString::from("-Oz")) {
        build.flag("-O2");
    }

    build.file("src/gl.cc")
        .flag("-std=c++17")
        .flag("-UMOZILLA_CONFIG_H")
        .flag("-fno-exceptions")
        .flag("-fno-rtti")
        .flag("-fno-math-errno")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<commit_msg>Bug 1700717 - Build SWGL with -ffast-math. r=lsalzman<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 cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    build.no_default_flags(true);
    if build.get_compiler().is_like_msvc() {
        build.flag("/EP").flag("/clang:-undef");
    } else {
        build.flag("-xc").flag("-P").flag("-undef");
    }
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    // SWGL relies heavily on inlining for performance so override -Oz with -O2
    if build.get_compiler().args().contains(&std::ffi::OsString::from("-Oz")) {
        build.flag("-O2");
    }

    build.file("src/gl.cc")
        .flag("-std=c++17")
        .flag("-UMOZILLA_CONFIG_H")
        .flag("-fno-exceptions")
        .flag("-fno-rtti")
        .flag("-fno-math-errno")
        .flag("-ffast-math") // most GLSL compilers assume something similar
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<|endoftext|>


<commit_before>#[lib]

extern crate libc;

// Significant portions generated from <project-dir>/src/mesos-c-api.h

/*
SchedulerDriverStatus scheduler_launchTasks() [int]
	SchedulerDriverPtr driver [void *]
	ProtobufObj * offerId [ProtobufObj *]
	ProtobufObj * tasks [ProtobufObj *]
	ProtobufObj * filters [ProtobufObj *]
*/
#[link(name="libmesos")]
extern "C" {
	fn scheduler_launchTasks(driver: *mut libc::c_void, offerId: *mut ProtobufObj, tasks: *mut ProtobufObj, filters: *mut ProtobufObj) -> libc::c_int;
}

/*
SchedulerDriverStatus scheduler_start() [int]
	SchedulerDriverPtr driver [void *]
*/
#[link(name="libmesos")]
extern "C" {
	fn scheduler_start(driver: *mut libc::c_void) -> libc::c_int;
}

/*
SchedulerDriverStatus scheduler_stop() [int]
	SchedulerDriverPtr driver [void *]
	int failover
*/
#[link(name="libmesos")]
extern "C" {
	fn scheduler_stop(driver: *mut libc::c_void, failover: libc::c_int) -> libc::c_int;
}

/*
SchedulerDriverStatus scheduler_abort() [int]
	SchedulerDriverPtr driver [void *]
*/
#[link(name="libmesos")]
extern "C" {
	fn scheduler_abort(driver: *mut libc::c_void) -> libc::c_int;
}

/*
SchedulerDriverStatus scheduler_join() [int]
	SchedulerDriverPtr driver [void *]
*/
#[link(name="libmesos")]
extern "C" {
	fn scheduler_join(driver: *mut libc::c_void) -> libc::c_int;
}

/*
SchedulerDriverStatus scheduler_run() [int]
	SchedulerDriverPtr driver [void *]
*/
#[link(name="libmesos")]
extern "C" {
	fn scheduler_run(driver: *mut libc::c_void) -> libc::c_int;
}

/*
SchedulerDriverStatus scheduler_requestResources() [int]
	SchedulerDriverPtr driver [void *]
	ProtobufObj * requestsData [ProtobufObj *]
*/
#[link(name="libmesos")]
extern "C" {
	fn scheduler_requestResources(driver: *mut libc::c_void, requestsData: *mut ProtobufObj) -> libc::c_int;
}

/*
SchedulerDriverStatus scheduler_declineOffer() [int]
	SchedulerDriverPtr driver [void *]
	ProtobufObj * offerId [ProtobufObj *]
	ProtobufObj * filters [ProtobufObj *]
*/
#[link(name="libmesos")]
extern "C" {
	fn scheduler_declineOffer(driver: *mut libc::c_void, offerId: *mut ProtobufObj, filters: *mut ProtobufObj) -> libc::c_int;
}

/*
SchedulerDriverStatus scheduler_killTask() [int]
	SchedulerDriverPtr driver [void *]
	ProtobufObj * taskId [ProtobufObj *]
*/
#[link(name="libmesos")]
extern "C" {
	fn scheduler_killTask(driver: *mut libc::c_void, taskId: *mut ProtobufObj) -> libc::c_int;
}

/*
SchedulerDriverStatus scheduler_reviveOffers() [int]
	SchedulerDriverPtr driver [void *]
*/
#[link(name="libmesos")]
extern "C" {
	fn scheduler_reviveOffers(driver: *mut libc::c_void) -> libc::c_int;
}

/*
SchedulerDriverStatus scheduler_sendFrameworkMessage() [int]
	SchedulerDriverPtr driver [void *]
	ProtobufObj * executor [ProtobufObj *]
	ProtobufObj * slaveId [ProtobufObj *]
	const char * data
*/
#[link(name="libmesos")]
extern "C" {
	fn scheduler_sendFrameworkMessage(driver: *mut libc::c_void, executor: *mut ProtobufObj, slaveId: *mut ProtobufObj, data: *const libc::c_char) -> libc::c_int;
}

/*
SchedulerPtrPair scheduler_init() [SchedulerPtrPair]
	SchedulerCallbacks * callbacks [SchedulerCallbacks *]
	void * payload
	ProtobufObj * framework [ProtobufObj *]
	const char * master
*/
#[link(name="libmesos")]
extern "C" {
	fn scheduler_init(callbacks: *mut SchedulerCallbacks, payload: *mut libc::c_void, framework: *mut ProtobufObj, master: *const libc::c_char) -> SchedulerPtrPair;
}

/*
void scheduler_destroy()
	void * driver
	void * scheduler
*/
#[link(name="libmesos")]
extern "C" {
	fn scheduler_destroy(driver: *mut libc::c_void, scheduler: *mut libc::c_void);
}

/*
ExecutorDriverStatus executor_start() [int]
	ExecutorDriverPtr driver [void *]
*/
#[link(name="libmesos")]
extern "C" {
	fn executor_start(driver: *mut libc::c_void) -> libc::c_int;
}

/*
ExecutorDriverStatus executor_stop() [int]
	ExecutorDriverPtr driver [void *]
*/
#[link(name="libmesos")]
extern "C" {
	fn executor_stop(driver: *mut libc::c_void) -> libc::c_int;
}

/*
ExecutorDriverStatus executor_abort() [int]
	ExecutorDriverPtr driver [void *]
*/
#[link(name="libmesos")]
extern "C" {
	fn executor_abort(driver: *mut libc::c_void) -> libc::c_int;
}

/*
ExecutorDriverStatus executor_join() [int]
	ExecutorDriverPtr driver [void *]
*/
#[link(name="libmesos")]
extern "C" {
	fn executor_join(driver: *mut libc::c_void) -> libc::c_int;
}

/*
ExecutorDriverStatus executor_run() [int]
	ExecutorDriverPtr driver [void *]
*/
#[link(name="libmesos")]
extern "C" {
	fn executor_run(driver: *mut libc::c_void) -> libc::c_int;
}

/*
ExecutorDriverStatus executor_sendStatusUpdate() [int]
	ExecutorDriverPtr driver [void *]
	ProtobufObj * status [ProtobufObj *]
*/
#[link(name="libmesos")]
extern "C" {
	fn executor_sendStatusUpdate(driver: *mut libc::c_void, status: *mut ProtobufObj) -> libc::c_int;
}

/*
ExecutorDriverStatus executor_sendFrameworkMessage() [int]
	ExecutorDriverPtr driver [void *]
	const char * data
*/
#[link(name="libmesos")]
extern "C" {
	fn executor_sendFrameworkMessage(driver: *mut libc::c_void, data: *const libc::c_char) -> libc::c_int;
}

/*
ExecutorPtrPair executor_init() [ExecutorPtrPair]
	ExecutorCallbacks * callbacks [ExecutorCallbacks *]
	void * payload
*/
#[link(name="libmesos")]
extern "C" {
	fn executor_init(callbacks: *mut ExecutorCallbacks, payload: *mut libc::c_void) -> ExecutorPtrPair;
}

/*
void executor_destroy()
	void * driver
	void * executor
*/
#[link(name="libmesos")]
extern "C" {
	fn executor_destroy(driver: *mut libc::c_void, executor: *mut libc::c_void);
}

/*
struct
		void * data
		int size
*/
#[repr(C)]
struct ProtobufObj {
	data: *mut libc::c_void,
	size: libc::c_int,
}

/*
struct
		scheduler_registeredCallBack_t registeredCallBack [void (*)(void *, ProtobufObj *, ProtobufObj *)]
		scheduler_reregisteredCallBack_t reregisteredCallBack [void (*)(void *, ProtobufObj *)]
		scheduler_resourceOffersCallBack_t resourceOffersCallBack [void (*)(void *, ProtobufObj *, int)]
		scheduler_statusUpdateCallBack_t statusUpdateCallBack [void (*)(void *, ProtobufObj *)]
		scheduler_disconnectedCallBack_t disconnectedCallBack [void (*)(void *)]
		scheduler_offerRescindedCallBack_t offerRescindedCallBack [void (*)(void *, ProtobufObj *)]
		scheduler_frameworkMessageCallBack_t frameworkMessageCallBack [void (*)(void *, ProtobufObj *, ProtobufObj *, ProtobufObj *)]
		scheduler_slaveLostCallBack_t slaveLostCallBack [void (*)(void *, ProtobufObj *)]
		scheduler_executorLostCallBack_t executorLostCallBack [void (*)(void *, ProtobufObj *, ProtobufObj *, int)]
		scheduler_errorCallBack_t errorCallBack [void (*)(void *, ProtobufObj *)]
*/
#[repr(C)]
struct SchedulerCallbacks {
	registeredCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj, *mut ProtobufObj),
	reregisteredCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj),
	resourceOffersCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj, libc::c_int),
	statusUpdateCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj),
	disconnectedCallBack: extern fn(*mut libc::c_void),
	offerRescindedCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj),
	frameworkMessageCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj, *mut ProtobufObj, *mut ProtobufObj),
	slaveLostCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj),
	executorLostCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj, *mut ProtobufObj, libc::c_int),
	errorCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj),
}

/*
struct
		void * scheduler
		void * driver
*/
#[repr(C)]
struct SchedulerPtrPair {
	scheduler: *mut libc::c_void,
	driver: *mut libc::c_void,
}

/*
struct
		executor_registeredCallBack_t registeredCallBack [void (*)(void *, ProtobufObj *, ProtobufObj *, ProtobufObj *)]
		executor_reregisteredCallBack_t reregisteredCallBack [void (*)(void *, ProtobufObj *)]
		executor_disconnectedCallBack_t disconnectedCallBack [void (*)(void *)]
		executor_launchTaskCallBack_t launchTaskCallBack [void (*)(void *, ProtobufObj *)]
		executor_killTaskCallBack_t killTaskCallBack [void (*)(void *, ProtobufObj *)]
		executor_frameworkMessageCallBack_t frameworkMessageCallBack [void (*)(void *, ProtobufObj *)]
		executor_shutdownCallBack_t shutdownCallBack [void (*)(void *)]
		executor_errorCallBack_t errorCallBack [void (*)(void *, ProtobufObj *)]
*/
#[repr(C)]
struct ExecutorCallbacks {
	registeredCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj, *mut ProtobufObj, *mut ProtobufObj),
	reregisteredCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj),
	disconnectedCallBack: extern fn(*mut libc::c_void),
	launchTaskCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj),
	killTaskCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj),
	frameworkMessageCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj),
	shutdownCallBack: extern fn(*mut libc::c_void),
	errorCallBack: extern fn(*mut libc::c_void, *mut ProtobufObj),
}

/*
struct
		void * executor
		void * driver
*/
#[repr(C)]
struct ExecutorPtrPair {
	executor: *mut libc::c_void,
	driver: *mut libc::c_void,
}

<commit_msg>Deleted duplicate generated code.<commit_after><|endoftext|>


<commit_before>use diesel;
use diesel::prelude::*;
use diesel::pg::PgConnection;
use pg::rows::Row;

use app::App;
use http;
use schema::*;
use util::{CargoResult, human};
use {Model, User, Crate};

#[derive(Insertable, Associations, Identifiable)]
#[belongs_to(Crate)]
#[belongs_to(User, foreign_key = "owner_id")]
#[belongs_to(Team, foreign_key = "owner_id")]
#[table_name = "crate_owners"]
#[primary_key(crate_id, owner_id, owner_kind)]
pub struct CrateOwner {
    pub crate_id: i32,
    pub owner_id: i32,
    pub created_by: i32,
    pub owner_kind: i32,
}

#[repr(u32)]
pub enum OwnerKind {
    User = 0,
    Team = 1,
}

/// Unifies the notion of a User or a Team.
pub enum Owner {
    User(User),
    Team(Team),
}

/// For now, just a Github Team. Can be upgraded to other teams
/// later if desirable.
#[derive(Queryable, Identifiable)]
pub struct Team {
    /// Unique table id
    pub id: i32,
    /// "github:org:team"
    /// An opaque unique ID, that was at one point parsed out to query Github.
    /// We only query membership with github using the github_id, though.
    /// This is the only name we should ever talk to Cargo about.
    pub login: String,
    /// We're assuming these are stable
    pub github_id: i32,
    /// Sugary goodness
    pub name: Option<String>,
    pub avatar: Option<String>,
}

#[derive(RustcEncodable)]
pub struct EncodableTeam {
    pub id: i32,
    pub login: String,
    pub name: Option<String>,
    pub avatar: Option<String>,
    pub url: Option<String>,
}

#[derive(RustcEncodable)]
pub struct EncodableOwner {
    pub id: i32,
    pub login: String,
    pub kind: String,
    pub email: Option<String>,
    pub url: Option<String>,
    pub name: Option<String>,
    pub avatar: Option<String>,
}

/// Access rights to the crate (publishing and ownership management)
/// NOTE: The order of these variants matters!
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Rights {
    None,
    Publish,
    Full,
}

impl Team {
    /// Tries to create the Team in the DB (assumes a `:` has already been found).
    pub fn create(
        app: &App,
        conn: &PgConnection,
        login: &str,
        req_user: &User,
    ) -> CargoResult<Self> {
        // must look like system:xxxxxxx
        let mut chunks = login.split(':');
        match chunks.next().unwrap() {
            // github:rust-lang:owners
            "github" => {
                // Ok to unwrap since we know one ":" is contained
                let org = chunks.next().unwrap();
                let team = chunks.next().ok_or_else(|| {
                    human(
                        "missing github team argument; \
                            format is github:org:team",
                    )
                })?;
                Team::create_github_team(app, conn, login, org, team, req_user)
            }
            _ => {
                Err(human(
                    "unknown organization handler, \
                            only 'github:org:team' is supported",
                ))
            }
        }
    }

    /// Tries to create a Github Team from scratch. Assumes `org` and `team` are
    /// correctly parsed out of the full `name`. `name` is passed as a
    /// convenience to avoid rebuilding it.
    pub fn create_github_team(
        app: &App,
        conn: &PgConnection,
        login: &str,
        org_name: &str,
        team_name: &str,
        req_user: &User,
    ) -> CargoResult<Self> {
        // GET orgs/:org/teams
        // check that `team` is the `slug` in results, and grab its data

        // "sanitization"
        fn whitelist(c: &char) -> bool {
            match *c {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '-' | '_' => false,
                _ => true,
            }
        }

        if let Some(c) = org_name.chars().find(whitelist) {
            return Err(human(&format_args!(
                "organization cannot contain special \
                                        characters like {}",
                c
            )));
        }

        #[derive(RustcDecodable)]
        struct GithubTeam {
            slug: String, // the name we want to find
            id: i32, // unique GH id (needed for membership queries)
            name: Option<String>, // Pretty name
        }

        // FIXME: we just set per_page=100 and don't bother chasing pagination
        // links. A hundred teams should be enough for any org, right?
        let url = format!("/orgs/{}/teams?per_page=100", org_name);
        let token = http::token(req_user.gh_access_token.clone());
        let (handle, data) = http::github(app, &url, &token)?;
        let teams: Vec<GithubTeam> = http::parse_github_response(handle, &data)?;

        let team = teams
            .into_iter()
            .find(|team| team.slug == team_name)
            .ok_or_else(|| {
                human(&format_args!(
                    "could not find the github team {}/{}",
                    org_name,
                    team_name
                ))
            })?;

        if !team_with_gh_id_contains_user(app, team.id, req_user)? {
            return Err(human("only members of a team can add it as an owner"));
        }

        #[derive(RustcDecodable)]
        struct Org {
            avatar_url: Option<String>,
        }

        let url = format!("/orgs/{}", org_name);
        let (handle, resp) = http::github(app, &url, &token)?;
        let org: Org = http::parse_github_response(handle, &resp)?;

        Team::insert(conn, login, team.id, team.name, org.avatar_url)
    }

    pub fn insert(
        conn: &PgConnection,
        login: &str,
        github_id: i32,
        name: Option<String>,
        avatar: Option<String>,
    ) -> CargoResult<Self> {
        use diesel::pg::upsert::*;

        #[derive(Insertable, AsChangeset)]
        #[table_name = "teams"]
        struct NewTeam<'a> {
            login: &'a str,
            github_id: i32,
            name: Option<String>,
            avatar: Option<String>,
        }
        let new_team = NewTeam {
            login: login,
            github_id: github_id,
            name: name,
            avatar: avatar,
        };

        diesel::insert(&new_team.on_conflict(
            teams::github_id,
            do_update().set(&new_team),
        )).into(teams::table)
            .get_result(conn)
            .map_err(Into::into)
    }

    /// Phones home to Github to ask if this User is a member of the given team.
    /// Note that we're assuming that the given user is the one interested in
    /// the answer. If this is not the case, then we could accidentally leak
    /// private membership information here.
    pub fn contains_user(&self, app: &App, user: &User) -> CargoResult<bool> {
        team_with_gh_id_contains_user(app, self.github_id, user)
    }

    pub fn encodable(self) -> EncodableTeam {
        let Team { id, name, login, avatar, .. } = self;
        let org_name = Team::get_org(&login);
        let team_name = Team::get_team(&login);
        let url = format!("https://github.com/orgs/{}/teams/{}", org_name, team_name);

        EncodableTeam {
            id: id,
            login: login,
            name: name,
            avatar: avatar,
            url: Some(url),
        }
    }

    fn get_org(login: &str) -> String {
        let login_string = login.to_string();
        let org: Vec<&str> = login_string.split(':').collect();
        org[1].to_string()
    }

    fn get_team(login: &str) -> String {
        let login_string = login.to_string();
        let org: Vec<&str> = login_string.split(':').collect();
        org[2].to_string()
    }
}

fn team_with_gh_id_contains_user(app: &App, github_id: i32, user: &User) -> CargoResult<bool> {
    // GET teams/:team_id/memberships/:user_name
    // check that "state": "active"

    #[derive(RustcDecodable)]
    struct Membership {
        state: String,
    }

    let url = format!("/teams/{}/memberships/{}", &github_id, &user.gh_login);
    let token = http::token(user.gh_access_token.clone());
    let (mut handle, resp) = http::github(app, &url, &token)?;

    // Officially how `false` is returned
    if handle.response_code().unwrap() == 404 {
        return Ok(false);
    }

    let membership: Membership = http::parse_github_response(handle, &resp)?;

    // There is also `state: pending` for which we could possibly give
    // some feedback, but it's not obvious how that should work.
    Ok(membership.state == "active")
}

impl Model for Team {
    fn from_row(row: &Row) -> Self {
        Team {
            id: row.get("id"),
            name: row.get("name"),
            github_id: row.get("github_id"),
            login: row.get("login"),
            avatar: row.get("avatar"),
        }
    }

    fn table_name(_: Option<Self>) -> &'static str {
        "teams"
    }
}

impl Owner {
    /// Finds the owner by name, failing out if it doesn't exist.
    /// May be a user's GH login, or a full team name. This is case
    /// sensitive.
    pub fn find_by_login(conn: &PgConnection, name: &str) -> CargoResult<Owner> {
        if name.contains(':') {
            teams::table
                .filter(teams::login.eq(name))
                .first(conn)
                .map(Owner::Team)
                .map_err(|_| {
                    human(&format_args!("could not find team with name {}", name))
                })
        } else {
            users::table
                .filter(users::gh_login.eq(name))
                .first(conn)
                .map(Owner::User)
                .map_err(|_| {
                    human(&format_args!("could not find user with login `{}`", name))
                })
        }
    }

    pub fn kind(&self) -> i32 {
        match *self {
            Owner::User(_) => OwnerKind::User as i32,
            Owner::Team(_) => OwnerKind::Team as i32,
        }
    }

    pub fn login(&self) -> &str {
        match *self {
            Owner::User(ref user) => &user.gh_login,
            Owner::Team(ref team) => &team.login,
        }
    }

    pub fn id(&self) -> i32 {
        match *self {
            Owner::User(ref user) => user.id,
            Owner::Team(ref team) => team.id,
        }
    }

    pub fn encodable(self) -> EncodableOwner {
        match self {
            Owner::User(User {
                            id,
                            email,
                            name,
                            gh_login,
                            gh_avatar,
                            ..
                        }) => {
                let url = format!("https://github.com/{}", gh_login);
                EncodableOwner {
                    id: id,
                    login: gh_login,
                    email: email,
                    avatar: gh_avatar,
                    url: Some(url),
                    name: name,
                    kind: String::from("user"),
                }
            }
            Owner::Team(Team {
                            id,
                            name,
                            login,
                            avatar,
                            ..
                        }) => {
                let url = {
                    let mut parts = login.split(':');
                    parts.next(); // discard github
                    format!(
                        "https://github.com/orgs/{}/teams/{}",
                        parts.next().unwrap(),
                        parts.next().unwrap()
                    )
                };
                EncodableOwner {
                    id: id,
                    login: login,
                    email: None,
                    url: Some(url),
                    avatar: avatar,
                    name: name,
                    kind: String::from("team"),
                }
            }
        }
    }
}

/// Given this set of owners, determines the strongest rights the
/// given user has.
///
/// Shortcircuits on `Full` because you can't beat it. In practice we'll always
/// see `[user, user, user, ..., team, team, team]`, so we could shortcircuit on
/// `Publish` as well, but this is a non-obvious invariant so we don't bother.
/// Sweet free optimization if teams are proving burdensome to check.
/// More than one team isn't really expected, though.
pub fn rights(app: &App, owners: &[Owner], user: &User) -> CargoResult<Rights> {
    let mut best = Rights::None;
    for owner in owners {
        match *owner {
            Owner::User(ref other_user) => {
                if other_user.id == user.id {
                    return Ok(Rights::Full);
                }
            }
            Owner::Team(ref team) => {
                if team.contains_user(app, user)? {
                    best = Rights::Publish;
                }
            }
        }
    }
    Ok(best)
}
<commit_msg>traded expect for unwrap in owner encodable<commit_after>use diesel;
use diesel::prelude::*;
use diesel::pg::PgConnection;
use pg::rows::Row;

use app::App;
use http;
use schema::*;
use util::{CargoResult, human};
use {Model, User, Crate};

#[derive(Insertable, Associations, Identifiable)]
#[belongs_to(Crate)]
#[belongs_to(User, foreign_key = "owner_id")]
#[belongs_to(Team, foreign_key = "owner_id")]
#[table_name = "crate_owners"]
#[primary_key(crate_id, owner_id, owner_kind)]
pub struct CrateOwner {
    pub crate_id: i32,
    pub owner_id: i32,
    pub created_by: i32,
    pub owner_kind: i32,
}

#[repr(u32)]
pub enum OwnerKind {
    User = 0,
    Team = 1,
}

/// Unifies the notion of a User or a Team.
pub enum Owner {
    User(User),
    Team(Team),
}

/// For now, just a Github Team. Can be upgraded to other teams
/// later if desirable.
#[derive(Queryable, Identifiable)]
pub struct Team {
    /// Unique table id
    pub id: i32,
    /// "github:org:team"
    /// An opaque unique ID, that was at one point parsed out to query Github.
    /// We only query membership with github using the github_id, though.
    /// This is the only name we should ever talk to Cargo about.
    pub login: String,
    /// We're assuming these are stable
    pub github_id: i32,
    /// Sugary goodness
    pub name: Option<String>,
    pub avatar: Option<String>,
}

#[derive(RustcEncodable)]
pub struct EncodableTeam {
    pub id: i32,
    pub login: String,
    pub name: Option<String>,
    pub avatar: Option<String>,
    pub url: Option<String>,
}

#[derive(RustcEncodable)]
pub struct EncodableOwner {
    pub id: i32,
    pub login: String,
    pub kind: String,
    pub email: Option<String>,
    pub url: Option<String>,
    pub name: Option<String>,
    pub avatar: Option<String>,
}

/// Access rights to the crate (publishing and ownership management)
/// NOTE: The order of these variants matters!
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Rights {
    None,
    Publish,
    Full,
}

impl Team {
    /// Tries to create the Team in the DB (assumes a `:` has already been found).
    pub fn create(
        app: &App,
        conn: &PgConnection,
        login: &str,
        req_user: &User,
    ) -> CargoResult<Self> {
        // must look like system:xxxxxxx
        let mut chunks = login.split(':');
        match chunks.next().unwrap() {
            // github:rust-lang:owners
            "github" => {
                // Ok to unwrap since we know one ":" is contained
                let org = chunks.next().unwrap();
                let team = chunks.next().ok_or_else(|| {
                    human(
                        "missing github team argument; \
                            format is github:org:team",
                    )
                })?;
                Team::create_github_team(app, conn, login, org, team, req_user)
            }
            _ => {
                Err(human(
                    "unknown organization handler, \
                            only 'github:org:team' is supported",
                ))
            }
        }
    }

    /// Tries to create a Github Team from scratch. Assumes `org` and `team` are
    /// correctly parsed out of the full `name`. `name` is passed as a
    /// convenience to avoid rebuilding it.
    pub fn create_github_team(
        app: &App,
        conn: &PgConnection,
        login: &str,
        org_name: &str,
        team_name: &str,
        req_user: &User,
    ) -> CargoResult<Self> {
        // GET orgs/:org/teams
        // check that `team` is the `slug` in results, and grab its data

        // "sanitization"
        fn whitelist(c: &char) -> bool {
            match *c {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '-' | '_' => false,
                _ => true,
            }
        }

        if let Some(c) = org_name.chars().find(whitelist) {
            return Err(human(&format_args!(
                "organization cannot contain special \
                                        characters like {}",
                c
            )));
        }

        #[derive(RustcDecodable)]
        struct GithubTeam {
            slug: String, // the name we want to find
            id: i32, // unique GH id (needed for membership queries)
            name: Option<String>, // Pretty name
        }

        // FIXME: we just set per_page=100 and don't bother chasing pagination
        // links. A hundred teams should be enough for any org, right?
        let url = format!("/orgs/{}/teams?per_page=100", org_name);
        let token = http::token(req_user.gh_access_token.clone());
        let (handle, data) = http::github(app, &url, &token)?;
        let teams: Vec<GithubTeam> = http::parse_github_response(handle, &data)?;

        let team = teams
            .into_iter()
            .find(|team| team.slug == team_name)
            .ok_or_else(|| {
                human(&format_args!(
                    "could not find the github team {}/{}",
                    org_name,
                    team_name
                ))
            })?;

        if !team_with_gh_id_contains_user(app, team.id, req_user)? {
            return Err(human("only members of a team can add it as an owner"));
        }

        #[derive(RustcDecodable)]
        struct Org {
            avatar_url: Option<String>,
        }

        let url = format!("/orgs/{}", org_name);
        let (handle, resp) = http::github(app, &url, &token)?;
        let org: Org = http::parse_github_response(handle, &resp)?;

        Team::insert(conn, login, team.id, team.name, org.avatar_url)
    }

    pub fn insert(
        conn: &PgConnection,
        login: &str,
        github_id: i32,
        name: Option<String>,
        avatar: Option<String>,
    ) -> CargoResult<Self> {
        use diesel::pg::upsert::*;

        #[derive(Insertable, AsChangeset)]
        #[table_name = "teams"]
        struct NewTeam<'a> {
            login: &'a str,
            github_id: i32,
            name: Option<String>,
            avatar: Option<String>,
        }
        let new_team = NewTeam {
            login: login,
            github_id: github_id,
            name: name,
            avatar: avatar,
        };

        diesel::insert(&new_team.on_conflict(
            teams::github_id,
            do_update().set(&new_team),
        )).into(teams::table)
            .get_result(conn)
            .map_err(Into::into)
    }

    /// Phones home to Github to ask if this User is a member of the given team.
    /// Note that we're assuming that the given user is the one interested in
    /// the answer. If this is not the case, then we could accidentally leak
    /// private membership information here.
    pub fn contains_user(&self, app: &App, user: &User) -> CargoResult<bool> {
        team_with_gh_id_contains_user(app, self.github_id, user)
    }

    pub fn encodable(self) -> EncodableTeam {
        let Team { id, name, login, avatar, .. } = self;
        let org_name = Team::get_org(&login);
        let team_name = Team::get_team(&login);
        let url = format!("https://github.com/orgs/{}/teams/{}", org_name, team_name);

        EncodableTeam {
            id: id,
            login: login,
            name: name,
            avatar: avatar,
            url: Some(url),
        }
    }

    fn get_org(login: &str) -> String {
        let login_string = login.to_string();
        let org: Vec<&str> = login_string.split(':').collect();
        org[1].to_string()
    }

    fn get_team(login: &str) -> String {
        let login_string = login.to_string();
        let org: Vec<&str> = login_string.split(':').collect();
        org[2].to_string()
    }
}

fn team_with_gh_id_contains_user(app: &App, github_id: i32, user: &User) -> CargoResult<bool> {
    // GET teams/:team_id/memberships/:user_name
    // check that "state": "active"

    #[derive(RustcDecodable)]
    struct Membership {
        state: String,
    }

    let url = format!("/teams/{}/memberships/{}", &github_id, &user.gh_login);
    let token = http::token(user.gh_access_token.clone());
    let (mut handle, resp) = http::github(app, &url, &token)?;

    // Officially how `false` is returned
    if handle.response_code().unwrap() == 404 {
        return Ok(false);
    }

    let membership: Membership = http::parse_github_response(handle, &resp)?;

    // There is also `state: pending` for which we could possibly give
    // some feedback, but it's not obvious how that should work.
    Ok(membership.state == "active")
}

impl Model for Team {
    fn from_row(row: &Row) -> Self {
        Team {
            id: row.get("id"),
            name: row.get("name"),
            github_id: row.get("github_id"),
            login: row.get("login"),
            avatar: row.get("avatar"),
        }
    }

    fn table_name(_: Option<Self>) -> &'static str {
        "teams"
    }
}

impl Owner {
    /// Finds the owner by name, failing out if it doesn't exist.
    /// May be a user's GH login, or a full team name. This is case
    /// sensitive.
    pub fn find_by_login(conn: &PgConnection, name: &str) -> CargoResult<Owner> {
        if name.contains(':') {
            teams::table
                .filter(teams::login.eq(name))
                .first(conn)
                .map(Owner::Team)
                .map_err(|_| {
                    human(&format_args!("could not find team with name {}", name))
                })
        } else {
            users::table
                .filter(users::gh_login.eq(name))
                .first(conn)
                .map(Owner::User)
                .map_err(|_| {
                    human(&format_args!("could not find user with login `{}`", name))
                })
        }
    }

    pub fn kind(&self) -> i32 {
        match *self {
            Owner::User(_) => OwnerKind::User as i32,
            Owner::Team(_) => OwnerKind::Team as i32,
        }
    }

    pub fn login(&self) -> &str {
        match *self {
            Owner::User(ref user) => &user.gh_login,
            Owner::Team(ref team) => &team.login,
        }
    }

    pub fn id(&self) -> i32 {
        match *self {
            Owner::User(ref user) => user.id,
            Owner::Team(ref team) => team.id,
        }
    }

    pub fn encodable(self) -> EncodableOwner {
        match self {
            Owner::User(User {
                            id,
                            email,
                            name,
                            gh_login,
                            gh_avatar,
                            ..
                        }) => {
                let url = format!("https://github.com/{}", gh_login);
                EncodableOwner {
                    id: id,
                    login: gh_login,
                    email: email,
                    avatar: gh_avatar,
                    url: Some(url),
                    name: name,
                    kind: String::from("user"),
                }
            }
            Owner::Team(Team {
                            id,
                            name,
                            login,
                            avatar,
                            ..
                        }) => {
                let url = {
                    let mut parts = login.split(':');
                    parts.next(); // discard github
                    format!(
                        "https://github.com/orgs/{}/teams/{}",
                        parts.next().expect("org failed"),
                        parts.next().expect("team failed")
                    )
                };
                EncodableOwner {
                    id: id,
                    login: login,
                    email: None,
                    url: Some(url),
                    avatar: avatar,
                    name: name,
                    kind: String::from("team"),
                }
            }
        }
    }
}

/// Given this set of owners, determines the strongest rights the
/// given user has.
///
/// Shortcircuits on `Full` because you can't beat it. In practice we'll always
/// see `[user, user, user, ..., team, team, team]`, so we could shortcircuit on
/// `Publish` as well, but this is a non-obvious invariant so we don't bother.
/// Sweet free optimization if teams are proving burdensome to check.
/// More than one team isn't really expected, though.
pub fn rights(app: &App, owners: &[Owner], user: &User) -> CargoResult<Rights> {
    let mut best = Rights::None;
    for owner in owners {
        match *owner {
            Owner::User(ref other_user) => {
                if other_user.id == user.id {
                    return Ok(Rights::Full);
                }
            }
            Owner::Team(ref team) => {
                if team.contains_user(app, user)? {
                    best = Rights::Publish;
                }
            }
        }
    }
    Ok(best)
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use types::*;
use types::RispType::*;
use tokenize::*;

fn parse_internal(tokenizer: &mut Iterator<Item=Token>) -> Result<RispType, RispError> {
    let mut tokenizer = tokenizer.peekable();
    if let Some(token) = tokenizer.next() {
        return match token {
            (TokenType::Number, token_string) => {
                Ok(Int(token_string.parse().unwrap()))
            }

            (TokenType::Symbol, token_string) => {
                Ok(symbol(token_string))
            }

            (TokenType::Keyword, token_string) => {
                Ok(keyword(&token_string[1..]))
            }

            (TokenType::ListStart, _token_string) => {
                let mut list = vec![];
                loop {
                    let token_option = tokenizer.peek().cloned();
                    if let Some(element_token) = token_option {
                        match element_token {
                            (TokenType::ListEnd, _) => {
                                break;
                            }
                            _ => {
                                let parsed_element = parse_internal(&mut tokenizer)?;
                                list.push(parsed_element);
                            }
                        }
                    } else {
                        return error_result("Unexpected end of list");
                    }
                }
                Ok(List(list))
            }

            (TokenType::ListEnd, _token_string) => {
                error_result("Unexpected end of list")
            }

            (TokenType::VectorStart, _token_string) => {
                let mut vector = vec![];
                loop {
                    let token_option = tokenizer.peek().cloned();
                    if let Some(element_token) = token_option {
                        match element_token {
                            (TokenType::VectorEnd, _) => {
                                break;
                            }
                            _ => {
                                let parsed_element = parse_internal(&mut tokenizer)?;
                                vector.push(parsed_element);
                            }
                        }
                    } else {
                        return error_result("Vector should end with ] but just ends");
                    }
                }
                Ok(Vector(vector))
            }

            (TokenType::VectorEnd, _token_string) => {
                error_result("Unexpected ]")
            }

            (TokenType::HashMapStart, _token_string) => {
                let mut map = HashMap::<String, RispType>::new();
                loop {
                    let token_option = tokenizer.peek().cloned();
                    if let Some(element_token) = token_option {
                        match element_token {
                            (TokenType::HashMapEnd, _) => {
                                break;
                            }

                            (TokenType::Keyword, keyword) => {
                                tokenizer.next();
                                let parsed_element = parse_internal(&mut tokenizer)?;
                                map.insert((&keyword[1..]).to_string(), parsed_element);
                            }

                            (_, token_string) => {
                                return error_result(format!("Expected keyword but got {:?}", token_string));
                            }
                        }
                    } else {
                        return error_result("HashMap should end with } but just ends");
                    }
                }
                Ok(Map(map))
            }

            (TokenType::HashMapEnd, _token_string) => {
                error_result("Unexpected }")
            }
        }
    }

    error_result("Error")
}

pub fn parse(input: &str) -> Result<RispType, RispError> {
    let mut tokenizer = Tokenizer::new(input);
    parse_internal(&mut tokenizer)
}

/* ------------------------------ Tests ----------------------------------------------- */

#[test]
fn test_parse_number() {
    assert_eq!(parse("0"), Ok(Int(0)));
    assert_eq!(parse("1"), Ok(Int(1)));
    assert_eq!(parse("42"), Ok(Int(42)));
    assert_eq!(parse("-42"), Ok(Int(-42)));
}

#[test]
fn test_list() {
    assert_eq!(parse("()"), Ok(List(vec![])));
    assert_eq!(parse("(42)"), Ok(List(vec![Int(42)])));
    assert_eq!(parse("(42 23)"), Ok(List(vec![Int(42), Int(23)])));
    assert_eq!(parse("(42 (23))"), Ok(List(vec![Int(42), List(vec![Int(23)])])));
}

#[test]
fn test_parse_symbols() {
    assert_eq!(parse("symbol"), Ok(Symbol("symbol".to_string())));
    assert_eq!(parse("(+ 1 2)"), Ok(List(vec![Symbol("+".to_string()), Int(1), Int(2)])));
}

#[test]
fn test_parse_vector() {
    assert_eq!(parse("[]"), Ok(Vector(vec![])));
    assert_eq!(parse("[42]"), Ok(Vector(vec![Int(42)])));
    assert_eq!(parse("[42 23]"), Ok(Vector(vec![Int(42), Int(23)])));
    assert_eq!(parse("[42 [23]]"), Ok(Vector(vec![Int(42), Vector(vec![Int(23)])])));
}

#[test]
fn test_parse_vector_errors() {
    assert_eq!(parse("["), error_result("Vector should end with ] but just ends"));
    assert_eq!(parse("]"), error_result("Unexpected ]"));
    assert_eq!(parse("(]"), error_result("Unexpected ]"));
}

#[test]
fn test_hash_map_empty() {
    assert_eq!(parse("{}"), Ok(Map(HashMap::new())));
}

#[test]
fn test_hash_map_with_1_key() {
    let expected_pairs : Vec<(String, RispType)> = vec![("key".to_string(), Int(123))];
    let expected_map : HashMap<String, RispType> = expected_pairs.into_iter().collect();
    assert_eq!(parse("{:key 123}"), Ok(Map(expected_map)));
}

#[test]
fn test_hash_map_with_2_keys() {
    let expected_pairs : Vec<(String, RispType)> = vec![
        ("key1".to_string(), Int(1)),
        ("key2".to_string(), Int(2))
    ];
    let expected_map : HashMap<String, RispType> = expected_pairs.into_iter().collect();
    assert_eq!(parse("{:key1 1 :key2 2}"), Ok(Map(expected_map)));
}

#[test]
fn test_hash_map_errors() {
    assert_eq!(parse("{"), error_result("HashMap should end with } but just ends"));
    assert_eq!(parse("}"), error_result("Unexpected }"));
    assert_eq!(parse("{123}"), error_result("Expected keyword but got \"123\""));
}

<commit_msg>Added test for parsing a keyword<commit_after>use std::collections::HashMap;
use types::*;
use types::RispType::*;
use tokenize::*;

fn parse_internal(tokenizer: &mut Iterator<Item=Token>) -> Result<RispType, RispError> {
    let mut tokenizer = tokenizer.peekable();
    if let Some(token) = tokenizer.next() {
        return match token {
            (TokenType::Number, token_string) => {
                Ok(Int(token_string.parse().unwrap()))
            }

            (TokenType::Symbol, token_string) => {
                Ok(symbol(token_string))
            }

            (TokenType::Keyword, token_string) => {
                Ok(keyword(&token_string[1..]))
            }

            (TokenType::ListStart, _token_string) => {
                let mut list = vec![];
                loop {
                    let token_option = tokenizer.peek().cloned();
                    if let Some(element_token) = token_option {
                        match element_token {
                            (TokenType::ListEnd, _) => {
                                break;
                            }
                            _ => {
                                let parsed_element = parse_internal(&mut tokenizer)?;
                                list.push(parsed_element);
                            }
                        }
                    } else {
                        return error_result("Unexpected end of list");
                    }
                }
                Ok(List(list))
            }

            (TokenType::ListEnd, _token_string) => {
                error_result("Unexpected end of list")
            }

            (TokenType::VectorStart, _token_string) => {
                let mut vector = vec![];
                loop {
                    let token_option = tokenizer.peek().cloned();
                    if let Some(element_token) = token_option {
                        match element_token {
                            (TokenType::VectorEnd, _) => {
                                break;
                            }
                            _ => {
                                let parsed_element = parse_internal(&mut tokenizer)?;
                                vector.push(parsed_element);
                            }
                        }
                    } else {
                        return error_result("Vector should end with ] but just ends");
                    }
                }
                Ok(Vector(vector))
            }

            (TokenType::VectorEnd, _token_string) => {
                error_result("Unexpected ]")
            }

            (TokenType::HashMapStart, _token_string) => {
                let mut map = HashMap::<String, RispType>::new();
                loop {
                    let token_option = tokenizer.peek().cloned();
                    if let Some(element_token) = token_option {
                        match element_token {
                            (TokenType::HashMapEnd, _) => {
                                break;
                            }

                            (TokenType::Keyword, keyword) => {
                                tokenizer.next();
                                let parsed_element = parse_internal(&mut tokenizer)?;
                                map.insert((&keyword[1..]).to_string(), parsed_element);
                            }

                            (_, token_string) => {
                                return error_result(format!("Expected keyword but got {:?}", token_string));
                            }
                        }
                    } else {
                        return error_result("HashMap should end with } but just ends");
                    }
                }
                Ok(Map(map))
            }

            (TokenType::HashMapEnd, _token_string) => {
                error_result("Unexpected }")
            }
        }
    }

    error_result("Error")
}

pub fn parse(input: &str) -> Result<RispType, RispError> {
    let mut tokenizer = Tokenizer::new(input);
    parse_internal(&mut tokenizer)
}

/* ------------------------------ Tests ----------------------------------------------- */

#[test]
fn test_parse_number() {
    assert_eq!(parse("0"), Ok(Int(0)));
    assert_eq!(parse("1"), Ok(Int(1)));
    assert_eq!(parse("42"), Ok(Int(42)));
    assert_eq!(parse("-42"), Ok(Int(-42)));
}

#[test]
fn test_list() {
    assert_eq!(parse("()"), Ok(List(vec![])));
    assert_eq!(parse("(42)"), Ok(List(vec![Int(42)])));
    assert_eq!(parse("(42 23)"), Ok(List(vec![Int(42), Int(23)])));
    assert_eq!(parse("(42 (23))"), Ok(List(vec![Int(42), List(vec![Int(23)])])));
}

#[test]
fn test_parse_symbols() {
    assert_eq!(parse("symbol"), Ok(Symbol("symbol".to_string())));
    assert_eq!(parse("(+ 1 2)"), Ok(List(vec![Symbol("+".to_string()), Int(1), Int(2)])));
}

#[test]
fn test_parse_vector() {
    assert_eq!(parse("[]"), Ok(Vector(vec![])));
    assert_eq!(parse("[42]"), Ok(Vector(vec![Int(42)])));
    assert_eq!(parse("[42 23]"), Ok(Vector(vec![Int(42), Int(23)])));
    assert_eq!(parse("[42 [23]]"), Ok(Vector(vec![Int(42), Vector(vec![Int(23)])])));
}

#[test]
fn test_parse_vector_errors() {
    assert_eq!(parse("["), error_result("Vector should end with ] but just ends"));
    assert_eq!(parse("]"), error_result("Unexpected ]"));
    assert_eq!(parse("(]"), error_result("Unexpected ]"));
}

#[test]
fn test_keyword() {
    assert_eq!(parse(":key"), Ok(keyword("key")));
}

#[test]
fn test_hash_map_empty() {
    assert_eq!(parse("{}"), Ok(Map(HashMap::new())));
}

#[test]
fn test_hash_map_with_1_key() {
    let expected_pairs : Vec<(String, RispType)> = vec![("key".to_string(), Int(123))];
    let expected_map : HashMap<String, RispType> = expected_pairs.into_iter().collect();
    assert_eq!(parse("{:key 123}"), Ok(Map(expected_map)));
}

#[test]
fn test_hash_map_with_2_keys() {
    let expected_pairs : Vec<(String, RispType)> = vec![
        ("key1".to_string(), Int(1)),
        ("key2".to_string(), Int(2))
    ];
    let expected_map : HashMap<String, RispType> = expected_pairs.into_iter().collect();
    assert_eq!(parse("{:key1 1 :key2 2}"), Ok(Map(expected_map)));
}

#[test]
fn test_hash_map_errors() {
    assert_eq!(parse("{"), error_result("HashMap should end with } but just ends"));
    assert_eq!(parse("}"), error_result("Unexpected }"));
    assert_eq!(parse("{123}"), error_result("Expected keyword but got \"123\""));
}

<|endoftext|>