<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Interface for the GPIO peripheral.
//!
//! To ensure fast performance, RPPAL interfaces with the GPIO peripheral by
//! directly accessing the registers through either `/dev/gpiomem` or `/dev/mem`.
//! GPIO interrupts are controlled using the `/dev/gpiochipN` (where N=0, 1 and 2)
//! character device.
//!
//! ## Pins
//!
//! Pins are addressed by their BCM numbers, rather than their
//! physical location.
//!
//! By default, pins are reset to their original state when they go out of scope.
//! Use [`InputPin::set_clear_on_drop(false)`], [`OutputPin::set_clear_on_drop(false)`]
//! or [`AltPin::set_clear_on_drop(false)`], respecively, to disable this behavior.
//! Note that `drop` methods aren't called when a program is abnormally terminated (for
//! instance when a SIGINT isn't caught).
//!
//! ## Permission denied
//!
//! In recent releases of Raspbian (December 2017 or later), users that are part of the
//! `gpio` group (like the default `pi` user) can access `/dev/gpiomem` and
//! `/dev/gpiochipN` without needing additional permissions. If you encounter any
//! Permission Denied errors when creating a new [`Gpio`] instance, either the current
//! user isn't a member of the `gpio` group, or your Raspbian distribution isn't
//! up-to-date and doesn't automatically configure permissions for the above-mentioned
//! files. Updating Raspbian to the latest release should fix any permission issues.
//! Alternatively, although not recommended, you can run your application with superuser
//! privileges by using `sudo`.
//!
//! If you're unable to update Raspbian and its packages (namely `raspberrypi-sys-mods`) to
//! the latest available release, or updating hasn't fixed the issue, you might be able to
//! manually update your udev rules to set the appropriate permissions. More information
//! can be found at [raspberrypi/linux#1225] and [raspberrypi/linux#2289].
//!
//! [raspberrypi/linux#1225]: https://github.com/raspberrypi/linux/issues/1225
//! [raspberrypi/linux#2289]: https://github.com/raspberrypi/linux/issues/2289
//! [`Gpio`]: struct.Gpio.html
//! [`InputPin::set_clear_on_drop(false)`]: struct.InputPin.html#method.set_clear_on_drop
//! [`OutputPin::set_clear_on_drop(false)`]: struct.InputPin.html#method.set_clear_on_drop
//! [`AltPin::set_clear_on_drop(false)`]: struct.InputPin.html#method.set_clear_on_drop
//! [`Error::InstanceExists`]: enum.Error.html#variant.InstanceExists
//!
//! ## Examples
//!
//! Basic example:
//!
//! ```
//! use std::thread::sleep;
//! use std::time::Duration;
//!
//! use rppal::gpio::Gpio;
//!
//! # fn main() -> rppal::gpio::Result<()> {
//! let gpio = Gpio::new()?;
//! let mut pin = gpio.get(23).unwrap().into_output();
//!
//! pin.set_high();
//! sleep(Duration::from_secs(1));
//! pin.set_low();
//! # Ok(())
//! # }
//! ```

use std::fmt;
use std::io;
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, Weak};
use std::time::Duration;

use lazy_static::lazy_static;
use quick_error::quick_error;

mod epoll;
mod interrupt;
mod ioctl;
mod mem;
mod pin;

pub use self::pin::{AltPin, InputPin, OutputPin, Pin};

quick_error! {
/// Errors that can occur when accessing the GPIO peripheral.
    #[derive(Debug)]
    pub enum Error {
/// Unknown model.
///
/// The Raspberry Pi model or SoC can't be identified. Support for
/// new models is usually added shortly after they are officially
/// announced and available to the public. Make sure you're using
/// the latest release of RPPAL.
///
/// You may also encounter this error if your Linux distribution
/// doesn't provide any of the common user-accessible system files
/// that are used to identify the model and SoC.
        UnknownModel { description("unknown Raspberry Pi model") }
/// Permission denied when opening `/dev/gpiomem`, `/dev/mem` or `/dev/gpiochipN` for
/// read/write access.
///
/// More information on possible causes for this error can be found [here].
///
/// [here]: index.html#permission-denied
        PermissionDenied { description("/dev/gpiomem, /dev/mem or /dev/gpiochipN insufficient permissions") }
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// Interrupt polling thread panicked.
        ThreadPanic { description("interrupt polling thread panicked") }
    }
}

/// Result type returned from methods that can have `rppal::gpio::Error`s.
pub type Result<T> = result::Result<T, Error>;

/// Pin modes.
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(u8)]
pub enum Mode {
    Input = 0b000,
    Output = 0b001,
    Alt5 = 0b010, // PWM
    Alt4 = 0b011, // SPI
    Alt0 = 0b100, // PCM
    Alt1 = 0b101, // SMI
    Alt2 = 0b110, // ---
    Alt3 = 0b111, // BSC-SPI
}

impl fmt::Display for Mode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Mode::Input => write!(f, "In"),
            Mode::Output => write!(f, "Out"),
            Mode::Alt0 => write!(f, "Alt0"),
            Mode::Alt1 => write!(f, "Alt1"),
            Mode::Alt2 => write!(f, "Alt2"),
            Mode::Alt3 => write!(f, "Alt3"),
            Mode::Alt4 => write!(f, "Alt4"),
            Mode::Alt5 => write!(f, "Alt5"),
        }
    }
}

/// Pin logic levels.
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(u8)]
pub enum Level {
    Low = 0,
    High = 1,
}

impl fmt::Display for Level {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Level::Low => write!(f, "Low"),
            Level::High => write!(f, "High"),
        }
    }
}

/// Built-in pull-up/pull-down resistor states.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum PullUpDown {
    Off = 0b00,
    PullDown = 0b01,
    PullUp = 0b10,
}

impl fmt::Display for PullUpDown {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            PullUpDown::Off => write!(f, "Off"),
            PullUpDown::PullDown => write!(f, "PullDown"),
            PullUpDown::PullUp => write!(f, "PullUp"),
        }
    }
}

/// Interrupt trigger conditions.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Trigger {
    Disabled = 0,
    RisingEdge = 1,
    FallingEdge = 2,
    Both = 3,
}

impl fmt::Display for Trigger {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Trigger::Disabled => write!(f, "Disabled"),
            Trigger::RisingEdge => write!(f, "RisingEdge"),
            Trigger::FallingEdge => write!(f, "FallingEdge"),
            Trigger::Both => write!(f, "Both"),
        }
    }
}

// Store Gpio's state separately, so we can conveniently share it through
// a cloned Arc.
pub(crate) struct GpioState {
    gpio_mem: mem::GpioMem,
    cdev: std::fs::File,
    sync_interrupts: Mutex<interrupt::EventLoop>,
    pins_taken: [AtomicBool; pin::MAX],
}

impl fmt::Debug for GpioState {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("EventLoop")
            .field("gpio_mem", &self.gpio_mem)
            .field("cdev", &self.cdev)
            .field("sync_interrupts", &self.sync_interrupts)
            .field("pins_taken", &format_args!("{{ .. }}"))
            .finish()
    }
}

// Share state between Gpio and Pin instances. GpioState is dropped after
// all Gpio and Pin instances go out of scope, guaranteeing we won't have
// any pins simultaneously using different EventLoop or GpioMem instances.
lazy_static! {
    static ref GPIO_STATE: Mutex<Weak<GpioState>> = Mutex::new(Weak::new());
}

/// Provides access to the Raspberry Pi's GPIO peripheral.
#[derive(Clone, Debug)]
pub struct Gpio {
    inner: Arc<GpioState>,
}

impl Gpio {
    /// Constructs a new `Gpio`.
    pub fn new() -> Result<Gpio> {
        let mut static_state = GPIO_STATE.lock().unwrap();

        // Create a strong reference if a GpioState instance already exists,
        // otherwise initialize it here so we can return any relevant errors.
        if let Some(ref state) = static_state.upgrade() {
            Ok(Gpio {
                inner: state.clone(),
            })
        } else {
            let gpio_mem = mem::GpioMem::open()?;
            let cdev = ioctl::find_gpiochip()?;
            let sync_interrupts =
                Mutex::new(interrupt::EventLoop::new(cdev.as_raw_fd(), pin::MAX)?);
            let pins_taken = init_array!(AtomicBool::new(false), pin::MAX);

            let gpio_state = Arc::new(GpioState {
                gpio_mem,
                cdev,
                sync_interrupts,
                pins_taken,
            });

            // Store a weak reference to our state. This gets dropped when
            // all Gpio and Pin instances go out of scope.
            *static_state = Arc::downgrade(&gpio_state);

            Ok(Gpio { inner: gpio_state })
        }
    }

    /// Returns a [`Pin`] for the specified GPIO pin number.
    ///
    /// Retrieving a GPIO pin using `get` grants exclusive access to the GPIO
    /// pin through an owned [`Pin`]. If the selected pin number is already
    /// in use, `get` returns `None`. After a [`Pin`] goes out of scope, it can be retrieved
    /// again using `get`.
    ///
    /// [`Pin`]: struct.Pin.html
    pub fn get(&self, pin: u8) -> Option<pin::Pin> {
        if pin as usize >= pin::MAX {
            return None;
        }

        // Returns true if the pin is currently taken, otherwise atomically sets
        // it to true here
        if self.inner.pins_taken[pin as usize].compare_and_swap(false, true, Ordering::SeqCst) {
            // Pin is currently taken
            None
        } else {
            // Return an owned Pin
            let pin_instance = pin::Pin::new(pin, self.inner.clone());

            Some(pin_instance)
        }
    }

