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

use std::os::unix::io::AsRawFd;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;

use crate::gpio::{
    interrupt::AsyncInterrupt,
    GpioState, Level, Mode,
    PullUpDown::{self, *},
    Result, Trigger,
};

// Maximum GPIO pins on the BCM2835. The actual number of pins
// exposed through the Pi's GPIO header depends on the model.
pub const MAX: usize = 54;

/// Unconfigured GPIO pin.
#[derive(Debug)]
pub struct Pin {
    pub(crate) pin: u8,
    gpio_state: Arc<GpioState>,
}

impl Pin {
    #[inline]
    pub(crate) fn new(pin: u8, gpio_state: Arc<GpioState>) -> Pin {
        Pin { pin, gpio_state }
    }

    /// Consumes the `Pin`, returns an [`InputPin`], sets its mode to [`Mode::Input`],
    /// and disables the pin's built-in pull-up/pull-down resistors.
    ///
    /// [`InputPin`]: struct.InputPin.html
    /// [`Mode::Input`]: enum.Mode.html#variant.Input
    #[inline]
    pub fn into_input(self) -> InputPin {
        InputPin::new(self, Off)
    }

    /// Consumes the `Pin`, returns an [`InputPin`], sets its mode to [`Mode::Input`],
    /// and enables the pin's built-in pull-down resistor.
    ///
    /// The pull-down resistor is disabled when `InputPin` goes out of scope if [`reset_on_drop`]
    /// is set to `true` (default).
    ///
    /// [`InputPin`]: struct.InputPin.html
    /// [`Mode::Input`]: enum.Mode.html#variant.Input
    /// [`reset_on_drop`]: struct.InputPin.html#method.set_reset_on_drop
    #[inline]
    pub fn into_input_pulldown(self) -> InputPin {
        InputPin::new(self, PullDown)
    }

    /// Consumes the `Pin`, returns an [`InputPin`], sets its mode to [`Mode::Input`],
    /// and enables the pin's built-in pull-up resistor.
    ///
    /// The pull-up resistor is disabled when `InputPin` goes out of scope if [`reset_on_drop`]
    /// is set to `true` (default).
    ///
    /// [`InputPin`]: struct.InputPin.html
    /// [`Mode::Input`]: enum.Mode.html#variant.Input
    /// [`reset_on_drop`]: struct.InputPin.html#method.set_reset_on_drop
    #[inline]
    pub fn into_input_pullup(self) -> InputPin {
        InputPin::new(self, PullUp)
    }

    /// Consumes the pin, returns an [`OutputPin`] and sets its mode to [`Mode::Output`].
    ///
    /// [`OutputPin`]: struct.OutputPin.html
    /// [`Mode::Output`]: enum.Mode.html#variant.Output
    #[inline]
    pub fn into_output(self) -> OutputPin {
        OutputPin::new(self)
    }

    /// Consumes the pin, returns an [`AltPin`] and sets its mode to the given mode.
    ///
    /// [`AltPin`]: struct.AltPin.html
    /// [`Mode`]: enum.Mode.html
    #[inline]
    pub fn into_alt(self, mode: Mode) -> AltPin {
        AltPin::new(self, mode)
    }

    /// Returns the GPIO pin number.
    ///
    /// Pins are addressed by their BCM numbers, rather than their physical location.
    #[inline]
    pub fn pin(&self) -> u8 {
        self.pin
    }

    #[inline]
    pub(crate) fn set_mode(&mut self, mode: Mode) {
        self.gpio_state.gpio_mem.set_mode(self.pin, mode);
    }

    /// Returns the current GPIO pin mode.
    #[inline]
    pub fn mode(&self) -> Mode {
        self.gpio_state.gpio_mem.mode(self.pin)
    }

    /// Configures the built-in GPIO pull-up/pull-down resistors.
    #[inline]
    pub(crate) fn set_pullupdown(&self, pud: PullUpDown) {
        self.gpio_state.gpio_mem.set_pullupdown(self.pin, pud);
    }

    /// Reads the pin's current logic level.
    #[inline]
    pub fn read(&self) -> Level {
        self.gpio_state.gpio_mem.level(self.pin)
    }

    #[inline]
    pub(crate) fn set_low(&mut self) {
        self.gpio_state.gpio_mem.set_low(self.pin);
    }

    #[inline]
    pub(crate) fn set_high(&mut self) {
        self.gpio_state.gpio_mem.set_high(self.pin);
    }

    #[inline]
    pub(crate) fn write(&mut self, level: Level) {
        match level {
            Level::Low => self.set_low(),
            Level::High => self.set_high(),
        };
    }
}

impl Drop for Pin {
    fn drop(&mut self) {
        // Release taken pin
        self.gpio_state.pins_taken[self.pin as usize].store(false, Ordering::SeqCst);
    }
}

macro_rules! impl_pin {
    () => {
        /// Returns the GPIO pin number.
        ///
        /// Pins are addressed by their BCM numbers, rather than their physical location.
        #[inline]
        pub fn pin(&self) -> u8 {
            self.pin.pin
        }
    }
}

macro_rules! impl_input {
    () => {
        /// Reads the pin's current logic level.
        #[inline]
        pub fn read(&self) -> Level {
            self.pin.read()
        }

        /// Returns `true` if the pin's logic level is [`Level::Low`].
        ///
        /// [`Level::Low`]: enum.Level.html
        #[inline]
        pub fn is_low(&self) -> bool {
            self.pin.read() == Level::Low
        }

        /// Returns `true` if the pin's logic level is [`Level::High`].
        ///
        /// [`Level::High`]: enum.Level.html
        #[inline]
        pub fn is_high(&self) -> bool {
            self.pin.read() == Level::High
        }
    }
}

macro_rules! impl_output {
    () => {
        /// Sets pin's logic level to [`Level::Low`].
        ///
        /// [`Level::Low`]: enum.Level.html
        #[inline]
        pub fn set_low(&mut self) {
            self.pin.set_low()
        }

        /// Sets pin's logic level to [`Level::High`].
        ///
        /// [`Level::High`]: enum.Level.html
        #[inline]
        pub fn set_high(&mut self) {
            self.pin.set_high()
        }

        /// Sets pin's logic level.
        #[inline]
        pub fn write(&mut self, level: Level) {
            self.pin.write(level)
        }
    }
}

macro_rules! impl_reset_on_drop {
    () => {
        /// Returns the value of `reset_on_drop`.
        pub fn reset_on_drop(&self) -> bool {
            self.reset_on_drop
        }

        /// When enabled, resets the pin's mode to its original state and disables the
        /// built-in pull-up/pull-down resistors, when the pin goes out of scope.
        /// By default, this is set to `true`.
        ///
        /// # Note
        ///
        /// Drop methods aren't called when a program is abnormally terminated, for
        /// instance when a user presses <kbd>Ctrl + C</kbd>, and the `SIGINT` signal
        /// isn't caught. You catch those using crates such as [`simple_signal`].
        ///
        /// [`simple_signal`]: https://crates.io/crates/simple-signal
        pub fn set_reset_on_drop(&mut self, reset_on_drop: bool) {
            self.reset_on_drop = reset_on_drop;
        }
    };
}

macro_rules! impl_drop {
    ($struct:ident) => {
        impl Drop for $struct {
            /// Resets the pin's mode and built-in pull-up/pull-down resistors if
            /// `reset_on_drop` is set to `true` (default).
            fn drop(&mut self) {
                if !self.reset_on_drop {
                    return;
                }

                if let Some(prev_mode) = self.prev_mode {
                    self.pin.set_mode(prev_mode);
                }

                if self.pud_mode != Off {
                    self.pin.set_pullupdown(Off);
                }
            }
        }
    };
}

/// GPIO pin configured as input.
#[derive(Debug)]
pub struct InputPin {
    pub(crate) pin: Pin,
    prev_mode: Option<Mode>,
    async_interrupt: Option<AsyncInterrupt>,
    reset_on_drop: bool,
    pud_mode: PullUpDown,
}

impl InputPin {
    pub(crate) fn new(mut pin: Pin, pud_mode: PullUpDown) -> InputPin {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == Mode::Input {
            None
        } else {
            pin.set_mode(Mode::Input);
            Some(prev_mode)
        };

        pin.set_pullupdown(pud_mode);

        InputPin {
            pin,
            prev_mode,
            async_interrupt: None,
            reset_on_drop: true,
            pud_mode,
        }
    }

    impl_pin!();
    impl_input!();

    /// Configures a synchronous interrupt trigger.
    ///
    /// After configuring a synchronous interrupt trigger, use [`poll_interrupt`] or
    /// [`Gpio::poll_interrupts`] to block while waiting for a trigger event.
    ///
    /// Any previously configured (a)synchronous interrupt triggers will be cleared.
    ///
    /// [`poll_interrupt`]: #method.poll_interrupt
    /// [`Gpio::poll_interrupts`]: struct.Gpio#method.poll_interrupts
    pub fn set_interrupt(&mut self, trigger: Trigger) -> Result<()> {
        self.clear_async_interrupt()?;

        // Each pin can only be configured for a single trigger type
        (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).set_interrupt(self.pin(), trigger)
    }

    /// Removes a previously configured synchronous interrupt trigger.
    pub fn clear_interrupt(&mut self) -> Result<()> {
        (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).clear_interrupt(self.pin())
    }

