<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` character device.
//!
//! On a typical up-to-date Raspbian installation, any user that's a member of the
//! `gpio` group can access `/dev/gpiomem`, while `/dev/mem` requires
//! superuser privileges.
//!
//! 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).
//!
//! Only a single [`Gpio`] instance can exist at any time. Multiple instances could
//! cause race conditions or pin configuration issues when several threads write to
//! the same register simultaneously. While other applications can't be prevented from
//! writing to the GPIO registers at the same time, limiting [`Gpio`] to a single instance
//! will at least make the Rust interface thread-safe.
//!
//! Constructing another instance before the existing one goes out of scope will return
//! an [`Error::InstanceExists`]. You can share a [`Gpio`] instance with other
//! threads using channels, cloning an `Arc<Mutex<Gpio>>` or globally sharing
//! a `Mutex<Gpio>`.
//!
//! [`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::{time::Duration, thread::sleep};
//! let gpio = Gpio::new()?;
//! let mut pin = *gpio.get(23);
//! let mut output_pin = pin.as_output();
//!
//! output_pin.set_high();
//!
//! sleep(Duration::from_secs(1));
//!
//! output_pin.set_high();
//! ```

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, MutexGuard};
use std::time::Duration;

use quick_error::quick_error;

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

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

// Used to limit Gpio to a single instance
static mut GPIO_INSTANCED: AtomicBool = AtomicBool::new(false);

quick_error! {
/// Errors that can occur when accessing the GPIO peripheral.
    #[derive(Debug)]
    pub enum Error {
/// Unknown SoC.
///
/// It wasn't possible to automatically identify the Raspberry Pi's SoC.
        UnknownSoC { description("unknown SoC") }
/// Permission denied when opening `/dev/gpiomem` and/or `/dev/mem` for read/write access.
///
/// Make sure the user has read and write access to `/dev/gpiomem`.
/// Common causes are either incorrect file permissions on `/dev/gpiomem`, or
/// the user isn't a member of the `gpio` group. If `/dev/gpiomem` is missing, upgrade to a more
/// recent version of Raspbian.
///
/// `/dev/mem` is a fallback when `/dev/gpiomem` can't be accessed. Getting read and write
/// access to `/dev/mem` is typically accomplished by executing the program as a
/// privileged user through `sudo`. A better solution that doesn't require `sudo` would be
/// to upgrade to a version of Raspbian that implements `/dev/gpiomem`.
        PermissionDenied { description("/dev/gpiomem and/or /dev/mem insufficient permissions") }
/// An instance of [`Gpio`] already exists.
///
/// Multiple instances of [`Gpio`] can cause race conditions or pin configuration issues when
/// several threads write to the same register simultaneously. While other applications
/// can't be prevented from writing to the GPIO registers at the same time, limiting [`Gpio`]
/// to a single instance will at least make the Rust interface less error-prone.
///
/// You can share a [`Gpio`] instance with other threads using channels, or cloning an
/// `Arc<Mutex<Gpio>>`. Although discouraged, you could also share it globally
/// wrapped in a `Mutex` using the `lazy_static` crate.
///
/// [`Gpio`]: struct.Gpio.html
        InstanceExists { description("an instance of Gpio already exists") }
/// 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"),
        }
    }
}

/// Provides access to the Raspberry Pi's GPIO peripheral.
pub struct Gpio {
    clear_on_drop: bool,
    pub(crate) gpio_mem: Arc<mem::GpioMem>,
    pins: [Arc<Mutex<pin::Pin>>; pin::MAX],
    sync_interrupts: Arc<Mutex<interrupt::EventLoop>>,
}

impl Gpio {
    /// Constructs a new `Gpio`.
    ///
    /// Only a single instance of `Gpio` can exist at any time. Constructing
    /// another instance before the existing one goes out of scope will return
    /// an [`Error::InstanceExists`]. You can share a `Gpio` instance with other
    /// threads using channels, cloning an `Arc<Mutex<Gpio>>` or globally sharing
    /// a `Mutex<Gpio>`.
    ///
    /// [`Error::InstanceExists`]: enum.Error.html#variant.InstanceExists
    pub fn new() -> Result<Gpio> {
        // Check if a Gpio instance already exists before initializing everything
        unsafe {
            if GPIO_INSTANCED.load(Ordering::SeqCst) {
                return Err(Error::InstanceExists);
            }
        }

        let cdev = ioctl::find_driver()?;
        let cdev_fd = cdev.as_raw_fd();

        let cdev = Arc::new(cdev);
        let event_loop = Arc::new(Mutex::new(interrupt::EventLoop::new(cdev_fd, pin::MAX)?));
        let gpio_mem = Arc::new(mem::GpioMem::open()?);

        let pins = unsafe {
            let mut pins: [Arc<Mutex<pin::Pin>>; pin::MAX] = std::mem::uninitialized();

            for (i, element) in pins.iter_mut().enumerate() {
                let pin = Arc::new(Mutex::new(pin::Pin::new(
                    i as u8,
                    event_loop.clone(),
                    gpio_mem.clone(),
                    cdev.clone(),
                )));
                std::ptr::write(element, pin)
            }

            pins
        };

        let gpio = Gpio {
            clear_on_drop: true,
            gpio_mem,
            pins,
            sync_interrupts: event_loop,
        };

        unsafe {
            // Returns true if GPIO_INSTANCED was set to true on a different thread
            // while we were still initializing ourselves, otherwise atomically sets
            // it to true here
            if GPIO_INSTANCED.compare_and_swap(false, true, Ordering::SeqCst) {
                return Err(Error::InstanceExists);
            }
        }

        Ok(gpio)
    }

    pub fn get(&self, pin: u8) -> Option<MutexGuard<pin::Pin>> {
        if pin as usize >= pin::MAX {
            None
        } else {
            Some(self.pins[pin as usize].lock().unwrap())
        }
    }

    /// 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 too `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<'a>],
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<(&'a InputPin<'a>, Level)>> {
        (*self.sync_interrupts.lock().unwrap()).poll(pins, reset, timeout)
    }
}

impl Drop for Gpio {
    fn drop(&mut self) {
        unsafe {
            GPIO_INSTANCED.store(false, Ordering::SeqCst);
        }
    }
}

impl fmt::Debug for Gpio {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Gpio")
            .field("clear_on_drop", &self.clear_on_drop)
            .field("gpio_mem", &*self.gpio_mem)
            .field("sync_interrupts", &format_args!("{{ .. }}"))
            .finish()
    }
}
<commit_msg>Remove unused clear_on_drop<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` character device.
//!
//! On a typical up-to-date Raspbian installation, any user that's a member of the
//! `gpio` group can access `/dev/gpiomem`, while `/dev/mem` requires
//! superuser privileges.
//!
//! 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).
//!
//! Only a single [`Gpio`] instance can exist at any time. Multiple instances could
//! cause race conditions or pin configuration issues when several threads write to
//! the same register simultaneously. While other applications can't be prevented from
//! writing to the GPIO registers at the same time, limiting [`Gpio`] to a single instance
//! will at least make the Rust interface thread-safe.
//!
//! Constructing another instance before the existing one goes out of scope will return
//! an [`Error::InstanceExists`]. You can share a [`Gpio`] instance with other
//! threads using channels, cloning an `Arc<Mutex<Gpio>>` or globally sharing
//! a `Mutex<Gpio>`.
//!
//! [`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::{time::Duration, thread::sleep};
//! let gpio = Gpio::new()?;
//! let mut pin = *gpio.get(23);
//! let mut output_pin = pin.as_output();
//!
//! output_pin.set_high();
//!
//! sleep(Duration::from_secs(1));
//!
//! output_pin.set_high();
//! ```

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, MutexGuard};
use std::time::Duration;

use quick_error::quick_error;

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

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

// Used to limit Gpio to a single instance
static mut GPIO_INSTANCED: AtomicBool = AtomicBool::new(false);

