<commit_before>// Copyright (c) 2017-2019 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 PWM peripheral.
//!
//! RPPAL controls the Raspberry Pi's PWM peripheral through the `/sys/class/pwm` sysfs
//! interface.
//!
//! ## PWM channels
//!
//! The BCM283x SoC supports two hardware PWM channels. By default, both channels
//! are disabled. To enable only PWM0 on its default pin (BCM GPIO 18, physical pin 12),
//! add `dtoverlay=pwm` to `/boot/config.txt`. If you need both PWM channels, replace
//! `pwm` with `pwm-2chan`, which enables PWM0 on BCM GPIO 18 (physical pin 12), and PWM1
//! on BCM GPIO 19 (physical pin 35). More details on enabling and configuring PWM on
//! other GPIO pins than the default ones can be found in `/boot/overlays/README`.
//!
//! The Raspberry Pi's analog audio output needs both PWM channels. Using both audio out
//! and PWM at the same time may cause issues.
//!
//! Some of the GPIO pins capable of supporting hardware PWM can also be configured for
//! use with other peripherals. Be careful not to enable two peripherals on the same pin
//! at the same time.
//!
//! ## Using PWM without superuser privileges (`sudo`)
//!
//! As of kernel version 4.14.34, released on April 16 2018, it's possible to
//! configure your Raspberry Pi to allow non-root access to PWM. 4.14.34 includes
//! a [patch] that allows udev to change file permissions when a
//! PWM channel is exported. This will let any user that's a member of the `gpio`
//! group configure PWM without having to use `sudo`.
//!
//! The udev rules needed to make this work haven't been patched in yet as of
//! June 2018, but you can easily add them yourself. Make sure you're running
//! 4.14.34 or later, and append the following snippet to
//! `/etc/udev/rules.d/99-com.rules`. Reboot the Raspberry Pi afterwards.
//!
//! ```text
//! SUBSYSTEM=="pwm*", PROGRAM="/bin/sh -c '\
//!     chown -R root:gpio /sys/class/pwm && chmod -R 770 /sys/class/pwm;\
//!     chown -R root:gpio /sys/devices/platform/soc/*.pwm/pwm/pwmchip* &&\
//!     chmod -R 770 /sys/devices/platform/soc/*.pwm/pwm/pwmchip*\
//! '"
//! ```
//!
//! ## Troubleshooting
//!
//! ### Permission denied
//!
//! If [`new`] returns an `io::ErrorKind::PermissionDenied`
//! error, make sure `/sys/class/pwm` and all of its subdirectories
//! are owned by `root:gpio`, the current user is a member of the `gpio` group
//! and udev is properly configured as mentioned above. Alternatively, you can
//! launch your application using `sudo`.
//!
//! ### Not found
//!
//! If [`new`] returns an `io::ErrorKind::NotFound` error, you may have
//! forgotten to enable the selected PWM channel. The configuration options
//! to enable either of the two PWM channels are listed above.
//!
//! [patch]: https://github.com/raspberrypi/linux/issues/1983
//! [`new`]: struct.Pwm.html#method.new

use std::error;
use std::fmt;
use std::io;
use std::result;
use std::time::Duration;

mod sysfs;

/// Errors that can occur when accessing the PWM peripheral.
#[derive(Debug)]
pub enum Error {
    /// IO error.
    Io(io::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Error::Io(ref err) => write!(f, "IO error: {}", err),
        }
    }
}

impl error::Error for Error {}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

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

/// PWM channels.
///
/// More information on enabling and configuring the PWM channels can be
/// found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Channel {
    Pwm0 = 0,
    Pwm1 = 1,
}

/// Output polarities.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Polarity {
    Normal,
    Inverse,
}

/// Provides access to the Raspberry Pi's PWM peripheral.
///
/// Before using `Pwm`, make sure the selected PWM channel has been configured
/// and activated. More information can be found [here].
///
/// [here]: index.html
pub struct Pwm {
    channel: Channel,
}

impl Pwm {
    /// Constructs a new `Pwm`.
    pub fn new(channel: Channel) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        let _ = pwm.disable();

        // Default settings
        let _ = pwm.set_pulse_width(Duration::from_secs(0));
        let _ = pwm.set_period(Duration::from_secs(0));
        let _ = pwm.set_polarity(Polarity::Normal);

        Ok(pwm)
    }

    /// Constructs a new `Pwm` using the specified settings.
    ///
    /// `period` represents the time it takes for the PWM channel to complete one cycle.
    ///
    /// `pulse_width` represents the amount of time the PWM channel's logic level is set
    /// high (or low if the polarity is set to [`Inverse`]) during a single period.
    ///
    /// `polarity` configures the active logic level as either high ([`Normal`]) or low ([`Inverse`]).
    ///
    /// `enabled` immediately enables PWM on the selected channel.
    ///
    /// This method will fail if `period` is shorter than `duty_cycle`.
    ///
    /// [`Normal`]: enum.Polarity.html
    /// [`Inverse`]: enum.Polarity.html
    pub fn with_period(
        channel: Channel,
        period: Duration,
        pulse_width: Duration,
        polarity: Polarity,
        enabled: bool,
    ) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs pwm interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        let _ = pwm.disable();

        // Set pulse width to 0 first in case the new period is shorter than the current pulse width
        let _ = sysfs::set_pulse_width(channel as u8, 0);

        pwm.set_period(period)?;
        pwm.set_pulse_width(pulse_width)?;
        pwm.set_polarity(polarity)?;
        if enabled {
            pwm.enable()?;
        }

        Ok(pwm)
    }

    /// Constructs a new `Pwm` using the specified settings.
    ///
    /// `with_frequency` is a convenience method that converts `frequency` to a period,
    /// and calculates the duty cycle as a percentage of the frequency.
    ///
    /// `frequency` is specified in herz (Hz).
    ///
    /// `duty_cycle` is specified as a floating point percentage, where `1.0` represents 100%.
    ///
    /// `polarity` configures the active logic level as either high ([`Normal`]) or low ([`Inverse`]).
    ///
    /// `enabled` immediately enables PWM on the selected channel.
    ///
    /// [`Normal`]: enum.Polarity.html
    /// [`Inverse`]: enum.Polarity.html
    pub fn with_frequency(
        channel: Channel,
        frequency: f64,
        duty_cycle: f64,
        polarity: Polarity,
        enabled: bool,
    ) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs pwm interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        let _ = pwm.disable();

        // Set pulse width to 0 first in case the new period is shorter than the current pulse width
        let _ = sysfs::set_pulse_width(channel as u8, 0);

        // Convert to nanoseconds
        let period = (1.0f64 / frequency) * 1_000_000_000f64;
        let pulse_width = period * duty_cycle.min(1.0);

        sysfs::set_period(channel as u8, period as u64)?;
        sysfs::set_pulse_width(channel as u8, pulse_width as u64)?;

        pwm.set_polarity(polarity)?;
        if enabled {
            pwm.enable()?;
        }

        Ok(pwm)
    }

    /// Gets the configured period.
    pub fn period(&self) -> Result<Duration> {
        Ok(Duration::from_nanos(sysfs::period(self.channel as u8)?))
    }

    /// Sets the period.
    ///
    /// `period` represents the time it takes for the PWM channel to complete one cycle.
    /// The specified period must be longer than or equal to the pulse width.
    pub fn set_period(&self, period: Duration) -> Result<()> {
        sysfs::set_period(
            self.channel as u8,
            u64::from(period.subsec_nanos())
                .saturating_add(period.as_secs().saturating_mul(1_000_000_000)),
        )?;

        Ok(())
    }

    /// Gets the configured pulse width.
    pub fn pulse_width(&self) -> Result<Duration> {
        Ok(Duration::from_nanos(sysfs::pulse_width(
            self.channel as u8,
        )?))
    }

    /// Sets the pulse width.
    ///
    /// `pulse_width` represents the amount of time the PWM channel's logic level is set
    /// high (or low if the polarity is set to [`Inverse`]) during a single period. The
    /// pulse width must be shorter than or equal to the period.
    ///
    /// [`Inverse`]: enum.Polarity.html
    pub fn set_pulse_width(&self, pulse_width: Duration) -> Result<()> {
        sysfs::set_pulse_width(
            self.channel as u8,
            u64::from(pulse_width.subsec_nanos())
                .saturating_add(pulse_width.as_secs().saturating_mul(1_000_000_000)),
        )?;

        Ok(())
    }

    /// Sets the frequency and duty cycle.
    ///
    /// `set_frequency` is a convenience method that converts `frequency` to a period,
    /// and calculates the duty cycle as a percentage of the frequency.
    ///
    /// `frequency` is specified in herz (Hz).
    ///
    /// `duty_cycle` is specified as a floating point percentage, where `1.0` represents 100%.
    pub fn set_frequency(&self, frequency: f64, duty_cycle: f64) -> Result<()> {
        // Set duty cycle to 0 first in case the new period is shorter than the current duty cycle
        let _ = sysfs::set_pulse_width(self.channel as u8, 0);

        // Convert to nanoseconds
        let period = (1.0f64 / frequency) * 1_000_000_000f64;
        let pulse_width = period * duty_cycle.min(1.0);

        sysfs::set_period(self.channel as u8, period as u64)?;
        sysfs::set_pulse_width(self.channel as u8, pulse_width as u64)?;

        Ok(())
    }

    /// Gets the configured polarity.
    pub fn polarity(&self) -> Result<Polarity> {
        Ok(sysfs::polarity(self.channel as u8)?)
    }

    /// Sets the polarity.
    ///
    /// `polarity` configures the active logic level as either high ([`Normal`]) or low ([`Inverse`]).
    ///
    /// By default, `polarity` is set to [`Normal`].
    ///
    /// [`Normal`]: enum.Polarity.html
    /// [`Inverse`]: enum.Polarity.html
    pub fn set_polarity(&self, polarity: Polarity) -> Result<()> {
        sysfs::set_polarity(self.channel as u8, polarity)?;

        Ok(())
    }

    /// Checks whether PWM is currently enabled on the selected channel.
    pub fn enabled(&self) -> Result<bool> {
        Ok(sysfs::enabled(self.channel as u8)?)
    }

    /// Enables PWM on the selected channel.
    pub fn enable(&self) -> Result<()> {
        sysfs::set_enabled(self.channel as u8, true)?;

        Ok(())
    }

    /// Disables PWM on the selected channel.
    pub fn disable(&self) -> Result<()> {
        sysfs::set_enabled(self.channel as u8, false)?;

        Ok(())
    }
}