    /// Blocks until an interrupt is triggered on the pin, or a timeout occurs.
    ///
    /// This only works after the pin has been configured for synchronous interrupts using
    /// [`set_interrupt`]. Asynchronous interrupt triggers are automatically polled on a separate thread.
    ///
    /// Calling `poll_interrupt` blocks any other calls to `poll_interrupt` (including on other `InputPin`s) or
    /// [`Gpio::poll_interrupts`] until it returns. If you need to poll multiple pins simultaneously, use
    /// [`Gpio::poll_interrupts`] to block while waiting for any of the interrupts to trigger, or switch to
    /// using asynchronous interrupts with [`set_async_interrupt`].
    ///
    /// If `reset` is set to `false`, returns immediately if an interrupt trigger event was cached in a
    /// previous call to `poll_interrupt`.
    /// If `reset` is set to `true`, clears any cached interrupt trigger events before polling.
    ///
    /// The `timeout` duration indicates how long the call will block while waiting
    /// for interrupt trigger events, after which an `Ok(None))` is returned.
    /// `timeout` can be set to `None` to wait indefinitely.
    ///
    /// [`set_interrupt`]: #method.set_interrupt
    /// [`Gpio::poll_interrupts`]: struct.Gpio#method.poll_interrupts
    /// [`set_async_interrupt`]: #method.set_async_interrupt
    pub fn poll_interrupt(
        &mut self,
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<Level>> {
        let opt =
            (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).poll(&[self], reset, timeout)?;

        if let Some(trigger) = opt {
            Ok(Some(trigger.1))
        } else {
            Ok(None)
        }
    }

    /// Configures an asynchronous interrupt trigger, which will execute the callback on a
    /// separate thread when the interrupt is triggered.
    ///
    /// The callback closure or function pointer is called with a single [`Level`] argument.
    ///
    /// Any previously configured (a)synchronous interrupt triggers will be cleared.
    ///
    /// [`Level`]: enum.Level.html
    pub fn set_async_interrupt<C>(&mut self, trigger: Trigger, callback: C) -> Result<()>
    where
        C: FnMut(Level) + Send + 'static,
    {
        self.clear_interrupt()?;
        self.clear_async_interrupt()?;

        self.async_interrupt = Some(AsyncInterrupt::new(
            self.pin.gpio_state.cdev.as_raw_fd(),
            self.pin(),
            trigger,
            callback,
        )?);

        Ok(())
    }

    /// Removes a previously configured asynchronous interrupt trigger.
    pub fn clear_async_interrupt(&mut self) -> Result<()> {
        if let Some(mut interrupt) = self.async_interrupt.take() {
            interrupt.stop()?;
        }

        Ok(())
    }

    impl_reset_on_drop!();
}

impl_drop!(InputPin);

/// GPIO pin configured as output.
#[derive(Debug)]
pub struct OutputPin {
    pin: Pin,
    prev_mode: Option<Mode>,
    reset_on_drop: bool,
    pud_mode: PullUpDown,
}

impl OutputPin {
    pub(crate) fn new(mut pin: Pin) -> OutputPin {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == Mode::Output {
            None
        } else {
            pin.set_mode(Mode::Output);
            Some(prev_mode)
        };

        OutputPin {
            pin,
            prev_mode,
            reset_on_drop: true,
            pud_mode: Off,
        }
    }

    impl_pin!();
    impl_input!();
    impl_output!();
    impl_reset_on_drop!();
}

impl_drop!(OutputPin);

/// GPIO pin configured with an alternate function.
#[derive(Debug)]
pub struct AltPin {
    pin: Pin,
    mode: Mode,
    prev_mode: Option<Mode>,
    reset_on_drop: bool,
    pud_mode: PullUpDown,
}

impl AltPin {
    pub(crate) fn new(mut pin: Pin, mode: Mode) -> AltPin {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == mode {
            None
        } else {
            pin.set_mode(mode);
            Some(prev_mode)
        };

        AltPin {
            pin,
            mode,
            prev_mode,
            reset_on_drop: true,
            pud_mode: Off,
        }
    }

    impl_pin!();
    impl_input!();
    impl_output!();
    impl_reset_on_drop!();
}

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

use std::os::unix::io::AsRawFd;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;

use crate::gpio::{
    interrupt::AsyncInterrupt,
    GpioState, Level, Mode,
    PullUpDown::{self, *},
    Result, Trigger,
};

// Maximum GPIO pins on the BCM2835. The actual number of pins
// exposed through the Pi's GPIO header depends on the model.
pub const MAX: usize = 54;

/// Unconfigured GPIO pin.
#[derive(Debug)]
pub struct Pin {
    pub(crate) pin: u8,
    gpio_state: Arc<GpioState>,
}

impl Pin {
    #[inline]
    pub(crate) fn new(pin: u8, gpio_state: Arc<GpioState>) -> Pin {
        Pin { pin, gpio_state }
    }

    /// Consumes the `Pin`, returns an [`InputPin`], sets its mode to [`Mode::Input`],
    /// and disables the pin's built-in pull-up/pull-down resistors.
    ///
    /// [`InputPin`]: struct.InputPin.html
    /// [`Mode::Input`]: enum.Mode.html#variant.Input
    #[inline]
    pub fn into_input(self) -> InputPin {
        InputPin::new(self, Off)
    }

    /// Consumes the `Pin`, returns an [`InputPin`], sets its mode to [`Mode::Input`],
    /// and enables the pin's built-in pull-down resistor.
    ///
    /// The pull-down resistor is disabled when `InputPin` goes out of scope if [`reset_on_drop`]
    /// is set to `true` (default).
    ///
    /// [`InputPin`]: struct.InputPin.html
    /// [`Mode::Input`]: enum.Mode.html#variant.Input
    /// [`reset_on_drop`]: struct.InputPin.html#method.set_reset_on_drop
    #[inline]
    pub fn into_input_pulldown(self) -> InputPin {
        InputPin::new(self, PullDown)
    }

    /// Consumes the `Pin`, returns an [`InputPin`], sets its mode to [`Mode::Input`],
    /// and enables the pin's built-in pull-up resistor.
    ///
    /// The pull-up resistor is disabled when `InputPin` goes out of scope if [`reset_on_drop`]
    /// is set to `true` (default).
    ///
    /// [`InputPin`]: struct.InputPin.html
    /// [`Mode::Input`]: enum.Mode.html#variant.Input
    /// [`reset_on_drop`]: struct.InputPin.html#method.set_reset_on_drop
    #[inline]
    pub fn into_input_pullup(self) -> InputPin {
        InputPin::new(self, PullUp)
    }

    /// Consumes the pin, returns an [`OutputPin`] and sets its mode to [`Mode::Output`].
    ///
    /// [`OutputPin`]: struct.OutputPin.html
    /// [`Mode::Output`]: enum.Mode.html#variant.Output
    #[inline]
    pub fn into_output(self) -> OutputPin {
        OutputPin::new(self)
    }

    /// Consumes the pin, returns an [`AltPin`] and sets its mode to the given mode.
    ///
    /// [`AltPin`]: struct.AltPin.html
    /// [`Mode`]: enum.Mode.html
    #[inline]
    pub fn into_alt(self, mode: Mode) -> AltPin {
        AltPin::new(self, mode)
    }

    /// Returns the GPIO pin number.
    ///
    /// Pins are addressed by their BCM numbers, rather than their physical location.
    #[inline]
    pub fn pin(&self) -> u8 {
        self.pin
    }

    #[inline]
    pub(crate) fn set_mode(&mut self, mode: Mode) {
        self.gpio_state.gpio_mem.set_mode(self.pin, mode);
    }

    /// Returns the current GPIO pin mode.
    #[inline]
    pub fn mode(&self) -> Mode {
        self.gpio_state.gpio_mem.mode(self.pin)
    }

    /// Configures the built-in GPIO pull-up/pull-down resistors.
    #[inline]
    pub(crate) fn set_pullupdown(&self, pud: PullUpDown) {
        self.gpio_state.gpio_mem.set_pullupdown(self.pin, pud);
    }

    /// Reads the pin's current logic level.
    #[inline]
    pub fn read(&self) -> Level {
        self.gpio_state.gpio_mem.level(self.pin)
    }

    #[inline]
    pub(crate) fn set_low(&mut self) {
        self.gpio_state.gpio_mem.set_low(self.pin);
    }

    #[inline]
    pub(crate) fn set_high(&mut self) {
        self.gpio_state.gpio_mem.set_high(self.pin);
    }

    #[inline]
    pub(crate) fn write(&mut self, level: Level) {
        match level {
            Level::Low => self.set_low(),
            Level::High => self.set_high(),
        };
    }
}

impl Drop for Pin {
    fn drop(&mut self) {
        // Release taken pin
        self.gpio_state.pins_taken[self.pin as usize].store(false, Ordering::SeqCst);
    }
}

macro_rules! impl_pin {
    () => {
        /// Returns the GPIO pin number.
        ///
        /// Pins are addressed by their BCM numbers, rather than their physical location.
        #[inline]
        pub fn pin(&self) -> u8 {
            self.pin.pin
        }
    }
}

macro_rules! impl_input {
    () => {
        /// Reads the pin's current logic level.
        #[inline]
        pub fn read(&self) -> Level {
            self.pin.read()
        }

        /// Returns `true` if the pin's logic level is [`Level::Low`].
        ///
        /// [`Level::Low`]: enum.Level.html
        #[inline]
        pub fn is_low(&self) -> bool {
            self.pin.read() == Level::Low
        }

        /// Returns `true` if the pin's logic level is [`Level::High`].
        ///
        /// [`Level::High`]: enum.Level.html
        #[inline]
        pub fn is_high(&self) -> bool {
            self.pin.read() == Level::High
        }
    }
}

macro_rules! impl_output {
    () => {
        /// Sets pin's logic level to [`Level::Low`].
        ///
        /// [`Level::Low`]: enum.Level.html
        #[inline]
        pub fn set_low(&mut self) {
            self.pin.set_low()
        }

        /// Sets pin's logic level to [`Level::High`].
        ///
        /// [`Level::High`]: enum.Level.html
        #[inline]
        pub fn set_high(&mut self) {
            self.pin.set_high()
        }

        /// Sets pin's logic level.
        #[inline]
        pub fn write(&mut self, level: Level) {
            self.pin.write(level)
        }
    }
}

macro_rules! impl_reset_on_drop {
    () => {
        /// Returns the value of `reset_on_drop`.
        pub fn reset_on_drop(&self) -> bool {
            self.reset_on_drop
        }

        /// When enabled, resets the pin's mode to its original state and disables the
        /// built-in pull-up/pull-down resistors, when the pin goes out of scope.
        /// By default, this is set to `true`.
        ///
        /// # Note
        ///
        /// Drop methods aren't called when a program is abnormally terminated, for
        /// instance when a user presses <kbd>Ctrl + C</kbd>, and the `SIGINT` signal
        /// isn't caught. You catch those using crates such as [`simple_signal`].
        ///
        /// [`simple_signal`]: https://crates.io/crates/simple-signal
        pub fn set_reset_on_drop(&mut self, reset_on_drop: bool) {
            self.reset_on_drop = reset_on_drop;
        }
    };
}

macro_rules! impl_drop {
    ($struct:ident) => {
        impl Drop for $struct {
            /// Resets the pin's mode and disables the built-in pull-up/pull-down
            /// resistors if `reset_on_drop` is set to `true` (default).
            fn drop(&mut self) {
                if !self.reset_on_drop {
                    return;
                }

                if let Some(prev_mode) = self.prev_mode {
                    self.pin.set_mode(prev_mode);
                }

                if self.pud_mode != Off {
                    self.pin.set_pullupdown(Off);
                }
            }
        }
    };
}

/// GPIO pin configured as input.
#[derive(Debug)]
pub struct InputPin {
    pub(crate) pin: Pin,
    prev_mode: Option<Mode>,
    async_interrupt: Option<AsyncInterrupt>,
    reset_on_drop: bool,
    pud_mode: PullUpDown,
}

impl InputPin {
    pub(crate) fn new(mut pin: Pin, pud_mode: PullUpDown) -> InputPin {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == Mode::Input {
            None
        } else {
            pin.set_mode(Mode::Input);
            Some(prev_mode)
        };

        pin.set_pullupdown(pud_mode);

        InputPin {
            pin,
            prev_mode,
            async_interrupt: None,
            reset_on_drop: true,
            pud_mode,
        }
    }