quick_error! {
/// Errors that can occur when accessing the GPIO peripheral.
    #[derive(Debug)]
    pub enum Error {
/// Unknown SoC.
///
/// It wasn't possible to automatically identify the Raspberry Pi's SoC.
        UnknownSoC { description("unknown SoC") }
/// Permission denied when opening `/dev/gpiomem` and/or `/dev/mem` for read/write access.
///
/// Make sure the user has read and write access to `/dev/gpiomem`.
/// Common causes are either incorrect file permissions on `/dev/gpiomem`, or
/// the user isn't a member of the `gpio` group. If `/dev/gpiomem` is missing, upgrade to a more
/// recent version of Raspbian.
///
/// `/dev/mem` is a fallback when `/dev/gpiomem` can't be accessed. Getting read and write
/// access to `/dev/mem` is typically accomplished by executing the program as a
/// privileged user through `sudo`. A better solution that doesn't require `sudo` would be
/// to upgrade to a version of Raspbian that implements `/dev/gpiomem`.
        PermissionDenied { description("/dev/gpiomem and/or /dev/mem insufficient permissions") }
/// An instance of [`Gpio`] already exists.
///
/// Multiple instances of [`Gpio`] can cause race conditions or pin configuration issues when
/// several threads write to the same register simultaneously. While other applications
/// can't be prevented from writing to the GPIO registers at the same time, limiting [`Gpio`]
/// to a single instance will at least make the Rust interface less error-prone.
///
/// You can share a [`Gpio`] instance with other threads using channels, or cloning an
/// `Arc<Mutex<Gpio>>`. Although discouraged, you could also share it globally
/// wrapped in a `Mutex` using the `lazy_static` crate.
///
/// [`Gpio`]: struct.Gpio.html
        InstanceExists { description("an instance of Gpio already exists") }
/// 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"),
        }
    }
}

/// Provides access to the Raspberry Pi's GPIO peripheral.
pub struct Gpio {
    pub(crate) gpio_mem: Arc<mem::GpioMem>,
    pins: [Arc<Mutex<pin::Pin>>; pin::MAX],
    sync_interrupts: Arc<Mutex<interrupt::EventLoop>>,
}

impl Gpio {
    /// Constructs a new `Gpio`.
    ///
    /// Only a single instance of `Gpio` can exist at any time. Constructing
    /// another instance before the existing one goes out of scope will return
    /// an [`Error::InstanceExists`]. You can share a `Gpio` instance with other
    /// threads using channels, cloning an `Arc<Mutex<Gpio>>` or globally sharing
    /// a `Mutex<Gpio>`.
    ///
    /// [`Error::InstanceExists`]: enum.Error.html#variant.InstanceExists
    pub fn new() -> Result<Gpio> {
        // Check if a Gpio instance already exists before initializing everything
        unsafe {
            if GPIO_INSTANCED.load(Ordering::SeqCst) {
                return Err(Error::InstanceExists);
            }
        }

        let cdev = ioctl::find_driver()?;
        let cdev_fd = cdev.as_raw_fd();

        let cdev = Arc::new(cdev);
        let event_loop = Arc::new(Mutex::new(interrupt::EventLoop::new(cdev_fd, pin::MAX)?));
        let gpio_mem = Arc::new(mem::GpioMem::open()?);

        let pins = unsafe {
            let mut pins: [Arc<Mutex<pin::Pin>>; pin::MAX] = std::mem::uninitialized();

            for (i, element) in pins.iter_mut().enumerate() {
                let pin = Arc::new(Mutex::new(pin::Pin::new(
                    i as u8,
                    event_loop.clone(),
                    gpio_mem.clone(),
                    cdev.clone(),
                )));
                std::ptr::write(element, pin)
            }

            pins
        };

        let gpio = Gpio {
            gpio_mem,
            pins,
            sync_interrupts: event_loop,
        };

        unsafe {
            // Returns true if GPIO_INSTANCED was set to true on a different thread
            // while we were still initializing ourselves, otherwise atomically sets
            // it to true here
            if GPIO_INSTANCED.compare_and_swap(false, true, Ordering::SeqCst) {
                return Err(Error::InstanceExists);
            }
        }

        Ok(gpio)
    }

    pub fn get(&self, pin: u8) -> Option<MutexGuard<pin::Pin>> {
        if pin as usize >= pin::MAX {
            None
        } else {
            Some(self.pins[pin as usize].lock().unwrap())
        }
    }

    /// 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 too `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<'a>],
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<(&'a InputPin<'a>, Level)>> {
        (*self.sync_interrupts.lock().unwrap()).poll(pins, reset, timeout)
    }
}

impl Drop for Gpio {
    fn drop(&mut self) {
        unsafe {
            GPIO_INSTANCED.store(false, Ordering::SeqCst);
        }
    }
}

impl fmt::Debug for Gpio {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Gpio")
            .field("gpio_mem", &*self.gpio_mem)
            .field("sync_interrupts", &format_args!("{{ .. }}"))
            .finish()
    }
}
<|endoftext|>


<commit_before>use super::cell::*;
use super::fluid::*;
use itertools::Itertools;
use std::mem;
use rand::{Isaac64Rng, Rng};
use noise::{Brownian2, perlin2};

const SPAWN_RATE: f64 = 0.1;
const CONSUMPTION: f64 = 0.002;
const SURVIVAL_THRESHOLD: f64 = 0.1;
const INHALE_CAP: usize = 20;

const FLUID_CYCLES: usize = 6;

#[derive(Debug, Clone)]
struct Mate {
    mate: (usize, usize),
    source: (usize, usize),
}

#[derive(Debug)]
struct Delta {
    movement_attempts: Vec<(usize, usize)>,
    mate_attempts: Vec<Mate>,
}

pub struct Hex {
    pub solution: Solution,
    pub cell: Option<Cell>,
    pub decision: Option<Decision>,
    delta: Delta,
}

impl Hex {
    pub fn color(&self) -> [f32; 4] {
        [0.25 * self.solution.fluids[1] as f32, 0.0, 0.25 * self.solution.fluids[0] as f32, 1.0]
    }
}

pub struct Grid {
    pub spawning: bool,
    pub width: usize,
    pub height: usize,
    tiles: Vec<Hex>,
}

impl Grid {
    pub fn new(width: usize, height: usize, rng: &mut Isaac64Rng) -> Self {
        Grid {
            spawning: true,
            width: width,
            height: height,
            tiles: randomizing_vec(width, height, rng),
        }
    }

    pub fn randomize(&mut self, rng: &mut Isaac64Rng) {
        self.tiles = randomizing_vec(self.width, self.height, rng);
    }

    pub fn hex(&self, x: usize, y: usize) -> &Hex {
        &self.tiles[x + y * self.width]
    }

    pub fn hex_mut(&mut self, x: usize, y: usize) -> &mut Hex {
        &mut self.tiles[x + y * self.width]
    }

    fn hex_and_neighbors(&mut self, x: usize, y: usize) -> (&mut Hex, [&Hex; 6]) {
        (unsafe { mem::transmute(self.hex_mut(x, y)) },
         if y % 2 == 0 {
            [// UpRight
             self.hex((x + self.width + 1) % self.width,
                      (y + self.height - 1) % self.height),
             // UpLeft
             self.hex(x, (y + self.height - 1) % self.height),
             // Left
             self.hex((x + self.width - 1) % self.width, y),
             // DownLeft
             self.hex(x, (y + self.height + 1) % self.height),
             // DownRight
             self.hex((x + self.width + 1) % self.width,
                      (y + self.height + 1) % self.height),
             // Right
             self.hex((x + self.width + 1) % self.width, y)]
        } else {
            [// UpRight
             self.hex(x, (y + self.height - 1) % self.height),
             // UpLeft
             self.hex((x + self.width - 1) % self.width,
                      (y + self.height - 1) % self.height),
             // Left
             self.hex((x + self.width - 1) % self.width, y),
             // DownLeft
             self.hex((x + self.width - 1) % self.width,
                      (y + self.height + 1) % self.height),
             // DownRight
             self.hex(x, (y + self.height + 1) % self.height),
             // Right
             self.hex((x + self.width + 1) % self.width, y)]
        })
    }