impl Drop for Pwm {
    fn drop(&mut self) {
        let _ = sysfs::set_enabled(self.channel as u8, false);
        let _ = sysfs::unexport(self.channel as u8);
    }
}
<commit_msg>Add duty_cycle()/set_duty_cycle() methods<commit_after>// Copyright (c) 2017-2019 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 PWM peripheral.
//!
//! RPPAL controls the Raspberry Pi's PWM peripheral through the `/sys/class/pwm` sysfs
//! interface.
//!
//! ## PWM channels
//!
//! The BCM283x SoC supports two hardware PWM channels. By default, both channels
//! are disabled. To enable only PWM0 on its default pin (BCM GPIO 18, physical pin 12),
//! add `dtoverlay=pwm` to `/boot/config.txt`. If you need both PWM channels, replace
//! `pwm` with `pwm-2chan`, which enables PWM0 on BCM GPIO 18 (physical pin 12), and PWM1
//! on BCM GPIO 19 (physical pin 35). More details on enabling and configuring PWM on
//! other GPIO pins than the default ones can be found in `/boot/overlays/README`.
//!
//! The Raspberry Pi's analog audio output needs both PWM channels. Using both audio out
//! and PWM at the same time may cause issues.
//!
//! Some of the GPIO pins capable of supporting hardware PWM can also be configured for
//! use with other peripherals. Be careful not to enable two peripherals on the same pin
//! at the same time.
//!
//! ## Using PWM without superuser privileges (`sudo`)
//!
//! As of kernel version 4.14.34, released on April 16 2018, it's possible to
//! configure your Raspberry Pi to allow non-root access to PWM. 4.14.34 includes
//! a [patch] that allows udev to change file permissions when a
//! PWM channel is exported. This will let any user that's a member of the `gpio`
//! group configure PWM without having to use `sudo`.
//!
//! The udev rules needed to make this work haven't been patched in yet as of
//! June 2018, but you can easily add them yourself. Make sure you're running
//! 4.14.34 or later, and append the following snippet to
//! `/etc/udev/rules.d/99-com.rules`. Reboot the Raspberry Pi afterwards.
//!
//! ```text
//! SUBSYSTEM=="pwm*", PROGRAM="/bin/sh -c '\
//!     chown -R root:gpio /sys/class/pwm && chmod -R 770 /sys/class/pwm;\
//!     chown -R root:gpio /sys/devices/platform/soc/*.pwm/pwm/pwmchip* &&\
//!     chmod -R 770 /sys/devices/platform/soc/*.pwm/pwm/pwmchip*\
//! '"
//! ```
//!
//! ## Troubleshooting
//!
//! ### Permission denied
//!
//! If [`new`] returns an `io::ErrorKind::PermissionDenied`
//! error, make sure `/sys/class/pwm` and all of its subdirectories
//! are owned by `root:gpio`, the current user is a member of the `gpio` group
//! and udev is properly configured as mentioned above. Alternatively, you can
//! launch your application using `sudo`.
//!
//! ### Not found
//!
//! If [`new`] returns an `io::ErrorKind::NotFound` error, you may have
//! forgotten to enable the selected PWM channel. The configuration options
//! to enable either of the two PWM channels are listed above.
//!
//! [patch]: https://github.com/raspberrypi/linux/issues/1983
//! [`new`]: struct.Pwm.html#method.new

use std::error;
use std::fmt;
use std::io;
use std::result;
use std::time::Duration;

mod sysfs;

/// Errors that can occur when accessing the PWM peripheral.
#[derive(Debug)]
pub enum Error {
    /// IO error.
    Io(io::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Error::Io(ref err) => write!(f, "IO error: {}", err),
        }
    }
}

impl error::Error for Error {}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

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

/// PWM channels.
///
/// More information on enabling and configuring the PWM channels can be
/// found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Channel {
    Pwm0 = 0,
    Pwm1 = 1,
}

/// Output polarities.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Polarity {
    Normal,
    Inverse,
}

/// Provides access to the Raspberry Pi's PWM peripheral.
///
/// Before using `Pwm`, make sure the selected PWM channel has been configured
/// and activated. More information can be found [here].
///
/// [here]: index.html
pub struct Pwm {
    channel: Channel,
}

impl Pwm {
    /// Constructs a new `Pwm`.
    pub fn new(channel: Channel) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        let _ = pwm.disable();

        // Default settings
        let _ = pwm.set_pulse_width(Duration::from_secs(0));
        let _ = pwm.set_period(Duration::from_secs(0));
        let _ = pwm.set_polarity(Polarity::Normal);

        Ok(pwm)
    }

    /// Constructs a new `Pwm` using the specified settings.
    ///
    /// `period` represents the time it takes for the PWM channel to complete one cycle.
    ///
    /// `pulse_width` represents the amount of time the PWM channel's logic level is set
    /// high (or low if the polarity is set to [`Inverse`]) during a single period.
    ///
    /// `polarity` configures the active logic level as either high ([`Normal`]) or low ([`Inverse`]).
    ///
    /// `enabled` immediately enables PWM on the selected channel.
    ///
    /// This method will fail if `period` is shorter than `duty_cycle`.
    ///
    /// [`Normal`]: enum.Polarity.html
    /// [`Inverse`]: enum.Polarity.html
    pub fn with_period(
        channel: Channel,
        period: Duration,
        pulse_width: Duration,
        polarity: Polarity,
        enabled: bool,
    ) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs pwm interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        let _ = pwm.disable();

        // Set pulse width to 0 first in case the new period is shorter than the current pulse width
        let _ = sysfs::set_pulse_width(channel as u8, 0);

        pwm.set_period(period)?;
        pwm.set_pulse_width(pulse_width)?;
        pwm.set_polarity(polarity)?;
        if enabled {
            pwm.enable()?;
        }

        Ok(pwm)
    }

    /// Constructs a new `Pwm` using the specified settings.
    ///
    /// `with_frequency` is a convenience method that converts `frequency` to a period,
    /// and calculates the duty cycle as a percentage of the frequency.
    ///
    /// `frequency` is specified in herz (Hz).
    ///
    /// `duty_cycle` is specified as a floating point ratio between `0.0` (0%) and `1.0` (100%).
    ///
    /// `polarity` configures the active logic level as either high ([`Normal`]) or low ([`Inverse`]).
    ///
    /// `enabled` immediately enables PWM on the selected channel.
    ///
    /// [`Normal`]: enum.Polarity.html
    /// [`Inverse`]: enum.Polarity.html
    pub fn with_frequency(
        channel: Channel,
        frequency: f64,
        duty_cycle: f64,
        polarity: Polarity,
        enabled: bool,
    ) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs pwm interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        let _ = pwm.disable();

        // Set pulse width to 0 first in case the new period is shorter than the current pulse width
        let _ = sysfs::set_pulse_width(channel as u8, 0);

        // Convert to nanoseconds
        let period = (1.0f64 / frequency) * 1_000_000_000f64;
        let pulse_width = period * duty_cycle.max(0.0).min(1.0);

        sysfs::set_period(channel as u8, period as u64)?;
        sysfs::set_pulse_width(channel as u8, pulse_width as u64)?;

        pwm.set_polarity(polarity)?;
        if enabled {
            pwm.enable()?;
        }

        Ok(pwm)
    }

    /// Returns the configured period.
    pub fn period(&self) -> Result<Duration> {
        Ok(Duration::from_nanos(sysfs::period(self.channel as u8)?))
    }

    /// Sets the period.
    ///
    /// `period` represents the time it takes for the PWM channel to complete one cycle.
    /// The specified period must be longer than or equal to the pulse width.
    pub fn set_period(&self, period: Duration) -> Result<()> {
        sysfs::set_period(
            self.channel as u8,
            u64::from(period.subsec_nanos())
                .saturating_add(period.as_secs().saturating_mul(1_000_000_000)),
        )?;

        Ok(())
    }

    /// Returns the configured pulse width.
    pub fn pulse_width(&self) -> Result<Duration> {
        Ok(Duration::from_nanos(sysfs::pulse_width(
            self.channel as u8,
        )?))
    }

    /// Sets the pulse width.
    ///
    /// `pulse_width` represents the amount of time the PWM channel's logic level is set
    /// high (or low if the polarity is set to [`Inverse`]) during a single period. The
    /// pulse width must be shorter than or equal to the period.
    ///
    /// [`Inverse`]: enum.Polarity.html
    pub fn set_pulse_width(&self, pulse_width: Duration) -> Result<()> {
        sysfs::set_pulse_width(
            self.channel as u8,
            u64::from(pulse_width.subsec_nanos())
                .saturating_add(pulse_width.as_secs().saturating_mul(1_000_000_000)),
        )?;

        Ok(())
    }

    /// Returns the configured duty cycle.
    ///
    /// `duty_cycle` is a convenience method that calculates the duty cycle as a
    /// floating point ratio between `0.0` (0%) and `1.0` (100%) based on the configured
    /// period and pulse width.
    pub fn duty_cycle(&self) -> Result<f64> {
        let period = sysfs::period(self.channel as u8)? as f64;
        let pulse_width = sysfs::pulse_width(self.channel as u8)? as f64;

        Ok((pulse_width / period).max(0.0).min(1.0))
    }

    /// Sets the duty cycle.
    ///
    /// `duty_cycle` represents the amount of time the PWM channel's logic level is set
    /// high (or low if the polarity is set to [`Inverse`]) during a single period. The
    /// duty cycle is specified as a floating point ratio between `0.0` (0%) and `1.0` (100%).
    ///
    /// `set_duty_cycle` is a convenience method that converts `duty_cycle` to the
    /// correct pulse width value, based on the configured period.
    ///
    /// [`Inverse`]: enum.Polarity.html
    pub fn set_duty_cycle(&self, duty_cycle: f64) -> Result<()> {
        let period = sysfs::period(self.channel as u8)? as f64;
        let pulse_width = period * duty_cycle.max(0.0).min(1.0);

        sysfs::set_pulse_width(self.channel as u8, pulse_width as u64)?;

        Ok(())
    }

    /// Sets the frequency and duty cycle.
    ///
    /// `set_frequency` is a convenience method that converts `frequency` to a period,
    /// and calculates the duty cycle as a percentage of the frequency.
    ///
    /// `frequency` is specified in herz (Hz).
    ///
    /// `duty_cycle` is specified as a floating point ratio between `0.0` (0%) and `1.0` (100%).
    pub fn set_frequency(&self, frequency: f64, duty_cycle: f64) -> Result<()> {
        // Set duty cycle to 0 first in case the new period is shorter than the current duty cycle
        let _ = sysfs::set_pulse_width(self.channel as u8, 0);

        // Convert to nanoseconds
        let period = (1.0f64 / frequency) * 1_000_000_000f64;
        let pulse_width = period * duty_cycle.max(0.0).min(1.0);

        sysfs::set_period(self.channel as u8, period as u64)?;
        sysfs::set_pulse_width(self.channel as u8, pulse_width as u64)?;

        Ok(())
    }

    /// Returns the configured polarity.
    pub fn polarity(&self) -> Result<Polarity> {
        Ok(sysfs::polarity(self.channel as u8)?)
    }

    /// Sets the polarity.
    ///
    /// `polarity` configures the active logic level as either high ([`Normal`]) or low ([`Inverse`]).
    ///
    /// By default, `polarity` is set to [`Normal`].
    ///
    /// [`Normal`]: enum.Polarity.html
    /// [`Inverse`]: enum.Polarity.html
    pub fn set_polarity(&self, polarity: Polarity) -> Result<()> {
        sysfs::set_polarity(self.channel as u8, polarity)?;

        Ok(())
    }

    /// Checks whether PWM is currently enabled on the selected channel.
    pub fn enabled(&self) -> Result<bool> {
        Ok(sysfs::enabled(self.channel as u8)?)
    }

    /// Enables PWM on the selected channel.
    pub fn enable(&self) -> Result<()> {
        sysfs::set_enabled(self.channel as u8, true)?;

        Ok(())
    }

    /// Disables PWM on the selected channel.
    pub fn disable(&self) -> Result<()> {
        sysfs::set_enabled(self.channel as u8, false)?;

        Ok(())
    }
}

impl Drop for Pwm {
    fn drop(&mut self) {
        let _ = sysfs::set_enabled(self.channel as u8, false);
        let _ = sysfs::unexport(self.channel as u8);
    }
}
<|endoftext|>


<commit_before>//! Rust representations of docker json structures