    impl_pin!();
    impl_input!();

    /// Configures a synchronous interrupt trigger.
    ///
    /// After configuring a synchronous interrupt trigger, use [`poll_interrupt`] or
    /// [`Gpio::poll_interrupts`] to block while waiting for a trigger event.
    ///
    /// Any previously configured (a)synchronous interrupt triggers will be cleared.
    ///
    /// [`poll_interrupt`]: #method.poll_interrupt
    /// [`Gpio::poll_interrupts`]: struct.Gpio#method.poll_interrupts
    pub fn set_interrupt(&mut self, trigger: Trigger) -> Result<()> {
        self.clear_async_interrupt()?;

        // Each pin can only be configured for a single trigger type
        (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).set_interrupt(self.pin(), trigger)
    }

    /// Removes a previously configured synchronous interrupt trigger.
    pub fn clear_interrupt(&mut self) -> Result<()> {
        (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).clear_interrupt(self.pin())
    }

    /// Blocks until an interrupt is triggered on the pin, or a timeout occurs.
    ///
    /// This only works after the pin has been configured for synchronous interrupts using
    /// [`set_interrupt`]. Asynchronous interrupt triggers are automatically polled on a separate thread.
    ///
    /// Calling `poll_interrupt` blocks any other calls to `poll_interrupt` (including on other `InputPin`s) or
    /// [`Gpio::poll_interrupts`] until it returns. If you need to poll multiple pins simultaneously, use
    /// [`Gpio::poll_interrupts`] to block while waiting for any of the interrupts to trigger, or switch to
    /// using asynchronous interrupts with [`set_async_interrupt`].
    ///
    /// If `reset` is set to `false`, returns immediately if an interrupt trigger event was cached in a
    /// previous call to `poll_interrupt`.
    /// If `reset` is set to `true`, clears any cached interrupt trigger events before polling.
    ///
    /// The `timeout` duration indicates how long the call will block while waiting
    /// for interrupt trigger events, after which an `Ok(None))` is returned.
    /// `timeout` can be set to `None` to wait indefinitely.
    ///
    /// [`set_interrupt`]: #method.set_interrupt
    /// [`Gpio::poll_interrupts`]: struct.Gpio#method.poll_interrupts
    /// [`set_async_interrupt`]: #method.set_async_interrupt
    pub fn poll_interrupt(
        &mut self,
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<Level>> {
        let opt =
            (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).poll(&[self], reset, timeout)?;

        if let Some(trigger) = opt {
            Ok(Some(trigger.1))
        } else {
            Ok(None)
        }
    }

    /// Configures an asynchronous interrupt trigger, which will execute the callback on a
    /// separate thread when the interrupt is triggered.
    ///
    /// The callback closure or function pointer is called with a single [`Level`] argument.
    ///
    /// Any previously configured (a)synchronous interrupt triggers will be cleared.
    ///
    /// [`Level`]: enum.Level.html
    pub fn set_async_interrupt<C>(&mut self, trigger: Trigger, callback: C) -> Result<()>
    where
        C: FnMut(Level) + Send + 'static,
    {
        self.clear_interrupt()?;
        self.clear_async_interrupt()?;

        self.async_interrupt = Some(AsyncInterrupt::new(
            self.pin.gpio_state.cdev.as_raw_fd(),
            self.pin(),
            trigger,
            callback,
        )?);

        Ok(())
    }

    /// Removes a previously configured asynchronous interrupt trigger.
    pub fn clear_async_interrupt(&mut self) -> Result<()> {
        if let Some(mut interrupt) = self.async_interrupt.take() {
            interrupt.stop()?;
        }

        Ok(())
    }

    impl_reset_on_drop!();
}

impl_drop!(InputPin);

/// GPIO pin configured as output.
#[derive(Debug)]
pub struct OutputPin {
    pin: Pin,
    prev_mode: Option<Mode>,
    reset_on_drop: bool,
    pud_mode: PullUpDown,
}

impl OutputPin {
    pub(crate) fn new(mut pin: Pin) -> OutputPin {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == Mode::Output {
            None
        } else {
            pin.set_mode(Mode::Output);
            Some(prev_mode)
        };

        OutputPin {
            pin,
            prev_mode,
            reset_on_drop: true,
            pud_mode: Off,
        }
    }

    impl_pin!();
    impl_input!();
    impl_output!();
    impl_reset_on_drop!();
}

impl_drop!(OutputPin);

/// GPIO pin configured with an alternate function.
#[derive(Debug)]
pub struct AltPin {
    pin: Pin,
    mode: Mode,
    prev_mode: Option<Mode>,
    reset_on_drop: bool,
    pud_mode: PullUpDown,
}

impl AltPin {
    pub(crate) fn new(mut pin: Pin, mode: Mode) -> AltPin {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == mode {
            None
        } else {
            pin.set_mode(mode);
            Some(prev_mode)
        };

        AltPin {
            pin,
            mode,
            prev_mode,
            reset_on_drop: true,
            pud_mode: Off,
        }
    }

    impl_pin!();
    impl_input!();
    impl_output!();
    impl_reset_on_drop!();
}

impl_drop!(AltPin);
<|endoftext|>


<commit_before>// TODO Better struct names
#![allow(non_snake_case)]
#[derive(RustcDecodable, Debug)]
pub struct Senderaddress {
    pub addressLine1: String,
    pub addressLine2: String,
    pub postalCode: String,
    pub city: String,
    pub countryCode: String,
    pub country: String,
}
#[derive(RustcDecodable, Debug)]
pub struct Recipientsignature {
    pub name: String,
}
#[derive(RustcDecodable, Debug)]
pub struct EventsetDefinition {
    pub term: String,
    pub explanation: String,
}
#[derive(RustcDecodable, Debug)]
pub struct Eventset {
    pub description: String,
    pub status: String,
    pub recipientSignature: Recipientsignature,
    pub unitId: String,
    pub unitInformationUrl: Option<String>,
    pub unitType: String,
    pub postalCode: String,
    pub city: String,
    pub countryCode: String,
    pub country: String,
    pub dateIso: String,
    pub displayDate: String,
    pub displayTime: String,
    pub consignmentEvent: bool,
    pub definitions: Option<Vec<EventsetDefinition>>
}
#[derive(RustcDecodable, Debug)]
pub struct Packageset {
    pub statusDescription: String,
    pub descriptions: Vec<String>,
    pub packageNumber: String,
    pub previousPackageNumber: String,
    pub productName: String,
    pub productCode: String,
    pub brand: String,
    pub lengthInCm: i64,
    pub widthInCm: i64,
    pub heightInCm: i64,
    pub volumeInDm3: f64,
    pub weightInKgs: f64,
    pub pickupCode: String,
    pub dateOfReturn: String,
    pub senderName: String,
    pub senderAddress: Senderaddress,
    pub recipientHandlingAddress: Senderaddress,
    pub eventSet: Vec<Eventset>,
}

#[derive(RustcDecodable, Debug)]
pub struct Consignmentset {
    pub consignmentId: String,
    pub previousConsignmentId: String,
    pub packageSet: Vec<Packageset>,
    pub senderName: String,
    pub senderAddress: Senderaddress,
    pub recipientHandlingAddress: Senderaddress,
    pub senderReference: String,
    pub totalWeightInKgs: f64,
    pub totalVolumeInDm3: f64,
}
#[derive(RustcDecodable, Debug)]
pub struct BringResponse {
    pub consignmentSet: Vec<Consignmentset>,
}
<commit_msg>add Option to all values, e.g if senderAddress isn't present<commit_after>#![allow(non_snake_case)]
#[derive(RustcDecodable, Debug)]
pub struct Senderaddress {
    pub addressLine1: Option<String>,
    pub addressLine2: Option<String>,
    pub postalCode: Option<String>,
    pub city: Option<String>,
    pub countryCode: Option<String>,
    pub country: Option<String>,
}
#[derive(RustcDecodable, Debug)]
pub struct Recipientsignature {
    pub name: Option<String>,
}
#[derive(RustcDecodable, Debug)]
pub struct EventsetDefinition {
    pub term: Option<String>,
    pub explanation: Option<String>,
}
#[derive(RustcDecodable, Debug)]
pub struct Eventset {
    pub description: Option<String>,
    pub status: Option<String>,
    pub recipientSignature: Option<Recipientsignature>,
    pub unitId: Option<String>,
    pub unitInformationUrl: Option<String>,
    pub unitType: Option<String>,
    pub postalCode: Option<String>,
    pub city: Option<String>,
    pub countryCode: Option<String>,
    pub country: Option<String>,
    pub dateIso: Option<String>,
    pub displayDate: Option<String>,
    pub displayTime: Option<String>,
    pub consignmentEvent: bool,
    pub definitions: Option<Vec<EventsetDefinition>>
}
#[derive(RustcDecodable, Debug)]
pub struct Packageset {
    pub statusDescription: String,
    pub descriptions: Vec<String>,
    pub packageNumber: String,
    pub previousPackageNumber: Option<String>,
    pub productName: Option<String>,
    pub productCode: Option<String>,
    pub brand: Option<String>,
    pub lengthInCm: Option<i64>,
    pub widthInCm: Option<i64>,
    pub heightInCm: Option<i64>,
    pub volumeInDm3: Option<f64>,
    pub weightInKgs: Option<f64>,
    pub pickupCode: Option<String>,
    pub dateOfReturn: Option<String>,
    pub senderName: Option<String>,
    pub senderAddress: Option<Senderaddress>,
    pub recipientHandlingAddress: Option<Senderaddress>,
    pub eventSet: Vec<Eventset>,
}

#[derive(RustcDecodable, Debug)]
pub struct Consignmentset {
    pub consignmentId: String,
    pub previousConsignmentId: String,
    pub packageSet: Vec<Packageset>,
    pub senderName: Option<String>,
    pub senderAddress: Option<Senderaddress>,
    pub recipientHandlingAddress: Option<Senderaddress>,
    pub senderReference: Option<String>,
    pub totalWeightInKgs: Option<f64>,
    pub totalVolumeInDm3: Option<f64>,
}
#[derive(RustcDecodable, Debug)]
pub struct BringResponse {
    pub consignmentSet: Vec<Consignmentset>,
}
<|endoftext|>


<commit_before>use iron::headers;
use iron::prelude::*;
use iron::method::Method;
use iron::modifiers::{Redirect, Header};
use iron::{Handler, Url, status};

use rand;
use rand::Rng;

use std::collections::HashMap;
use std::sync::RwLock;

use urlencoded::UrlEncodedBody;

pub struct UrlShortenerHandler {
    shortened_urls: RwLock<HashMap<String, Url>>,
}

impl UrlShortenerHandler {
    pub fn new() -> Self {
        UrlShortenerHandler { shortened_urls: RwLock::new(HashMap::new()) }
    }