    pub fn cycle(&mut self, rng: &mut Isaac64Rng) {
        if self.spawning {
            self.cycle_spawn(rng);
        }
        self.cycle_cells();
        self.cycle_decisions(rng);
        for _ in 0..FLUID_CYCLES {
            self.cycle_fluids();
        }
        self.cycle_death();
    }

    fn cycle_spawn(&mut self, rng: &mut Isaac64Rng) {
        if rng.next_f64() < SPAWN_RATE {
            let tile = rng.gen_range(0, self.width * self.height);
            if self.tiles[tile].cell.is_none() {
                self.tiles[tile].cell = Some(Cell::new(rng));
            }
        }
    }

    fn cycle_cells(&mut self) {
        for x in 0..self.width {
            for y in 0..self.height {
                let (this, neighbors) = self.hex_and_neighbors(x, y);
                this.decision = if let Some(ref mut this_cell) = this.cell {
                    let neighbor_presents = [neighbors[0].cell.is_some(),
                                             neighbors[1].cell.is_some(),
                                             neighbors[2].cell.is_some(),
                                             neighbors[3].cell.is_some(),
                                             neighbors[4].cell.is_some(),
                                             neighbors[5].cell.is_some()];

                    Some(this_cell.decide([&this.solution.fluids,
                                           &neighbors[0].solution.fluids,
                                           &neighbors[1].solution.fluids,
                                           &neighbors[2].solution.fluids,
                                           &neighbors[3].solution.fluids,
                                           &neighbors[4].solution.fluids,
                                           &neighbors[5].solution.fluids],
                                          &neighbor_presents))
                } else {
                    None
                }
            }
        }
    }

    fn cycle_decisions(&mut self, rng: &mut Isaac64Rng) {
        // Compute the deltas resulting from the decision.
        for x in 0..self.width {
            for y in 0..self.height {
                let (width, height) = (self.width, self.height);
                let (this, neighbors) = self.hex_and_neighbors(x, y);
                // Clear the movements from the previous cycle.
                this.delta.movement_attempts.clear();
                this.delta.mate_attempts.clear();
                this.solution.coefficients = if let Some(ref decision) = this.decision {
                    decision.coefficients
                } else {
                    // Set the diffusion coefficients to the normal values.
                    [0.5, 1.0]
                };

                // Only add movements here if no cell is present.
                if this.cell.is_none() {
                    // Add any neighbor movements to the movement_attempts vector.
                    for (n, &facing) in neighbors.iter().zip(&[Direction::DownLeft,
                                                               Direction::DownRight,
                                                               Direction::Right,
                                                               Direction::UpRight,
                                                               Direction::UpLeft,
                                                               Direction::Left]) {
                        match n.decision {
                            Some(Decision { choice: Choice::Move(direction), .. }) => {
                                // It attempted to move into this hex cell.
                                if facing == direction {
                                    this.delta
                                        .movement_attempts
                                        .push(in_direction(x, y, width, height, facing.flip()));

                                    // No need to continue if we reach 2 attempts.
                                    if this.delta.movement_attempts.len() == 2 {
                                        break;
                                    }
                                }
                            }
                            Some(Decision { choice: Choice::Divide { mate, spawn }, .. }) => {
                                // It attempted to spawn into this hex cell.
                                if facing == spawn {
                                    let source = in_direction(x, y, width, height, facing.flip());;
                                    this.delta
                                        .mate_attempts
                                        .push(Mate {
                                            mate: in_direction(source.0,
                                                               source.1,
                                                               width,
                                                               height,
                                                               mate),
                                            source: source,
                                        });

                                    // No need to continue if we reach 2 attempts.
                                    if this.delta.mate_attempts.len() == 2 {
                                        break;
                                    }
                                }
                            }
                            _ => {}
                        }
                    }
                }
            }
        }

        // Perform the deltas.
        for x in 0..self.width {
            for y in 0..self.height {
                // Handle movement.
                if self.hex(x, y).delta.movement_attempts.len() == 1 {
                    let from_coord = self.hex(x, y).delta.movement_attempts[0];
                    self.hex_mut(x, y).cell = self.hex_mut(from_coord.0, from_coord.1).cell.take();
                    // Handle mating.
                } else if self.hex(x, y).delta.mate_attempts.len() == 1 {
                    let mate = self.hex(x, y).delta.mate_attempts[0].clone();
                    self.hex_mut(x, y).cell = if mate.mate == (x, y) {
                        Some(self.hex(mate.source.0, mate.source.1)
                            .cell
                            .as_ref()
                            .unwrap()
                            .divide(rng))
                    } else {
                        if self.hex(mate.mate.0, mate.mate.1).cell.is_some() {
                            Some(self.hex(mate.source.0, mate.source.1)
                                .cell
                                .as_ref()
                                .unwrap()
                                .mate(&self.hex(mate.mate.0, mate.mate.1)
                                          .cell
                                          .as_ref()
                                          .unwrap(),
                                      rng))
                        } else {
                            None
                        }
                    };
                }

                // Clear the decisions.
                self.hex_mut(x, y).decision = None;
            }
        }
    }

    fn cycle_fluids(&mut self) {
        // Then update diffusion.
        for x in 0..self.width {
            for y in 0..self.height {
                let (this, neighbors) = self.hex_and_neighbors(x, y);

                for n in &neighbors {
                    this.solution.diffuse_from(&n.solution);
                }
            }
        }

        // Finish the cycle.
        for hex in &mut self.tiles {
            hex.solution.end_cycle();
        }
    }

    fn cycle_death(&mut self) {
        // Finish the cycle.
        for hex in &mut self.tiles {
            if hex.cell.is_some() {
                if hex.solution.fluids[0] <= CONSUMPTION {
                    if hex.cell.as_ref().unwrap().inhale != 0 {
                        hex.cell.as_mut().unwrap().inhale -= 1;
                    } else {
                        hex.cell = None;
                    }
                } else {
                    hex.solution.fluids[0] -= CONSUMPTION;
                    if hex.solution.fluids[0] < SURVIVAL_THRESHOLD {
                        if hex.cell.as_ref().unwrap().inhale != 0 {
                            hex.cell.as_mut().unwrap().inhale -= 1;
                        } else {
                            hex.cell = None;
                        }
                    } else {
                        if hex.cell.as_ref().unwrap().inhale < INHALE_CAP {
                            hex.cell.as_mut().unwrap().inhale += 1;
                        }
                    }
                }
            }
        }
    }
}

fn randomizing_vec(width: usize, height: usize, rng: &mut Isaac64Rng) -> Vec<Hex> {
    let seeds = [rng.gen(), rng.gen()];
    let noise = Brownian2::new(perlin2, 4).wavelength(32.0);
    (0..height)
        .cartesian_product((0..width))
        .map(|(x, y)| {
            Hex {
                solution: Solution::new([noise.apply(&seeds[0], &[x as f64, y as f64]), 1.0],
                                        [0.5, 1.0]),
                cell: None,
                decision: None,
                delta: Delta {
                    movement_attempts: Vec::with_capacity(6),
                    mate_attempts: Vec::with_capacity(6),
                },
            }
        })
        .collect_vec()
}

fn in_direction(x: usize,
                y: usize,
                width: usize,
                height: usize,
                direction: Direction)
                -> (usize, usize) {
    let diff = direction.delta(y % 2 == 0);
    (((width + x) as isize + diff.0) as usize % width,
     ((height + y) as isize + diff.1) as usize % height)
}
<commit_msg>increase consumption to make it much more difficult<commit_after>use super::cell::*;
use super::fluid::*;
use itertools::Itertools;
use std::mem;
use rand::{Isaac64Rng, Rng};
use noise::{Brownian2, perlin2};