    /// Blocks until an interrupt is triggered on any of the specified pins, or until a timeout occurs.
    ///
    /// This only works for pins that have been configured for synchronous interrupts using
    /// [`InputPin::set_interrupt`]. Asynchronous interrupt triggers are automatically polled on a separate thread.
    ///
    /// If `reset` is set to `false`, returns immediately if an interrupt trigger event was cached in a
    /// previous call to [`InputPin::poll_interrupt`] or `poll_interrupts`.
    /// If `reset` is set to `true`, clears any cached interrupt trigger events before polling.
    ///
    /// The `timeout` duration indicates how long the call to `poll_interrupts` will block while waiting
    /// for interrupt trigger events, after which an `Ok(None))` is returned.
    /// `timeout` can be set to `None` to wait indefinitely.
    ///
    /// When an interrupt event is triggered, `poll_interrupts` returns
    /// `Ok((&`[`InputPin`]`, `[`Level`]`))` containing the corresponding pin and logic level. If multiple events trigger
    /// at the same time, only the first one is returned. The remaining events are cached and will be returned
    /// the next time [`InputPin::poll_interrupt`] or `poll_interrupts` is called.
    ///
    /// [`InputPin::set_interrupt`]: struct.InputPin#method.set_interrupt
    /// [`InputPin::poll_interrupt`]: struct.InputPin#method.poll_interrupt
    /// [`InputPin`]: struct.InputPin
    /// [`Level`]: struct.Level
    pub fn poll_interrupts<'a>(
        &self,
        pins: &[&'a InputPin],
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<(&'a InputPin, Level)>> {
        (*self.inner.sync_interrupts.lock().unwrap()).poll(pins, reset, timeout)
    }
}
<commit_msg>Change Mode enum variant order for doc clarity<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Interface for the GPIO peripheral.
//!
//! To ensure fast performance, RPPAL interfaces with the GPIO peripheral by
//! directly accessing the registers through either `/dev/gpiomem` or `/dev/mem`.
//! GPIO interrupts are controlled using the `/dev/gpiochipN` (where N=0, 1 and 2)
//! character device.
//!
//! ## Pins
//!
//! Pins are addressed by their BCM numbers, rather than their
//! physical location.
//!
//! By default, pins are reset to their original state when they go out of scope.
//! Use [`InputPin::set_clear_on_drop(false)`], [`OutputPin::set_clear_on_drop(false)`]
//! or [`AltPin::set_clear_on_drop(false)`], respecively, to disable this behavior.
//! Note that `drop` methods aren't called when a program is abnormally terminated (for
//! instance when a SIGINT isn't caught).
//!
//! ## Permission denied
//!
//! In recent releases of Raspbian (December 2017 or later), users that are part of the
//! `gpio` group (like the default `pi` user) can access `/dev/gpiomem` and
//! `/dev/gpiochipN` without needing additional permissions. If you encounter any
//! Permission Denied errors when creating a new [`Gpio`] instance, either the current
//! user isn't a member of the `gpio` group, or your Raspbian distribution isn't
//! up-to-date and doesn't automatically configure permissions for the above-mentioned
//! files. Updating Raspbian to the latest release should fix any permission issues.
//! Alternatively, although not recommended, you can run your application with superuser
//! privileges by using `sudo`.
//!
//! If you're unable to update Raspbian and its packages (namely `raspberrypi-sys-mods`) to
//! the latest available release, or updating hasn't fixed the issue, you might be able to
//! manually update your udev rules to set the appropriate permissions. More information
//! can be found at [raspberrypi/linux#1225] and [raspberrypi/linux#2289].
//!
//! [raspberrypi/linux#1225]: https://github.com/raspberrypi/linux/issues/1225
//! [raspberrypi/linux#2289]: https://github.com/raspberrypi/linux/issues/2289
//! [`Gpio`]: struct.Gpio.html
//! [`InputPin::set_clear_on_drop(false)`]: struct.InputPin.html#method.set_clear_on_drop
//! [`OutputPin::set_clear_on_drop(false)`]: struct.InputPin.html#method.set_clear_on_drop
//! [`AltPin::set_clear_on_drop(false)`]: struct.InputPin.html#method.set_clear_on_drop
//! [`Error::InstanceExists`]: enum.Error.html#variant.InstanceExists
//!
//! ## Examples
//!
//! Basic example:
//!
//! ```
//! use std::thread::sleep;
//! use std::time::Duration;
//!
//! use rppal::gpio::Gpio;
//!
//! # fn main() -> rppal::gpio::Result<()> {
//! let gpio = Gpio::new()?;
//! let mut pin = gpio.get(23).unwrap().into_output();
//!
//! pin.set_high();
//! sleep(Duration::from_secs(1));
//! pin.set_low();
//! # Ok(())
//! # }
//! ```

use std::fmt;
use std::io;
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, Weak};
use std::time::Duration;

use lazy_static::lazy_static;
use quick_error::quick_error;

mod epoll;
mod interrupt;
mod ioctl;
mod mem;
mod pin;

pub use self::pin::{AltPin, InputPin, OutputPin, Pin};

quick_error! {
/// Errors that can occur when accessing the GPIO peripheral.
    #[derive(Debug)]
    pub enum Error {
/// Unknown model.
///
/// The Raspberry Pi model or SoC can't be identified. Support for
/// new models is usually added shortly after they are officially
/// announced and available to the public. Make sure you're using
/// the latest release of RPPAL.
///
/// You may also encounter this error if your Linux distribution
/// doesn't provide any of the common user-accessible system files
/// that are used to identify the model and SoC.
        UnknownModel { description("unknown Raspberry Pi model") }
/// Permission denied when opening `/dev/gpiomem`, `/dev/mem` or `/dev/gpiochipN` for
/// read/write access.
///
/// More information on possible causes for this error can be found [here].
///
/// [here]: index.html#permission-denied
        PermissionDenied { description("/dev/gpiomem, /dev/mem or /dev/gpiochipN insufficient permissions") }
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// Interrupt polling thread panicked.
        ThreadPanic { description("interrupt polling thread panicked") }
    }
}

/// Result type returned from methods that can have `rppal::gpio::Error`s.
pub type Result<T> = result::Result<T, Error>;

/// Pin modes.
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(u8)]
pub enum Mode {
    Input = 0b000,
    Output = 0b001,
    Alt0 = 0b100,
    Alt1 = 0b101,
    Alt2 = 0b110,
    Alt3 = 0b111,
    Alt4 = 0b011,
    Alt5 = 0b010,
}

impl fmt::Display for Mode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Mode::Input => write!(f, "In"),
            Mode::Output => write!(f, "Out"),
            Mode::Alt0 => write!(f, "Alt0"),
            Mode::Alt1 => write!(f, "Alt1"),
            Mode::Alt2 => write!(f, "Alt2"),
            Mode::Alt3 => write!(f, "Alt3"),
            Mode::Alt4 => write!(f, "Alt4"),
            Mode::Alt5 => write!(f, "Alt5"),
        }
    }
}

/// Pin logic levels.
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(u8)]
pub enum Level {
    Low = 0,
    High = 1,
}

impl fmt::Display for Level {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Level::Low => write!(f, "Low"),
            Level::High => write!(f, "High"),
        }
    }
}

/// Built-in pull-up/pull-down resistor states.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum PullUpDown {
    Off = 0b00,
    PullDown = 0b01,
    PullUp = 0b10,
}

impl fmt::Display for PullUpDown {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            PullUpDown::Off => write!(f, "Off"),
            PullUpDown::PullDown => write!(f, "PullDown"),
            PullUpDown::PullUp => write!(f, "PullUp"),
        }
    }
}

/// Interrupt trigger conditions.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Trigger {
    Disabled = 0,
    RisingEdge = 1,
    FallingEdge = 2,
    Both = 3,
}

impl fmt::Display for Trigger {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Trigger::Disabled => write!(f, "Disabled"),
            Trigger::RisingEdge => write!(f, "RisingEdge"),
            Trigger::FallingEdge => write!(f, "FallingEdge"),
            Trigger::Both => write!(f, "Both"),
        }
    }
}

// Store Gpio's state separately, so we can conveniently share it through
// a cloned Arc.
pub(crate) struct GpioState {
    gpio_mem: mem::GpioMem,
    cdev: std::fs::File,
    sync_interrupts: Mutex<interrupt::EventLoop>,
    pins_taken: [AtomicBool; pin::MAX],
}

impl fmt::Debug for GpioState {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("EventLoop")
            .field("gpio_mem", &self.gpio_mem)
            .field("cdev", &self.cdev)
            .field("sync_interrupts", &self.sync_interrupts)
            .field("pins_taken", &format_args!("{{ .. }}"))
            .finish()
    }
}

// Share state between Gpio and Pin instances. GpioState is dropped after
// all Gpio and Pin instances go out of scope, guaranteeing we won't have
// any pins simultaneously using different EventLoop or GpioMem instances.
lazy_static! {
    static ref GPIO_STATE: Mutex<Weak<GpioState>> = Mutex::new(Weak::new());
}

/// Provides access to the Raspberry Pi's GPIO peripheral.
#[derive(Clone, Debug)]
pub struct Gpio {
    inner: Arc<GpioState>,
}