    fn extract_url(req: &mut Request) -> Result<Url, &'static str> {
        req.get_ref::<UrlEncodedBody>()
            .map_err(|_| "URL encoded body missing")
            .and_then(|data| match data.get("url") {
                Some(urls) => Ok(urls.first().unwrap()),
                None => Err("Parameter 'url' missing from the URL encoded body"),
            })
            .and_then(|url| Url::parse(url).map_err(|_| "Parameter 'url' is invalid"))
    }

    fn gen_unused_random_key(shortened_urls: &HashMap<String, Url>) -> String {
        let mut url_key: Option<String> = None;
        while url_key.is_none() {
            let random_key: String = rand::thread_rng().gen_ascii_chars().take(5).collect();
            url_key = if shortened_urls.contains_key(&random_key) {
                None
            } else {
                Some(random_key)
            }
        }
        url_key.unwrap()
    }

    fn handle_post_request(&self, req: &mut Request) -> IronResult<Response> {
        match Self::extract_url(req) {
            Ok(long_url) => {
                let mut shortened_urls = self.shortened_urls.write().unwrap();
                let url_key = Self::gen_unused_random_key(&shortened_urls);

                let short_url = format!("http://localhost:3000/{}", url_key);
                shortened_urls.insert(url_key, long_url);
                Ok(Response::with((status::Created, Header(headers::Location(short_url)))))
            }
            Err(e) => Ok(Response::with((status::BadRequest, e))),
        }
    }

    fn handle_get_request(&self, req: &mut Request) -> IronResult<Response> {
        let request_path = &req.url.path().join("/");
        match self.shortened_urls.read().unwrap().get(request_path) {
            Some(url) => Ok(Response::with((status::Found, Redirect(url.clone())))),
            None => Ok(Response::with(status::NotFound)),
        }
    }
}

impl Handler for UrlShortenerHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        match &req.method {
            &Method::Post => self.handle_post_request(req),
            &Method::Get | &Method::Head => self.handle_get_request(req),
            _ => Ok(Response::with(status::NotFound)),
        }
    }
}

#[cfg(test)]
mod tests {
    use hyper::header::Location;

    use iron::headers::ContentType;
    use iron::{Headers, status};

    use iron_test::{request, response};

    use super::UrlShortenerHandler;

    #[test]
    fn short_url_not_found() {
        let response = request::get("http://localhost:3000/hello",
                                    Headers::new(),
                                    &UrlShortenerHandler::new())
            .unwrap();
        assert_eq!(response.status.unwrap(), status::NotFound);
    }

    #[test]
    fn short_url_found() {
        let handler = UrlShortenerHandler::new();

        let mut request_headers = Headers::new();
        request_headers.set(ContentType::form_url_encoded());
        let response = request::post("http://localhost:3000",
                                     request_headers,
                                     "url=https://www.helloclue.com/",
                                     &handler)
            .unwrap();
        let short_url = response.headers.get::<Location>().unwrap();
        let response = request::get(short_url, Headers::new(), &handler).unwrap();
        assert_eq!(response.status.unwrap(), status::Found);
        assert_eq!(response.headers.get::<Location>().unwrap().0,
                   "https://www.helloclue.com/");
    }

    #[test]
    fn post_wrong_contenttype() {
        let response = request::post("http://localhost:3000",
                                     Headers::new(),
                                     "",
                                     &UrlShortenerHandler::new())
            .unwrap();
        assert_eq!(response.status.unwrap(), status::BadRequest);
        assert_eq!(response::extract_body_to_string(response),
                   "URL encoded body missing")
    }
}
<commit_msg>Increase size of random keys to 7 characters<commit_after>use iron::headers;
use iron::prelude::*;
use iron::method::Method;
use iron::modifiers::{Redirect, Header};
use iron::{Handler, Url, status};

use rand;
use rand::Rng;

use std::collections::HashMap;
use std::sync::RwLock;

use urlencoded::UrlEncodedBody;

pub struct UrlShortenerHandler {
    shortened_urls: RwLock<HashMap<String, Url>>,
}

impl UrlShortenerHandler {
    pub fn new() -> Self {
        UrlShortenerHandler { shortened_urls: RwLock::new(HashMap::new()) }
    }

    fn extract_url(req: &mut Request) -> Result<Url, &'static str> {
        req.get_ref::<UrlEncodedBody>()
            .map_err(|_| "URL encoded body missing")
            .and_then(|data| match data.get("url") {
                Some(urls) => Ok(urls.first().unwrap()),
                None => Err("Parameter 'url' missing from the URL encoded body"),
            })
            .and_then(|url| Url::parse(url).map_err(|_| "Parameter 'url' is invalid"))
    }

    fn gen_unused_random_key(shortened_urls: &HashMap<String, Url>) -> String {
        let mut url_key: Option<String> = None;
        while url_key.is_none() {
            let random_key: String = rand::thread_rng().gen_ascii_chars().take(7).collect();
            url_key = if shortened_urls.contains_key(&random_key) {
                None
            } else {
                Some(random_key)
            }
        }
        url_key.unwrap()
    }

    fn handle_post_request(&self, req: &mut Request) -> IronResult<Response> {
        match Self::extract_url(req) {
            Ok(long_url) => {
                let mut shortened_urls = self.shortened_urls.write().unwrap();
                let url_key = Self::gen_unused_random_key(&shortened_urls);

                let short_url = format!("http://localhost:3000/{}", url_key);
                shortened_urls.insert(url_key, long_url);
                Ok(Response::with((status::Created, Header(headers::Location(short_url)))))
            }
            Err(e) => Ok(Response::with((status::BadRequest, e))),
        }
    }

    fn handle_get_request(&self, req: &mut Request) -> IronResult<Response> {
        let request_path = &req.url.path().join("/");
        match self.shortened_urls.read().unwrap().get(request_path) {
            Some(url) => Ok(Response::with((status::Found, Redirect(url.clone())))),
            None => Ok(Response::with(status::NotFound)),
        }
    }
}

impl Handler for UrlShortenerHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        match &req.method {
            &Method::Post => self.handle_post_request(req),
            &Method::Get | &Method::Head => self.handle_get_request(req),
            _ => Ok(Response::with(status::NotFound)),
        }
    }
}

#[cfg(test)]
mod tests {
    use hyper::header::Location;

    use iron::headers::ContentType;
    use iron::{Headers, status};

    use iron_test::{request, response};

    use super::UrlShortenerHandler;

    #[test]
    fn short_url_not_found() {
        let response = request::get("http://localhost:3000/hello",
                                    Headers::new(),
                                    &UrlShortenerHandler::new())
            .unwrap();
        assert_eq!(response.status.unwrap(), status::NotFound);
    }

    #[test]
    fn short_url_found() {
        let handler = UrlShortenerHandler::new();

        let mut request_headers = Headers::new();
        request_headers.set(ContentType::form_url_encoded());
        let response = request::post("http://localhost:3000",
                                     request_headers,
                                     "url=https://www.helloclue.com/",
                                     &handler)
            .unwrap();
        let short_url = response.headers.get::<Location>().unwrap();
        let response = request::get(short_url, Headers::new(), &handler).unwrap();
        assert_eq!(response.status.unwrap(), status::Found);
        assert_eq!(response.headers.get::<Location>().unwrap().0,
                   "https://www.helloclue.com/");
    }

    #[test]
    fn post_wrong_contenttype() {
        let response = request::post("http://localhost:3000",
                                     Headers::new(),
                                     "",
                                     &UrlShortenerHandler::new())
            .unwrap();
        assert_eq!(response.status.unwrap(), status::BadRequest);
        assert_eq!(response::extract_body_to_string(response),
                   "URL encoded body missing")
    }
}
<|endoftext|>


<commit_before>#![feature(rustc_private)]
extern crate llvm_sys as llvm;
extern crate libc;
use std::ptr;
use std::ffi;

use self::llvm::prelude::{LLVMContextRef, LLVMModuleRef, LLVMBuilderRef, LLVMValueRef, LLVMTypeRef};
use self::llvm::core::*;
use self::llvm::target::*;
use self::llvm::target_machine::*;

use std::collections::{HashMap};
use std::mem;

use syntax::ast::{Block, Expr, Decl, TType, OptionalTypeExprTupleList};
use syntax::ptr::{B};
//FIXME this import is for integration testing purposes
use syntax::parse::*;//{Parser};
use syntax::parse::parser::{Parser};
use link::link;
use helpers::*;

pub struct Context<'a>{
    context : LLVMContextRef,
    pub module : LLVMModuleRef,
    builder : LLVMBuilderRef,
    sym_tab : HashMap<String, LLVMValueRef>,
    bb_stack : Vec<*mut llvm::LLVMBasicBlock>,
    proto_map : HashMap<&'a str, bool>
}