use std::collections::HashMap;

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct SearchResult {
    pub description: String,
    pub is_official: bool,
    pub is_trusted: bool,
    pub name: String,
    pub star_count: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Image {
    pub Created: u64,
    pub Id: String,
    pub ParentId: String,
    pub Labels: Option<HashMap<String, String>>,
    pub RepoTags: Vec<String>,
    pub RepoDigests: Option<Vec<String>>,
    pub Size: u64,
    pub VirtualSize: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct ImageDetails {
    pub Architecture: String,
    pub Author: String,
    pub Comment: String,
    pub Config: Config,
    pub Created: String,
    pub DockerVersion: String,
    pub Id: String,
    pub Os: String,
    pub Parent: String,
    pub Size: u64,
    pub VirtualSize: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Container {
    pub Created: u64,
    pub Command: String,
    pub Id: String,
    pub Image: String,
    pub Labels: HashMap<String, String>,
    pub Names: Vec<String>,
    pub Ports: Vec<Port>,
    pub Status: String,
    pub SizeRw: Option<u64>,
    pub SizeRootFs: Option<u64>,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct ContainerDetails {
    pub AppArmorProfile: String,
    pub Args: Vec<String>,
    pub Config: Config,
    pub Created: String,
    pub Driver: String,
    pub ExecDriver: String,
    // pub ExecIDs: ??
    pub HostConfig: HostConfig,
    pub HostnamePath: String,
    pub HostsPath: String,
    pub LogPath: String,
    pub Id: String,
    pub Image: String,
    pub MountLabel: String,
    pub NetworkSettings: NetworkSettings,
    pub Path: String,
    pub ProcessLabel: String,
    pub ResolvConfPath: String,
    pub RestartCount: u64,
    pub State: State,
    pub Mounts: Vec<Mount>,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Mount {
    pub Source: String,
    pub Destination: String,
    pub Mode: String,
    pub RW: bool,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct State {
    pub Error: String,
    pub ExitCode: u64,
    pub FinishedAt: String,
    pub OOMKilled: bool,
    pub Paused: bool,
    pub Pid: u64,
    pub Restarting: bool,
    pub Running: bool,
    pub StartedAt: String,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct NetworkSettings {
    pub Bridge: String,
    pub Gateway: String,
    pub IPAddress: String,
    pub IPPrefixLen: u64,
    pub MacAddress: String, /*    pub PortMapping: Option<???>,
                             *   pub Ports: Option<???> */
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct HostConfig {
    pub CgroupParent: Option<String>,
    pub ContainerIDFile: String,
    pub CpuShares: Option<u64>,
    pub CpusetCpus: Option<String>,
    pub Memory: Option<u64>,
    pub MemorySwap: Option<u64>,
    pub NetworkMode: String,
    pub PidMode: Option<String>,
    // pub PortBindings: ???
    pub Privileged: bool,
    pub PublishAllPorts: bool,
    pub ReadonlyRootfs: Option<bool>, /* pub RestartPolicy: ???
                                       * pub SecurityOpt: Option<???>,
                                       * pub Ulimits: Option<???>
                                       * pub VolumesFrom: Option<??/> */
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Config {
    pub AttachStderr: bool,
    pub AttachStdin: bool,
    pub AttachStdout: bool,
    pub Cmd: Option<Vec<String>>,
    pub Domainname: String,
    pub Entrypoint: Option<Vec<String>>,
    pub Env: Option<Vec<String>>,
    // ExposedPorts
    pub Hostname: String,
    pub Image: String,
    pub Labels: HashMap<String, String>,
    // pub MacAddress: String,
    pub OnBuild: Option<String>,
    // pub NetworkDisabled: bool,
    pub OpenStdin: bool,
    pub StdinOnce: bool,
    pub Tty: bool,
    pub User: String,
    pub WorkingDir: String,
}

impl Config {
    pub fn env(&self) -> HashMap<String, String> {
        let mut map = HashMap::new();
        match self.Env {
            Some(ref vars) => {
                for e in vars {
                    let pair: Vec<&str> = e.split("=").collect();
                    map.insert(pair[0].to_owned(), pair[1].to_owned());
                }
            }
            _ => (),
        };
        map
    }
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Port {
    pub IP: Option<String>,
    pub PrivatePort: u64,
    pub PublicPort: Option<u64>,
    pub Type: String,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Stats {
    pub read: String,
    pub networks: HashMap<String, Network>,
    pub memory_stats: MemoryStats,
    pub blkio_stats: BlkioStats,
    pub cpu_stats: CpuStats,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Network {
    pub rx_dropped: u64,
    pub rx_bytes: u64,
    pub rx_errors: u64,
    pub tx_packets: u64,
    pub tx_dropped: u64,
    pub rx_packets: u64,
    pub tx_errors: u64,
    pub tx_bytes: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct MemoryStats {
    pub max_usage: u64,
    pub usage: u64,
    pub failcnt: u64,
    pub limit: u64,
    pub stats: MemoryStat,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct MemoryStat {
    pub total_pgmajfault: u64,
    pub cache: u64,
    pub mapped_file: u64,
    pub total_inactive_file: u64,
    pub pgpgout: u64,
    pub rss: u64,
    pub total_mapped_file: u64,
    pub writeback: u64,
    pub unevictable: u64,
    pub pgpgin: u64,
    pub total_unevictable: u64,
    pub pgmajfault: u64,
    pub total_rss: u64,
    pub total_rss_huge: u64,
    pub total_writeback: u64,
    pub total_inactive_anon: u64,
    pub rss_huge: u64,
    pub hierarchical_memory_limit: u64,
    pub hierarchical_memsw_limit: u64,
    pub total_pgfault: u64,
    pub total_active_file: u64,
    pub active_anon: u64,
    pub total_active_anon: u64,
    pub total_pgpgout: u64,
    pub total_cache: u64,
    pub inactive_anon: u64,
    pub active_file: u64,
    pub pgfault: u64,
    pub inactive_file: u64,
    pub total_pgpgin: u64,
    pub swap: u64,
    pub total_swap: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct CpuStats {
    pub cpu_usage: CpuUsage,
    pub system_cpu_usage: u64,
    pub throttling_data: ThrottlingData,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct CpuUsage {
    pub percpu_usage: Vec<u64>,
    pub usage_in_usermode: u64,
    pub total_usage: u64,
    pub usage_in_kernelmode: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct ThrottlingData {
    pub periods: u64,
    pub throttled_periods: u64,
    pub throttled_time: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct BlkioStats {
    pub io_service_bytes_recursive: Vec<BlkioStat>,
    pub io_serviced_recursive: Vec<BlkioStat>,
    pub io_queue_recursive: Vec<BlkioStat>,
    pub io_service_time_recursive: Vec<BlkioStat>,
    pub io_wait_time_recursive: Vec<BlkioStat>,
    pub io_merged_recursive: Vec<BlkioStat>,
    pub io_time_recursive: Vec<BlkioStat>,
    pub sectors_recursive: Vec<BlkioStat>,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct BlkioStat {
    pub major: u64,
    pub minor: u64,
    pub op: String,
    pub value: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Change {
    pub Kind: u64,
    pub Path: String,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Top {
    pub Titles: Vec<String>,
    pub Processes: Vec<Vec<String>>,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Version {
    pub ApiVersion: String,
    pub Version: String,
    pub GitCommit: String,
    pub GoVersion: String,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Info {
    pub Containers: u64,
    pub Images: u64,
    pub Driver: String,
    pub DockerRootDir: String,
    pub DriverStatus: Vec<Vec<String>>,
    pub ExecutionDriver: String,
    pub ID: String,
    pub KernelVersion: String,
    // pub Labels: Option<???>,
    pub MemTotal: u64,
    pub MemoryLimit: bool,
    pub NCPU: u64,
    pub NEventsListener: u64,
    pub NGoroutines: u64,
    pub Name: String,
    pub OperatingSystem: String,
    // pub RegistryConfig:???
    pub SwapLimit: bool,
    pub SystemTime: Option<String>,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct ContainerCreateInfo {
    pub Id: String,
    pub Warnings: Option<Vec<String>>,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct History {
    pub Id: String,
    pub Created: u64,
    pub CreatedBy: String,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Exit {
    pub StatusCode: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Event {
    pub status: String,
    pub id: String,
    pub from: Option<String>,
    pub time: u64,
    pub timeNano: u64,
}

#[derive(Debug)]
pub enum BuildOutput {
    Stream(String),
    Err(String),
}

// fixme: all fields are options because PullInfo.progressDefault is sometimes an empty object instead of a null/absent value
#[derive(Clone, Debug, RustcDecodable)]
pub struct ProgressDetail {
    current: Option<u64>,
    total: Option<u64>,
    status: Option<String>, // fixme: it looks like this field isn't deserializing properly
}

#[derive(Clone, Debug, RustcDecodable)]
#[allow(non_snake_case)]
pub struct PullInfo {
    pub id: Option<String>,
    pub status: String,
    pub progress: Option<String>,
    pub progressDetail: Option<ProgressDetail>,
}

#[derive(Debug)]
pub enum PullOutput {
    Status {
        id: Option<String>,
        status: String,
        progress: Option<String>,
        progress_detail: Option<ProgressDetail>,
    },
    Err(String),
}

#[derive(Debug)]
pub enum Status {
    Untagged(String),
    Deleted(String),
}
<commit_msg>ExecDriver field no longer exists (API version 1.23)<commit_after>//! Rust representations of docker json structures

use std::collections::HashMap;

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct SearchResult {
    pub description: String,
    pub is_official: bool,
    pub is_trusted: bool,
    pub name: String,
    pub star_count: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Image {
    pub Created: u64,
    pub Id: String,
    pub ParentId: String,
    pub Labels: Option<HashMap<String, String>>,
    pub RepoTags: Vec<String>,
    pub RepoDigests: Option<Vec<String>>,
    pub Size: u64,
    pub VirtualSize: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct ImageDetails {
    pub Architecture: String,
    pub Author: String,
    pub Comment: String,
    pub Config: Config,
    pub Created: String,
    pub DockerVersion: String,
    pub Id: String,
    pub Os: String,
    pub Parent: String,
    pub Size: u64,
    pub VirtualSize: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Container {
    pub Created: u64,
    pub Command: String,
    pub Id: String,
    pub Image: String,
    pub Labels: HashMap<String, String>,
    pub Names: Vec<String>,
    pub Ports: Vec<Port>,
    pub Status: String,
    pub SizeRw: Option<u64>,
    pub SizeRootFs: Option<u64>,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct ContainerDetails {
    pub AppArmorProfile: String,
    pub Args: Vec<String>,
    pub Config: Config,
    pub Created: String,
    pub Driver: String,
    // pub ExecIDs: ??
    pub HostConfig: HostConfig,
    pub HostnamePath: String,
    pub HostsPath: String,
    pub LogPath: String,
    pub Id: String,
    pub Image: String,
    pub MountLabel: String,
    pub NetworkSettings: NetworkSettings,
    pub Path: String,
    pub ProcessLabel: String,
    pub ResolvConfPath: String,
    pub RestartCount: u64,
    pub State: State,
    pub Mounts: Vec<Mount>,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Mount {
    pub Source: String,
    pub Destination: String,
    pub Mode: String,
    pub RW: bool,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct State {
    pub Error: String,
    pub ExitCode: u64,
    pub FinishedAt: String,
    pub OOMKilled: bool,
    pub Paused: bool,
    pub Pid: u64,
    pub Restarting: bool,
    pub Running: bool,
    pub StartedAt: String,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct NetworkSettings {
    pub Bridge: String,
    pub Gateway: String,
    pub IPAddress: String,
    pub IPPrefixLen: u64,
    pub MacAddress: String, /*    pub PortMapping: Option<???>,
                             *   pub Ports: Option<???> */
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct HostConfig {
    pub CgroupParent: Option<String>,
    pub ContainerIDFile: String,
    pub CpuShares: Option<u64>,
    pub CpusetCpus: Option<String>,
    pub Memory: Option<u64>,
    pub MemorySwap: Option<u64>,
    pub NetworkMode: String,
    pub PidMode: Option<String>,
    // pub PortBindings: ???
    pub Privileged: bool,
    pub PublishAllPorts: bool,
    pub ReadonlyRootfs: Option<bool>, /* pub RestartPolicy: ???
                                       * pub SecurityOpt: Option<???>,
                                       * pub Ulimits: Option<???>
                                       * pub VolumesFrom: Option<??/> */
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Config {
    pub AttachStderr: bool,
    pub AttachStdin: bool,
    pub AttachStdout: bool,
    pub Cmd: Option<Vec<String>>,
    pub Domainname: String,
    pub Entrypoint: Option<Vec<String>>,
    pub Env: Option<Vec<String>>,
    // ExposedPorts
    pub Hostname: String,
    pub Image: String,
    pub Labels: HashMap<String, String>,
    // pub MacAddress: String,
    pub OnBuild: Option<String>,
    // pub NetworkDisabled: bool,
    pub OpenStdin: bool,
    pub StdinOnce: bool,
    pub Tty: bool,
    pub User: String,
    pub WorkingDir: String,
}

impl Config {
    pub fn env(&self) -> HashMap<String, String> {
        let mut map = HashMap::new();
        match self.Env {
            Some(ref vars) => {
                for e in vars {
                    let pair: Vec<&str> = e.split("=").collect();
                    map.insert(pair[0].to_owned(), pair[1].to_owned());
                }
            }
            _ => (),
        };
        map
    }
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Port {
    pub IP: Option<String>,
    pub PrivatePort: u64,
    pub PublicPort: Option<u64>,
    pub Type: String,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Stats {
    pub read: String,
    pub networks: HashMap<String, Network>,
    pub memory_stats: MemoryStats,
    pub blkio_stats: BlkioStats,
    pub cpu_stats: CpuStats,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Network {
    pub rx_dropped: u64,
    pub rx_bytes: u64,
    pub rx_errors: u64,
    pub tx_packets: u64,
    pub tx_dropped: u64,
    pub rx_packets: u64,
    pub tx_errors: u64,
    pub tx_bytes: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct MemoryStats {
    pub max_usage: u64,
    pub usage: u64,
    pub failcnt: u64,
    pub limit: u64,
    pub stats: MemoryStat,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct MemoryStat {
    pub total_pgmajfault: u64,
    pub cache: u64,
    pub mapped_file: u64,
    pub total_inactive_file: u64,
    pub pgpgout: u64,
    pub rss: u64,
    pub total_mapped_file: u64,
    pub writeback: u64,
    pub unevictable: u64,
    pub pgpgin: u64,
    pub total_unevictable: u64,
    pub pgmajfault: u64,
    pub total_rss: u64,
    pub total_rss_huge: u64,
    pub total_writeback: u64,
    pub total_inactive_anon: u64,
    pub rss_huge: u64,
    pub hierarchical_memory_limit: u64,
    pub hierarchical_memsw_limit: u64,
    pub total_pgfault: u64,
    pub total_active_file: u64,
    pub active_anon: u64,
    pub total_active_anon: u64,
    pub total_pgpgout: u64,
    pub total_cache: u64,
    pub inactive_anon: u64,
    pub active_file: u64,
    pub pgfault: u64,
    pub inactive_file: u64,
    pub total_pgpgin: u64,
    pub swap: u64,
    pub total_swap: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct CpuStats {
    pub cpu_usage: CpuUsage,
    pub system_cpu_usage: u64,
    pub throttling_data: ThrottlingData,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct CpuUsage {
    pub percpu_usage: Vec<u64>,
    pub usage_in_usermode: u64,
    pub total_usage: u64,
    pub usage_in_kernelmode: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct ThrottlingData {
    pub periods: u64,
    pub throttled_periods: u64,
    pub throttled_time: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct BlkioStats {
    pub io_service_bytes_recursive: Vec<BlkioStat>,
    pub io_serviced_recursive: Vec<BlkioStat>,
    pub io_queue_recursive: Vec<BlkioStat>,
    pub io_service_time_recursive: Vec<BlkioStat>,
    pub io_wait_time_recursive: Vec<BlkioStat>,
    pub io_merged_recursive: Vec<BlkioStat>,
    pub io_time_recursive: Vec<BlkioStat>,
    pub sectors_recursive: Vec<BlkioStat>,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct BlkioStat {
    pub major: u64,
    pub minor: u64,
    pub op: String,
    pub value: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Change {
    pub Kind: u64,
    pub Path: String,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Top {
    pub Titles: Vec<String>,
    pub Processes: Vec<Vec<String>>,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Version {
    pub ApiVersion: String,
    pub Version: String,
    pub GitCommit: String,
    pub GoVersion: String,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Info {
    pub Containers: u64,
    pub Images: u64,
    pub Driver: String,
    pub DockerRootDir: String,
    pub DriverStatus: Vec<Vec<String>>,
    pub ExecutionDriver: String,
    pub ID: String,
    pub KernelVersion: String,
    // pub Labels: Option<???>,
    pub MemTotal: u64,
    pub MemoryLimit: bool,
    pub NCPU: u64,
    pub NEventsListener: u64,
    pub NGoroutines: u64,
    pub Name: String,
    pub OperatingSystem: String,
    // pub RegistryConfig:???
    pub SwapLimit: bool,
    pub SystemTime: Option<String>,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct ContainerCreateInfo {
    pub Id: String,
    pub Warnings: Option<Vec<String>>,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct History {
    pub Id: String,
    pub Created: u64,
    pub CreatedBy: String,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Exit {
    pub StatusCode: u64,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[allow(non_snake_case)]
pub struct Event {
    pub status: String,
    pub id: String,
    pub from: Option<String>,
    pub time: u64,
    pub timeNano: u64,
}

#[derive(Debug)]
pub enum BuildOutput {
    Stream(String),
    Err(String),
}

// fixme: all fields are options because PullInfo.progressDefault is sometimes an empty object instead of a null/absent value
#[derive(Clone, Debug, RustcDecodable)]
pub struct ProgressDetail {
    current: Option<u64>,
    total: Option<u64>,
    status: Option<String>, // fixme: it looks like this field isn't deserializing properly
}

#[derive(Clone, Debug, RustcDecodable)]
#[allow(non_snake_case)]
pub struct PullInfo {
    pub id: Option<String>,
    pub status: String,
    pub progress: Option<String>,
    pub progressDetail: Option<ProgressDetail>,
}

#[derive(Debug)]
pub enum PullOutput {
    Status {
        id: Option<String>,
        status: String,
        progress: Option<String>,
        progress_detail: Option<ProgressDetail>,
    },
    Err(String),
}

#[derive(Debug)]
pub enum Status {
    Untagged(String),
    Deleted(String),
}
<|endoftext|>


<commit_before>use crate::{error::ElasticsearchError, http_method::HttpMethod, response::ElasticsearchResponse};

use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io;
use std::io::Read;
use std::io::Write;

use base64;
use base64::write::EncoderWriter as Base64Encoder;
use reqwest::{
    header::{HeaderMap, HeaderValue, ACCEPT, AUTHORIZATION, CONTENT_TYPE, USER_AGENT},
    Client, Method,
};
use serde::{de::DeserializeOwned, Serialize};
use url::Url;

/// Credentials for authentication
#[derive(Debug, Clone)]
pub enum Credentials {
    /// A username and password to use for Basic authentication
    Basic(String, String),
    /// An access_token to use for Bearer authentication
    Bearer(String),
    /// Bytes of a PKCS#12 archive and password to use for PKI (Client Certificate) authentication
    Cert(Vec<u8>, String),
    /// An id an api_key to use for API key authentication
    ApiKey(String, String),
}

/// Error that can occur when building a connection
#[derive(Debug)]
pub enum BuildError {
    /// IO error
    IoError(io::Error),

    /// Certificate error
    CertError(reqwest::Error),
}

impl From<io::Error> for BuildError {
    fn from(err: io::Error) -> BuildError {
        BuildError::IoError(err)
    }
}

impl From<reqwest::Error> for BuildError {
    fn from(err: reqwest::Error) -> BuildError {
        BuildError::CertError(err)
    }
}

impl Error for BuildError {
    fn description(&self) -> &str {
        match *self {
            BuildError::IoError(ref err) => err.description(),
            BuildError::CertError(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&dyn Error> {
        match *self {
            BuildError::IoError(ref err) => Some(err as &dyn Error),
            BuildError::CertError(ref err) => Some(err as &dyn Error),
        }
    }
}

impl fmt::Display for BuildError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            BuildError::IoError(ref err) => fmt::Display::fmt(err, f),
            BuildError::CertError(ref err) => fmt::Display::fmt(err, f),
        }
    }
}

pub static DEFAULT_ADDRESS: &str = "http://localhost:9200";

static DEFAULT_USER_AGENT: &str = concat!("elasticsearch-rs/", env!("CARGO_PKG_VERSION"));

static DEFAULT_CONTENT_TYPE: &str = "application/json";

static DEFAULT_ACCEPT: &str = "application/json";

pub struct ConnectionBuilder {
    client_builder: reqwest::ClientBuilder,
    url: Url,
    credentials: Option<Credentials>,
    proxy: Option<Url>,
    disable_proxy: bool,
}

impl ConnectionBuilder {
    pub fn new(url: Url) -> Self {
        ConnectionBuilder {
            client_builder: reqwest::ClientBuilder::new(),
            url,
            credentials: None,
            proxy: None,
            disable_proxy: false,
        }
    }

    /// Configures a proxy
    pub fn proxy(mut self, url: Url) -> Self {
        self.proxy = Some(url);
        self
    }

    /// Whether to disable proxies, including system proxies
    pub fn disable_proxy(mut self) -> Self {
        self.disable_proxy = true;
        self
    }

    pub fn auth(mut self, credentials: Credentials) -> Self {
        self.credentials = Some(credentials);
        self
    }

    /// Builds a connection to an Elasticsearch node
    pub fn build(self) -> Result<Connection, BuildError> {
        let mut client_builder = self.client_builder;
        if let Some(c) = &self.credentials {
            client_builder = match c {
                Credentials::Cert(b, p) => {
                    let pkcs12 = reqwest::Identity::from_pkcs12_der(&b, p)?;
                    client_builder.identity(pkcs12)
                }
                _ => client_builder,
            }
        };

        if self.disable_proxy {
            client_builder = client_builder.no_proxy();
        } else if let Some(url) = &self.proxy {
            client_builder = match url.scheme() {
                "https" => client_builder.proxy(reqwest::Proxy::https(&url.to_string())?),
                _ => client_builder.proxy(reqwest::Proxy::http(&url.to_string())?),
            };
        }

        let client = client_builder.build()?;
        Ok(Connection {
            url: self.url,
            credentials: self.credentials,
            client,
        })
    }
}

impl Default for ConnectionBuilder {
    fn default() -> Self {
        ConnectionBuilder::new(Url::parse(DEFAULT_ADDRESS).unwrap())
    }
}

/// A connection to an Elasticsearch node, used to send an API request
#[derive(Debug, Clone)]
pub struct Connection {
    client: reqwest::Client,
    url: Url,
    credentials: Option<Credentials>,
}

impl Connection {
    fn method(&self, method: HttpMethod) -> Method {
        match method {
            HttpMethod::Get => Method::GET,
            HttpMethod::Put => Method::PUT,
            HttpMethod::Post => Method::POST,
            HttpMethod::Delete => Method::DELETE,
            HttpMethod::Head => Method::HEAD,
        }
    }

    pub fn new(url: Url) -> Connection {
        Connection {
            client: reqwest::Client::new(),
            url,
            credentials: None,
        }
    }

    /// Creates an asynchronous request that can be awaited
    pub async fn send<B, Q>(
        &self,
        method: HttpMethod,
        path: &str,
        query_string: Option<&Q>,
        body: Option<B>,
    ) -> Result<ElasticsearchResponse, ElasticsearchError>
    where
        B: Serialize,
        Q: Serialize + ?Sized,
    {
        let url = self.url.join(path)?;
        let reqwest_method = self.method(method);
        let mut request_builder = self.client.request(reqwest_method, &url.to_string());
        let mut headers = HeaderMap::new();

        headers.insert(CONTENT_TYPE, HeaderValue::from_static(DEFAULT_CONTENT_TYPE));
        headers.insert(ACCEPT, HeaderValue::from_static(DEFAULT_ACCEPT));
        headers.insert(USER_AGENT, HeaderValue::from_static(DEFAULT_USER_AGENT));

        request_builder = request_builder.headers(headers);

        if let Some(b) = body {
            request_builder = request_builder.json(&b);
        };

        if let Some(q) = query_string {
            request_builder = request_builder.query(q);
        }

        if let Some(c) = &self.credentials {
            request_builder = match c {
                Credentials::Basic(u, p) => request_builder.basic_auth(u, Some(p)),
                Credentials::Bearer(t) => request_builder.bearer_auth(t),
                Credentials::Cert(_, _) => request_builder,
                Credentials::ApiKey(i, k) => {
                    let mut header_value = b"ApiKey ".to_vec();
                    {
                        let mut encoder = Base64Encoder::new(&mut header_value, base64::STANDARD);
                        write!(encoder, "{}:", i).unwrap();
                        write!(encoder, "{}", k).unwrap();
                    }
                    request_builder.header(
                        AUTHORIZATION,
                        HeaderValue::from_bytes(&header_value).unwrap(),
                    )
                }
            }
        }

        let response = request_builder.send().await?;
        Ok(ElasticsearchResponse::new(response))
    }
}

impl Default for Connection {
    fn default() -> Self {
        Connection::new(Url::parse(DEFAULT_ADDRESS).unwrap())
    }
}
<commit_msg>Move default HTTP headers to ConnectionBuilder<commit_after>use crate::{error::ElasticsearchError, http_method::HttpMethod, response::ElasticsearchResponse};

use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io;
use std::io::Read;
use std::io::Write;

use base64;
use base64::write::EncoderWriter as Base64Encoder;
use reqwest::{
    header::{HeaderMap, HeaderValue, ACCEPT, AUTHORIZATION, CONTENT_TYPE, USER_AGENT},
    Client, Method,
};
use serde::{de::DeserializeOwned, Serialize};
use url::Url;

/// Credentials for authentication
#[derive(Debug, Clone)]
pub enum Credentials {
    /// A username and password to use for Basic authentication
    Basic(String, String),
    /// An access_token to use for Bearer authentication
    Bearer(String),
    /// Bytes of a PKCS#12 archive and password to use for PKI (Client Certificate) authentication
    Cert(Vec<u8>, String),
    /// An id an api_key to use for API key authentication
    ApiKey(String, String),
}

/// Error that can occur when building a connection
#[derive(Debug)]
pub enum BuildError {
    /// IO error
    IoError(io::Error),

    /// Certificate error
    CertError(reqwest::Error),
}

impl From<io::Error> for BuildError {
    fn from(err: io::Error) -> BuildError {
        BuildError::IoError(err)
    }
}

impl From<reqwest::Error> for BuildError {
    fn from(err: reqwest::Error) -> BuildError {
        BuildError::CertError(err)
    }
}

impl Error for BuildError {
    fn description(&self) -> &str {
        match *self {
            BuildError::IoError(ref err) => err.description(),
            BuildError::CertError(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&dyn Error> {
        match *self {
            BuildError::IoError(ref err) => Some(err as &dyn Error),
            BuildError::CertError(ref err) => Some(err as &dyn Error),
        }
    }
}

impl fmt::Display for BuildError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            BuildError::IoError(ref err) => fmt::Display::fmt(err, f),
            BuildError::CertError(ref err) => fmt::Display::fmt(err, f),
        }
    }
}

pub static DEFAULT_ADDRESS: &str = "http://localhost:9200";

static DEFAULT_USER_AGENT: &str = concat!("elasticsearch-rs/", env!("CARGO_PKG_VERSION"));

static DEFAULT_CONTENT_TYPE: &str = "application/json";

static DEFAULT_ACCEPT: &str = "application/json";

pub struct ConnectionBuilder {
    client_builder: reqwest::ClientBuilder,
    url: Url,
    credentials: Option<Credentials>,
    proxy: Option<Url>,
    disable_proxy: bool,
}

impl ConnectionBuilder {
    pub fn new(url: Url) -> Self {
        ConnectionBuilder {
            client_builder: reqwest::ClientBuilder::new(),
            url,
            credentials: None,
            proxy: None,
            disable_proxy: false,
        }
    }

    /// Configures a proxy
    pub fn proxy(mut self, url: Url) -> Self {
        self.proxy = Some(url);
        self
    }

    /// Whether to disable proxies, including system proxies
    pub fn disable_proxy(mut self) -> Self {
        self.disable_proxy = true;
        self
    }

    pub fn auth(mut self, credentials: Credentials) -> Self {
        self.credentials = Some(credentials);
        self
    }

    /// Builds a connection to an Elasticsearch node
    pub fn build(self) -> Result<Connection, BuildError> {
        let mut client_builder = self.client_builder;

        let mut headers = HeaderMap::new();
        headers.insert(CONTENT_TYPE, HeaderValue::from_static(DEFAULT_CONTENT_TYPE));
        headers.insert(ACCEPT, HeaderValue::from_static(DEFAULT_ACCEPT));
        headers.insert(USER_AGENT, HeaderValue::from_static(DEFAULT_USER_AGENT));
        client_builder = client_builder.default_headers(headers);

        if let Some(c) = &self.credentials {
            client_builder = match c {
                Credentials::Cert(b, p) => {
                    let pkcs12 = reqwest::Identity::from_pkcs12_der(&b, p)?;
                    client_builder.identity(pkcs12)
                }
                _ => client_builder,
            }
        };

        if self.disable_proxy {
            client_builder = client_builder.no_proxy();
        } else if let Some(url) = &self.proxy {
            client_builder = match url.scheme() {
                "https" => client_builder.proxy(reqwest::Proxy::https(&url.to_string())?),
                _ => client_builder.proxy(reqwest::Proxy::http(&url.to_string())?),
            };
        }

        let client = client_builder.build()?;
        Ok(Connection {
            url: self.url,
            credentials: self.credentials,
            client,
        })
    }
}

impl Default for ConnectionBuilder {
    fn default() -> Self {
        ConnectionBuilder::new(Url::parse(DEFAULT_ADDRESS).unwrap())
    }
}

/// A connection to an Elasticsearch node, used to send an API request
#[derive(Debug, Clone)]
pub struct Connection {
    client: reqwest::Client,
    url: Url,
    credentials: Option<Credentials>,
}

impl Connection {
    fn method(&self, method: HttpMethod) -> Method {
        match method {
            HttpMethod::Get => Method::GET,
            HttpMethod::Put => Method::PUT,
            HttpMethod::Post => Method::POST,
            HttpMethod::Delete => Method::DELETE,
            HttpMethod::Head => Method::HEAD,
        }
    }

    pub fn new(url: Url) -> Connection {
        Connection {
            client: reqwest::Client::new(),
            url,
            credentials: None,
        }
    }

    /// Creates an asynchronous request that can be awaited
    pub async fn send<B, Q>(
        &self,
        method: HttpMethod,
        path: &str,
        query_string: Option<&Q>,
        body: Option<B>,
    ) -> Result<ElasticsearchResponse, ElasticsearchError>
    where
        B: Serialize,
        Q: Serialize + ?Sized,
    {
        let url = self.url.join(path)?;
        let reqwest_method = self.method(method);
        let mut request_builder = self.client.request(reqwest_method, &url.to_string());

        if let Some(b) = body {
            request_builder = request_builder.json(&b);
        };

        if let Some(q) = query_string {
            request_builder = request_builder.query(q);
        }

        if let Some(c) = &self.credentials {
            request_builder = match c {
                Credentials::Basic(u, p) => request_builder.basic_auth(u, Some(p)),
                Credentials::Bearer(t) => request_builder.bearer_auth(t),
                Credentials::Cert(_, _) => request_builder,
                Credentials::ApiKey(i, k) => {
                    let mut header_value = b"ApiKey ".to_vec();
                    {
                        let mut encoder = Base64Encoder::new(&mut header_value, base64::STANDARD);
                        write!(encoder, "{}:", i).unwrap();
                        write!(encoder, "{}", k).unwrap();
                    }
                    request_builder.header(
                        AUTHORIZATION,
                        HeaderValue::from_bytes(&header_value).unwrap(),
                    )
                }
            }
        }

        let response = request_builder.send().await?;
        Ok(ElasticsearchResponse::new(response))
    }
}

impl Default for Connection {
    fn default() -> Self {
        Connection::new(Url::parse(DEFAULT_ADDRESS).unwrap())
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014-2015 Mathias Hällman
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#![feature(collections)]
#![feature(core)]
#![feature(hash)]
#![feature(int_uint)]
#![feature(io)]
#![feature(libc)]
#![feature(path)]
#![feature(rustc_private)]
#![feature(slicing_syntax)]
#![feature(std_misc)]
#![feature(unicode)]

#![cfg_attr(test, feature(os))]

#[macro_use]
extern crate bitflags;

#[cfg(not(test))]
use std::collections::HashMap;

#[allow(dead_code, unused_imports)]  // temporary until buffer is used for real
mod buffer;
mod frame;
mod input;
mod keymap;
mod screen;
mod view;

#[cfg(not(test))]
struct Rim {
  frame: frame::Frame,
  frame_ctx: frame::FrameContext,
  windows: HashMap<frame::WindowId, view::View>,
  focus: frame::WindowId,
  buffer: buffer::Buffer,
}

#[cfg(not(test))]
impl Rim {
  fn new() -> Rim {
    let (frame, frame_ctx, first_window) = frame::Frame::new();
    let mut windows = HashMap::new();
    windows.insert(first_window.clone(), view::View::new());
    Rim {
      frame: frame,
      frame_ctx: frame_ctx,
      windows: windows,
      focus: first_window,
      buffer: buffer::Buffer::open(&Path::new("src/rim.rs")).unwrap(),
    }
  }

  fn switch_focus(&mut self, direction: frame::Direction)
      -> Option<(frame::WindowId, frame::WindowId)> {
    self.frame.get_adjacent_window(&self.frame_ctx, &self.focus, direction).
    map(|window| self.move_focus(window)).ok()
  }

  fn shift_focus(&mut self, order: frame::WindowOrder)
      -> Option<(frame::WindowId, frame::WindowId)> {
    self.frame.get_sequent_window(&self.frame_ctx, &self.focus, order, true).
    map(|window| self.move_focus(window)).ok()
  }

  fn move_focus(&mut self, window: frame::WindowId)
      -> (frame::WindowId, frame::WindowId) {
    let old_focus = std::mem::replace(&mut self.focus, window);
    (old_focus, self.focus.clone())
  }

  fn split_window(&mut self, orientation: frame::Orientation) {
    self.frame.split_window(&mut self.frame_ctx, &self.focus, orientation).
    map(|new_window| self.windows.insert(new_window, view::View::new())).
    ok().expect("Failed to split window.");
  }

  fn resize_window(&mut self, orientation: frame::Orientation, amount: int)
     -> bool {
    self.frame.resize_window(&self.frame_ctx, &self.focus, orientation, amount).
    map(|absorbed| absorbed != 0).unwrap()
  }

  fn close_window(&mut self) -> bool {
    self.frame.get_closest_neighbouring_window(&self.frame_ctx, &self.focus).
    map(|neighbour| {
      let (old_focus, _) = self.move_focus(neighbour);
      self.frame.close_window(&mut self.frame_ctx, &old_focus).unwrap();
      self.windows.remove(&old_focus); }).
    is_ok()
  }

  fn draw_window(&self, window: &frame::WindowId, screen: &mut screen::Screen) {
    self.windows.get(window).
    map(|view| self.draw_view(window, view, screen)).
    expect("Couldn't find view for window.");
    screen.flush();
  }

  fn draw_view(&self, window: &frame::WindowId, view: &view::View,
               screen: &mut screen::Screen) {
    self.frame.get_window_rect(&self.frame_ctx, window).
    map(|rect| { let screen::Rect(position, _) = rect; position }).
    map(|pos| view.draw(&self.buffer, self.focus == *window, pos, screen)).
    ok().expect("Couldn't find rect for window.");
  }

  fn draw_all(&self, screen: &mut screen::Screen) {
    screen.clear();
    self.frame.draw_borders(screen);
    for (window, view) in self.windows.iter() {
      self.draw_view(window, view, screen);
    }
    screen.flush();
  }

  fn update_view_sizes(&mut self) {
    let window_sizes: Vec<(frame::WindowId, screen::Size)> =
      self.windows.iter().
      map(|(win, _)|
        self.frame.get_window_rect(&self.frame_ctx, win).
        map(|rect| { let screen::Rect(_, size) = rect; (win.clone(), size) }).
        ok().expect("Couldn't find rect for window.")).
      collect();
    for &(ref win, size) in window_sizes.iter() {
      self.windows.remove(win).
      map(|mut view| {
        view.set_size(size, &self.buffer);
        self.windows.insert(win.clone(), view); }).
      expect("Couldn't find view for window.");
    }
  }

  fn handle_key_by_view(&mut self, key: keymap::Key) -> bool {
     self.windows.remove(&self.focus).
     map(|mut view| {
       let swallowed = match key {
         keymap::Key::Sym{sym: keymap::KeySym::Left, mods:_}  => {
           view.move_caret(view::CaretMovement::CharPrev, &self.buffer); true
         }
         keymap::Key::Sym{sym: keymap::KeySym::Right, mods:_} => {
           view.move_caret(view::CaretMovement::CharNext, &self.buffer); true
         }
         keymap::Key::Sym{sym: keymap::KeySym::Up, mods:_}    => {
           view.move_caret(view::CaretMovement::LineUp, &self.buffer); true
         }
         keymap::Key::Sym{sym: keymap::KeySym::Down, mods:_}  => {
           view.move_caret(view::CaretMovement::LineDown, &self.buffer); true
         }
         _                                                    => false,
       };
       self.windows.insert(self.focus.clone(), view);
       swallowed }).
     expect("Found no view for focused window.")
  }
}

#[cfg(not(test))]
impl Drop for Rim {
  fn drop(&mut self) {
    self.buffer.write().unwrap();  // what could go wrong!
  }
}

#[cfg(not(test))]
fn main() {
  let mut screen = screen::Screen::setup().unwrap();

  let mut rim = Rim::new();

  let (key_tx, key_rx) = std::sync::mpsc::channel();
  let _term_input = input::start(key_tx);

  let draw_window_pair =
    |&: (win1, win2), rim: &Rim, screen: &mut screen::Screen|
      { rim.draw_window(&win1, screen); rim.draw_window(&win2, screen); };

  loop {
    match key_rx.try_recv().ok().and_then(|key|
        if !rim.handle_key_by_view(key) { Some(key) }
        else { rim.draw_window(&rim.focus, &mut screen); None }) {
      Some(keymap::Key::Unicode{codepoint: 'h', mods:_}) => {
        rim.switch_focus(frame::Direction::Left).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'l', mods:_}) => {
        rim.switch_focus(frame::Direction::Right).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'k', mods:_}) => {
        rim.switch_focus(frame::Direction::Up).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'j', mods:_}) => {
        rim.switch_focus(frame::Direction::Down).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'n', mods:_}) => {
        rim.shift_focus(frame::WindowOrder::NextWindow).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'N', mods:_}) => {
        rim.shift_focus(frame::WindowOrder::PreviousWindow).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: '=', mods:_}) => {
        rim.frame.reset_layout();
        rim.update_view_sizes();
        rim.draw_all(&mut screen);
      }
      Some(keymap::Key::Unicode{codepoint: 'v', mods:_}) => {
        rim.split_window(frame::Orientation::Vertical);
        rim.update_view_sizes();
        rim.draw_all(&mut screen);
      }
      Some(keymap::Key::Unicode{codepoint: 's', mods:_}) => {
        rim.split_window(frame::Orientation::Horizontal);
        rim.update_view_sizes();
        rim.draw_all(&mut screen);
      }
      Some(keymap::Key::Unicode{codepoint: 'y', mods})   => {
        let amount = if mods.contains(keymap::MOD_CTRL) { -10 }
                     else                               { 10 };
        if rim.resize_window(frame::Orientation::Horizontal, amount) {
          rim.update_view_sizes();
          rim.draw_all(&mut screen);
        }
      }
      Some(keymap::Key::Unicode{codepoint: 'u', mods})   => {
        let amount = if mods.contains(keymap::MOD_CTRL) { -10 }
                     else                               { 10 };
        if rim.resize_window(frame::Orientation::Vertical, amount) {
          rim.update_view_sizes();
          rim.draw_all(&mut screen);
        }
      }
      Some(keymap::Key::Unicode{codepoint: 'c', mods:_}) =>
        if rim.close_window() {
          rim.update_view_sizes();
          rim.draw_all(&mut screen);
        },
      Some(keymap::Key::Unicode{codepoint: 'q', mods:_}) =>
        break,
      _                                                  =>
        (),
    }

    if screen.update_size() {
      rim.frame.set_size(screen.size());
      rim.update_view_sizes();
      rim.draw_all(&mut screen);
    }
  }
}
<commit_msg>Remove slicing_syntax feature declaration<commit_after>/*
 * Copyright (c) 2014-2015 Mathias Hällman
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#![feature(collections)]
#![feature(core)]
#![feature(hash)]
#![feature(int_uint)]
#![feature(io)]
#![feature(libc)]
#![feature(path)]
#![feature(rustc_private)]
#![feature(std_misc)]
#![feature(unicode)]

#![cfg_attr(test, feature(os))]

#[macro_use]
extern crate bitflags;

#[cfg(not(test))]
use std::collections::HashMap;

#[allow(dead_code, unused_imports)]  // temporary until buffer is used for real
mod buffer;
mod frame;
mod input;
mod keymap;
mod screen;
mod view;

#[cfg(not(test))]
struct Rim {
  frame: frame::Frame,
  frame_ctx: frame::FrameContext,
  windows: HashMap<frame::WindowId, view::View>,
  focus: frame::WindowId,
  buffer: buffer::Buffer,
}

#[cfg(not(test))]
impl Rim {
  fn new() -> Rim {
    let (frame, frame_ctx, first_window) = frame::Frame::new();
    let mut windows = HashMap::new();
    windows.insert(first_window.clone(), view::View::new());
    Rim {
      frame: frame,
      frame_ctx: frame_ctx,
      windows: windows,
      focus: first_window,
      buffer: buffer::Buffer::open(&Path::new("src/rim.rs")).unwrap(),
    }
  }

  fn switch_focus(&mut self, direction: frame::Direction)
      -> Option<(frame::WindowId, frame::WindowId)> {
    self.frame.get_adjacent_window(&self.frame_ctx, &self.focus, direction).
    map(|window| self.move_focus(window)).ok()
  }

  fn shift_focus(&mut self, order: frame::WindowOrder)
      -> Option<(frame::WindowId, frame::WindowId)> {
    self.frame.get_sequent_window(&self.frame_ctx, &self.focus, order, true).
    map(|window| self.move_focus(window)).ok()
  }

  fn move_focus(&mut self, window: frame::WindowId)
      -> (frame::WindowId, frame::WindowId) {
    let old_focus = std::mem::replace(&mut self.focus, window);
    (old_focus, self.focus.clone())
  }

  fn split_window(&mut self, orientation: frame::Orientation) {
    self.frame.split_window(&mut self.frame_ctx, &self.focus, orientation).
    map(|new_window| self.windows.insert(new_window, view::View::new())).
    ok().expect("Failed to split window.");
  }

  fn resize_window(&mut self, orientation: frame::Orientation, amount: int)
     -> bool {
    self.frame.resize_window(&self.frame_ctx, &self.focus, orientation, amount).
    map(|absorbed| absorbed != 0).unwrap()
  }

  fn close_window(&mut self) -> bool {
    self.frame.get_closest_neighbouring_window(&self.frame_ctx, &self.focus).
    map(|neighbour| {
      let (old_focus, _) = self.move_focus(neighbour);
      self.frame.close_window(&mut self.frame_ctx, &old_focus).unwrap();
      self.windows.remove(&old_focus); }).
    is_ok()
  }

  fn draw_window(&self, window: &frame::WindowId, screen: &mut screen::Screen) {
    self.windows.get(window).
    map(|view| self.draw_view(window, view, screen)).
    expect("Couldn't find view for window.");
    screen.flush();
  }

  fn draw_view(&self, window: &frame::WindowId, view: &view::View,
               screen: &mut screen::Screen) {
    self.frame.get_window_rect(&self.frame_ctx, window).
    map(|rect| { let screen::Rect(position, _) = rect; position }).
    map(|pos| view.draw(&self.buffer, self.focus == *window, pos, screen)).
    ok().expect("Couldn't find rect for window.");
  }

  fn draw_all(&self, screen: &mut screen::Screen) {
    screen.clear();
    self.frame.draw_borders(screen);
    for (window, view) in self.windows.iter() {
      self.draw_view(window, view, screen);
    }
    screen.flush();
  }

  fn update_view_sizes(&mut self) {
    let window_sizes: Vec<(frame::WindowId, screen::Size)> =
      self.windows.iter().
      map(|(win, _)|
        self.frame.get_window_rect(&self.frame_ctx, win).
        map(|rect| { let screen::Rect(_, size) = rect; (win.clone(), size) }).
        ok().expect("Couldn't find rect for window.")).
      collect();
    for &(ref win, size) in window_sizes.iter() {
      self.windows.remove(win).
      map(|mut view| {
        view.set_size(size, &self.buffer);
        self.windows.insert(win.clone(), view); }).
      expect("Couldn't find view for window.");
    }
  }

  fn handle_key_by_view(&mut self, key: keymap::Key) -> bool {
     self.windows.remove(&self.focus).
     map(|mut view| {
       let swallowed = match key {
         keymap::Key::Sym{sym: keymap::KeySym::Left, mods:_}  => {
           view.move_caret(view::CaretMovement::CharPrev, &self.buffer); true
         }
         keymap::Key::Sym{sym: keymap::KeySym::Right, mods:_} => {
           view.move_caret(view::CaretMovement::CharNext, &self.buffer); true
         }
         keymap::Key::Sym{sym: keymap::KeySym::Up, mods:_}    => {
           view.move_caret(view::CaretMovement::LineUp, &self.buffer); true
         }
         keymap::Key::Sym{sym: keymap::KeySym::Down, mods:_}  => {
           view.move_caret(view::CaretMovement::LineDown, &self.buffer); true
         }
         _                                                    => false,
       };
       self.windows.insert(self.focus.clone(), view);
       swallowed }).
     expect("Found no view for focused window.")
  }
}

#[cfg(not(test))]
impl Drop for Rim {
  fn drop(&mut self) {
    self.buffer.write().unwrap();  // what could go wrong!
  }
}

#[cfg(not(test))]
fn main() {
  let mut screen = screen::Screen::setup().unwrap();

  let mut rim = Rim::new();

  let (key_tx, key_rx) = std::sync::mpsc::channel();
  let _term_input = input::start(key_tx);

  let draw_window_pair =
    |&: (win1, win2), rim: &Rim, screen: &mut screen::Screen|
      { rim.draw_window(&win1, screen); rim.draw_window(&win2, screen); };

  loop {
    match key_rx.try_recv().ok().and_then(|key|
        if !rim.handle_key_by_view(key) { Some(key) }
        else { rim.draw_window(&rim.focus, &mut screen); None }) {
      Some(keymap::Key::Unicode{codepoint: 'h', mods:_}) => {
        rim.switch_focus(frame::Direction::Left).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'l', mods:_}) => {
        rim.switch_focus(frame::Direction::Right).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'k', mods:_}) => {
        rim.switch_focus(frame::Direction::Up).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'j', mods:_}) => {
        rim.switch_focus(frame::Direction::Down).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'n', mods:_}) => {
        rim.shift_focus(frame::WindowOrder::NextWindow).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'N', mods:_}) => {
        rim.shift_focus(frame::WindowOrder::PreviousWindow).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: '=', mods:_}) => {
        rim.frame.reset_layout();
        rim.update_view_sizes();
        rim.draw_all(&mut screen);
      }
      Some(keymap::Key::Unicode{codepoint: 'v', mods:_}) => {
        rim.split_window(frame::Orientation::Vertical);
        rim.update_view_sizes();
        rim.draw_all(&mut screen);
      }
      Some(keymap::Key::Unicode{codepoint: 's', mods:_}) => {
        rim.split_window(frame::Orientation::Horizontal);
        rim.update_view_sizes();
        rim.draw_all(&mut screen);
      }
      Some(keymap::Key::Unicode{codepoint: 'y', mods})   => {
        let amount = if mods.contains(keymap::MOD_CTRL) { -10 }
                     else                               { 10 };
        if rim.resize_window(frame::Orientation::Horizontal, amount) {
          rim.update_view_sizes();
          rim.draw_all(&mut screen);
        }
      }
      Some(keymap::Key::Unicode{codepoint: 'u', mods})   => {
        let amount = if mods.contains(keymap::MOD_CTRL) { -10 }
                     else                               { 10 };
        if rim.resize_window(frame::Orientation::Vertical, amount) {
          rim.update_view_sizes();
          rim.draw_all(&mut screen);
        }
      }
      Some(keymap::Key::Unicode{codepoint: 'c', mods:_}) =>
        if rim.close_window() {
          rim.update_view_sizes();
          rim.draw_all(&mut screen);
        },
      Some(keymap::Key::Unicode{codepoint: 'q', mods:_}) =>
        break,
      _                                                  =>
        (),
    }

    if screen.update_size() {
      rim.frame.set_size(screen.size());
      rim.update_view_sizes();
      rim.draw_all(&mut screen);
    }
  }
}
<|endoftext|>


<commit_before>// Copyright 2014 Strahinja Val Markovic
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use base::unicode::{bytesFollowing, readCodepoint};
use super::{Expression, ParseState, ParseResult};

macro_rules! class( ( $ex:expr ) => (
      &base::CharClass::new( $ex.as_bytes() ) ) );

fn toU32Vector( input: &[u8] ) -> Vec<u32> {
  let mut i = 0;
  let mut out_vec : Vec<u32> = vec!();
  loop {
    match input.get( i ) {
      Some( byte ) => match bytesFollowing( *byte ) {
        Some( num_following ) => {
          if num_following > 0 {
            match readCodepoint( &input[ i.. ] ) {
              Some( ch ) => {
                out_vec.push( ch as u32 );
                i += num_following + 1
              }
              _ => { out_vec.push( *byte as u32 ); i += 1 }
            };
          } else { out_vec.push( *byte as u32 ); i += 1 }
        }
        _ => { out_vec.push( *byte as u32 ); i += 1 }
      },
      _ => return out_vec
    }
  }
}

pub struct CharClass {
  // All the single chars in the char class.
  // May be unicode codepoints or binary octets stored as codepoints.
  single_chars: Vec<u32>,

  // Sequence of [from, to] (inclusive bounds) char ranges.
  // May be unicode codepoints or binary octets stored as codepoints.
  ranges: Vec<( u32, u32 )>
}

impl CharClass {
  // Takes the inner content of square brackets, so for [a-z], send "a-z".
  pub fn new( contents: &[u8] ) -> CharClass {
    fn rangeAtIndex( index: usize, chars: &[u32] ) -> Option<( u32, u32 )> {
      match ( chars.get( index ),
              chars.get( index + 1 ),
              chars.get( index + 2 ) ) {
        ( Some( char1 ), Some( char2 ), Some( char3 ) )
            if *char2 == '-' as u32 => Some( ( *char1, *char3 ) ),
        _ => None
      }
    }

    let chars = toU32Vector( &contents );
    let mut char_class = CharClass { single_chars: Vec::new(),
                                     ranges: Vec::new() };
    let mut index = 0;
    loop {
      match rangeAtIndex( index, &chars ) {
        Some( range ) => {
          char_class.ranges.push( range );
          index += 3;
        }
        _ => {
          if index >= chars.len() {
            break
          }
          char_class.single_chars.push( chars[ index ] );
          index += 1;
        }
      };
    }

    char_class
  }

  fn matches( &self, character: u32 ) -> bool {
    return self.single_chars.contains( &character ) ||
      self.ranges.iter().any(
        | &(from, to) | character >= from && character <= to );
  }

  fn applyToUtf8<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    match readCodepoint( parse_state.input ) {
      Some( ch ) if self.matches( ch as u32 ) => {
        let num_following = bytesFollowing( parse_state.input[ 0 ] ).unwrap();
        parse_state.offsetToResult( parse_state.offset + num_following + 1 )
      }
      _ => None
    }
  }

  fn applyToBytes<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    match parse_state.input.get( 0 ) {
      Some( byte ) if self.matches( *byte as u32 ) => {
        parse_state.offsetToResult( parse_state.offset + 1 )
      }
      _ => None
    }
  }
}

impl Expression for CharClass {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    self.applyToUtf8( parse_state ).or( self.applyToBytes( parse_state ) )
  }
}

#[cfg(test)]
mod tests {
  use base;
  use base::{Node, Data, ParseResult, Expression, ParseState};
  use base::test_utils::ToParseState;
  use base::unicode::bytesFollowing;
  use super::{CharClass};

  fn charClassMatch( char_class: &Expression, input: &[u8] ) -> bool {
    fn bytesRead( input: &[u8] ) -> usize {
      bytesFollowing( input[ 0 ] ).map_or( 1, |num| num + 1 )
    }

    match char_class.apply( &ToParseState( input ) ) {
      Some( ParseResult { nodes, parse_state } ) => {
        let bytes_read = bytesRead( input );
        assert_eq!( nodes[ 0 ],
                    Node::withoutName( 0, bytes_read, Data( input ) ) );
        assert_eq!( parse_state, ParseState{ input: &[], offset: bytes_read } );
        true
      }
      _ => false
    }
  }

  #[test]
  fn CharClass_Match() {
    assert!( charClassMatch( class!( "a"          ), b"a" ) );
    assert!( charClassMatch( class!( "abcdef"     ), b"e" ) );
    assert!( charClassMatch( class!( "a-z"        ), b"a" ) );
    assert!( charClassMatch( class!( "a-z"        ), b"c" ) );
    assert!( charClassMatch( class!( "a-z"        ), b"z" ) );
    assert!( charClassMatch( class!( "0-9"        ), b"2" ) );
    assert!( charClassMatch( class!( "α-ω"        ), "η".as_bytes() ) );
    assert!( charClassMatch( class!( "-"          ), b"-" ) );
    assert!( charClassMatch( class!( "a-"         ), b"-" ) );
    assert!( charClassMatch( class!( "-a"         ), b"-" ) );
    assert!( charClassMatch( class!( "a-zA-Z-"    ), b"-" ) );
    assert!( charClassMatch( class!( "aa-zA-Z-a"  ), b"-" ) );
    assert!( charClassMatch( class!( "a-zA-Z-"    ), b"z" ) );
    assert!( charClassMatch( class!( "aa-zA-Z-0"  ), b"0" ) );
    assert!( charClassMatch( class!( "a-cdefgh-k" ), b"e" ) );
    assert!( charClassMatch( class!( "---"        ), b"-" ) );
    assert!( charClassMatch( class!( "a-a"        ), b"a" ) );
  }

  // TODO: Use these tests for the char-class unescape code that will be written
  // in the code generator.
  // #[test]
  // fn CharClass_Match_ClassEscapedChars() {
  //   assert!( charClassMatch( &class!( r"\]" ), b"]" ) );
  //   assert!( charClassMatch( &class!( r"\\" ),  br"\" ) );
  //   assert!( !charClassMatch( &class!( r"\]" ), br"\" ) );
  // }

  #[test]
  fn CharClass_Match_NonUnicode() {
    assert!( charClassMatch( &CharClass::new( &[255] ), &[255] ) );
  }

  #[test]
  fn CharClass_NoMatch() {
    assert!( !charClassMatch( class!( "a"   ), b"b" ) );
    assert!( !charClassMatch( class!( "-"   ), b"a" ) );
    assert!( !charClassMatch( class!( "z-a" ), b"a" ) );
    assert!( !charClassMatch( class!( "z-a" ), b"b" ) );
    assert!( !charClassMatch( class!( "a-z" ), b"0" ) );
    assert!( !charClassMatch( class!( "a-z" ), b"A" ) );
  }

  // TODO: tests for escaped chars in class
}
<commit_msg>Deleted some commented-out & obsolete code<commit_after>// Copyright 2014 Strahinja Val Markovic
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use base::unicode::{bytesFollowing, readCodepoint};
use super::{Expression, ParseState, ParseResult};

macro_rules! class( ( $ex:expr ) => (
      &base::CharClass::new( $ex.as_bytes() ) ) );

fn toU32Vector( input: &[u8] ) -> Vec<u32> {
  let mut i = 0;
  let mut out_vec : Vec<u32> = vec!();
  loop {
    match input.get( i ) {
      Some( byte ) => match bytesFollowing( *byte ) {
        Some( num_following ) => {
          if num_following > 0 {
            match readCodepoint( &input[ i.. ] ) {
              Some( ch ) => {
                out_vec.push( ch as u32 );
                i += num_following + 1
              }
              _ => { out_vec.push( *byte as u32 ); i += 1 }
            };
          } else { out_vec.push( *byte as u32 ); i += 1 }
        }
        _ => { out_vec.push( *byte as u32 ); i += 1 }
      },
      _ => return out_vec
    }
  }
}

pub struct CharClass {
  // All the single chars in the char class.
  // May be unicode codepoints or binary octets stored as codepoints.
  single_chars: Vec<u32>,

  // Sequence of [from, to] (inclusive bounds) char ranges.
  // May be unicode codepoints or binary octets stored as codepoints.
  ranges: Vec<( u32, u32 )>
}

impl CharClass {
  // Takes the inner content of square brackets, so for [a-z], send "a-z".
  pub fn new( contents: &[u8] ) -> CharClass {
    fn rangeAtIndex( index: usize, chars: &[u32] ) -> Option<( u32, u32 )> {
      match ( chars.get( index ),
              chars.get( index + 1 ),
              chars.get( index + 2 ) ) {
        ( Some( char1 ), Some( char2 ), Some( char3 ) )
            if *char2 == '-' as u32 => Some( ( *char1, *char3 ) ),
        _ => None
      }
    }

    let chars = toU32Vector( &contents );
    let mut char_class = CharClass { single_chars: Vec::new(),
                                     ranges: Vec::new() };
    let mut index = 0;
    loop {
      match rangeAtIndex( index, &chars ) {
        Some( range ) => {
          char_class.ranges.push( range );
          index += 3;
        }
        _ => {
          if index >= chars.len() {
            break
          }
          char_class.single_chars.push( chars[ index ] );
          index += 1;
        }
      };
    }

    char_class
  }

  fn matches( &self, character: u32 ) -> bool {
    return self.single_chars.contains( &character ) ||
      self.ranges.iter().any(
        | &(from, to) | character >= from && character <= to );
  }

  fn applyToUtf8<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    match readCodepoint( parse_state.input ) {
      Some( ch ) if self.matches( ch as u32 ) => {
        let num_following = bytesFollowing( parse_state.input[ 0 ] ).unwrap();
        parse_state.offsetToResult( parse_state.offset + num_following + 1 )
      }
      _ => None
    }
  }

  fn applyToBytes<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    match parse_state.input.get( 0 ) {
      Some( byte ) if self.matches( *byte as u32 ) => {
        parse_state.offsetToResult( parse_state.offset + 1 )
      }
      _ => None
    }
  }
}

impl Expression for CharClass {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    self.applyToUtf8( parse_state ).or( self.applyToBytes( parse_state ) )
  }
}

#[cfg(test)]
mod tests {
  use base;
  use base::{Node, Data, ParseResult, Expression, ParseState};
  use base::test_utils::ToParseState;
  use base::unicode::bytesFollowing;
  use super::{CharClass};

  fn charClassMatch( char_class: &Expression, input: &[u8] ) -> bool {
    fn bytesRead( input: &[u8] ) -> usize {
      bytesFollowing( input[ 0 ] ).map_or( 1, |num| num + 1 )
    }

    match char_class.apply( &ToParseState( input ) ) {
      Some( ParseResult { nodes, parse_state } ) => {
        let bytes_read = bytesRead( input );
        assert_eq!( nodes[ 0 ],
                    Node::withoutName( 0, bytes_read, Data( input ) ) );
        assert_eq!( parse_state, ParseState{ input: &[], offset: bytes_read } );
        true
      }
      _ => false
    }
  }

  #[test]
  fn CharClass_Match() {
    assert!( charClassMatch( class!( "a"          ), b"a" ) );
    assert!( charClassMatch( class!( "abcdef"     ), b"e" ) );
    assert!( charClassMatch( class!( "a-z"        ), b"a" ) );
    assert!( charClassMatch( class!( "a-z"        ), b"c" ) );
    assert!( charClassMatch( class!( "a-z"        ), b"z" ) );
    assert!( charClassMatch( class!( "0-9"        ), b"2" ) );
    assert!( charClassMatch( class!( "α-ω"        ), "η".as_bytes() ) );
    assert!( charClassMatch( class!( "-"          ), b"-" ) );
    assert!( charClassMatch( class!( "a-"         ), b"-" ) );
    assert!( charClassMatch( class!( "-a"         ), b"-" ) );
    assert!( charClassMatch( class!( "a-zA-Z-"    ), b"-" ) );
    assert!( charClassMatch( class!( "aa-zA-Z-a"  ), b"-" ) );
    assert!( charClassMatch( class!( "a-zA-Z-"    ), b"z" ) );
    assert!( charClassMatch( class!( "aa-zA-Z-0"  ), b"0" ) );
    assert!( charClassMatch( class!( "a-cdefgh-k" ), b"e" ) );
    assert!( charClassMatch( class!( "---"        ), b"-" ) );
    assert!( charClassMatch( class!( "a-a"        ), b"a" ) );
  }

  #[test]
  fn CharClass_Match_NonUnicode() {
    assert!( charClassMatch( &CharClass::new( &[255] ), &[255] ) );
  }

  #[test]
  fn CharClass_NoMatch() {
    assert!( !charClassMatch( class!( "a"   ), b"b" ) );
    assert!( !charClassMatch( class!( "-"   ), b"a" ) );
    assert!( !charClassMatch( class!( "z-a" ), b"a" ) );
    assert!( !charClassMatch( class!( "z-a" ), b"b" ) );
    assert!( !charClassMatch( class!( "a-z" ), b"0" ) );
    assert!( !charClassMatch( class!( "a-z" ), b"A" ) );
  }

  // TODO: tests for escaped chars in class
}
<|endoftext|>


<commit_before>extern crate env_logger;

extern crate gluon_base as base;
extern crate gluon_parser as parser;
extern crate gluon_check as check;

use base::types::{Field, Type};

mod support;
use support::{intern, typ};

macro_rules! assert_pass {
    ($e: expr) => {{
        if !$e.is_ok() {
            panic!("assert_pass: {}", $e.unwrap_err());
        }
    }};
}

#[test]
fn infer_fields() {
    let _ = env_logger::init();

    let text = r#"
let f vec = vec.x #Int+ vec.y
f
"#;
    let result = support::typecheck(text);
    let record = Type::record(vec![],
                              vec![Field {
                                       name: intern("x"),
                                       typ: typ("Int"),
                                   },
                                   Field {
                                       name: intern("y"),
                                       typ: typ("Int"),
                                   }]);
    assert_eq!(result.map(support::close_record),
               Ok(Type::function(vec![record], typ("Int"))));
}

#[test]
fn infer_additional_fields() {
    let _ = env_logger::init();

    let text = r#"
let f vec = vec.x #Int+ vec.y
f { x = 1, y = 2, z = 3 }
"#;
    let result = support::typecheck(text);
    assert_eq!(result, Ok(typ("Int")));
}

#[test]
fn field_access_on_record_with_type() {
    let _ = env_logger::init();

    let text = r#"
type Test = Int
let record = { Test, x = 1, y = "" }
record.y
"#;
    let result = support::typecheck(text);
    assert_eq!(result, Ok(typ("String")));
}

// Test that arguments that have an applied (`Test a`) type properly unify even if they are not
// explicitly specified. The risk is that `x: Test a` is just resolved to `{ value : a }` which
// then fails to unify if it is unified with only typevariables (`$0 $1`)
#[test]
fn late_merge_with_signature() {
    let _ = env_logger::init();

    let text = r#"
type Monad m = { flat_map : (a -> m b) -> m a -> m b }
type Test a = { value : a }
let monad : Monad Test = {
    flat_map = \f x -> f x.value
}
monad
"#;
    let result = support::typecheck(text);
    assert!(result.is_ok(), "{}", result.unwrap_err());
}

#[test]
fn if_else_different_records() {
    let _ = env_logger::init();

    let text = r#"
if True then
    { y = "" }
else
    { x = 1 }
"#;
    let result = support::typecheck(text);
    assert!(result.is_err());
}

#[test]
fn missing_field() {
    let _ = env_logger::init();

    let text = r#"
let f vec = vec.x #Int+ vec.y
f { x = 1 }
"#;
    let result = support::typecheck(text);

    assert!(result.is_err());
}
<commit_msg>test: Test that two different polymorphic records can unify to the same type<commit_after>extern crate env_logger;

extern crate gluon_base as base;
extern crate gluon_parser as parser;
extern crate gluon_check as check;

use base::types::{Field, Type};

mod support;
use support::{intern, typ};

macro_rules! assert_pass {
    ($e: expr) => {{
        if !$e.is_ok() {
            panic!("assert_pass: {}", $e.unwrap_err());
        }
    }};
}

#[test]
fn infer_fields() {
    let _ = env_logger::init();

    let text = r#"
let f vec = vec.x #Int+ vec.y
f
"#;
    let result = support::typecheck(text);
    let record = Type::record(vec![],
                              vec![Field {
                                       name: intern("x"),
                                       typ: typ("Int"),
                                   },
                                   Field {
                                       name: intern("y"),
                                       typ: typ("Int"),
                                   }]);
    assert_eq!(result.map(support::close_record),
               Ok(Type::function(vec![record], typ("Int"))));
}

#[test]
fn infer_additional_fields() {
    let _ = env_logger::init();

    let text = r#"
let f vec = vec.x #Int+ vec.y
f { x = 1, y = 2, z = 3 }
"#;
    let result = support::typecheck(text);
    assert_eq!(result, Ok(typ("Int")));
}

#[test]
fn field_access_on_record_with_type() {
    let _ = env_logger::init();

    let text = r#"
type Test = Int
let record = { Test, x = 1, y = "" }
record.y
"#;
    let result = support::typecheck(text);
    assert_eq!(result, Ok(typ("String")));
}

// Test that arguments that have an applied (`Test a`) type properly unify even if they are not
// explicitly specified. The risk is that `x: Test a` is just resolved to `{ value : a }` which
// then fails to unify if it is unified with only typevariables (`$0 $1`)
#[test]
fn late_merge_with_signature() {
    let _ = env_logger::init();

    let text = r#"
type Monad m = { flat_map : (a -> m b) -> m a -> m b }
type Test a = { value : a }
let monad : Monad Test = {
    flat_map = \f x -> f x.value
}
monad
"#;
    let result = support::typecheck(text);
    assert!(result.is_ok(), "{}", result.unwrap_err());
}

#[test]
fn equality_of_records_with_differing_fields() {
    let _ = env_logger::init();

    let text = r#"
let eq x y : a -> a -> () = ()
let f v1 v2 =
    v1.x
    v2.y
    eq v1 v2
()
"#;
    let result = support::typecheck(text);
    assert!(result.is_ok(), "{}", result.unwrap_err());
}

#[test]
fn if_else_different_records() {
    let _ = env_logger::init();

    let text = r#"
if True then
    { y = "" }
else
    { x = 1 }
"#;
    let result = support::typecheck(text);
    assert!(result.is_err());
}

#[test]
fn missing_field() {
    let _ = env_logger::init();

    let text = r#"
let f vec = vec.x #Int+ vec.y
f { x = 1 }
"#;
    let result = support::typecheck(text);

    assert!(result.is_err());
}
<|endoftext|>