impl Gpio {
    /// Constructs a new `Gpio`.
    pub fn new() -> Result<Gpio> {
        let mut static_state = GPIO_STATE.lock().unwrap();

        // Create a strong reference if a GpioState instance already exists,
        // otherwise initialize it here so we can return any relevant errors.
        if let Some(ref state) = static_state.upgrade() {
            Ok(Gpio {
                inner: state.clone(),
            })
        } else {
            let gpio_mem = mem::GpioMem::open()?;
            let cdev = ioctl::find_gpiochip()?;
            let sync_interrupts =
                Mutex::new(interrupt::EventLoop::new(cdev.as_raw_fd(), pin::MAX)?);
            let pins_taken = init_array!(AtomicBool::new(false), pin::MAX);

            let gpio_state = Arc::new(GpioState {
                gpio_mem,
                cdev,
                sync_interrupts,
                pins_taken,
            });

            // Store a weak reference to our state. This gets dropped when
            // all Gpio and Pin instances go out of scope.
            *static_state = Arc::downgrade(&gpio_state);

            Ok(Gpio { inner: gpio_state })
        }
    }

    /// Returns a [`Pin`] for the specified GPIO pin number.
    ///
    /// Retrieving a GPIO pin using `get` grants exclusive access to the GPIO
    /// pin through an owned [`Pin`]. If the selected pin number is already
    /// in use, `get` returns `None`. After a [`Pin`] goes out of scope, it can be retrieved
    /// again using `get`.
    ///
    /// [`Pin`]: struct.Pin.html
    pub fn get(&self, pin: u8) -> Option<pin::Pin> {
        if pin as usize >= pin::MAX {
            return None;
        }

        // Returns true if the pin is currently taken, otherwise atomically sets
        // it to true here
        if self.inner.pins_taken[pin as usize].compare_and_swap(false, true, Ordering::SeqCst) {
            // Pin is currently taken
            None
        } else {
            // Return an owned Pin
            let pin_instance = pin::Pin::new(pin, self.inner.clone());

            Some(pin_instance)
        }
    }

    /// Blocks until an interrupt is triggered on any of the specified pins, or until a timeout occurs.
    ///
    /// This only works for pins that have been configured for synchronous interrupts using
    /// [`InputPin::set_interrupt`]. Asynchronous interrupt triggers are automatically polled on a separate thread.
    ///
    /// If `reset` is set to `false`, returns immediately if an interrupt trigger event was cached in a
    /// previous call to [`InputPin::poll_interrupt`] or `poll_interrupts`.
    /// If `reset` is set to `true`, clears any cached interrupt trigger events before polling.
    ///
    /// The `timeout` duration indicates how long the call to `poll_interrupts` will block while waiting
    /// for interrupt trigger events, after which an `Ok(None))` is returned.
    /// `timeout` can be set to `None` to wait indefinitely.
    ///
    /// When an interrupt event is triggered, `poll_interrupts` returns
    /// `Ok((&`[`InputPin`]`, `[`Level`]`))` containing the corresponding pin and logic level. If multiple events trigger
    /// at the same time, only the first one is returned. The remaining events are cached and will be returned
    /// the next time [`InputPin::poll_interrupt`] or `poll_interrupts` is called.
    ///
    /// [`InputPin::set_interrupt`]: struct.InputPin#method.set_interrupt
    /// [`InputPin::poll_interrupt`]: struct.InputPin#method.poll_interrupt
    /// [`InputPin`]: struct.InputPin
    /// [`Level`]: struct.Level
    pub fn poll_interrupts<'a>(
        &self,
        pins: &[&'a InputPin],
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<(&'a InputPin, Level)>> {
        (*self.inner.sync_interrupts.lock().unwrap()).poll(pins, reset, timeout)
    }
}
<|endoftext|>


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

/*! Task-local reference counted boxes

The `Rc` type provides shared ownership of an immutable value. Destruction is deterministic, and
will occur as soon as the last owner is gone. It is marked as non-sendable because it avoids the
overhead of atomic reference counting.

*/

use ptr::RawPtr;
use unstable::intrinsics::transmute;
use ops::Drop;
use kinds::{Freeze, Send};
use clone::{Clone, DeepClone};

struct RcBox<T> {
    value: T,
    count: uint
}

/// Immutable reference counted pointer type
#[unsafe_no_drop_flag]
#[no_send]
pub struct Rc<T> {
    priv ptr: *mut RcBox<T>
}

impl<T: Freeze> Rc<T> {
    /// Construct a new reference-counted box from a `Freeze` value
    #[inline]
    pub fn new(value: T) -> Rc<T> {
        unsafe {
            Rc::new_unchecked(value)
        }
    }
}

impl<T: Send> Rc<T> {
    /// Construct a new reference-counted box from a `Send` value
    #[inline]
    pub fn from_send(value: T) -> Rc<T> {
        unsafe {
            Rc::new_unchecked(value)
        }
    }
}

impl<T> Rc<T> {
    /// Unsafety construct a new reference-counted box from any value.
    ///
    /// It is possible to create cycles, which will leak, and may interact
    /// poorly with managed pointers.
    #[inline]
    pub unsafe fn new_unchecked(value: T) -> Rc<T> {
        Rc{ptr: transmute(~RcBox{value: value, count: 1})}
    }

    /// Borrow the value contained in the reference-counted box
    #[inline]
    pub fn borrow<'r>(&'r self) -> &'r T {
        unsafe { &(*self.ptr).value }
    }
}

impl<T> Clone for Rc<T> {
    #[inline]
    fn clone(&self) -> Rc<T> {
        unsafe {
            (*self.ptr).count += 1;
            Rc{ptr: self.ptr}
        }
    }
}

impl<T: DeepClone> DeepClone for Rc<T> {
    #[inline]
    fn deep_clone(&self) -> Rc<T> {
        unsafe { Rc::new_unchecked(self.borrow().deep_clone()) }
    }
}

#[unsafe_destructor]
impl<T> Drop for Rc<T> {
    fn drop(&mut self) {
        unsafe {
            if self.ptr.is_not_null() {
                (*self.ptr).count -= 1;
                if (*self.ptr).count == 0 {
                    let _: ~RcBox<T> = transmute(self.ptr);
                }
            }
        }
    }
}

#[cfg(test)]
mod test_rc {
    use super::*;
    use mutable::Mut;

    #[test]
    fn test_clone() {
        let x = Rc::from_send(Mut::new(5));
        let y = x.clone();
        do x.borrow().with_mut |inner| {
            *inner = 20;
        }
        assert_eq!(y.borrow().with(|v| *v), 20);
    }

    #[test]
    fn test_deep_clone() {
        let x = Rc::from_send(Mut::new(5));
        let y = x.deep_clone();
        do x.borrow().with_mut |inner| {
            *inner = 20;
        }
        assert_eq!(y.borrow().with(|v| *v), 5);
    }

    #[test]
    fn test_simple() {
        let x = Rc::new(5);
        assert_eq!(*x.borrow(), 5);
    }

    #[test]
    fn test_simple_clone() {
        let x = Rc::new(5);
        let y = x.clone();
        assert_eq!(*x.borrow(), 5);
        assert_eq!(*y.borrow(), 5);
    }

    #[test]
    fn test_destructor() {
        let x = Rc::from_send(~5);
        assert_eq!(**x.borrow(), 5);
    }
}
<commit_msg>Add Rc::from_mut<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*! Task-local reference counted boxes

The `Rc` type provides shared ownership of an immutable value. Destruction is deterministic, and
will occur as soon as the last owner is gone. It is marked as non-sendable because it avoids the
overhead of atomic reference counting.

*/

use ptr::RawPtr;
use unstable::intrinsics::transmute;
use ops::Drop;
use kinds::{Freeze, Send};
use clone::{Clone, DeepClone};
use mutable::Mut;

struct RcBox<T> {
    value: T,
    count: uint
}

/// Immutable reference counted pointer type
#[unsafe_no_drop_flag]
#[no_send]
pub struct Rc<T> {
    priv ptr: *mut RcBox<T>
}

impl<T: Freeze> Rc<T> {
    /// Construct a new reference-counted box from a `Freeze` value
    #[inline]
    pub fn new(value: T) -> Rc<T> {
        unsafe {
            Rc::new_unchecked(value)
        }
    }
}

impl<T: Send> Rc<T> {
    /// Construct a new reference-counted box from a `Send` value
    #[inline]
    pub fn from_send(value: T) -> Rc<T> {
        unsafe {
            Rc::new_unchecked(value)
        }
    }
}

impl<T: Freeze> Rc<Mut<T>> {
    /// Construct a new reference-counted box from a `Mut`-wrapped `Freeze` value
    #[inline]
    pub fn from_mut(value: Mut<T>) -> Rc<Mut<T>> {
        unsafe {
            Rc::new_unchecked(value)
        }
    }
}

impl<T> Rc<T> {
    /// Unsafety construct a new reference-counted box from any value.
    ///
    /// It is possible to create cycles, which will leak, and may interact
    /// poorly with managed pointers.
    #[inline]
    pub unsafe fn new_unchecked(value: T) -> Rc<T> {
        Rc{ptr: transmute(~RcBox{value: value, count: 1})}
    }

    /// Borrow the value contained in the reference-counted box
    #[inline]
    pub fn borrow<'r>(&'r self) -> &'r T {
        unsafe { &(*self.ptr).value }
    }
}

impl<T> Clone for Rc<T> {
    #[inline]
    fn clone(&self) -> Rc<T> {
        unsafe {
            (*self.ptr).count += 1;
            Rc{ptr: self.ptr}
        }
    }
}

impl<T: DeepClone> DeepClone for Rc<T> {
    #[inline]
    fn deep_clone(&self) -> Rc<T> {
        unsafe { Rc::new_unchecked(self.borrow().deep_clone()) }
    }
}

#[unsafe_destructor]
impl<T> Drop for Rc<T> {
    fn drop(&mut self) {
        unsafe {
            if self.ptr.is_not_null() {
                (*self.ptr).count -= 1;
                if (*self.ptr).count == 0 {
                    let _: ~RcBox<T> = transmute(self.ptr);
                }
            }
        }
    }
}