impl<'a> Context<'a>{
    fn new(module_name : &str) -> Self{
        unsafe{
            let llvm_context =  LLVMContextCreate();
            let llvm_module = LLVMModuleCreateWithNameInContext(c_str_ptr!(module_name),
                                                                llvm_context);
            let builder = LLVMCreateBuilderInContext(llvm_context);
            let sym_tab = HashMap::new();
            let bb_stack = Vec::new();
            let proto_map = HashMap::new();

            Context {
                context : llvm_context,
                module : llvm_module,
                builder : builder,
                sym_tab : sym_tab,
                bb_stack : bb_stack,
                proto_map : proto_map
            }
        }
    }

    pub fn dump(&self){
        unsafe{
            LLVMDumpModule(self.module);
        }
    }
}

impl<'a> Drop for Context<'a>{
    fn drop(&mut self){
        unsafe{
            LLVMDisposeBuilder(self.builder);
            LLVMDisposeModule(self.module);
            LLVMContextDispose(self.context);
        }
    }
}

//TODO move these in a seperate file
type IRBuildingResult = Result<LLVMValueRef, String>;

trait IRBuilder{
    fn codegen(&self, ctxt : &mut Context) -> IRBuildingResult;
}

fn std_functions_call_factory(fn_name : &str,
                              args : &OptionalTypeExprTupleList,
                              ctxt : &mut Context) -> Option<LLVMValueRef>{
    unsafe{
        match fn_name {
            "print" =>{
                debug_assert!(args.is_some(), "No args passed to print()");
                let lst = args.as_ref().unwrap();
                debug_assert!(lst.len() == 1, "One arg should be passed to print()");
                debug_assert!(lst[0].0 == TType::TString);
                let str_arg = match &*lst[0].1 {
                    &Expr::StringExpr(ref value) => c_str_ptr!(&*(value.clone())),
                    _ => panic!("Expected a string expr")
                };

                let print_function : LLVMValueRef;
                //check if we already have a prototype defined
                if !ctxt.proto_map.contains_key("printf"){
                    let print_ty = LLVMIntTypeInContext(ctxt.context, 32);
                    let mut pf_type_args_vec = Vec::new();
                    pf_type_args_vec.push(LLVMPointerType(LLVMIntTypeInContext(ctxt.context, 8),
                    0));
                    let proto = LLVMFunctionType(print_ty, pf_type_args_vec.as_mut_ptr(), 1, 1);
                    print_function = LLVMAddFunction(ctxt.module,
                                                         c_str_ptr!("printf"),
                                                         proto);
                    ctxt.proto_map.insert("printf", true);
                }
                else{
                    print_function = LLVMGetNamedFunction(ctxt.module, c_str_ptr!("printf"));
                }

                let gstr = LLVMBuildGlobalStringPtr(ctxt.builder,
                                                    str_arg,
                                                    c_str_ptr!(".str"));
                let mut pf_args = Vec::new();
                pf_args.push(gstr);

                Some(LLVMBuildCall(ctxt.builder,
                                   print_function,
                                   pf_args.as_mut_ptr(),
                                   1,
                                   c_str_ptr!("call")))
            },
            _ => {None}
        }
    }
}

fn get_llvm_type_for_ttype(ty : &TType, ctxt : &mut Context) -> LLVMTypeRef{
    unsafe{
        match ty {
            &TType::TVoid => LLVMVoidTypeInContext(ctxt.context),
            &TType::TInt32 => LLVMIntTypeInContext(ctxt.context, 32),
            _ => panic!("Other TTypes not mapped yet to the corresponding LLVM types")
        }
    }
}

impl IRBuilder for Expr{
    fn codegen(&self, ctxt : &mut Context) -> IRBuildingResult{
        macro_rules! build_binary_instrs{
            ($fun : ident, $e1:ident, $e2:ident, $s : expr) => {{
                let ev1 = try!($e1.codegen(ctxt));
                let ev2 = try!($e2.codegen(ctxt));
                Ok($fun(ctxt.builder, ev1, ev2, $s.as_ptr() as *const i8))
            }}
        }
        unsafe{
            match self{
                &Expr::NumExpr(ref i) => {
                    let ty = LLVMIntTypeInContext(ctxt.context, 32);
                    Ok(LLVMConstInt(ty, *i as u64, 0))
                },
                &Expr::AddExpr(ref e1, ref e2) => {
                    build_binary_instrs!(LLVMBuildFAdd, e1, e2, "add_tmp")
                },
                &Expr::SubExpr(ref e1, ref e2) => {
                    build_binary_instrs!(LLVMBuildFSub, e1, e2, "sub_tmp")
                },
                &Expr::MulExpr(ref e1, ref e2) => {
                    build_binary_instrs!(LLVMBuildFMul, e1, e2, "mul_tmp")
                },
                &Expr::DivExpr(ref e1, ref e2) => {
                    build_binary_instrs!(LLVMBuildFDiv, e1, e2, "div_tmp")
                },
                &Expr::IfThenElseExpr(ref conditional_expr, ref then_expr, ref else_expr) => {
                    let cond_code = try!(conditional_expr.codegen(ctxt));
                    let zero = LLVMConstInt(LLVMIntTypeInContext(ctxt.context, 32), 0u64, 0);
                    let if_cond = LLVMBuildICmp(ctxt.builder, llvm::LLVMIntPredicate::LLVMIntNE, cond_code, zero, c_str_ptr!("ifcond"));
                    let bb = LLVMGetInsertBlock(ctxt.builder);
                    let function = LLVMGetBasicBlockParent(bb);
                    let then_block = LLVMAppendBasicBlockInContext(ctxt.context, function, c_str_ptr!("thencond"));
                    let else_block = LLVMAppendBasicBlockInContext(ctxt.context, function, c_str_ptr!("elsecond"));
                    let ifcont_block = LLVMAppendBasicBlockInContext(ctxt.context, function, c_str_ptr!("ifcont"));
                    LLVMBuildCondBr(ctxt.builder, if_cond, then_block, else_block);

                    LLVMPositionBuilderAtEnd(ctxt.builder, then_block);
                    let then_code = try!(then_expr.codegen(ctxt));
                    LLVMBuildBr(ctxt.builder, ifcont_block);
                    let then_end = LLVMGetInsertBlock(ctxt.builder);

                    LLVMPositionBuilderAtEnd(ctxt.builder, else_block);
                    let else_code = try!(else_expr.codegen(ctxt));
                    LLVMBuildBr(ctxt.builder, ifcont_block);
                    let else_end = LLVMGetInsertBlock(ctxt.builder);

                    LLVMPositionBuilderAtEnd(ctxt.builder, ifcont_block);

                    let phi_node = LLVMBuildPhi(ctxt.builder, LLVMIntTypeInContext(ctxt.context, 32), c_str_ptr!("ifphi"));
                    LLVMAddIncoming(phi_node, vec![then_code].as_mut_ptr(), vec![then_end].as_mut_ptr(), 1);
                    LLVMAddIncoming(phi_node, vec![else_code].as_mut_ptr(), vec![else_end].as_mut_ptr(), 1);
                    Ok(phi_node)

                },
                &Expr::CallExpr(ref fn_name, ref optional_args) => {
                    //FIXME instead of directly passing to the factory
                    //fn_name can be checked in a map that records names of std functions
                    match std_functions_call_factory(&*fn_name, optional_args, ctxt) {
                        Some(call) => Ok(call),
                        _ => {
                            //user defined function call
                            let mut pf_args = Vec::new();
                            //FIXME pass args if present in the call
                            if optional_args.is_some() {

                            }
                            //pf_args.push(gstr);
                            let _fn = ctxt.sym_tab[&*fn_name];
                            Ok(LLVMBuildCall(ctxt.builder,
                                            _fn,
                                            pf_args.as_mut_ptr(),
                                            0,
                                            c_str_ptr!("")))
                        }
                    }
                },
                &Expr::LetExpr(ref decls, ref expr) => {
                    debug_assert!(!decls.is_empty(), "Declarations in a let block can't be empty");
                    debug_assert!(expr.is_some(), "Expr in a let block can't be empty");
                    for decl in &*decls {
                        match decl {
                            &Decl::FunDec(ref name, ref params, ref ty, ref body, ref body_ty) => {
                                let llvm_ty = get_llvm_type_for_ttype(ty, ctxt);
                                let proto = LLVMFunctionType(llvm_ty, ptr::null_mut(), 0, 0);
                                let function = LLVMAddFunction(ctxt.module,
                                                               c_str_ptr!(&(*name.clone())),
                                                               proto);
                                let bb = LLVMAppendBasicBlockInContext(ctxt.context,
                                                                       function,
                                                                       c_str_ptr!("entry"));
                                ctxt.sym_tab.insert(name.clone(), function);
                                LLVMPositionBuilderAtEnd(ctxt.builder, bb);
                                //trans_expr(body, &mut ctxt);
                                let value_ref = try!(body.codegen(ctxt));
                                if *ty == TType::TVoid{
                                    LLVMBuildRetVoid(ctxt.builder);
                                }
                                else{
                                    LLVMBuildRet(ctxt.builder, value_ref);
                                }
                            },
                            _ => panic!("More decl types should be covered")
                        }

                    }
                    //trans_expr(&*expr.unwrap(), &mut ctxt);
                    //FIXME should the previous bb be popped here?
                    let bb = ctxt.bb_stack.pop().unwrap();
                    LLVMPositionBuilderAtEnd(ctxt.builder, bb);
                    let e = &expr.as_ref().unwrap();
                    let v = try!(e.codegen(ctxt));
                    Ok(v)
                }
                t => Err(format!("error: {:?}", t))
            }
        }
    }
}