const SPAWN_RATE: f64 = 0.1;
const CONSUMPTION: f64 = 0.01;
const SURVIVAL_THRESHOLD: f64 = 0.1;
const INHALE_CAP: usize = 20;

const FLUID_CYCLES: usize = 6;

#[derive(Debug, Clone)]
struct Mate {
    mate: (usize, usize),
    source: (usize, usize),
}

#[derive(Debug)]
struct Delta {
    movement_attempts: Vec<(usize, usize)>,
    mate_attempts: Vec<Mate>,
}

pub struct Hex {
    pub solution: Solution,
    pub cell: Option<Cell>,
    pub decision: Option<Decision>,
    delta: Delta,
}

impl Hex {
    pub fn color(&self) -> [f32; 4] {
        [0.25 * self.solution.fluids[1] as f32, 0.0, 0.25 * self.solution.fluids[0] as f32, 1.0]
    }
}

pub struct Grid {
    pub spawning: bool,
    pub width: usize,
    pub height: usize,
    tiles: Vec<Hex>,
}

impl Grid {
    pub fn new(width: usize, height: usize, rng: &mut Isaac64Rng) -> Self {
        Grid {
            spawning: true,
            width: width,
            height: height,
            tiles: randomizing_vec(width, height, rng),
        }
    }

    pub fn randomize(&mut self, rng: &mut Isaac64Rng) {
        self.tiles = randomizing_vec(self.width, self.height, rng);
    }

    pub fn hex(&self, x: usize, y: usize) -> &Hex {
        &self.tiles[x + y * self.width]
    }

    pub fn hex_mut(&mut self, x: usize, y: usize) -> &mut Hex {
        &mut self.tiles[x + y * self.width]
    }

    fn hex_and_neighbors(&mut self, x: usize, y: usize) -> (&mut Hex, [&Hex; 6]) {
        (unsafe { mem::transmute(self.hex_mut(x, y)) },
         if y % 2 == 0 {
            [// UpRight
             self.hex((x + self.width + 1) % self.width,
                      (y + self.height - 1) % self.height),
             // UpLeft
             self.hex(x, (y + self.height - 1) % self.height),
             // Left
             self.hex((x + self.width - 1) % self.width, y),
             // DownLeft
             self.hex(x, (y + self.height + 1) % self.height),
             // DownRight
             self.hex((x + self.width + 1) % self.width,
                      (y + self.height + 1) % self.height),
             // Right
             self.hex((x + self.width + 1) % self.width, y)]
        } else {
            [// UpRight
             self.hex(x, (y + self.height - 1) % self.height),
             // UpLeft
             self.hex((x + self.width - 1) % self.width,
                      (y + self.height - 1) % self.height),
             // Left
             self.hex((x + self.width - 1) % self.width, y),
             // DownLeft
             self.hex((x + self.width - 1) % self.width,
                      (y + self.height + 1) % self.height),
             // DownRight
             self.hex(x, (y + self.height + 1) % self.height),
             // Right
             self.hex((x + self.width + 1) % self.width, y)]
        })
    }

    pub fn cycle(&mut self, rng: &mut Isaac64Rng) {
        if self.spawning {
            self.cycle_spawn(rng);
        }
        self.cycle_cells();
        self.cycle_decisions(rng);
        for _ in 0..FLUID_CYCLES {
            self.cycle_fluids();
        }
        self.cycle_death();
    }

    fn cycle_spawn(&mut self, rng: &mut Isaac64Rng) {
        if rng.next_f64() < SPAWN_RATE {
            let tile = rng.gen_range(0, self.width * self.height);
            if self.tiles[tile].cell.is_none() {
                self.tiles[tile].cell = Some(Cell::new(rng));
            }
        }
    }

    fn cycle_cells(&mut self) {
        for x in 0..self.width {
            for y in 0..self.height {
                let (this, neighbors) = self.hex_and_neighbors(x, y);
                this.decision = if let Some(ref mut this_cell) = this.cell {
                    let neighbor_presents = [neighbors[0].cell.is_some(),
                                             neighbors[1].cell.is_some(),
                                             neighbors[2].cell.is_some(),
                                             neighbors[3].cell.is_some(),
                                             neighbors[4].cell.is_some(),
                                             neighbors[5].cell.is_some()];

                    Some(this_cell.decide([&this.solution.fluids,
                                           &neighbors[0].solution.fluids,
                                           &neighbors[1].solution.fluids,
                                           &neighbors[2].solution.fluids,
                                           &neighbors[3].solution.fluids,
                                           &neighbors[4].solution.fluids,
                                           &neighbors[5].solution.fluids],
                                          &neighbor_presents))
                } else {
                    None
                }
            }
        }
    }

    fn cycle_decisions(&mut self, rng: &mut Isaac64Rng) {
        // Compute the deltas resulting from the decision.
        for x in 0..self.width {
            for y in 0..self.height {
                let (width, height) = (self.width, self.height);
                let (this, neighbors) = self.hex_and_neighbors(x, y);
                // Clear the movements from the previous cycle.
                this.delta.movement_attempts.clear();
                this.delta.mate_attempts.clear();
                this.solution.coefficients = if let Some(ref decision) = this.decision {
                    decision.coefficients
                } else {
                    // Set the diffusion coefficients to the normal values.
                    [0.5, 1.0]
                };

                // Only add movements here if no cell is present.
                if this.cell.is_none() {
                    // Add any neighbor movements to the movement_attempts vector.
                    for (n, &facing) in neighbors.iter().zip(&[Direction::DownLeft,
                                                               Direction::DownRight,
                                                               Direction::Right,
                                                               Direction::UpRight,
                                                               Direction::UpLeft,
                                                               Direction::Left]) {
                        match n.decision {
                            Some(Decision { choice: Choice::Move(direction), .. }) => {
                                // It attempted to move into this hex cell.
                                if facing == direction {
                                    this.delta
                                        .movement_attempts
                                        .push(in_direction(x, y, width, height, facing.flip()));

                                    // No need to continue if we reach 2 attempts.
                                    if this.delta.movement_attempts.len() == 2 {
                                        break;
                                    }
                                }
                            }
                            Some(Decision { choice: Choice::Divide { mate, spawn }, .. }) => {
                                // It attempted to spawn into this hex cell.
                                if facing == spawn {
                                    let source = in_direction(x, y, width, height, facing.flip());;
                                    this.delta
                                        .mate_attempts
                                        .push(Mate {
                                            mate: in_direction(source.0,
                                                               source.1,
                                                               width,
                                                               height,
                                                               mate),
                                            source: source,
                                        });

                                    // No need to continue if we reach 2 attempts.
                                    if this.delta.mate_attempts.len() == 2 {
                                        break;
                                    }
                                }
                            }
                            _ => {}
                        }
                    }
                }
            }
        }

        // Perform the deltas.
        for x in 0..self.width {
            for y in 0..self.height {
                // Handle movement.
                if self.hex(x, y).delta.movement_attempts.len() == 1 {
                    let from_coord = self.hex(x, y).delta.movement_attempts[0];
                    self.hex_mut(x, y).cell = self.hex_mut(from_coord.0, from_coord.1).cell.take();
                    // Handle mating.
                } else if self.hex(x, y).delta.mate_attempts.len() == 1 {
                    let mate = self.hex(x, y).delta.mate_attempts[0].clone();
                    self.hex_mut(x, y).cell = if mate.mate == (x, y) {
                        Some(self.hex(mate.source.0, mate.source.1)
                            .cell
                            .as_ref()
                            .unwrap()
                            .divide(rng))
                    } else {
                        if self.hex(mate.mate.0, mate.mate.1).cell.is_some() {
                            Some(self.hex(mate.source.0, mate.source.1)
                                .cell
                                .as_ref()
                                .unwrap()
                                .mate(&self.hex(mate.mate.0, mate.mate.1)
                                          .cell
                                          .as_ref()
                                          .unwrap(),
                                      rng))
                        } else {
                            None
                        }
                    };
                }

                // Clear the decisions.
                self.hex_mut(x, y).decision = None;
            }
        }
    }