#[cfg(test)]
mod test_rc {
    use super::*;
    use mutable::Mut;

    #[test]
    fn test_clone() {
        let x = Rc::from_send(Mut::new(5));
        let y = x.clone();
        do x.borrow().with_mut |inner| {
            *inner = 20;
        }
        assert_eq!(y.borrow().with(|v| *v), 20);
    }

    #[test]
    fn test_deep_clone() {
        let x = Rc::from_send(Mut::new(5));
        let y = x.deep_clone();
        do x.borrow().with_mut |inner| {
            *inner = 20;
        }
        assert_eq!(y.borrow().with(|v| *v), 5);
    }

    #[test]
    fn test_simple() {
        let x = Rc::new(5);
        assert_eq!(*x.borrow(), 5);
    }

    #[test]
    fn test_simple_clone() {
        let x = Rc::new(5);
        let y = x.clone();
        assert_eq!(*x.borrow(), 5);
        assert_eq!(*y.borrow(), 5);
    }

    #[test]
    fn test_destructor() {
        let x = Rc::from_send(~5);
        assert_eq!(**x.borrow(), 5);
    }

    #[test]
    fn test_from_mut() {
        let a = 10;
        let _x = Rc::from_mut(Mut::new(&a));
    }
}
<|endoftext|>


<commit_before>#![feature(io, path, fs)]

extern crate icepick;

use icepick::search::Search;
use std::io::prelude::*;
use std::io::BufReader;
use std::fs::{OpenOptions};
use std::path::Path;

#[allow(dead_code)]
fn main() {
    let lines = read_lines("benches/30000.txt");

    let mut search = Search::blank(&lines, None, 20);

    search = search.append_to_search("t").append_to_search("o").append_to_search("a").append_to_search("w").append_to_search("c").backspace().backspace().backspace().append_to_search("w").backspace().append_to_search("a");

    println!("\n{}", search.selection().unwrap_or("None".to_string()));
}

fn read_lines(fname: &str) -> Vec<String> {
    let path = Path::new(fname);
    let mut file = BufReader::new(OpenOptions::new().read(true).write(true).open(&path).unwrap());
    let mut result = Vec::new();
    loop {
        let mut buf = String::new();
        match file.read_line(&mut buf) {
            Ok(_) => result.push(buf),
            Err(_) => break,
        }
    }
    result
}
<commit_msg>bench_main: only read line if it has characters<commit_after>#![feature(io, path, fs)]

extern crate icepick;

use icepick::search::Search;
use std::io::prelude::*;
use std::io::BufReader;
use std::fs::{OpenOptions};
use std::path::Path;

#[allow(dead_code)]
fn main() {
    let lines = read_lines("benches/30000.txt");

    let mut search = Search::blank(&lines, None, 20);

    search = search.append_to_search("t").append_to_search("o").append_to_search("a").append_to_search("w").append_to_search("c").backspace().backspace().backspace().append_to_search("w").backspace().append_to_search("a");

    println!("\n{}", search.selection().unwrap_or("None".to_string()));
}

fn read_lines(fname: &str) -> Vec<String> {
    let path = Path::new(fname);
    let mut file = BufReader::new(OpenOptions::new().read(true).open(&path).unwrap());
    let mut result = Vec::new();
    loop {
        let mut buf = String::new();
        match file.read_line(&mut buf) {
            Ok(_) if buf.len() > 0  => {
                result.push(buf);
            },
            _ => break,
        }
    }
    result
}
<|endoftext|>


<commit_before>use crate::ffi::bindings::*;
use bitflags::bitflags;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

bitflags! {
    /**
    Event types that you can request to be notified about.

    Types can be combined with the Bitwise Or operator `|` when passed to
    `Device.register_events()`.
    */
    // Checked against local
    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
    pub struct EventTypes: u64 {
        /// A corrected texture memory error is not an ECC error, so it does not
        /// generate a single bit event.
        const SINGLE_BIT_ECC_ERROR  = nvmlEventTypeSingleBitEccError as u64;
        /// An uncorrected texture memory error is not an ECC error, so it does not
        /// generate a double bit event.
        const DOUBLE_BIT_ECC_ERROR  = nvmlEventTypeDoubleBitEccError as u64;
        /**
        Power state change event.

        On the Fermi architecture, a PState change is an indicator that the GPU
        is throttling down due to no work being executed on the GPU, power
        capping, or thermal capping. In a typical situation, Fermi-based
        GPUs should stay in performance state zero for the duration of the
        execution of a compute process.
        */
        const PSTATE_CHANGE         = nvmlEventTypePState as u64;
        const CRITICAL_XID_ERROR    = nvmlEventTypeXidCriticalError as u64;
        /// Only supports the Kepler architecture.
        const CLOCK_CHANGE          = nvmlEventTypeClock as u64;
    }
}
<commit_msg>Add new `EventTypes` flags<commit_after>use crate::ffi::bindings::*;
use bitflags::bitflags;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

bitflags! {
    /**
    Event types that you can request to be notified about.

    Types can be combined with the Bitwise Or operator `|` when passed to
    `Device.register_events()`.
    */
    // Checked against local
    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
    pub struct EventTypes: u64 {
        /// A corrected texture memory error is not an ECC error, so it does not
        /// generate a single bit event.
        const SINGLE_BIT_ECC_ERROR  = nvmlEventTypeSingleBitEccError as u64;
        /// An uncorrected texture memory error is not an ECC error, so it does not
        /// generate a double bit event.
        const DOUBLE_BIT_ECC_ERROR  = nvmlEventTypeDoubleBitEccError as u64;
        /**
        Power state change event.

        On the Fermi architecture, a PState change is an indicator that the GPU
        is throttling down due to no work being executed on the GPU, power
        capping, or thermal capping. In a typical situation, Fermi-based
        GPUs should stay in performance state zero for the duration of the
        execution of a compute process.
        */
        const PSTATE_CHANGE         = nvmlEventTypePState as u64;
        const CRITICAL_XID_ERROR    = nvmlEventTypeXidCriticalError as u64;
        /// Only supports the Kepler architecture.
        const CLOCK_CHANGE          = nvmlEventTypeClock as u64;
        /// Power source change event (battery vs. AC power).
        const POWER_SOURCE_CHANGE   = nvmlEventTypePowerSourceChange as u64;
        /// MIG configuration changes.
        const MIG_CONFIG_CHANGE     = nvmlEventMigConfigChange as u64;
    }
}
<|endoftext|>


<commit_before>use super::{
    child_properties::ChildProperties, imports::Imports, info_base::InfoBase,
    signatures::Signatures, *,
};
use crate::{config::gobjects::GObject, env::Env, library, nameutil::*, traits::*};
use log::info;
use std::ops::Deref;

#[derive(Debug, Default)]
pub struct Info {
    pub base: InfoBase,
    pub c_type: String,
    pub c_class_type: Option<String>,
    pub get_type: String,
    pub is_interface: bool,
    pub supertypes: Vec<general::StatusedTypeId>,
    pub final_type: bool,
    pub generate_trait: bool,
    pub trait_name: String,
    pub has_constructors: bool,
    pub has_methods: bool,
    pub has_functions: bool,
    pub signals: Vec<signals::Info>,
    pub notify_signals: Vec<signals::Info>,
    pub properties: Vec<properties::Property>,
    pub builder_properties: Vec<properties::Property>,
    pub builder_postprocess: Option<String>,
    pub child_properties: ChildProperties,
    pub signatures: Signatures,
}

impl Info {
    pub fn has_signals(&self) -> bool {
        self.signals.iter().any(|s| s.trampoline.is_ok())
            || self.notify_signals.iter().any(|s| s.trampoline.is_ok())
    }

    pub fn has_action_signals(&self) -> bool {
        self.signals.iter().any(|s| s.action_emit_name.is_some())
    }
}

impl Deref for Info {
    type Target = InfoBase;

    fn deref(&self) -> &InfoBase {
        &self.base
    }
}

pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing class {}", obj.name);
    let full_name = obj.name.clone();

    let class_tid = env.library.find_type(0, &full_name)?;

    let type_ = env.type_(class_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let klass: &library::Class = type_.maybe_ref()?;

    let mut imports = Imports::with_defined(&env.library, &name);
    if obj.generate_display_trait {
        imports.add("std::fmt");
    }

    let supertypes = supertypes::analyze(env, class_tid, &mut imports);

    let final_type = klass.final_type;
    let trait_name = obj
        .trait_name
        .as_ref()
        .cloned()
        .unwrap_or_else(|| format!("{}Ext", name));

    let mut signatures = Signatures::with_capacity(klass.functions.len());

    let mut functions = functions::analyze(
        env,
        &klass.functions,
        class_tid,
        !final_type,
        false,
        obj,
        &mut imports,
        Some(&mut signatures),
        Some(deps),
    );
    let mut specials = special_functions::extract(&mut functions, type_, obj);
    // `copy` will duplicate an object while `clone` just adds a reference
    special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy);
    // these are all automatically derived on objects and compare by pointer. If such functions
    // exist they will provide additional functionality
    for t in &[
        special_functions::Type::Hash,
        special_functions::Type::Equal,
        special_functions::Type::Compare,
    ] {
        special_functions::unhide(&mut functions, &specials, *t);
        specials.traits_mut().remove(t);
    }
    special_functions::analyze_imports(&specials, &mut imports);

    let signals = signals::analyze(
        env,
        &klass.signals,
        class_tid,
        !final_type,
        obj,
        &mut imports,
    );
    let (properties, notify_signals) = properties::analyze(
        env,
        &klass.properties,
        class_tid,
        !final_type,
        obj,
        &mut imports,
        &signatures,
        deps,
    );

    let builder_properties =
        class_builder::analyze(env, &klass.properties, class_tid, obj, &mut imports);

    let (version, deprecated_version) = info_base::versions(
        env,
        obj,
        &functions,
        klass.version,
        klass.deprecated_version,
    );

    let child_properties =
        child_properties::analyze(env, obj.child_properties.as_ref(), class_tid, &mut imports);

    let has_methods = functions
        .iter()
        .any(|f| f.kind == library::FunctionKind::Method && f.status.need_generate());
    let has_signals = signals.iter().any(|s| s.trampoline.is_ok())
        || notify_signals.iter().any(|s| s.trampoline.is_ok());

    // There's no point in generating a trait if there are no signals, methods, properties
    // and child properties: it would be empty
    //
    // There's also no point in generating a trait for final types: there are no possible subtypes
    let generate_trait = !final_type
        && (has_signals || has_methods || !properties.is_empty() || !child_properties.is_empty());

    if !builder_properties.is_empty() {
        imports.add("glib::object::Cast");
        imports.add("glib::StaticType");
        imports.add("glib::ToValue");
    }

    if generate_trait {
        imports.add("glib::object::IsA");
    }

    if obj.concurrency == library::Concurrency::SendUnique {
        imports.add("glib::ObjectExt");
    }

    let base = InfoBase {
        full_name,
        type_id: class_tid,
        name,
        functions,
        specials,
        imports,
        version,
        deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
        concurrency: obj.concurrency,
    };

    // patch up trait methods in the symbol table
    if generate_trait {
        let mut symbols = env.symbols.borrow_mut();
        for func in base.methods() {
            if let Some(symbol) = symbols.by_c_name_mut(&func.glib_name) {
                symbol.make_trait_method(&trait_name);
            }
        }
    }

    let has_constructors = !base.constructors().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base,
        c_type: klass.c_type.clone(),
        c_class_type: klass.c_class_type.clone(),
        get_type: klass.glib_get_type.clone(),
        is_interface: false,
        supertypes,
        final_type,
        generate_trait,
        trait_name,
        has_constructors,
        has_methods,
        has_functions,
        signals,
        notify_signals,
        properties,
        builder_properties,
        builder_postprocess: obj.builder_postprocess.clone(),
        child_properties,
        signatures,
    };

    Some(info)
}

pub fn interface(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing interface {}", obj.name);
    let full_name = obj.name.clone();

    let iface_tid = env.library.find_type(0, &full_name)?;

    let type_ = env.type_(iface_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let iface: &library::Interface = type_.maybe_ref()?;

    let mut imports = Imports::with_defined(&env.library, &name);
    imports.add("glib::object::IsA");
    if obj.generate_display_trait {
        imports.add("std::fmt");
    }

    let supertypes = supertypes::analyze(env, iface_tid, &mut imports);

    let trait_name = obj
        .trait_name
        .as_ref()
        .cloned()
        .unwrap_or_else(|| format!("{}Ext", name));

    let mut signatures = Signatures::with_capacity(iface.functions.len());

    let functions = functions::analyze(
        env,
        &iface.functions,
        iface_tid,
        true,
        false,
        obj,
        &mut imports,
        Some(&mut signatures),
        Some(deps),
    );

    let signals = signals::analyze(env, &iface.signals, iface_tid, true, obj, &mut imports);
    let (properties, notify_signals) = properties::analyze(
        env,
        &iface.properties,
        iface_tid,
        true,
        obj,
        &mut imports,
        &signatures,
        deps,
    );

    let (version, deprecated_version) = info_base::versions(
        env,
        obj,
        &functions,
        iface.version,
        iface.deprecated_version,
    );

    if obj.concurrency == library::Concurrency::SendUnique {
        imports.add("glib::ObjectExt");
    }

    let base = InfoBase {
        full_name,
        type_id: iface_tid,
        name,
        functions,
        specials: Default::default(),
        imports,
        version,
        deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
        concurrency: obj.concurrency,
    };

    let has_methods = !base.methods().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base,
        c_type: iface.c_type.clone(),
        c_class_type: None,
        get_type: iface.glib_get_type.clone(),
        is_interface: true,
        supertypes,
        final_type: false,
        generate_trait: true,
        trait_name,
        has_methods,
        has_functions,
        signals,
        notify_signals,
        properties,
        signatures,
        ..Default::default()
    };

    Some(info)
}
<commit_msg>analysis/object: Remove unused has_methods member<commit_after>use super::{
    child_properties::ChildProperties, imports::Imports, info_base::InfoBase,
    signatures::Signatures, *,
};
use crate::{config::gobjects::GObject, env::Env, library, nameutil::*, traits::*};
use log::info;
use std::ops::Deref;

#[derive(Debug, Default)]
pub struct Info {
    pub base: InfoBase,
    pub c_type: String,
    pub c_class_type: Option<String>,
    pub get_type: String,
    pub is_interface: bool,
    pub supertypes: Vec<general::StatusedTypeId>,
    pub final_type: bool,
    pub generate_trait: bool,
    pub trait_name: String,
    pub has_constructors: bool,
    pub has_functions: bool,
    pub signals: Vec<signals::Info>,
    pub notify_signals: Vec<signals::Info>,
    pub properties: Vec<properties::Property>,
    pub builder_properties: Vec<properties::Property>,
    pub builder_postprocess: Option<String>,
    pub child_properties: ChildProperties,
    pub signatures: Signatures,
}

impl Info {
    pub fn has_signals(&self) -> bool {
        self.signals.iter().any(|s| s.trampoline.is_ok())
            || self.notify_signals.iter().any(|s| s.trampoline.is_ok())
    }

    pub fn has_action_signals(&self) -> bool {
        self.signals.iter().any(|s| s.action_emit_name.is_some())
    }
}

impl Deref for Info {
    type Target = InfoBase;

    fn deref(&self) -> &InfoBase {
        &self.base
    }
}

pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing class {}", obj.name);
    let full_name = obj.name.clone();

    let class_tid = env.library.find_type(0, &full_name)?;

    let type_ = env.type_(class_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let klass: &library::Class = type_.maybe_ref()?;

    let mut imports = Imports::with_defined(&env.library, &name);
    if obj.generate_display_trait {
        imports.add("std::fmt");
    }

    let supertypes = supertypes::analyze(env, class_tid, &mut imports);

    let final_type = klass.final_type;
    let trait_name = obj
        .trait_name
        .as_ref()
        .cloned()
        .unwrap_or_else(|| format!("{}Ext", name));

    let mut signatures = Signatures::with_capacity(klass.functions.len());

    let mut functions = functions::analyze(
        env,
        &klass.functions,
        class_tid,
        !final_type,
        false,
        obj,
        &mut imports,
        Some(&mut signatures),
        Some(deps),
    );
    let mut specials = special_functions::extract(&mut functions, type_, obj);
    // `copy` will duplicate an object while `clone` just adds a reference
    special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy);
    // these are all automatically derived on objects and compare by pointer. If such functions
    // exist they will provide additional functionality
    for t in &[
        special_functions::Type::Hash,
        special_functions::Type::Equal,
        special_functions::Type::Compare,
    ] {
        special_functions::unhide(&mut functions, &specials, *t);
        specials.traits_mut().remove(t);
    }
    special_functions::analyze_imports(&specials, &mut imports);

    let signals = signals::analyze(
        env,
        &klass.signals,
        class_tid,
        !final_type,
        obj,
        &mut imports,
    );
    let (properties, notify_signals) = properties::analyze(
        env,
        &klass.properties,
        class_tid,
        !final_type,
        obj,
        &mut imports,
        &signatures,
        deps,
    );

    let builder_properties =
        class_builder::analyze(env, &klass.properties, class_tid, obj, &mut imports);

    let (version, deprecated_version) = info_base::versions(
        env,
        obj,
        &functions,
        klass.version,
        klass.deprecated_version,
    );

    let child_properties =
        child_properties::analyze(env, obj.child_properties.as_ref(), class_tid, &mut imports);

    let has_methods = functions
        .iter()
        .any(|f| f.kind == library::FunctionKind::Method && f.status.need_generate());
    let has_signals = signals.iter().any(|s| s.trampoline.is_ok())
        || notify_signals.iter().any(|s| s.trampoline.is_ok());

    // There's no point in generating a trait if there are no signals, methods, properties
    // and child properties: it would be empty
    //
    // There's also no point in generating a trait for final types: there are no possible subtypes
    let generate_trait = !final_type
        && (has_signals || has_methods || !properties.is_empty() || !child_properties.is_empty());

    if !builder_properties.is_empty() {
        imports.add("glib::object::Cast");
        imports.add("glib::StaticType");
        imports.add("glib::ToValue");
    }

    if generate_trait {
        imports.add("glib::object::IsA");
    }

    if obj.concurrency == library::Concurrency::SendUnique {
        imports.add("glib::ObjectExt");
    }

    let base = InfoBase {
        full_name,
        type_id: class_tid,
        name,
        functions,
        specials,
        imports,
        version,
        deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
        concurrency: obj.concurrency,
    };

    // patch up trait methods in the symbol table
    if generate_trait {
        let mut symbols = env.symbols.borrow_mut();
        for func in base.methods() {
            if let Some(symbol) = symbols.by_c_name_mut(&func.glib_name) {
                symbol.make_trait_method(&trait_name);
            }
        }
    }

    let has_constructors = !base.constructors().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base,
        c_type: klass.c_type.clone(),
        c_class_type: klass.c_class_type.clone(),
        get_type: klass.glib_get_type.clone(),
        is_interface: false,
        supertypes,
        final_type,
        generate_trait,
        trait_name,
        has_constructors,
        has_functions,
        signals,
        notify_signals,
        properties,
        builder_properties,
        builder_postprocess: obj.builder_postprocess.clone(),
        child_properties,
        signatures,
    };

    Some(info)
}