pub fn translate(expr : &Expr) -> Option<Context>{
    let mut ctxt = Context::new("main_mod");
    unsafe{
        let r = LLVM_InitializeNativeTarget();
        assert_eq!(r, 0);
        LLVM_InitializeNativeAsmPrinter();
        //build outer embedding main() fn
        let ty = LLVMIntTypeInContext(ctxt.context, 32);
        let proto = LLVMFunctionType(ty, ptr::null_mut(), 0, 0);
        let function = LLVMAddFunction(ctxt.module,
                                       c_str_ptr!("main"),
                                       proto);
        let bb = LLVMAppendBasicBlockInContext(ctxt.context,
                                               function,
                                               c_str_ptr!("entry"));
        LLVMPositionBuilderAtEnd(ctxt.builder, bb);
        ctxt.bb_stack.push(bb);
        trans_expr(expr, &mut ctxt);

        //exit function
        //let exit_ty = LLVMVoidTypeInContext(ctxt.context);
        //let mut exit_type_args_vec = Vec::new();
        //exit_type_args_vec.push(LLVMIntTypeInContext(ctxt.context, 32));
        //let exit_proto = LLVMFunctionType(exit_ty, exit_type_args_vec.as_mut_ptr(), 1, 0);
        //let exit_function = LLVMAddFunction(ctxt.module,
        //                                              ffi::CString::new("exit").unwrap().as_ptr(),
        //                                              exit_proto);
        //let mut exit_args = Vec::new();
        //exit_args.push(LLVMConstInt(LLVMIntTypeInContext(ctxt.context, 32), 0 as u64, 0));
        //LLVMBuildCall(ctxt.builder,
        //                          exit_function,
        //                          exit_args.as_mut_ptr(),
        //                          1,
        //                          ffi::CString::new("call").unwrap().as_ptr());
        LLVMBuildRet(ctxt.builder,
                     LLVMConstInt(LLVMIntTypeInContext(ctxt.context, 32), 0 as u64, 0));

        //add translated code as part of the block
        link(&ctxt);
    }
    Some(ctxt)
}

fn trans_expr(expr: &Expr, ctxt : &mut Context){
    let result = expr.codegen(ctxt); //ctxt is already &mut
    match result {
        Ok(_) => {},
        Err(msg) => panic!(msg)
    }
}

//#[test]
fn test_translate_std_print_call() {
    let expr = &Expr::CallExpr("print".to_string(),
                                  Some(vec![(TType::TString,
                                             B(Expr::StringExpr("abhi".to_string())))]));
    let ctxt = translate(expr);
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}