    fn cycle_fluids(&mut self) {
        // Then update diffusion.
        for x in 0..self.width {
            for y in 0..self.height {
                let (this, neighbors) = self.hex_and_neighbors(x, y);

                for n in &neighbors {
                    this.solution.diffuse_from(&n.solution);
                }
            }
        }

        // Finish the cycle.
        for hex in &mut self.tiles {
            hex.solution.end_cycle();
        }
    }

    fn cycle_death(&mut self) {
        // Finish the cycle.
        for hex in &mut self.tiles {
            if hex.cell.is_some() {
                if hex.solution.fluids[0] <= CONSUMPTION {
                    if hex.cell.as_ref().unwrap().inhale != 0 {
                        hex.cell.as_mut().unwrap().inhale -= 1;
                    } else {
                        hex.cell = None;
                    }
                } else {
                    hex.solution.fluids[0] -= CONSUMPTION;
                    if hex.solution.fluids[0] < SURVIVAL_THRESHOLD {
                        if hex.cell.as_ref().unwrap().inhale != 0 {
                            hex.cell.as_mut().unwrap().inhale -= 1;
                        } else {
                            hex.cell = None;
                        }
                    } else {
                        if hex.cell.as_ref().unwrap().inhale < INHALE_CAP {
                            hex.cell.as_mut().unwrap().inhale += 1;
                        }
                    }
                }
            }
        }
    }
}

fn randomizing_vec(width: usize, height: usize, rng: &mut Isaac64Rng) -> Vec<Hex> {
    let seeds = [rng.gen(), rng.gen()];
    let noise = Brownian2::new(perlin2, 4).wavelength(32.0);
    (0..height)
        .cartesian_product((0..width))
        .map(|(x, y)| {
            Hex {
                solution: Solution::new([noise.apply(&seeds[0], &[x as f64, y as f64]), 1.0],
                                        [0.5, 1.0]),
                cell: None,
                decision: None,
                delta: Delta {
                    movement_attempts: Vec::with_capacity(6),
                    mate_attempts: Vec::with_capacity(6),
                },
            }
        })
        .collect_vec()
}

fn in_direction(x: usize,
                y: usize,
                width: usize,
                height: usize,
                direction: Direction)
                -> (usize, usize) {
    let diff = direction.delta(y % 2 == 0);
    (((width + x) as isize + diff.0) as usize % width,
     ((height + y) as isize + diff.1) as usize % height)
}
<|endoftext|>


<commit_before>//! Representations of voice information.

use super::{
    guild::Member,
    id::{ChannelId, GuildId, RoleId, UserId},
    user::User,
};
use chrono::{DateTime, Utc};
use serde::de::{self, Deserialize, Deserializer, Visitor, MapAccess, IgnoredAny};
use std::fmt;

/// Information about an available voice region.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct VoiceRegion {
    /// Whether it is a custom voice region, which is used for events.
    pub custom: bool,
    /// Whether it is a deprecated voice region, which you should avoid using.
    pub deprecated: bool,
    /// The internal Id of the voice region.
    pub id: String,
    /// A recognizable name of the location of the voice region.
    pub name: String,
    /// Whether the voice region is optimal for use by the current user.
    pub optional: bool,
    /// an example hostname.
    pub sample_hostname: String,
    /// An example port.
    pub sample_port: u64,
    /// Indicator of whether the voice region is only for VIP guilds.
    pub vip: bool,
    #[serde(skip)]
    pub(crate) _nonexhaustive: (),
}

/// A user's state within a voice channel.
#[derive(Clone, Serialize)]
pub struct VoiceState {
    pub channel_id: Option<ChannelId>,
    pub deaf: bool,
    pub guild_id: Option<GuildId>,
    pub member: Option<Member>,
    pub mute: bool,
    pub self_deaf: bool,
    pub self_mute: bool,
    pub self_stream: Option<bool>,
    pub self_video: bool,
    pub session_id: String,
    pub suppress: bool,
    pub token: Option<String>,
    pub user_id: UserId,
    #[serde(skip)]
    pub(crate) _nonexhaustive: (),
}

impl fmt::Debug for VoiceState {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("VoiceState")
            .field("channel_id", &self.channel_id)
            .field("deaf", &self.deaf)
            .field("guild_id", &self.guild_id)
            .field("member", &self.member)
            .field("mute", &self.mute)
            .field("self_deaf", &self.self_deaf)
            .field("self_mute", &self.self_mute)
            .field("self_stream", &self.self_stream)
            .field("self_video", &self.self_video)
            .field("session_id", &self.session_id)
            .field("suppress", &self.suppress)
            .field("user_id", &self.user_id)
            .finish()
    }
}

impl<'de> Deserialize<'de> for VoiceState {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {

        #[derive(Deserialize)]
        #[serde(field_identifier, rename_all = "snake_case")]
        enum Field {
            ChannelId,
            Deaf,
            GuildId,
            Member,
            Mute,
            SelfDeaf,
            SelfMute,
            SelfStream,
            SelfVideo,
            SessionId,
            Suppress,
            Token,
            UserId,
        }

        #[derive(Deserialize)]
        struct PartialMember {
            deaf: bool,
            joined_at: Option<DateTime<Utc>>,
            mute: bool,
            nick: Option<String>,
            roles: Vec<RoleId>,
            user: User,
            #[serde(skip)]
            _nonexhaustive: (),
        }

        struct VoiceStateVisitor;

        impl<'de> Visitor<'de> for VoiceStateVisitor {
            type Value = VoiceState;

            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                f.write_str("struct VoiceState")
            }

            fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<Self::Value, V::Error> {
                let mut channel_id = None;
                let mut deaf = None;
                let mut guild_id = None;
                let mut member = None;
                let mut mute = None;
                let mut self_deaf = None;
                let mut self_mute = None;
                let mut self_stream = None;
                let mut self_video = None;
                let mut session_id = None;
                let mut suppress = None;
                let mut token = None;
                let mut user_id = None;

                loop {
                    let key = match map.next_key() {
                        Ok(Some(key)) => key,
                        Ok(None) => break,
                        Err(_) => {
                            map.next_value::<IgnoredAny>()?;
                            continue;
                        }
                    };

                    match key {
                        Field::ChannelId => {
                            if channel_id.is_some() {
                                return Err(de::Error::duplicate_field("channel_id"));
                            }
                            channel_id = map.next_value()?;
                        }
                        Field::Deaf => {
                            if deaf.is_some() {
                                return Err(de::Error::duplicate_field("deaf"));
                            }
                            deaf = Some(map.next_value()?);
                        }
                        Field::GuildId => {
                            if guild_id.is_some() {
                                return Err(de::Error::duplicate_field("guild_id"));
                            }
                            guild_id = map.next_value()?;
                        }
                        Field::Member => {
                            if member.is_some() {
                                return Err(de::Error::duplicate_field("member"));
                            }

                            let partial_member: Option<PartialMember> = map.next_value()?;
                            if let Some(partial_member) = partial_member {
                                member = Some(Member {
                                    deaf: partial_member.deaf,
                                    guild_id: GuildId(0),
                                    joined_at: partial_member.joined_at,
                                    mute: partial_member.mute,
                                    nick: partial_member.nick,
                                    roles: partial_member.roles,
                                    user: partial_member.user,
                                    _nonexhaustive: ()
                                });
                            }
                        }
                        Field::Mute => {
                            if mute.is_some() {
                                return Err(de::Error::duplicate_field("mute"));
                            }
                            mute = Some(map.next_value()?);
                        }
                        Field::SelfDeaf => {
                            if self_deaf.is_some() {
                                return Err(de::Error::duplicate_field("self_deaf"));
                            }
                            self_deaf = Some(map.next_value()?);
                        }
                        Field::SelfMute => {
                            if self_mute.is_some() {
                                return Err(de::Error::duplicate_field("self_mute"));
                            }
                            self_mute = Some(map.next_value()?);
                        }
                        Field::SelfStream => {
                            if self_stream.is_some() {
                                return Err(de::Error::duplicate_field("self_stream"));
                            }
                            self_stream = map.next_value()?;
                        }
                        Field::SelfVideo => {
                            if self_video.is_some() {
                                return Err(de::Error::duplicate_field("self_video"));
                            }
                            self_video = Some(map.next_value()?);
                        }
                        Field::SessionId => {
                            if session_id.is_some() {
                                return Err(de::Error::duplicate_field("session_id"));
                            }
                            session_id = Some(map.next_value()?);
                        }
                        Field::Suppress => {
                            if suppress.is_some() {
                                return Err(de::Error::duplicate_field("suppress"));
                            }
                            suppress = Some(map.next_value()?);
                        }
                        Field::Token => {
                            if token.is_some() {
                                return Err(de::Error::duplicate_field("token"));
                            }
                            token = map.next_value()?;
                        }
                        Field::UserId => {
                            if user_id.is_some() {
                                return Err(de::Error::duplicate_field("user_id"));
                            }
                            user_id = Some(map.next_value()?);
                        }
                    }
                }

                let deaf = deaf.ok_or_else(|| de::Error::missing_field("deaf"))?;
                let mute = mute.ok_or_else(|| de::Error::missing_field("mute"))?;
                let self_deaf = self_deaf.ok_or_else(|| de::Error::missing_field("self_deaf"))?;
                let self_mute = self_mute.ok_or_else(|| de::Error::missing_field("self_mute"))?;
                let self_video = self_video.ok_or_else(|| de::Error::missing_field("self_video"))?;
                let session_id = session_id.ok_or_else(|| de::Error::missing_field("session_id"))?;
                let suppress = suppress.ok_or_else(|| de::Error::missing_field("suppress"))?;
                let user_id = user_id.ok_or_else(|| de::Error::missing_field("user_id"))?;

                if let (Some(guild_id), Some( member)) = (guild_id, member.as_mut()) {
                    member.guild_id = guild_id;
                }

                Ok(VoiceState {
                    channel_id,
                    deaf,
                    guild_id,
                    member,
                    mute,
                    self_deaf,
                    self_mute,
                    self_stream,
                    self_video,
                    session_id,
                    suppress,
                    token,
                    user_id,
                    _nonexhaustive: ()
                })
            }
        }

        const FIELDS: &[&str] = &[
            "channel_id",
            "deaf",
            "guild_id",
            "member",
            "mute",
            "self_deaf",
            "self_mute",
            "self_stream",
            "self_video",
            "session_id",
            "suppress",
            "token",
            "user_id",
        ];

        deserializer.deserialize_struct("VoiceState", FIELDS, VoiceStateVisitor)
    }
}
<commit_msg>Fix voice region deserialization (#1107)<commit_after>//! Representations of voice information.

use super::{
    guild::Member,
    id::{ChannelId, GuildId, RoleId, UserId},
    user::User,
};
use chrono::{DateTime, Utc};
use serde::de::{self, Deserialize, Deserializer, Visitor, MapAccess, IgnoredAny};
use std::fmt;

/// Information about an available voice region.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct VoiceRegion {
    /// Whether it is a custom voice region, which is used for events.
    pub custom: bool,
    /// Whether it is a deprecated voice region, which you should avoid using.
    pub deprecated: bool,
    /// The internal Id of the voice region.
    pub id: String,
    /// A recognizable name of the location of the voice region.
    pub name: String,
    /// Whether the voice region is optimal for use by the current user.
    pub optimal: bool,
    /// Indicator of whether the voice region is only for VIP guilds.
    pub vip: bool,
    #[serde(skip)]
    pub(crate) _nonexhaustive: (),
}

/// A user's state within a voice channel.
#[derive(Clone, Serialize)]
pub struct VoiceState {
    pub channel_id: Option<ChannelId>,
    pub deaf: bool,
    pub guild_id: Option<GuildId>,
    pub member: Option<Member>,
    pub mute: bool,
    pub self_deaf: bool,
    pub self_mute: bool,
    pub self_stream: Option<bool>,
    pub self_video: bool,
    pub session_id: String,
    pub suppress: bool,
    pub token: Option<String>,
    pub user_id: UserId,
    #[serde(skip)]
    pub(crate) _nonexhaustive: (),
}

impl fmt::Debug for VoiceState {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("VoiceState")
            .field("channel_id", &self.channel_id)
            .field("deaf", &self.deaf)
            .field("guild_id", &self.guild_id)
            .field("member", &self.member)
            .field("mute", &self.mute)
            .field("self_deaf", &self.self_deaf)
            .field("self_mute", &self.self_mute)
            .field("self_stream", &self.self_stream)
            .field("self_video", &self.self_video)
            .field("session_id", &self.session_id)
            .field("suppress", &self.suppress)
            .field("user_id", &self.user_id)
            .finish()
    }
}

impl<'de> Deserialize<'de> for VoiceState {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {

        #[derive(Deserialize)]
        #[serde(field_identifier, rename_all = "snake_case")]
        enum Field {
            ChannelId,
            Deaf,
            GuildId,
            Member,
            Mute,
            SelfDeaf,
            SelfMute,
            SelfStream,
            SelfVideo,
            SessionId,
            Suppress,
            Token,
            UserId,
        }

        #[derive(Deserialize)]
        struct PartialMember {
            deaf: bool,
            joined_at: Option<DateTime<Utc>>,
            mute: bool,
            nick: Option<String>,
            roles: Vec<RoleId>,
            user: User,
            #[serde(skip)]
            _nonexhaustive: (),
        }

        struct VoiceStateVisitor;

        impl<'de> Visitor<'de> for VoiceStateVisitor {
            type Value = VoiceState;

            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                f.write_str("struct VoiceState")
            }

            fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<Self::Value, V::Error> {
                let mut channel_id = None;
                let mut deaf = None;
                let mut guild_id = None;
                let mut member = None;
                let mut mute = None;
                let mut self_deaf = None;
                let mut self_mute = None;
                let mut self_stream = None;
                let mut self_video = None;
                let mut session_id = None;
                let mut suppress = None;
                let mut token = None;
                let mut user_id = None;

                loop {
                    let key = match map.next_key() {
                        Ok(Some(key)) => key,
                        Ok(None) => break,
                        Err(_) => {
                            map.next_value::<IgnoredAny>()?;
                            continue;
                        }
                    };

                    match key {
                        Field::ChannelId => {
                            if channel_id.is_some() {
                                return Err(de::Error::duplicate_field("channel_id"));
                            }
                            channel_id = map.next_value()?;
                        }
                        Field::Deaf => {
                            if deaf.is_some() {
                                return Err(de::Error::duplicate_field("deaf"));
                            }
                            deaf = Some(map.next_value()?);
                        }
                        Field::GuildId => {
                            if guild_id.is_some() {
                                return Err(de::Error::duplicate_field("guild_id"));
                            }
                            guild_id = map.next_value()?;
                        }
                        Field::Member => {
                            if member.is_some() {
                                return Err(de::Error::duplicate_field("member"));
                            }

                            let partial_member: Option<PartialMember> = map.next_value()?;
                            if let Some(partial_member) = partial_member {
                                member = Some(Member {
                                    deaf: partial_member.deaf,
                                    guild_id: GuildId(0),
                                    joined_at: partial_member.joined_at,
                                    mute: partial_member.mute,
                                    nick: partial_member.nick,
                                    roles: partial_member.roles,
                                    user: partial_member.user,
                                    _nonexhaustive: ()
                                });
                            }
                        }
                        Field::Mute => {
                            if mute.is_some() {
                                return Err(de::Error::duplicate_field("mute"));
                            }
                            mute = Some(map.next_value()?);
                        }
                        Field::SelfDeaf => {
                            if self_deaf.is_some() {
                                return Err(de::Error::duplicate_field("self_deaf"));
                            }
                            self_deaf = Some(map.next_value()?);
                        }
                        Field::SelfMute => {
                            if self_mute.is_some() {
                                return Err(de::Error::duplicate_field("self_mute"));
                            }
                            self_mute = Some(map.next_value()?);
                        }
                        Field::SelfStream => {
                            if self_stream.is_some() {
                                return Err(de::Error::duplicate_field("self_stream"));
                            }
                            self_stream = map.next_value()?;
                        }
                        Field::SelfVideo => {
                            if self_video.is_some() {
                                return Err(de::Error::duplicate_field("self_video"));
                            }
                            self_video = Some(map.next_value()?);
                        }
                        Field::SessionId => {
                            if session_id.is_some() {
                                return Err(de::Error::duplicate_field("session_id"));
                            }
                            session_id = Some(map.next_value()?);
                        }
                        Field::Suppress => {
                            if suppress.is_some() {
                                return Err(de::Error::duplicate_field("suppress"));
                            }
                            suppress = Some(map.next_value()?);
                        }
                        Field::Token => {
                            if token.is_some() {
                                return Err(de::Error::duplicate_field("token"));
                            }
                            token = map.next_value()?;
                        }
                        Field::UserId => {
                            if user_id.is_some() {
                                return Err(de::Error::duplicate_field("user_id"));
                            }
                            user_id = Some(map.next_value()?);
                        }
                    }
                }

                let deaf = deaf.ok_or_else(|| de::Error::missing_field("deaf"))?;
                let mute = mute.ok_or_else(|| de::Error::missing_field("mute"))?;
                let self_deaf = self_deaf.ok_or_else(|| de::Error::missing_field("self_deaf"))?;
                let self_mute = self_mute.ok_or_else(|| de::Error::missing_field("self_mute"))?;
                let self_video = self_video.ok_or_else(|| de::Error::missing_field("self_video"))?;
                let session_id = session_id.ok_or_else(|| de::Error::missing_field("session_id"))?;
                let suppress = suppress.ok_or_else(|| de::Error::missing_field("suppress"))?;
                let user_id = user_id.ok_or_else(|| de::Error::missing_field("user_id"))?;

                if let (Some(guild_id), Some( member)) = (guild_id, member.as_mut()) {
                    member.guild_id = guild_id;
                }

                Ok(VoiceState {
                    channel_id,
                    deaf,
                    guild_id,
                    member,
                    mute,
                    self_deaf,
                    self_mute,
                    self_stream,
                    self_video,
                    session_id,
                    suppress,
                    token,
                    user_id,
                    _nonexhaustive: ()
                })
            }
        }

        const FIELDS: &[&str] = &[
            "channel_id",
            "deaf",
            "guild_id",
            "member",
            "mute",
            "self_deaf",
            "self_mute",
            "self_stream",
            "self_video",
            "session_id",
            "suppress",
            "token",
            "user_id",
        ];

        deserializer.deserialize_struct("VoiceState", FIELDS, VoiceStateVisitor)
    }
}
<|endoftext|>


<commit_before>use error::*;
use futures::future;
use futures::{Future, Stream};
use hyper;
use hyper::client::HttpConnector;
use hyper::header::Location;
use hyper::header::*;
use hyper::server::Service;
use hyper::{Request, Response};
use hyper::{StatusCode, Uri};
use hyper_tls::HttpsConnector;
use mime;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use tokio_core::reactor::Handle;

pub fn https_connector(handle: &Handle) -> HttpsConnector<HttpConnector> {
    HttpsConnector::new(4, handle).expect("Could not create an https connector")
}

pub trait ResponseExt {
    fn with_body<T: Into<hyper::Body>>(self, body: T) -> Self;
    fn with_file_unsafe(self, path: &Path) -> Self;
    fn with_header<T: Header>(self, header: T) -> Self;
    fn filename(&self) -> Option<String>;
    fn get_body_bytes(self) -> Box<Future<Item = Vec<u8>, Error = Error>>;
    /// Try to populate a vector with the contents of the response body, but stop after `limit`
    /// bytes with an error.
    fn get_body_bytes_with_limit(self, limit: u32) -> Box<Future<Item = Vec<u8>, Error = Error>>;
}

impl ResponseExt for Response {
    fn with_body<T: Into<hyper::Body>>(self, body: T) -> Self {
        let mut res = self;
        res.set_body(body.into());
        res
    }

    fn with_file_unsafe(self, path: &Path) -> Self {
        let mut body: Vec<u8> = Vec::new();
        let filename = path
            .file_name()
            .unwrap()
            .to_string_lossy()
            .into_owned()
            .into_bytes();
        let mut file = File::open(&path).expect(&format!("could not open file {:?}", &path));
        file.read_to_end(&mut body)
            .expect(&format!("could not read file {:?}", &path));
        self.with_body(body).with_header(ContentDisposition {
            disposition: DispositionType::Attachment,
            parameters: vec![DispositionParam::Filename(
                Charset::Ext("utf-8".to_string()),
                None,
                filename,
            )],
        })
    }

    fn with_header<T: Header>(mut self, header: T) -> Self {
        {
            let h = self.headers_mut();
            h.set(header);
        }
        self
    }

    fn get_body_bytes_with_limit(self, limit: u32) -> Box<Future<Item = Vec<u8>, Error = Error>> {
        Box::new(
            self.body()
                .from_err()
                .fold(Vec::<u8>::new(), move |mut acc, chunk| {
                    if (acc.len() + chunk.len()) > limit as usize {
                        return future::err(
                            ErrorKind::UnprocessableEntity("too big".to_string()).into(),
                        );
                    }

                    acc.extend_from_slice(&chunk);
                    future::ok::<_, Error>(acc)
                }),
        )
    }

    fn filename(&self) -> Option<String> {
        match self.headers().get::<ContentDisposition>() {
            Some(&ContentDisposition {
                parameters: ref params,
                ..
            }) => params
                .iter()
                .find(|param| match **param {
                    DispositionParam::Filename(_, _, _) => true,
                    _ => false,
                })
                .and_then(|param| {
                    if let DispositionParam::Filename(_, _, ref bytes) = *param {
                        String::from_utf8(bytes.to_owned()).ok()
                    } else {
                        None
                    }
                }),
            _ => None,
        }
    }

    fn get_body_bytes(self) -> Box<Future<Item = Vec<u8>, Error = Error>> {
        Box::new(
            self.body()
                .map_err(Error::from)
                .fold(Vec::new(), |mut acc, chunk| {
                    acc.extend_from_slice(&chunk);
                    future::ok::<_, Error>(acc)
                }),
        )
    }
}

pub trait RequestExt {
    fn get_body_bytes(self) -> Box<Future<Item = Vec<u8>, Error = Error>>;
    fn has_content_type(&self, mime: mime::Mime) -> bool;
    fn with_header<T: Header>(self, header: T) -> Self;
    fn with_body(self, body: Vec<u8>) -> Self;
}

impl RequestExt for Request {
    fn get_body_bytes(self) -> Box<Future<Item = Vec<u8>, Error = Error>> {
        Box::new(
            self.body()
                .map_err(Error::from)
                .fold(Vec::new(), |mut acc, chunk| {
                    acc.extend_from_slice(&chunk);
                    future::ok::<_, Error>(acc)
                }),
        )
    }