pub fn interface(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing interface {}", obj.name);
    let full_name = obj.name.clone();

    let iface_tid = env.library.find_type(0, &full_name)?;

    let type_ = env.type_(iface_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let iface: &library::Interface = type_.maybe_ref()?;

    let mut imports = Imports::with_defined(&env.library, &name);
    imports.add("glib::object::IsA");
    if obj.generate_display_trait {
        imports.add("std::fmt");
    }

    let supertypes = supertypes::analyze(env, iface_tid, &mut imports);

    let trait_name = obj
        .trait_name
        .as_ref()
        .cloned()
        .unwrap_or_else(|| format!("{}Ext", name));

    let mut signatures = Signatures::with_capacity(iface.functions.len());

    let functions = functions::analyze(
        env,
        &iface.functions,
        iface_tid,
        true,
        false,
        obj,
        &mut imports,
        Some(&mut signatures),
        Some(deps),
    );

    let signals = signals::analyze(env, &iface.signals, iface_tid, true, obj, &mut imports);
    let (properties, notify_signals) = properties::analyze(
        env,
        &iface.properties,
        iface_tid,
        true,
        obj,
        &mut imports,
        &signatures,
        deps,
    );

    let (version, deprecated_version) = info_base::versions(
        env,
        obj,
        &functions,
        iface.version,
        iface.deprecated_version,
    );

    if obj.concurrency == library::Concurrency::SendUnique {
        imports.add("glib::ObjectExt");
    }

    let base = InfoBase {
        full_name,
        type_id: iface_tid,
        name,
        functions,
        specials: Default::default(),
        imports,
        version,
        deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
        concurrency: obj.concurrency,
    };

    let has_functions = !base.functions().is_empty();

    let info = Info {
        base,
        c_type: iface.c_type.clone(),
        c_class_type: None,
        get_type: iface.glib_get_type.clone(),
        is_interface: true,
        supertypes,
        final_type: false,
        generate_trait: true,
        trait_name,
        has_functions,
        signals,
        notify_signals,
        properties,
        signatures,
        ..Default::default()
    };

    Some(info)
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use rand::thread_rng;

use schema::*;
use Index;
use Searcher;
use rand::distributions::{IndependentSample, Range};

fn check_index_content(searcher: &Searcher, vals: &HashSet<u32>) {
    assert!(searcher.segment_readers().len() < 20);
    assert_eq!(searcher.num_docs() as usize, vals.len());
}

#[test]
fn test_indexing() {

    let mut schema_builder = SchemaBuilder::default();

    let id_field = schema_builder.add_u32_field("id", U32_INDEXED);
    let multiples_field = schema_builder.add_u32_field("multiples", U32_INDEXED);
    let schema = schema_builder.build();

    let index = Index::create_from_tempdir(schema).unwrap();

    let universe = Range::new(0u32, 20u32);
    let mut rng = thread_rng();

    let mut index_writer = index.writer_with_num_threads(3, 120_000_000).unwrap();

    let mut committed_docs: HashSet<u32> = HashSet::new();
    let mut uncommitted_docs: HashSet<u32> = HashSet::new();

    for _ in 0..200 {
        let random_val = universe.ind_sample(&mut rng);
        if random_val == 0 {
            index_writer.commit().expect("Commit failed");
            committed_docs.extend(&uncommitted_docs);
            uncommitted_docs.clear();
            index.load_searchers().unwrap();
            let searcher = index.searcher();
            // check that everything is correct.
            check_index_content(&searcher, &committed_docs);
        }
        else {
            if committed_docs.remove(&random_val) ||
               uncommitted_docs.remove(&random_val) {
                let doc_id_term = Term::from_field_u32(id_field, random_val);
                index_writer.delete_term(doc_id_term);
            }
            else {
                uncommitted_docs.insert(random_val);
                let mut doc = Document::new();
                doc.add_u32(id_field, random_val);
                for i in 1u32..10u32 {
                    doc.add_u32(multiples_field, random_val * i);
                }
                index_writer.add_document(doc);
            }
        }
    }
}
<commit_msg>NOBUG Marked the functional test as ignore<commit_after>use std::collections::HashSet;
use rand::thread_rng;

use schema::*;
use Index;
use Searcher;
use rand::distributions::{IndependentSample, Range};

fn check_index_content(searcher: &Searcher, vals: &HashSet<u32>) {
    assert!(searcher.segment_readers().len() < 20);
    assert_eq!(searcher.num_docs() as usize, vals.len());
}

#[test]
#[ignore]
fn test_indexing() {

    let mut schema_builder = SchemaBuilder::default();

    let id_field = schema_builder.add_u32_field("id", U32_INDEXED);
    let multiples_field = schema_builder.add_u32_field("multiples", U32_INDEXED);
    let schema = schema_builder.build();

    let index = Index::create_from_tempdir(schema).unwrap();

    let universe = Range::new(0u32, 20u32);
    let mut rng = thread_rng();

    let mut index_writer = index.writer_with_num_threads(3, 120_000_000).unwrap();

    let mut committed_docs: HashSet<u32> = HashSet::new();
    let mut uncommitted_docs: HashSet<u32> = HashSet::new();

    for _ in 0..200 {
        let random_val = universe.ind_sample(&mut rng);
        if random_val == 0 {
            index_writer.commit().expect("Commit failed");
            committed_docs.extend(&uncommitted_docs);
            uncommitted_docs.clear();
            index.load_searchers().unwrap();
            let searcher = index.searcher();
            // check that everything is correct.
            check_index_content(&searcher, &committed_docs);
        }
        else {
            if committed_docs.remove(&random_val) ||
               uncommitted_docs.remove(&random_val) {
                let doc_id_term = Term::from_field_u32(id_field, random_val);
                index_writer.delete_term(doc_id_term);
            }
            else {
                uncommitted_docs.insert(random_val);
                let mut doc = Document::new();
                doc.add_u32(id_field, random_val);
                for i in 1u32..10u32 {
                    doc.add_u32(multiples_field, random_val * i);
                }
                index_writer.add_document(doc);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::sync::atomic::{AtomicUsize, Ordering};
use std::ptr;

use ctxt::SemContext;
use gc::Address;
use gc::root::IndirectObj;
use gc::swiper::{PROMOTION_AGE, Region};
use gc::swiper::card::CardTable;
use gc::swiper::crossing::CrossingMap;
use gc::swiper::old::OldGen;
use object::Obj;
use timer::{in_ms, Timer};

pub struct YoungGen {
    // bounds of from- & to-space
    total: Region,

    // address that separates from & to-space
    separator: Address,

    // address of next free memory
    free: AtomicUsize,

    // end of free memory (either separator or total.end)
    end: AtomicUsize,
}

impl YoungGen {
    pub fn new(young_start: Address, young_end: Address) -> YoungGen {
        let half_size = (young_end.to_usize() - young_start.to_usize()) / 2;
        let half_address = young_start.offset(half_size);

        YoungGen {
            total: Region::new(young_start, young_end),
            separator: half_address,
            free: AtomicUsize::new(young_start.to_usize()),
            end: AtomicUsize::new(half_address.to_usize()),
        }
    }

    pub fn alloc(&self, size: usize) -> *const u8 {
        let mut old = self.free.load(Ordering::Relaxed);
        let mut new;

        loop {
            new = old + size;

            if new >= self.end.load(Ordering::Relaxed) {
                return ptr::null();
            }

            let res =
                self.free
                    .compare_exchange_weak(old, new, Ordering::SeqCst, Ordering::Relaxed);

            match res {
                Ok(_) => break,
                Err(x) => old = x,
            }
        }

        old as *const u8
    }

    pub fn collect(
        &self,
        ctxt: &SemContext,
        rootset: Vec<IndirectObj>,
        card_table: &CardTable,
        _crossing_map: &CrossingMap,
        old: &OldGen,
    ) {
        let mut timer = Timer::new(ctxt.args.flag_gc_events);

        let mut scan;
        let scan_end: Address = self.free.load(Ordering::SeqCst).into();

        let mut free;
        let new_end;

        if self.end.load(Ordering::Relaxed) == self.separator.to_usize() {
            scan = self.total.start;
            free = self.separator;
            new_end = self.total.end;

        } else {
            scan = self.separator;
            free = self.total.start;
            new_end = self.separator;
        }

        for &root in &rootset {
            let root_ptr = root.get();

            if self.total.includes(Address::from_ptr(root_ptr)) {
                root.set(copy(root_ptr, &mut free, old));
            }
        }

        // copy objects for old -> young references
        card_table.visit_dirty(|_card| {

        });

        // for card in dirty_cards {
        //     for object in card.objects {
        //         object.visit_reference_fields(|child| {
        //             let child_ptr = child.get();

        //             if self.total.includes(Address::from_ptr(child_ptr)) {
        //                 child.set(copy(child_ptr, &mut free, old));
        //             }
        //         });
        //     }
        // }

        while scan < scan_end {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|child| {
                let child_ptr = child.get();

                if self.total.includes(Address::from_ptr(child_ptr)) {
                    child.set(copy(child_ptr, &mut free, old));
                }
            });

            scan = scan.offset(object.size());
        }

        // swap spaces
        self.end.store(new_end.to_usize(), Ordering::Relaxed);

        // update start of free memory
        self.free.store(free.to_usize(), Ordering::SeqCst);

        // memset from-space to garbage data for debug builds
        // makes sure that no pointer into from-space is left
        if cfg!(debug_assertions) {
            let start = if new_end == self.separator {
                self.total.start
            } else {
                self.separator
            };

            let size = self.separator.to_usize() - self.total.start.to_usize();

            unsafe {
                ptr::write_bytes(start.to_mut_ptr::<u8>(), 0xcc, size);
            }
        }

        timer.stop_with(|dur| {
            if ctxt.args.flag_gc_events {
                println!("GC minor: collect garbage ({} ms)", in_ms(dur));
            }
        });
    }
}

pub fn copy(obj: *mut Obj, free: &mut Address, old: &OldGen) -> *mut Obj {
    let obj = unsafe { &mut *obj };
    let addr = get_forwarding_address(obj);

    if is_forwarding_address(addr) {
        unmark_forwarding_address(addr).to_mut_ptr::<Obj>()
    } else {
        let obj_size = obj.size();
        let age = obj.header_mut().increase_age();

        let addr: Address;

        // if object is old enough we copy it into the old generation
        if age >= PROMOTION_AGE {
            addr = Address::from_ptr(old.alloc(obj_size));
            copy_object(obj, addr, obj_size);

        // otherwise the object remains in the young generation for now
        } else {
            addr = *free;
            copy_object(obj, addr, obj_size);
            *free = addr.offset(obj_size);
        }

        set_forwarding_address(obj, addr);

        addr.to_mut_ptr::<Obj>()
    }
}

fn copy_object(obj: &Obj, addr: Address, size: usize) {
    unsafe {
        ptr::copy_nonoverlapping(obj as *const Obj as *const u8, addr.to_mut_ptr::<u8>(), size);
    }
}

pub fn is_forwarding_address(obj: Address) -> bool {
    (obj.to_usize() & 1) == 1
}

pub fn mark_forwarding_address(obj: Address) -> Address {
    (obj.to_usize() | 1).into()
}

pub fn unmark_forwarding_address(obj: Address) -> Address {
    (obj.to_usize() & !1).into()
}

pub fn get_forwarding_address(obj: &Obj) -> Address {
    unsafe { *(obj as *const Obj as *const Address) }
}

pub fn set_forwarding_address(obj: &mut Obj, addr: Address) {
    unsafe {
        *(obj as *mut Obj as *mut Address) = mark_forwarding_address(addr);
    }
}<commit_msg>add more stuff to visiting dirty cards<commit_after>use std::sync::atomic::{AtomicUsize, Ordering};
use std::ptr;

use ctxt::SemContext;
use gc::Address;
use gc::root::IndirectObj;
use gc::swiper::{PROMOTION_AGE, Region};
use gc::swiper::card::CardTable;
use gc::swiper::crossing::CrossingMap;
use gc::swiper::old::OldGen;
use object::Obj;
use timer::{in_ms, Timer};

pub struct YoungGen {
    // bounds of from- & to-space
    total: Region,

    // address that separates from & to-space
    separator: Address,

    // address of next free memory
    free: AtomicUsize,

    // end of free memory (either separator or total.end)
    end: AtomicUsize,
}

impl YoungGen {
    pub fn new(young_start: Address, young_end: Address) -> YoungGen {
        let half_size = (young_end.to_usize() - young_start.to_usize()) / 2;
        let half_address = young_start.offset(half_size);

        YoungGen {
            total: Region::new(young_start, young_end),
            separator: half_address,
            free: AtomicUsize::new(young_start.to_usize()),
            end: AtomicUsize::new(half_address.to_usize()),
        }
    }

    pub fn alloc(&self, size: usize) -> *const u8 {
        let mut old = self.free.load(Ordering::Relaxed);
        let mut new;

        loop {
            new = old + size;

            if new >= self.end.load(Ordering::Relaxed) {
                return ptr::null();
            }

            let res =
                self.free
                    .compare_exchange_weak(old, new, Ordering::SeqCst, Ordering::Relaxed);

            match res {
                Ok(_) => break,
                Err(x) => old = x,
            }
        }

        old as *const u8
    }

    pub fn collect(
        &self,
        ctxt: &SemContext,
        rootset: Vec<IndirectObj>,
        card_table: &CardTable,
        crossing_map: &CrossingMap,
        old: &OldGen,
    ) {
        let mut timer = Timer::new(ctxt.args.flag_gc_events);

        let mut scan;
        let scan_end: Address = self.free.load(Ordering::SeqCst).into();

        let mut free;
        let new_end;

        if self.end.load(Ordering::Relaxed) == self.separator.to_usize() {
            scan = self.total.start;
            free = self.separator;
            new_end = self.total.end;

        } else {
            scan = self.separator;
            free = self.total.start;
            new_end = self.separator;
        }

        for &root in &rootset {
            let root_ptr = root.get();

            if self.total.includes(Address::from_ptr(root_ptr)) {
                root.set(copy(root_ptr, &mut free, old));
            }
        }

        // copy objects for old -> young references
        card_table.visit_dirty(|card| {
            let crossing_entry = crossing_map.get(card);

            // card contains: any data but no references, then first object
            if crossing_entry.is_first_object() {
                unimplemented!();

            // card contains: references, then first object
            } else if crossing_entry.is_references_at_start() {
                unimplemented!();

            // object spans multiple cards
            } else if crossing_entry.is_previous_card() {
                unimplemented!();

            } else {
                assert!(crossing_entry.is_no_references());
                panic!("dirty card without references: can this ever happen?");
            }
        });

        while scan < scan_end {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|child| {
                let child_ptr = child.get();

                if self.total.includes(Address::from_ptr(child_ptr)) {
                    child.set(copy(child_ptr, &mut free, old));
                }
            });

            scan = scan.offset(object.size());
        }

        // swap spaces
        self.end.store(new_end.to_usize(), Ordering::Relaxed);

        // update start of free memory
        self.free.store(free.to_usize(), Ordering::SeqCst);

        // memset from-space to garbage data for debug builds
        // makes sure that no pointer into from-space is left
        if cfg!(debug_assertions) {
            let start = if new_end == self.separator {
                self.total.start
            } else {
                self.separator
            };

            let size = self.separator.to_usize() - self.total.start.to_usize();

            unsafe {
                ptr::write_bytes(start.to_mut_ptr::<u8>(), 0xcc, size);
            }
        }

        timer.stop_with(|dur| {
            if ctxt.args.flag_gc_events {
                println!("GC minor: collect garbage ({} ms)", in_ms(dur));
            }
        });
    }
}

pub fn copy(obj: *mut Obj, free: &mut Address, old: &OldGen) -> *mut Obj {
    let obj = unsafe { &mut *obj };
    let addr = get_forwarding_address(obj);

    if is_forwarding_address(addr) {
        unmark_forwarding_address(addr).to_mut_ptr::<Obj>()
    } else {
        let obj_size = obj.size();
        let age = obj.header_mut().increase_age();

        let addr: Address;

        // if object is old enough we copy it into the old generation
        if age >= PROMOTION_AGE {
            addr = Address::from_ptr(old.alloc(obj_size));
            copy_object(obj, addr, obj_size);

        // otherwise the object remains in the young generation for now
        } else {
            addr = *free;
            copy_object(obj, addr, obj_size);
            *free = addr.offset(obj_size);
        }

        set_forwarding_address(obj, addr);

        addr.to_mut_ptr::<Obj>()
    }
}

fn copy_object(obj: &Obj, addr: Address, size: usize) {
    unsafe {
        ptr::copy_nonoverlapping(obj as *const Obj as *const u8, addr.to_mut_ptr::<u8>(), size);
    }
}

pub fn is_forwarding_address(obj: Address) -> bool {
    (obj.to_usize() & 1) == 1
}

pub fn mark_forwarding_address(obj: Address) -> Address {
    (obj.to_usize() | 1).into()
}

pub fn unmark_forwarding_address(obj: Address) -> Address {
    (obj.to_usize() & !1).into()
}

pub fn get_forwarding_address(obj: &Obj) -> Address {
    unsafe { *(obj as *const Obj as *const Address) }
}

pub fn set_forwarding_address(obj: &mut Obj, addr: Address) {
    unsafe {
        *(obj as *mut Obj as *mut Address) = mark_forwarding_address(addr);
    }
}<|endoftext|>


<commit_before>use std::io::Read;

use crypto::hmac::Hmac;
use crypto::mac::Mac;
use crypto::mac::MacResult;
use crypto::sha1::Sha1;
use hex::FromHex;
use rocket::http::Status;
use rocket::data::{self, Data, FromData};
use rocket::request::Request;
use rocket::outcome::Outcome::*;
use serde_json;

use config::CONFIG;
use error::{DashError, DashResult};
use github::models::{CommentFromJson, IssueFromJson, PullRequestFromJson};

#[derive(Debug)]
pub struct Event {
    pub delivery_id: String,
    pub event_name: String,
    pub payload: Payload,
}

impl FromData for Event {
    type Error = &'static str;
    fn from_data(request: &Request, data: Data) -> data::Outcome<Self, Self::Error> {
        let headers = request.headers();

        // see [this document](https://developer.github.com/webhooks/securing/) for more information
        let signature = match headers.get_one("X-Hub-Signature") {
            Some(s) => s,
            None => return Failure((Status::BadRequest, "missing signature header")),
        };

        // see [this document](https://developer.github.com/webhooks/#events) for available types
        let event_name = match headers.get_one("X-Github-Event") {
            Some(e) => e,
            None => return Failure((Status::BadRequest, "missing event header")),
        };

        // unique id for each delivery
        let delivery_id = match headers.get_one("X-Github-Delivery") {
            Some(d) => d,
            None => return Failure((Status::BadRequest, "missing delivery header")),
        };

        let mut body = String::new();
        match data.open().read_to_string(&mut body) {
            Ok(_) => (),
            Err(why) => {
                error!("unable to read request body: {:?}", why);
                return Failure((Status::InternalServerError, "unable to read request body"));
            }
        };

        for secret in &CONFIG.github_webhook_secrets {
            if authenticate(secret, &body, signature) {
                // once we know it's from github, we'll parse it

                let payload = match parse_event(event_name, &body) {
                    Ok(p) => p,
                    Err(DashError::Serde(why)) => {
                        info!("failed to parse webhook payload: {:?}", why);
                        return Failure((Status::BadRequest,
                                        "failed to deserialize request payload"));
                    }
                    Err(why) => {
                        error!("non-json-parsing error with webhook payload: {:?}", why);
                        return Failure((Status::InternalServerError,
                                        "unknown failure, check the logs"));
                    }
                };

                let full_event = Event {
                    delivery_id: delivery_id.to_owned(),
                    event_name: event_name.to_owned(),
                    payload: payload,
                };

                info!("Received valid webhook ({} id {})",
                      full_event.event_name,
                      full_event.delivery_id);

                return Success(full_event);
            }
        }

        warn!("Received invalid webhook: {:?}", request);
        Failure((Status::Forbidden, "unable to authenticate webhook"))
    }
}

fn authenticate(secret: &str, payload: &str, signature: &str) -> bool {
    // https://developer.github.com/webhooks/securing/#validating-payloads-from-github
    let sans_prefix = signature[5..].as_bytes();
    match Vec::from_hex(sans_prefix) {
        Ok(sigbytes) => {
            let mut mac = Hmac::new(Sha1::new(), secret.as_bytes());
            mac.input(payload.as_bytes());
            // constant time comparison
            mac.result() == MacResult::new(&sigbytes)
        }
        Err(_) => false,
    }
}

fn parse_event(event_name: &str, body: &str) -> DashResult<Payload> {
    match event_name {
        "issue_comment" => Ok(Payload::IssueComment(serde_json::from_str(body)?)),
        "issues" => Ok(Payload::Issues(serde_json::from_str(body)?)),
        "pull_request" => Ok(Payload::PullRequest(serde_json::from_str(body)?)),

        "commit_comment" |
        "create" |
        "delete" |
        "deployment" |
        "deployment_status" |
        "fork" |
        "gollum" |
        "label" |
        "member" |
        "membership" |
        "milestone" |
        "organization" |
        "page_build" |
        "public" |
        "pull_request_review_comment" |
        "pull_request_review" |
        "push" |
        "repository" |
        "release" |
        "status" |
        "team" |
        "team_add" |
        "watch" => {
            info!("Received {} event, ignoring...", event_name);
            Ok(Payload::Unsupported)
        }

        _ => {
            warn!("Received unrecognized event {}, check GitHub's API to see what's updated.",
                  event_name);
            Ok(Payload::Unsupported)
        }
    }
}

#[derive(Debug)]
pub enum Payload {
    Issues(IssuesEvent),
    IssueComment(IssueCommentEvent),
    PullRequest(PullRequestEvent),

    Unsupported,
}

#[derive(Debug, Deserialize)]
pub struct IssuesEvent {
    pub action: String,
    pub issue: IssueFromJson,
    pub repository: Repository,
}

#[derive(Debug, Deserialize)]
pub struct IssueCommentEvent {
    pub action: String,
    pub issue: IssueFromJson,
    pub repository: Repository,
    pub comment: CommentFromJson,
}

#[derive(Debug, Deserialize)]
pub struct PullRequestEvent {
    pub action: String,
    pub repository: Repository,
    pub number: i32,
    pub pull_request: PullRequestFromJson,
}

#[derive(Debug, Deserialize)]
pub struct Repository {
    pub full_name: String,
}

#[derive(Debug, Deserialize)]
pub struct StatusEvent {
    pub commit: Commit,
    pub state: String,
    pub target_url: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct Commit {
    pub committer: Committer,
}

#[derive(Debug, Deserialize)]
pub struct Committer {
    pub login: String,
}
<commit_msg>Make it easier to debug webhook delivery<commit_after>use std::io::Read;

use crypto::hmac::Hmac;
use crypto::mac::Mac;
use crypto::mac::MacResult;
use crypto::sha1::Sha1;
use hex::FromHex;
use rocket::http::Status;
use rocket::data::{self, Data, FromData};
use rocket::request::Request;
use rocket::outcome::Outcome::*;
use serde_json;

use config::CONFIG;
use error::{DashError, DashResult};
use github::models::{CommentFromJson, IssueFromJson, PullRequestFromJson};

#[derive(Debug)]
pub struct Event {
    pub delivery_id: String,
    pub event_name: String,
    pub payload: Payload,
}

impl FromData for Event {
    type Error = &'static str;
    fn from_data(request: &Request, data: Data) -> data::Outcome<Self, Self::Error> {
        let headers = request.headers();

        // see [this document](https://developer.github.com/webhooks/securing/) for more information
        let signature = match headers.get_one("X-Hub-Signature") {
            Some(s) => s,
            None => return Failure((Status::BadRequest, "missing signature header")),
        };

        // see [this document](https://developer.github.com/webhooks/#events) for available types
        let event_name = match headers.get_one("X-Github-Event") {
            Some(e) => e,
            None => return Failure((Status::BadRequest, "missing event header")),
        };

        // unique id for each delivery
        let delivery_id = match headers.get_one("X-Github-Delivery") {
            Some(d) => d,
            None => return Failure((Status::BadRequest, "missing delivery header")),
        };

        let mut body = String::new();
        match data.open().read_to_string(&mut body) {
            Ok(_) => (),
            Err(why) => {
                error!("unable to read request body: {:?}", why);
                return Failure((Status::InternalServerError, "unable to read request body"));
            }
        };

        for secret in &CONFIG.github_webhook_secrets {
            if authenticate(secret, &body, signature) {
                // once we know it's from github, we'll parse it

                let payload = match parse_event(event_name, &body) {
                    Ok(p) => p,
                    Err(DashError::Serde(why)) => {
                        info!("failed to parse webhook payload: {:?}", why);
                        return Failure((Status::BadRequest,
                                        "failed to deserialize request payload"));
                    }
                    Err(why) => {
                        error!("non-json-parsing error with webhook payload: {:?}", why);
                        return Failure((Status::InternalServerError,
                                        "unknown failure, check the logs"));
                    }
                };

                let full_event = Event {
                    delivery_id: delivery_id.to_owned(),
                    event_name: event_name.to_owned(),
                    payload: payload,
                };

                info!("Received valid webhook ({} id {})",
                      full_event.event_name,
                      full_event.delivery_id);

                return Success(full_event);
            }
        }

        warn!("Received invalid webhook: {:?}", request);
        warn!("Invalid webhook body: {}", body);
        warn!("Tried {} webhook secrets", CONFIG.github_webhook_secrets.len());
        Failure((Status::Forbidden, "unable to authenticate webhook"))
    }
}

fn authenticate(secret: &str, payload: &str, signature: &str) -> bool {
    // https://developer.github.com/webhooks/securing/#validating-payloads-from-github
    let sans_prefix = signature[5..].as_bytes();
    match Vec::from_hex(sans_prefix) {
        Ok(sigbytes) => {
            let mut mac = Hmac::new(Sha1::new(), secret.as_bytes());
            mac.input(payload.as_bytes());
            // constant time comparison
            mac.result() == MacResult::new(&sigbytes)
        }
        Err(_) => false,
    }
}

fn parse_event(event_name: &str, body: &str) -> DashResult<Payload> {
    match event_name {
        "issue_comment" => Ok(Payload::IssueComment(serde_json::from_str(body)?)),
        "issues" => Ok(Payload::Issues(serde_json::from_str(body)?)),
        "pull_request" => Ok(Payload::PullRequest(serde_json::from_str(body)?)),

        "commit_comment" |
        "create" |
        "delete" |
        "deployment" |
        "deployment_status" |
        "fork" |
        "gollum" |
        "label" |
        "member" |
        "membership" |
        "milestone" |
        "organization" |
        "page_build" |
        "public" |
        "pull_request_review_comment" |
        "pull_request_review" |
        "push" |
        "repository" |
        "release" |
        "status" |
        "team" |
        "team_add" |
        "watch" => {
            info!("Received {} event, ignoring...", event_name);
            Ok(Payload::Unsupported)
        }

        _ => {
            warn!("Received unrecognized event {}, check GitHub's API to see what's updated.",
                  event_name);
            Ok(Payload::Unsupported)
        }
    }
}

#[derive(Debug)]
pub enum Payload {
    Issues(IssuesEvent),
    IssueComment(IssueCommentEvent),
    PullRequest(PullRequestEvent),

    Unsupported,
}

#[derive(Debug, Deserialize)]
pub struct IssuesEvent {
    pub action: String,
    pub issue: IssueFromJson,
    pub repository: Repository,
}

#[derive(Debug, Deserialize)]
pub struct IssueCommentEvent {
    pub action: String,
    pub issue: IssueFromJson,
    pub repository: Repository,
    pub comment: CommentFromJson,
}

#[derive(Debug, Deserialize)]
pub struct PullRequestEvent {
    pub action: String,
    pub repository: Repository,
    pub number: i32,
    pub pull_request: PullRequestFromJson,
}

#[derive(Debug, Deserialize)]
pub struct Repository {
    pub full_name: String,
}

#[derive(Debug, Deserialize)]
pub struct StatusEvent {
    pub commit: Commit,
    pub state: String,
    pub target_url: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct Commit {
    pub committer: Committer,
}

#[derive(Debug, Deserialize)]
pub struct Committer {
    pub login: String,
}
<|endoftext|>