//#[test]
fn test_prsr_bcknd_intgrtion_prnt_call() {
    let mut p = Parser::new("print(\"Grrrr!\n\")".to_string());
    p.start_lexer();
    let tup = p.expr();
    let (ty, b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    // let ctxt = translate();&Expr::CallExpr("print".to_string(),
    //                               Some(vec![(TType::TString,
    //                                          B(Expr::StringExpr("abhi".to_string())))])));
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}

//#[test]
fn test_translate_add_expr(){
    let mut p = Parser::new(String::from("let function foo() : int = 1+3 in foo() end"));
    p.start_lexer();
    let tup = p.expr();
    let (_ , b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    ctxt.unwrap().dump();
}
//#[test]
fn test_prsr_bcknd_intgrtion_let_blk() {
    let mut p = Parser::new("let function foo() = print(\"Grrrr!\n\") in foo() end".to_string());
    p.start_lexer();
    let tup = p.expr();
    let (ty, b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}

#[test]
fn test_prsr_bcknd_intgrtion_if_then_expr() {
    let mut p = Parser::new("let function foo()  = if 8 then print(\"rust\") else print(\"c++\") in foo() end".to_string());
    p.start_lexer();
    let tup = p.expr();
    let (ty, b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}

//#[test]
//fn test_prsr_bcknd_intgrtion_for_expr() {
//    let mut p = Parser::new("let function foo() = for i:= 1 to 1 do 1 in foo() end".to_string());
//    p.start_lexer();
//    let tup = p.expr();
//    let (ty, b_expr) = tup.unwrap();
//    let ctxt = translate(&*b_expr);
//    assert_eq!(ctxt.is_some(), true);
//    ctxt.unwrap().dump();
//}
<commit_msg>Use LLVMBuildAdd instead to add ints<commit_after>#![feature(rustc_private)]
extern crate llvm_sys as llvm;
extern crate libc;
use std::ptr;
use std::ffi;

use self::llvm::prelude::{LLVMContextRef, LLVMModuleRef, LLVMBuilderRef, LLVMValueRef, LLVMTypeRef};
use self::llvm::core::*;
use self::llvm::target::*;
use self::llvm::target_machine::*;

use std::collections::{HashMap};
use std::mem;

use syntax::ast::{Block, Expr, Decl, TType, OptionalTypeExprTupleList};
use syntax::ptr::{B};
//FIXME this import is for integration testing purposes
use syntax::parse::*;//{Parser};
use syntax::parse::parser::{Parser};
use link::link;
use helpers::*;

pub struct Context<'a>{
    context : LLVMContextRef,
    pub module : LLVMModuleRef,
    builder : LLVMBuilderRef,
    sym_tab : HashMap<String, LLVMValueRef>,
    bb_stack : Vec<*mut llvm::LLVMBasicBlock>,
    proto_map : HashMap<&'a str, bool>
}

impl<'a> Context<'a>{
    fn new(module_name : &str) -> Self{
        unsafe{
            let llvm_context =  LLVMContextCreate();
            let llvm_module = LLVMModuleCreateWithNameInContext(c_str_ptr!(module_name),
                                                                llvm_context);
            let builder = LLVMCreateBuilderInContext(llvm_context);
            let sym_tab = HashMap::new();
            let bb_stack = Vec::new();
            let proto_map = HashMap::new();

            Context {
                context : llvm_context,
                module : llvm_module,
                builder : builder,
                sym_tab : sym_tab,
                bb_stack : bb_stack,
                proto_map : proto_map
            }
        }
    }

    pub fn dump(&self){
        unsafe{
            LLVMDumpModule(self.module);
        }
    }
}

impl<'a> Drop for Context<'a>{
    fn drop(&mut self){
        unsafe{
            LLVMDisposeBuilder(self.builder);
            LLVMDisposeModule(self.module);
            LLVMContextDispose(self.context);
        }
    }
}

//TODO move these in a seperate file
type IRBuildingResult = Result<LLVMValueRef, String>;

trait IRBuilder{
    fn codegen(&self, ctxt : &mut Context) -> IRBuildingResult;
}

fn std_functions_call_factory(fn_name : &str,
                              args : &OptionalTypeExprTupleList,
                              ctxt : &mut Context) -> Option<LLVMValueRef>{
    unsafe{
        match fn_name {
            "print" =>{
                debug_assert!(args.is_some(), "No args passed to print()");
                let lst = args.as_ref().unwrap();
                debug_assert!(lst.len() == 1, "One arg should be passed to print()");
                debug_assert!(lst[0].0 == TType::TString);
                let str_arg = match &*lst[0].1 {
                    &Expr::StringExpr(ref value) => c_str_ptr!(&*(value.clone())),
                    _ => panic!("Expected a string expr")
                };

                let print_function : LLVMValueRef;
                //check if we already have a prototype defined
                if !ctxt.proto_map.contains_key("printf"){
                    let print_ty = LLVMIntTypeInContext(ctxt.context, 32);
                    let mut pf_type_args_vec = Vec::new();
                    pf_type_args_vec.push(LLVMPointerType(LLVMIntTypeInContext(ctxt.context, 8),
                    0));
                    let proto = LLVMFunctionType(print_ty, pf_type_args_vec.as_mut_ptr(), 1, 1);
                    print_function = LLVMAddFunction(ctxt.module,
                                                         c_str_ptr!("printf"),
                                                         proto);
                    ctxt.proto_map.insert("printf", true);
                }
                else{
                    print_function = LLVMGetNamedFunction(ctxt.module, c_str_ptr!("printf"));
                }

                let gstr = LLVMBuildGlobalStringPtr(ctxt.builder,
                                                    str_arg,
                                                    c_str_ptr!(".str"));
                let mut pf_args = Vec::new();
                pf_args.push(gstr);

                Some(LLVMBuildCall(ctxt.builder,
                                   print_function,
                                   pf_args.as_mut_ptr(),
                                   1,
                                   c_str_ptr!("call")))
            },
            _ => {None}
        }
    }
}

fn get_llvm_type_for_ttype(ty : &TType, ctxt : &mut Context) -> LLVMTypeRef{
    unsafe{
        match ty {
            &TType::TVoid => LLVMVoidTypeInContext(ctxt.context),
            &TType::TInt32 => LLVMIntTypeInContext(ctxt.context, 32),
            _ => panic!("Other TTypes not mapped yet to the corresponding LLVM types")
        }
    }
}

impl IRBuilder for Expr{
    fn codegen(&self, ctxt : &mut Context) -> IRBuildingResult{
        macro_rules! build_binary_instrs{
            ($fun : ident, $e1:ident, $e2:ident, $s : expr) => {{
                let ev1 = try!($e1.codegen(ctxt));
                let ev2 = try!($e2.codegen(ctxt));
                Ok($fun(ctxt.builder, ev1, ev2, $s.as_ptr() as *const i8))
            }}
        }
        unsafe{
            match self{
                &Expr::NumExpr(ref i) => {
                    let ty = LLVMIntTypeInContext(ctxt.context, 32);
                    Ok(LLVMConstInt(ty, *i as u64, 0))
                },
                &Expr::AddExpr(ref e1, ref e2) => {
                    build_binary_instrs!(LLVMBuildAdd, e1, e2, "add_tmp")
                },
                &Expr::SubExpr(ref e1, ref e2) => {
                    build_binary_instrs!(LLVMBuildSub, e1, e2, "sub_tmp")
                },
                &Expr::MulExpr(ref e1, ref e2) => {
                    build_binary_instrs!(LLVMBuildMul, e1, e2, "mul_tmp")
                },
                &Expr::DivExpr(ref e1, ref e2) => {
                    build_binary_instrs!(LLVMBuildSDiv, e1, e2, "div_tmp")
                },
                &Expr::IfThenElseExpr(ref conditional_expr, ref then_expr, ref else_expr) => {
                    let cond_code = try!(conditional_expr.codegen(ctxt));
                    let zero = LLVMConstInt(LLVMIntTypeInContext(ctxt.context, 32), 0u64, 0);
                    let if_cond = LLVMBuildICmp(ctxt.builder, llvm::LLVMIntPredicate::LLVMIntNE, cond_code, zero, c_str_ptr!("ifcond"));
                    let bb = LLVMGetInsertBlock(ctxt.builder);
                    let function = LLVMGetBasicBlockParent(bb);
                    let then_block = LLVMAppendBasicBlockInContext(ctxt.context, function, c_str_ptr!("thencond"));
                    let else_block = LLVMAppendBasicBlockInContext(ctxt.context, function, c_str_ptr!("elsecond"));
                    let ifcont_block = LLVMAppendBasicBlockInContext(ctxt.context, function, c_str_ptr!("ifcont"));
                    LLVMBuildCondBr(ctxt.builder, if_cond, then_block, else_block);

                    LLVMPositionBuilderAtEnd(ctxt.builder, then_block);
                    let then_code = try!(then_expr.codegen(ctxt));
                    LLVMBuildBr(ctxt.builder, ifcont_block);
                    let then_end = LLVMGetInsertBlock(ctxt.builder);

                    LLVMPositionBuilderAtEnd(ctxt.builder, else_block);
                    let else_code = try!(else_expr.codegen(ctxt));
                    LLVMBuildBr(ctxt.builder, ifcont_block);
                    let else_end = LLVMGetInsertBlock(ctxt.builder);

                    LLVMPositionBuilderAtEnd(ctxt.builder, ifcont_block);

                    let phi_node = LLVMBuildPhi(ctxt.builder, LLVMIntTypeInContext(ctxt.context, 32), c_str_ptr!("ifphi"));
                    LLVMAddIncoming(phi_node, vec![then_code].as_mut_ptr(), vec![then_end].as_mut_ptr(), 1);
                    LLVMAddIncoming(phi_node, vec![else_code].as_mut_ptr(), vec![else_end].as_mut_ptr(), 1);
                    Ok(phi_node)

                },
                &Expr::CallExpr(ref fn_name, ref optional_args) => {
                    //FIXME instead of directly passing to the factory
                    //fn_name can be checked in a map that records names of std functions
                    match std_functions_call_factory(&*fn_name, optional_args, ctxt) {
                        Some(call) => Ok(call),
                        _ => {
                            //user defined function call
                            let mut pf_args = Vec::new();
                            //FIXME pass args if present in the call
                            if optional_args.is_some() {

                            }
                            //pf_args.push(gstr);
                            let _fn = ctxt.sym_tab[&*fn_name];
                            Ok(LLVMBuildCall(ctxt.builder,
                                            _fn,
                                            pf_args.as_mut_ptr(),
                                            0,
                                            c_str_ptr!("")))
                        }
                    }
                },
                &Expr::LetExpr(ref decls, ref expr) => {
                    debug_assert!(!decls.is_empty(), "Declarations in a let block can't be empty");
                    debug_assert!(expr.is_some(), "Expr in a let block can't be empty");
                    for decl in &*decls {
                        match decl {
                            &Decl::FunDec(ref name, ref params, ref ty, ref body, ref body_ty) => {
                                let llvm_ty = get_llvm_type_for_ttype(ty, ctxt);
                                let proto = LLVMFunctionType(llvm_ty, ptr::null_mut(), 0, 0);
                                let function = LLVMAddFunction(ctxt.module,
                                                               c_str_ptr!(&(*name.clone())),
                                                               proto);
                                let bb = LLVMAppendBasicBlockInContext(ctxt.context,
                                                                       function,
                                                                       c_str_ptr!("entry"));
                                ctxt.sym_tab.insert(name.clone(), function);
                                LLVMPositionBuilderAtEnd(ctxt.builder, bb);
                                //trans_expr(body, &mut ctxt);
                                let value_ref = try!(body.codegen(ctxt));
                                if *ty == TType::TVoid{
                                    LLVMBuildRetVoid(ctxt.builder);
                                }
                                else{
                                    LLVMBuildRet(ctxt.builder, value_ref);
                                }
                            },
                            _ => panic!("More decl types should be covered")
                        }

                    }
                    //trans_expr(&*expr.unwrap(), &mut ctxt);
                    //FIXME should the previous bb be popped here?
                    let bb = ctxt.bb_stack.pop().unwrap();
                    LLVMPositionBuilderAtEnd(ctxt.builder, bb);
                    let e = &expr.as_ref().unwrap();
                    let v = try!(e.codegen(ctxt));
                    Ok(v)
                }
                t => Err(format!("error: {:?}", t))
            }
        }
    }
}

pub fn translate(expr : &Expr) -> Option<Context>{
    let mut ctxt = Context::new("main_mod");
    unsafe{
        let r = LLVM_InitializeNativeTarget();
        assert_eq!(r, 0);
        LLVM_InitializeNativeAsmPrinter();
        //build outer embedding main() fn
        let ty = LLVMIntTypeInContext(ctxt.context, 32);
        let proto = LLVMFunctionType(ty, ptr::null_mut(), 0, 0);
        let function = LLVMAddFunction(ctxt.module,
                                       c_str_ptr!("main"),
                                       proto);
        let bb = LLVMAppendBasicBlockInContext(ctxt.context,
                                               function,
                                               c_str_ptr!("entry"));
        LLVMPositionBuilderAtEnd(ctxt.builder, bb);
        ctxt.bb_stack.push(bb);
        trans_expr(expr, &mut ctxt);

        //exit function
        //let exit_ty = LLVMVoidTypeInContext(ctxt.context);
        //let mut exit_type_args_vec = Vec::new();
        //exit_type_args_vec.push(LLVMIntTypeInContext(ctxt.context, 32));
        //let exit_proto = LLVMFunctionType(exit_ty, exit_type_args_vec.as_mut_ptr(), 1, 0);
        //let exit_function = LLVMAddFunction(ctxt.module,
        //                                              ffi::CString::new("exit").unwrap().as_ptr(),
        //                                              exit_proto);
        //let mut exit_args = Vec::new();
        //exit_args.push(LLVMConstInt(LLVMIntTypeInContext(ctxt.context, 32), 0 as u64, 0));
        //LLVMBuildCall(ctxt.builder,
        //                          exit_function,
        //                          exit_args.as_mut_ptr(),
        //                          1,
        //                          ffi::CString::new("call").unwrap().as_ptr());
        LLVMBuildRet(ctxt.builder,
                     LLVMConstInt(LLVMIntTypeInContext(ctxt.context, 32), 0 as u64, 0));

        //add translated code as part of the block
        link(&ctxt);
    }
    Some(ctxt)
}

fn trans_expr(expr: &Expr, ctxt : &mut Context){
    let result = expr.codegen(ctxt); //ctxt is already &mut
    match result {
        Ok(_) => {},
        Err(msg) => panic!(msg)
    }
}

//#[test]
fn test_translate_std_print_call() {
    let expr = &Expr::CallExpr("print".to_string(),
                                  Some(vec![(TType::TString,
                                             B(Expr::StringExpr("abhi".to_string())))]));
    let ctxt = translate(expr);
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}

//#[test]
fn test_prsr_bcknd_intgrtion_prnt_call() {
    let mut p = Parser::new("print(\"Grrrr!\n\")".to_string());
    p.start_lexer();
    let tup = p.expr();
    let (ty, b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    // let ctxt = translate();&Expr::CallExpr("print".to_string(),
    //                               Some(vec![(TType::TString,
    //                                          B(Expr::StringExpr("abhi".to_string())))])));
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}

//#[test]
fn test_translate_add_expr(){
    let mut p = Parser::new(String::from("let function foo() : int = 1+3 in foo() end"));
    p.start_lexer();
    let tup = p.expr();
    let (_ , b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    ctxt.unwrap().dump();
}
//#[test]
fn test_prsr_bcknd_intgrtion_let_blk() {
    let mut p = Parser::new("let function foo() = print(\"Grrrr!\n\") in foo() end".to_string());
    p.start_lexer();
    let tup = p.expr();
    let (ty, b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}

#[test]
fn test_prsr_bcknd_intgrtion_if_then_expr() {
    let mut p = Parser::new("let function foo()  = if 0 then print(\"rust\n\") else print(\"c++\n\") in foo() end".to_string());
    p.start_lexer();
    let tup = p.expr();
    let (ty, b_expr) = tup.unwrap();
    let ctxt = translate(&*b_expr);
    assert_eq!(ctxt.is_some(), true);
    ctxt.unwrap().dump();
}

//#[test]
//fn test_prsr_bcknd_intgrtion_for_expr() {
//    let mut p = Parser::new("let function foo() = for i:= 1 to 1 do 1 in foo() end".to_string());
//    p.start_lexer();
//    let tup = p.expr();
//    let (ty, b_expr) = tup.unwrap();
//    let ctxt = translate(&*b_expr);
//    assert_eq!(ctxt.is_some(), true);
//    ctxt.unwrap().dump();
//}
<|endoftext|>


<commit_before>// Lumol, an extensible molecular simulation engine
// Copyright (C) Lumol's contributors — BSD license

//! Convert TOML values to Lumol types.

use FromToml;
use FromTomlWithData;
use error::{Error, Result};
use extract;

use lumol::energy::{BornMayerHuggins, Buckingham, Gaussian, MorsePotential, Torsion};
use lumol::energy::{CosineHarmonic, Harmonic, LennardJones, NullPotential, Mie};
use lumol::energy::{Ewald, Wolf};
use lumol::energy::{PairPotential, TableComputation};
use lumol::units;
use toml::value::Table;

impl FromToml for NullPotential {
    fn from_toml(_: &Table) -> Result<NullPotential> {
        Ok(NullPotential)
    }
}

impl FromToml for Harmonic {
    fn from_toml(table: &Table) -> Result<Harmonic> {
        let k = extract::str("k", table, "harmonic potential")?;
        let x0 = extract::str("x0", table, "harmonic potential")?;
        Ok(Harmonic {
            k: units::from_str(k)?,
            x0: units::from_str(x0)?,
        })
    }
}

impl FromToml for LennardJones {
    fn from_toml(table: &Table) -> Result<LennardJones> {
        let sigma = extract::str("sigma", table, "Lennard-Jones potential")?;
        let epsilon = extract::str("epsilon", table, "Lennard-Jones potential")?;
        Ok(LennardJones {
            sigma: units::from_str(sigma)?,
            epsilon: units::from_str(epsilon)?,
        })
    }
}

impl FromToml for Mie {
    fn from_toml(table: &Table) -> Result<Mie> {
        let sigma = extract::str("sigma", table, "Mie potential")?;
        let epsilon = extract::str("epsilon", table, "Mie potential")?;
        let m = extract::number("m", table, "Mie potential")?;
        let n = extract::number("n", table, "Mie potential")?;

        if n < 3.0 {
            warn!("'n' is smaller than 3. Tail corrections for Mie potential are set to zero.");
        };
        Ok(Mie::new(
                units::from_str(sigma)?,
                units::from_str(epsilon)?,
                n as f64,
                m as f64)
        )
    }
}

impl FromToml for CosineHarmonic {
    fn from_toml(table: &Table) -> Result<CosineHarmonic> {
        let k = extract::str("k", table, "cosine harmonic potential")?;
        let x0 = extract::str("x0", table, "cosine harmonic potential")?;
        Ok(CosineHarmonic::new(units::from_str(k)?, units::from_str(x0)?))
    }
}

impl FromToml for Torsion {
    fn from_toml(table: &Table) -> Result<Torsion> {
        let n = extract::uint("n", table, "torsion potential")?;
        let k = extract::str("k", table, "torsion potential")?;
        let delta = extract::str("delta", table, "torsion potential")?;
        Ok(Torsion {
            n: n as usize,
            k: units::from_str(k)?,
            delta: units::from_str(delta)?,
        })
    }
}

impl FromToml for Buckingham {
    fn from_toml(table: &Table) -> Result<Buckingham> {
        let a = extract::str("A", table, "Buckingham potential")?;
        let c = extract::str("C", table, "Buckingham potential")?;
        let rho = extract::str("rho", table, "Buckingham potential")?;

        Ok(Buckingham {
            a: units::from_str(a)?,
            c: units::from_str(c)?,
            rho: units::from_str(rho)?,
        })
    }
}

impl FromToml for BornMayerHuggins {
    fn from_toml(table: &Table) -> Result<BornMayerHuggins> {
        let a = extract::str("A", table, "Born-Mayer-Huggins potential")?;
        let c = extract::str("C", table, "Born-Mayer-Huggins potential")?;
        let d = extract::str("D", table, "Born-Mayer-Huggins potential")?;
        let rho = extract::str("rho", table, "Born-Mayer-Huggins potential")?;
        let sigma = extract::str("sigma", table, "Born-Mayer-Huggins potential")?;

        Ok(BornMayerHuggins {
            a: units::from_str(a)?,
            c: units::from_str(c)?,
            d: units::from_str(d)?,
            sigma: units::from_str(sigma)?,
            rho: units::from_str(rho)?,
        })
    }
}

impl FromToml for MorsePotential {
    fn from_toml(table: &Table) -> Result<MorsePotential> {
        let a = extract::str("A", table, "Morse potential")?;
        let depth = extract::str("depth", table, "Morse potential")?;
        let x0 = extract::str("x0", table, "Morse potential")?;
        Ok(MorsePotential {
            a: units::from_str(a)?,
            depth: units::from_str(depth)?,
            x0: units::from_str(x0)?,
        })
    }
}

impl FromToml for Gaussian {
    fn from_toml(table: &Table) -> Result<Gaussian> {
        let a = units::from_str(extract::str("A", table, "Gaussian potential")?)?;
        let b = units::from_str(extract::str("B", table, "Gaussian potential")?)?;

        if b <= 0.0 {
            Err(Error::from("'B' parameter has to be positive in Gaussian potential"))
        } else {
            Ok(Gaussian::new(a, b))
        }
    }
}

impl FromTomlWithData for TableComputation {
    type Data = Box<PairPotential>;

    fn from_toml(table: &Table, potential: Box<PairPotential>) -> Result<TableComputation> {
        let table = try!(table["table"]
            .as_table()
            .ok_or(Error::from("'table' key in computation must be a TOML table")));
        let n = extract::uint("n", table, "table computation")?;
        let max = extract::str("max", table, "table computation")?;
        Ok(TableComputation::new(potential, n as usize, units::from_str(max)?))
    }
}

impl FromToml for Wolf {
    fn from_toml(table: &Table) -> Result<Wolf> {
        let cutoff = extract::str("cutoff", table, "Wolf coulombic potential")?;
        Ok(Wolf::new(units::from_str(cutoff)?))
    }
}

impl FromToml for Ewald {
    fn from_toml(table: &Table) -> Result<Ewald> {
        let cutoff = extract::str("cutoff", table, "Ewald coulombic potential")?;
        let kmax = extract::uint("kmax", table, "Ewald coulombic potential")?;
        Ok(Ewald::new(units::from_str(cutoff)?, kmax as usize))
    }
}
<commit_msg>Fixed bug, confused 'n' with 'm'<commit_after>// Lumol, an extensible molecular simulation engine
// Copyright (C) Lumol's contributors — BSD license

//! Convert TOML values to Lumol types.

use FromToml;
use FromTomlWithData;
use error::{Error, Result};
use extract;

use lumol::energy::{BornMayerHuggins, Buckingham, Gaussian, MorsePotential, Torsion};
use lumol::energy::{CosineHarmonic, Harmonic, LennardJones, NullPotential, Mie};
use lumol::energy::{Ewald, Wolf};
use lumol::energy::{PairPotential, TableComputation};
use lumol::units;
use toml::value::Table;

impl FromToml for NullPotential {
    fn from_toml(_: &Table) -> Result<NullPotential> {
        Ok(NullPotential)
    }
}

impl FromToml for Harmonic {
    fn from_toml(table: &Table) -> Result<Harmonic> {
        let k = extract::str("k", table, "harmonic potential")?;
        let x0 = extract::str("x0", table, "harmonic potential")?;
        Ok(Harmonic {
            k: units::from_str(k)?,
            x0: units::from_str(x0)?,
        })
    }
}

impl FromToml for LennardJones {
    fn from_toml(table: &Table) -> Result<LennardJones> {
        let sigma = extract::str("sigma", table, "Lennard-Jones potential")?;
        let epsilon = extract::str("epsilon", table, "Lennard-Jones potential")?;
        Ok(LennardJones {
            sigma: units::from_str(sigma)?,
            epsilon: units::from_str(epsilon)?,
        })
    }
}

impl FromToml for Mie {
    fn from_toml(table: &Table) -> Result<Mie> {
        let sigma = extract::str("sigma", table, "Mie potential")?;
        let epsilon = extract::str("epsilon", table, "Mie potential")?;
        let m = extract::number("m", table, "Mie potential")?;
        let n = extract::number("n", table, "Mie potential")?;

        if m < 3.0 {
            warn!("'m' is smaller than 3. Tail corrections for Mie potential are set to zero.");
        };
        Ok(Mie::new(
                units::from_str(sigma)?,
                units::from_str(epsilon)?,
                n as f64,
                m as f64)
        )
    }
}

impl FromToml for CosineHarmonic {
    fn from_toml(table: &Table) -> Result<CosineHarmonic> {
        let k = extract::str("k", table, "cosine harmonic potential")?;
        let x0 = extract::str("x0", table, "cosine harmonic potential")?;
        Ok(CosineHarmonic::new(units::from_str(k)?, units::from_str(x0)?))
    }
}

impl FromToml for Torsion {
    fn from_toml(table: &Table) -> Result<Torsion> {
        let n = extract::uint("n", table, "torsion potential")?;
        let k = extract::str("k", table, "torsion potential")?;
        let delta = extract::str("delta", table, "torsion potential")?;
        Ok(Torsion {
            n: n as usize,
            k: units::from_str(k)?,
            delta: units::from_str(delta)?,
        })
    }
}

impl FromToml for Buckingham {
    fn from_toml(table: &Table) -> Result<Buckingham> {
        let a = extract::str("A", table, "Buckingham potential")?;
        let c = extract::str("C", table, "Buckingham potential")?;
        let rho = extract::str("rho", table, "Buckingham potential")?;

        Ok(Buckingham {
            a: units::from_str(a)?,
            c: units::from_str(c)?,
            rho: units::from_str(rho)?,
        })
    }
}

impl FromToml for BornMayerHuggins {
    fn from_toml(table: &Table) -> Result<BornMayerHuggins> {
        let a = extract::str("A", table, "Born-Mayer-Huggins potential")?;
        let c = extract::str("C", table, "Born-Mayer-Huggins potential")?;
        let d = extract::str("D", table, "Born-Mayer-Huggins potential")?;
        let rho = extract::str("rho", table, "Born-Mayer-Huggins potential")?;
        let sigma = extract::str("sigma", table, "Born-Mayer-Huggins potential")?;

        Ok(BornMayerHuggins {
            a: units::from_str(a)?,
            c: units::from_str(c)?,
            d: units::from_str(d)?,
            sigma: units::from_str(sigma)?,
            rho: units::from_str(rho)?,
        })
    }
}

impl FromToml for MorsePotential {
    fn from_toml(table: &Table) -> Result<MorsePotential> {
        let a = extract::str("A", table, "Morse potential")?;
        let depth = extract::str("depth", table, "Morse potential")?;
        let x0 = extract::str("x0", table, "Morse potential")?;
        Ok(MorsePotential {
            a: units::from_str(a)?,
            depth: units::from_str(depth)?,
            x0: units::from_str(x0)?,
        })
    }
}

impl FromToml for Gaussian {
    fn from_toml(table: &Table) -> Result<Gaussian> {
        let a = units::from_str(extract::str("A", table, "Gaussian potential")?)?;
        let b = units::from_str(extract::str("B", table, "Gaussian potential")?)?;

        if b <= 0.0 {
            Err(Error::from("'B' parameter has to be positive in Gaussian potential"))
        } else {
            Ok(Gaussian::new(a, b))
        }
    }
}

impl FromTomlWithData for TableComputation {
    type Data = Box<PairPotential>;

    fn from_toml(table: &Table, potential: Box<PairPotential>) -> Result<TableComputation> {
        let table = try!(table["table"]
            .as_table()
            .ok_or(Error::from("'table' key in computation must be a TOML table")));
        let n = extract::uint("n", table, "table computation")?;
        let max = extract::str("max", table, "table computation")?;
        Ok(TableComputation::new(potential, n as usize, units::from_str(max)?))
    }
}

impl FromToml for Wolf {
    fn from_toml(table: &Table) -> Result<Wolf> {
        let cutoff = extract::str("cutoff", table, "Wolf coulombic potential")?;
        Ok(Wolf::new(units::from_str(cutoff)?))
    }
}

impl FromToml for Ewald {
    fn from_toml(table: &Table) -> Result<Ewald> {
        let cutoff = extract::str("cutoff", table, "Ewald coulombic potential")?;
        let kmax = extract::uint("kmax", table, "Ewald coulombic potential")?;
        Ok(Ewald::new(units::from_str(cutoff)?, kmax as usize))
    }
}
<|endoftext|>