    fn has_content_type(&self, mime: mime::Mime) -> bool {
        let content_type = self.headers().get::<ContentType>().cloned();
        if let Some(content_type) = content_type {
            content_type.type_() == mime.type_() && content_type.subtype() == mime.subtype()
        } else {
            false
        }
    }

    fn with_header<T: Header>(mut self, header: T) -> Self {
        {
            let h = self.headers_mut();
            h.set(header);
        }
        self
    }

    fn with_body(self, body: Vec<u8>) -> Self {
        let mut req = self;
        let len = body.len();
        req.set_body(hyper::Body::from(body));
        req.with_header(ContentLength(len as u64))
    }
}

pub trait ClientExt {
    fn get_follow_redirect(self, uri: &Uri) -> Box<Future<Item = Response, Error = Error>>;
}

impl<S> ClientExt for S
where
    S: Service<Request = Request, Response = Response, Error = hyper::Error> + 'static,
{
    fn get_follow_redirect(self, uri: &Uri) -> Box<Future<Item = Response, Error = Error>> {
        Box::new(future::loop_fn(uri.clone(), move |uri| {
            let request = Request::new(hyper::Method::Get, uri);
            self.call(request).map_err(Error::from).and_then(|res| {
                match determine_get_result(res) {
                    Ok(GetResult::Redirect(redirect_uri)) => {
                        Ok(future::Loop::Continue(redirect_uri))
                    }
                    Ok(GetResult::Ok(res)) => Ok(future::Loop::Break(res)),
                    Err(err) => Err(err),
                }
            })
        }))
    }
}

enum GetResult {
    Ok(Response),
    Redirect(Uri),
}

fn determine_get_result(res: Response) -> Result<GetResult> {
    match res.status() {
        StatusCode::TemporaryRedirect | StatusCode::PermanentRedirect => {
            match res.headers().get::<Location>() {
                Some(location) => Ok(GetResult::Redirect(location.parse()?)),
                None => Err("Redirect without Location header".into()),
            }
        }
        _ => Ok(GetResult::Ok(res)),
    }
}

pub fn extract_filename_from_uri(uri: &Uri) -> Option<String> {
    match uri.path().split('/').last() {
        Some(name) if !name.is_empty() => Some(name.to_string()),
        _ => None,
    }
}

#[cfg(test)]
mod extract_filename_tests {
    use super::extract_filename_from_uri;
    use hyper::Uri;

    #[test]
    fn test_extract_filename_from_uri_works() {
        let assert_extracted = |input: &'static str, expected_output: Option<&'static str>| {
            let uri = input.parse::<Uri>().unwrap();
            assert_eq!(
                extract_filename_from_uri(&uri),
                expected_output.map(|o| o.to_string())
            );
        };

        assert_extracted("/logo.png", Some("logo.png"));
        assert_extracted("/assets/", None);
        assert_extracted("/assets/icon", Some("icon"));
        assert_extracted("/", None);
        assert_extracted("http://www.store2be.com", None);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use futures::future;
    use hyper;
    use hyper::server::Service;

    #[derive(Debug, Clone)]
    struct MockServer {
        response_to_logo_png: hyper::header::ContentDisposition,
    }

    impl MockServer {
        fn respond_to_logo_png_with(
            content_disposition: hyper::header::ContentDisposition,
        ) -> MockServer {
            MockServer {
                response_to_logo_png: content_disposition,
            }
        }
    }

    impl Service for MockServer {
        type Request = hyper::Request;
        type Response = hyper::Response;
        type Error = hyper::Error;
        type Future = Box<Future<Item = hyper::Response, Error = hyper::Error>>;

        fn call(&self, req: Self::Request) -> Self::Future {
            let res = match req.path() {
                "/assets/logo.png" => hyper::Response::new()
                    .with_body(b"54321" as &[u8])
                    .with_header(self.response_to_logo_png.clone()),
                _ => hyper::Response::new().with_status(hyper::StatusCode::NotFound),
            };
            Box::new(future::ok(res))
        }
    }

    #[test]
    fn test_filename_prefers_content_disposition() {
        let response_header = hyper::header::ContentDisposition {
            disposition: hyper::header::DispositionType::Attachment,
            parameters: vec![hyper::header::DispositionParam::Filename(
                hyper::header::Charset::Ext("UTF-8".to_string()),
                None,
                b"this_should_be_the_filename.png".to_vec(),
            )],
        };
        let server = MockServer::respond_to_logo_png_with(response_header);

        let request: hyper::client::Request<hyper::Body> = Request::new(
            hyper::Method::Get,
            "http://127.0.0.1:8738/assets/logo.png".parse().unwrap(),
        );

        let response = server.call(request).wait().unwrap();
        assert_eq!(
            response.filename(),
            Some("this_should_be_the_filename.png".to_string())
        )
    }

    #[test]
    fn test_filename_works_with_content_disposition_inline() {
        let response_header = hyper::header::ContentDisposition {
            disposition: hyper::header::DispositionType::Inline,
            parameters: vec![hyper::header::DispositionParam::Filename(
                hyper::header::Charset::Ext("UTF-8".to_string()),
                None,
                b"this_should_be_the_filename.png".to_vec(),
            )],
        };

        let server = MockServer::respond_to_logo_png_with(response_header);

        let request: hyper::client::Request<hyper::Body> = Request::new(
            hyper::Method::Get,
            "http://127.0.0.1:8738/assets/logo.png".parse().unwrap(),
        );

        let response = server.call(request).wait().unwrap();
        assert_eq!(
            response.filename(),
            Some("this_should_be_the_filename.png".to_string())
        )
    }

    // S3 returns Content-Disposition without disposition (just filename)
    #[test]
    fn test_content_disposition_works_without_disposition() {
        let server = MockServer::respond_to_logo_png_with(hyper::header::ContentDisposition {
            disposition: hyper::header::DispositionType::Ext("".to_string()),
            parameters: vec![hyper::header::DispositionParam::Filename(
                hyper::header::Charset::Ext("UTF-8".to_string()),
                None,
                b"this_should_be_the_filename.png".to_vec(),
            )],
        });

        let request: hyper::client::Request<hyper::Body> = Request::new(
            hyper::Method::Get,
            "http://127.0.0.1:8740/assets/logo.png".parse().unwrap(),
        );

        let response = server.call(request).wait().unwrap();
        assert_eq!(
            response.filename(),
            Some("this_should_be_the_filename.png".to_string())
        )
    }

    struct MockFileServer;

    impl Service for MockFileServer {
        type Request = hyper::Request;
        type Response = hyper::Response;
        type Error = hyper::Error;
        type Future = Box<Future<Item = hyper::Response, Error = hyper::Error>>;

        fn call(&self, _: Self::Request) -> Self::Future {
            let mut response_body: Vec<u8> = Vec::with_capacity(3000);

            for n in 0..3000 {
                response_body.push((n / 250) as u8);
            }

            let res = hyper::Response::new().with_body(response_body);
            Box::new(future::ok(res))
        }
    }

    #[test]
    fn test_get_body_bytes_with_limit_is_enforced() {
        let request = Request::new(hyper::Method::Get, "/".parse().unwrap());
        let response = MockFileServer.call(request).wait().unwrap();
        let result = response.get_body_bytes_with_limit(2000).wait();
        match result {
            Err(Error(ErrorKind::UnprocessableEntity(_), _)) => (),
            other => panic!("Wrong result to get_body_bytes_max_size: {:?}", other),
        }
    }

    #[test]
    fn test_get_body_bytes_with_limit_is_not_excessively_zealous() {
        let request = Request::new(hyper::Method::Get, "/".parse().unwrap());
        let response = MockFileServer.call(request).wait().unwrap();
        let result = response.get_body_bytes_with_limit(3000).wait();
        match result {
            Ok(_) => (),
            other => panic!("Wrong result to get_body_bytes_max_size: {:?}", other),
        }
    }
}
<commit_msg>🔥 Delete more dead code<commit_after><|endoftext|>