<commit_before>#[macro_use]
extern crate pest;

use pest::prelude::*;

impl_rdp! {
    grammar! {
        program     = { template ~ eoi }
        template    = { statement* }
        statement   = { content | variable | html | section | inverted | comment | partial }
        content     = @{ (!open ~ any)+ }
        variable    = { open ~ path ~ close }
        html        = { ["{{{"] ~ path ~ ["}}}"]}
        section     = { ["{{#"] ~ path ~ close ~ template ~ ["{{/"] ~ path ~ close }
        inverted    = { ["{{^"] ~ path ~ close ~ template ~ ["{{/"] ~ path ~ close }
        comment     = { ["{{!"] ~ (!close ~ any)* ~ close }
        partial     = { ["{{>"] ~ partial_id ~ close }
        partial_id  = { (['a'..'z'] | ['A'..'Z'] | ['0'..'9'] | ["-"] | ["_"] | ["/"])+ }
        open        = _{ ["{{"] }
        close       = _{ ["}}"] }
        path        = @{ identifier ~ (["."] ~ identifier)* }
        identifier  = { (['a'..'z'] | ['A'..'Z'] | ['0'..'9'] | ["-"] | ["_"])+ }
        whitespace  = _{ [" "] | ["\t"] | ["\r"] | ["\n"]}
    }
}

#[cfg(test)]
mod tests {
    use pest::prelude::*;
    use super::{Rdp, Rule};

    #[test]
    fn identifier() {
        let mut parser = Rdp::new(StringInput::new("abc"));
        assert!(parser.identifier());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::identifier, 0, 3)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn path() {
        let mut parser = Rdp::new(StringInput::new("a.b.c"));
        assert!(parser.path());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::path, 0, 5),
                            Token::new(Rule::identifier, 0, 1),
                            Token::new(Rule::identifier, 2, 3),
                            Token::new(Rule::identifier, 4, 5)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn partial_id() {
        let mut parser = Rdp::new(StringInput::new("a/b/c"));
        assert!(parser.partial_id());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::partial_id, 0, 5)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn partial() {
        let mut parser = Rdp::new(StringInput::new("{{> a/b}}"));
        assert!(parser.partial());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::partial, 0, 9), Token::new(Rule::partial_id, 4, 7)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn comment() {
        let mut parser = Rdp::new(StringInput::new("{{! a b c}}"));
        assert!(parser.comment());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::comment, 0, 11)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn inverted() {
        let mut parser = Rdp::new(StringInput::new("{{^ a}}{{/ a}}"));
        assert!(parser.inverted());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::inverted, 0, 14),
                            Token::new(Rule::path, 4, 5),
                            Token::new(Rule::identifier, 4, 5),
                            Token::new(Rule::path, 11, 12),
                            Token::new(Rule::identifier, 11, 12)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn section() {
        let mut parser = Rdp::new(StringInput::new("{{# a}}{{/ a}}"));
        assert!(parser.section());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::section, 0, 14),
                            Token::new(Rule::path, 4, 5),
                            Token::new(Rule::identifier, 4, 5),
                            Token::new(Rule::path, 11, 12),
                            Token::new(Rule::identifier, 11, 12)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn variable() {
        let mut parser = Rdp::new(StringInput::new("{{ a }}"));
        assert!(parser.variable());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::variable, 0, 7),
                            Token::new(Rule::path, 3, 4),
                            Token::new(Rule::identifier, 3, 4)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn html() {
        let mut parser = Rdp::new(StringInput::new("{{{ a }}}"));
        assert!(parser.html());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::html, 0, 9),
                            Token::new(Rule::path, 4, 5),
                            Token::new(Rule::identifier, 4, 5)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn program() {
        let mut parser = Rdp::new(StringInput::new("
            {{> includes/header }}

                {{# robots}}

{{ name.first }}

                {{/ robots}}
                {{^ robots}}
                    {{! else clause }}
                    No robots
                {{/ robots}}

            {{> includes/footer }}
            {{{ unescaped.html }}}
        "));
        assert!(parser.program());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::program, 0, 380),
                            Token::new(Rule::template, 0, 371),
                            Token::new(Rule::statement, 0, 13),
                            Token::new(Rule::content, 0, 13),
                            Token::new(Rule::statement, 13, 35),
                            Token::new(Rule::partial, 13, 35),
                            Token::new(Rule::partial_id, 17, 32),
                            Token::new(Rule::statement, 48, 69),
                            Token::new(Rule::content, 48, 69),
                            Token::new(Rule::statement, 69, 156),
                            Token::new(Rule::section, 69, 156),
                            Token::new(Rule::path, 73, 79),
                            Token::new(Rule::identifier, 73, 79),
                            Token::new(Rule::template, 102, 144),
                            Token::new(Rule::statement, 102, 106),
                            Token::new(Rule::content, 102, 106),
                            Token::new(Rule::statement, 106, 122),
                            Token::new(Rule::variable, 106, 122),
                            Token::new(Rule::path, 109, 119),
                            Token::new(Rule::identifier, 109, 113),
                            Token::new(Rule::identifier, 114, 119),
                            Token::new(Rule::statement, 122, 144),
                            Token::new(Rule::content, 122, 144),
                            Token::new(Rule::path, 148, 154),
                            Token::new(Rule::identifier, 148, 154),
                            Token::new(Rule::statement, 173, 283),
                            Token::new(Rule::inverted, 173, 283),
                            Token::new(Rule::path, 177, 183),
                            Token::new(Rule::identifier, 177, 183),
                            Token::new(Rule::comment, 206, 224),
                            Token::new(Rule::template, 245, 271),
                            Token::new(Rule::statement, 245, 271),
                            Token::new(Rule::content, 245, 271),
                            Token::new(Rule::path, 275, 281),
                            Token::new(Rule::identifier, 275, 281),
                            Token::new(Rule::statement, 296, 314),
                            Token::new(Rule::content, 296, 314),
                            Token::new(Rule::statement, 314, 336),
                            Token::new(Rule::partial, 314, 336),
                            Token::new(Rule::partial_id, 318, 333),
                            Token::new(Rule::statement, 349, 371),
                            Token::new(Rule::html, 349, 371),
                            Token::new(Rule::path, 353, 367),
                            Token::new(Rule::identifier, 353, 362),
                            Token::new(Rule::identifier, 363, 367)];
        assert_eq!(&expected, parser.queue());
    }
}
<commit_msg>Silence comment rule<commit_after>#[macro_use]
extern crate pest;

use pest::prelude::*;

impl_rdp! {
    grammar! {
        program     = { template ~ eoi }
        template    = { statement* }
        statement   = { content | variable | html | section | inverted | partial }
        content     = @{ (!open ~ any)+ }
        variable    = { open ~ path ~ close }
        html        = { ["{{{"] ~ path ~ ["}}}"]}
        section     = { ["{{#"] ~ path ~ close ~ template ~ ["{{/"] ~ path ~ close }
        inverted    = { ["{{^"] ~ path ~ close ~ template ~ ["{{/"] ~ path ~ close }
        comment     = _{ ["{{!"] ~ (!close ~ any)* ~ close }
        partial     = { ["{{>"] ~ partial_id ~ close }
        partial_id  = { (['a'..'z'] | ['A'..'Z'] | ['0'..'9'] | ["-"] | ["_"] | ["/"])+ }
        open        = _{ ["{{"] }
        close       = _{ ["}}"] }
        path        = @{ identifier ~ (["."] ~ identifier)* }
        identifier  = { (['a'..'z'] | ['A'..'Z'] | ['0'..'9'] | ["-"] | ["_"])+ }
        whitespace  = _{ [" "] | ["\t"] | ["\r"] | ["\n"]}
    }
}

#[cfg(test)]
mod tests {
    use pest::prelude::*;
    use super::{Rdp, Rule};

    #[test]
    fn identifier() {
        let mut parser = Rdp::new(StringInput::new("abc"));
        assert!(parser.identifier());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::identifier, 0, 3)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn path() {
        let mut parser = Rdp::new(StringInput::new("a.b.c"));
        assert!(parser.path());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::path, 0, 5),
                            Token::new(Rule::identifier, 0, 1),
                            Token::new(Rule::identifier, 2, 3),
                            Token::new(Rule::identifier, 4, 5)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn partial_id() {
        let mut parser = Rdp::new(StringInput::new("a/b/c"));
        assert!(parser.partial_id());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::partial_id, 0, 5)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn partial() {
        let mut parser = Rdp::new(StringInput::new("{{> a/b}}"));
        assert!(parser.partial());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::partial, 0, 9), Token::new(Rule::partial_id, 4, 7)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn comment() {
        let mut parser = Rdp::new(StringInput::new("{{! a b c}}"));
        assert!(parser.comment());
        assert!(parser.end());
        assert!(parser.queue().is_empty());
    }

    #[test]
    fn inverted() {
        let mut parser = Rdp::new(StringInput::new("{{^ a}}{{/ a}}"));
        assert!(parser.inverted());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::inverted, 0, 14),
                            Token::new(Rule::path, 4, 5),
                            Token::new(Rule::identifier, 4, 5),
                            Token::new(Rule::path, 11, 12),
                            Token::new(Rule::identifier, 11, 12)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn section() {
        let mut parser = Rdp::new(StringInput::new("{{# a}}{{/ a}}"));
        assert!(parser.section());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::section, 0, 14),
                            Token::new(Rule::path, 4, 5),
                            Token::new(Rule::identifier, 4, 5),
                            Token::new(Rule::path, 11, 12),
                            Token::new(Rule::identifier, 11, 12)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn variable() {
        let mut parser = Rdp::new(StringInput::new("{{ a }}"));
        assert!(parser.variable());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::variable, 0, 7),
                            Token::new(Rule::path, 3, 4),
                            Token::new(Rule::identifier, 3, 4)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn html() {
        let mut parser = Rdp::new(StringInput::new("{{{ a }}}"));
        assert!(parser.html());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::html, 0, 9),
                            Token::new(Rule::path, 4, 5),
                            Token::new(Rule::identifier, 4, 5)];
        assert_eq!(&expected, parser.queue());
    }

    #[test]
    fn program() {
        let mut parser = Rdp::new(StringInput::new("
            {{> includes/header }}

                {{# robots}}

{{ name.first }}

                {{/ robots}}
                {{^ robots}}
                    {{! else clause }}
                    No robots
                {{/ robots}}

            {{> includes/footer }}
            {{{ unescaped.html }}}
        "));
        assert!(parser.program());
        assert!(parser.end());

        let expected = vec![Token::new(Rule::program, 0, 380),
                            Token::new(Rule::template, 0, 371),
                            Token::new(Rule::statement, 0, 13),
                            Token::new(Rule::content, 0, 13),
                            Token::new(Rule::statement, 13, 35),
                            Token::new(Rule::partial, 13, 35),
                            Token::new(Rule::partial_id, 17, 32),
                            Token::new(Rule::statement, 48, 69),
                            Token::new(Rule::content, 48, 69),
                            Token::new(Rule::statement, 69, 156),
                            Token::new(Rule::section, 69, 156),
                            Token::new(Rule::path, 73, 79),
                            Token::new(Rule::identifier, 73, 79),
                            Token::new(Rule::template, 102, 144),
                            Token::new(Rule::statement, 102, 106),
                            Token::new(Rule::content, 102, 106),
                            Token::new(Rule::statement, 106, 122),
                            Token::new(Rule::variable, 106, 122),
                            Token::new(Rule::path, 109, 119),
                            Token::new(Rule::identifier, 109, 113),
                            Token::new(Rule::identifier, 114, 119),
                            Token::new(Rule::statement, 122, 144),
                            Token::new(Rule::content, 122, 144),
                            Token::new(Rule::path, 148, 154),
                            Token::new(Rule::identifier, 148, 154),
                            Token::new(Rule::statement, 173, 283),
                            Token::new(Rule::inverted, 173, 283),
                            Token::new(Rule::path, 177, 183),
                            Token::new(Rule::identifier, 177, 183),
                            Token::new(Rule::template, 245, 271),
                            Token::new(Rule::statement, 245, 271),
                            Token::new(Rule::content, 245, 271),
                            Token::new(Rule::path, 275, 281),
                            Token::new(Rule::identifier, 275, 281),
                            Token::new(Rule::statement, 296, 314),
                            Token::new(Rule::content, 296, 314),
                            Token::new(Rule::statement, 314, 336),
                            Token::new(Rule::partial, 314, 336),
                            Token::new(Rule::partial_id, 318, 333),
                            Token::new(Rule::statement, 349, 371),
                            Token::new(Rule::html, 349, 371),
                            Token::new(Rule::path, 353, 367),
                            Token::new(Rule::identifier, 353, 362),
                            Token::new(Rule::identifier, 363, 367)];
        assert_eq!(&expected, parser.queue());
    }
}
<|endoftext|>


<commit_before>//! Compile-time assertions to ensure that invariants are met.
//!
//! # Usage
//!
//! This crate is available [on crates.io][crate] and can be used by adding the
//! following to your project's `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! static_assertions = "0.1"
//! ```
//!
//! and this to your crate root:
//!
//! ```
//! #[macro_use]
//! extern crate static_assertions;
//! # fn main() {}
//! ```
//!
//! # Assert Equal Size
//!
//! When performing operations such as pointer casts or dealing with [`usize`]
//! versus [`u64`] versus [`u32`], the size of your types matter. This is where
//! [`assert_eq_size`] comes into play. Types provided as arguments to
//! [`assert_eq_size`] are ensured to be the same size at compile-time. If the
//! types differ in size, the code will fail to compile.
//!
//! ```
//! # #[macro_use]
//! # extern crate static_assertions;
//! # fn main() {
//! assert_eq_size!([u8; 4], (u16, u16), u32);
//!
//! // Produces a compilation failure:
//! // assert_eq_size!(u32, u8);
//! # }
//! ```
//!
//! Similar to [`assert_eq_size`], there is [`assert_eq_size_val`]. Instead of
//! specifying types to compare, values' sizes can be directly compared against
//! each other.
//!
//! ```
//! # #[macro_use]
//! # extern crate static_assertions;
//! # fn main() {
//! let x = 42u8;
//! let y = true;
//!
//! assert_eq_size_val!(x, y);
//! # }
//! ```
//! [`assert_eq_size_val`] doesn't consume its arguments and thus works for
//! non-[`Clone`]able values.
//!
//! ```
//! # #[macro_use]
//! # extern crate static_assertions;
//! # fn main() {
//! struct Buffer([u8; 256]);
//!
//! let buf = Buffer([0; 256]);
//! let val = [0u64; 32];
//!
//! assert_eq_size_val!(buf, val);
//!
//! // `buf` and `val` can be used here
//! # }
//! ```
//!
//! # Assert Constant Expression
//!
//! Constant expressions can be ensured to have certain properties via
//! [`const_assert`]. If the expression evaluates to `false`, the file will fail
//! to compile. This is synonymous to [`static_assert` in C++][static_assert].
//!
//! ```
//! # #[macro_use]
//! # extern crate static_assertions;
//! # fn main() {
//! const NUM: usize = 32;
//!
//! const_assert!(NUM * NUM == 1024);
//! # }
//! ```
//!
//! As a shorthand for `const_assert!(a == b)`, there's [`const_assert_eq`]:
//!
//! ```
//! # #[macro_use]
//! # extern crate static_assertions;
//! # fn main() {
//! const NUM: usize = 32;
//! const_assert_eq!(NUM + NUM, 64);
//!
//! const TWO: usize = 2;
//! const_assert_eq!(TWO * TWO, TWO + TWO, 4);
//! # }
//! ```
//!
//! **Limitation:** Due to implementation details, [`assert_eq_size`],
//! [`const_assert`], and [`const_assert_eq`] can only be used from within the
//! context of a function.
//!
//! [crate]: https://crates.io/crates/static_assertions
//! [static_assert]: http://en.cppreference.com/w/cpp/language/static_assert
//! [`Clone`]: https://doc.rust-lang.org/std/clone/trait.Clone.html
//! [`usize`]: https://doc.rust-lang.org/std/primitive.usize.html
//! [`u64`]: https://doc.rust-lang.org/std/primitive.u64.html
//! [`u32`]: https://doc.rust-lang.org/std/primitive.u32.html
//! [`assert_eq_size_val`]: macro.assert_eq_size_val.html
//! [`assert_eq_size`]: macro.assert_eq_size.html
//! [`const_assert`]: macro.const_assert.html
//! [`const_assert_eq`]: macro.const_assert_eq.html

#![no_std]

#[doc(hidden)]
pub extern crate core as _core;

/// Asserts at compile-time that the types have equal sizes.
///
/// This especially is useful for when coercing pointers between different types
/// and ensuring the underlying values are the same size.
///
/// # Example
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// struct Byte(u8);
///
/// # fn main() {
/// assert_eq_size!(Byte, u8);
///
/// // Supports unlimited arguments:
/// assert_eq_size!([Byte; 4], [u16; 2], u32);
///
/// // Fails to compile:
/// // assert_eq_size!(Byte, u16);
/// # }
/// ```
#[macro_export]
macro_rules! assert_eq_size {
    ($x:ty, $($xs:ty),+) => {
        #[allow(unused_unsafe)]
        unsafe {
            use $crate::_core::mem::{forget, transmute, uninitialized};
            $(forget::<$xs>(transmute(uninitialized::<$x>()));)+
        }
    }
}

/// Asserts at compile-time that the values have equal sizes.
///
/// # Example
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// # fn main() {
/// struct Byte(u8);
///
/// let x = 10u8;
/// let y = Byte(42); // Works for non-cloneable types
///
/// assert_eq_size_val!(x, y);
/// assert_eq_size_val!(x, y, 0u8);
///
/// // Fails to compile:
/// // assert_eq_size_val!(x, 0u32);
/// # }
/// ```
#[macro_export]
macro_rules! assert_eq_size_val {
    ($x:expr, $($xs:expr),+) => {
        #[allow(unused_unsafe)]
        unsafe {
            use $crate::_core::{mem, ptr};
            let mut copy = ptr::read(&$x);
            $(ptr::write(&mut copy, mem::transmute(ptr::read(&$xs)));)+
            mem::forget(copy);
        }
    }
}

/// Asserts at compile-time that the constant expression evaluates to `true`.
///
/// # Example
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// # fn main() {
/// const_assert!(2 + 2 == 4);
///
/// const FIVE: usize = 5;
/// const_assert!(FIVE - FIVE == 0);
///
/// // Fails to compile:
/// // const_assert!(1 >= 2);
/// # }
/// ```
#[macro_export]
macro_rules! const_assert {
    ($cond:expr) => {
        // Causes overflow if condition is false
        let _ = [(); 0 - (!($cond) as usize)];
    };
    ($($xs:expr),+) => {
        const_assert!($($xs)&&+);
    };
    ($($xs:expr);+ $(;)*) => {
        const_assert!($($xs),+);
    };
}

/// Asserts at compile-time that the constants are equal in value.
#[macro_export]
macro_rules! const_assert_eq {
    ($x:expr, $($xs:expr),+) => {
        const_assert!($($x == $xs),+);
    }
}
<commit_msg>Document limitations within its own section<commit_after>//! Compile-time assertions to ensure that invariants are met.
//!
//! # Usage
//!
//! This crate is available [on crates.io][crate] and can be used by adding the
//! following to your project's `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! static_assertions = "0.1"
//! ```
//!
//! and this to your crate root:
//!
//! ```
//! #[macro_use]
//! extern crate static_assertions;
//! # fn main() {}
//! ```
//!
//! # Assert Equal Size
//!
//! When performing operations such as pointer casts or dealing with [`usize`]
//! versus [`u64`] versus [`u32`], the size of your types matter. This is where
//! [`assert_eq_size`] comes into play. Types provided as arguments to
//! [`assert_eq_size`] are ensured to be the same size at compile-time. If the
//! types differ in size, the code will fail to compile.
//!
//! ```
//! # #[macro_use]
//! # extern crate static_assertions;
//! # fn main() {
//! assert_eq_size!([u8; 4], (u16, u16), u32);
//!
//! // Produces a compilation failure:
//! // assert_eq_size!(u32, u8);
//! # }
//! ```
//!
//! Similar to [`assert_eq_size`], there is [`assert_eq_size_val`]. Instead of
//! specifying types to compare, values' sizes can be directly compared against
//! each other.
//!
//! ```
//! # #[macro_use]
//! # extern crate static_assertions;
//! # fn main() {
//! let x = 42u8;
//! let y = true;
//!
//! assert_eq_size_val!(x, y);
//! # }
//! ```
//! [`assert_eq_size_val`] doesn't consume its arguments and thus works for
//! non-[`Clone`]able values.
//!
//! ```
//! # #[macro_use]
//! # extern crate static_assertions;
//! # fn main() {
//! struct Buffer([u8; 256]);
//!
//! let buf = Buffer([0; 256]);
//! let val = [0u64; 32];
//!
//! assert_eq_size_val!(buf, val);
//!
//! // `buf` and `val` can be used here
//! # }
//! ```
//!
//! # Assert Constant Expression
//!
//! Constant expressions can be ensured to have certain properties via
//! [`const_assert`]. If the expression evaluates to `false`, the file will fail
//! to compile. This is synonymous to [`static_assert` in C++][static_assert].
//!
//! ```
//! # #[macro_use]
//! # extern crate static_assertions;
//! # fn main() {
//! const NUM: usize = 32;
//!
//! const_assert!(NUM * NUM == 1024);
//! # }
//! ```
//!
//! As a shorthand for `const_assert!(a == b)`, there's [`const_assert_eq`]:
//!
//! ```
//! # #[macro_use]
//! # extern crate static_assertions;
//! # fn main() {
//! const NUM: usize = 32;
//! const_assert_eq!(NUM + NUM, 64);
//!
//! const TWO: usize = 2;
//! const_assert_eq!(TWO * TWO, TWO + TWO, 4);
//! # }
//! ```
//!
//! # Limitations
//!
//! Due to implementation details, [`assert_eq_size`], [`const_assert`], and
//! [`const_assert_eq`] can only be used from within the context of a function.
//!
//! [crate]: https://crates.io/crates/static_assertions
//! [static_assert]: http://en.cppreference.com/w/cpp/language/static_assert
//! [`Clone`]: https://doc.rust-lang.org/std/clone/trait.Clone.html
//! [`usize`]: https://doc.rust-lang.org/std/primitive.usize.html
//! [`u64`]: https://doc.rust-lang.org/std/primitive.u64.html
//! [`u32`]: https://doc.rust-lang.org/std/primitive.u32.html
//! [`assert_eq_size_val`]: macro.assert_eq_size_val.html
//! [`assert_eq_size`]: macro.assert_eq_size.html
//! [`const_assert`]: macro.const_assert.html
//! [`const_assert_eq`]: macro.const_assert_eq.html

#![no_std]

#[doc(hidden)]
pub extern crate core as _core;

/// Asserts at compile-time that the types have equal sizes.
///
/// This especially is useful for when coercing pointers between different types
/// and ensuring the underlying values are the same size.
///
/// # Example
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// struct Byte(u8);
///
/// # fn main() {
/// assert_eq_size!(Byte, u8);
///
/// // Supports unlimited arguments:
/// assert_eq_size!([Byte; 4], [u16; 2], u32);
///
/// // Fails to compile:
/// // assert_eq_size!(Byte, u16);
/// # }
/// ```
#[macro_export]
macro_rules! assert_eq_size {
    ($x:ty, $($xs:ty),+) => {
        #[allow(unused_unsafe)]
        unsafe {
            use $crate::_core::mem::{forget, transmute, uninitialized};
            $(forget::<$xs>(transmute(uninitialized::<$x>()));)+
        }
    }
}

/// Asserts at compile-time that the values have equal sizes.
///
/// # Example
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// # fn main() {
/// struct Byte(u8);
///
/// let x = 10u8;
/// let y = Byte(42); // Works for non-cloneable types
///
/// assert_eq_size_val!(x, y);
/// assert_eq_size_val!(x, y, 0u8);
///
/// // Fails to compile:
/// // assert_eq_size_val!(x, 0u32);
/// # }
/// ```
#[macro_export]
macro_rules! assert_eq_size_val {
    ($x:expr, $($xs:expr),+) => {
        #[allow(unused_unsafe)]
        unsafe {
            use $crate::_core::{mem, ptr};
            let mut copy = ptr::read(&$x);
            $(ptr::write(&mut copy, mem::transmute(ptr::read(&$xs)));)+
            mem::forget(copy);
        }
    }
}

/// Asserts at compile-time that the constant expression evaluates to `true`.
///
/// # Example
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// # fn main() {
/// const_assert!(2 + 2 == 4);
///
/// const FIVE: usize = 5;
/// const_assert!(FIVE - FIVE == 0);
///
/// // Fails to compile:
/// // const_assert!(1 >= 2);
/// # }
/// ```
#[macro_export]
macro_rules! const_assert {
    ($cond:expr) => {
        // Causes overflow if condition is false
        let _ = [(); 0 - (!($cond) as usize)];
    };
    ($($xs:expr),+) => {
        const_assert!($($xs)&&+);
    };
    ($($xs:expr);+ $(;)*) => {
        const_assert!($($xs),+);
    };
}

/// Asserts at compile-time that the constants are equal in value.
#[macro_export]
macro_rules! const_assert_eq {
    ($x:expr, $($xs:expr),+) => {
        const_assert!($($x == $xs),+);
    }
}
<|endoftext|>


<commit_before>#![feature(collections, core, std_misc, io)]

#[macro_use] extern crate log;
extern crate "postgres" as pg;
extern crate "rustc-serialize" as rustc_serialize;
extern crate curl;
extern crate flate2;
extern crate git2;
extern crate oauth2;
extern crate openssl;
extern crate r2d2;
extern crate r2d2_postgres;
extern crate rand;
extern crate s3;
extern crate semver;
extern crate time;
extern crate url;

extern crate "conduit-conditional-get" as conduit_conditional_get;
extern crate "conduit-cookie" as conduit_cookie;
extern crate "conduit-git-http-backend" as conduit_git_http_backend;
extern crate "conduit-json-parser" as conduit_json_parser;
extern crate "conduit-log-requests" as conduit_log_requests;
extern crate "conduit-middleware" as conduit_middleware;
extern crate "conduit-router" as conduit_router;
extern crate "conduit-static" as conduit_static;
extern crate conduit;

pub use app::App;
pub use config::Config;
pub use self::dependency::Dependency;
pub use self::download::{CrateDownload, VersionDownload};
pub use self::keyword::Keyword;
pub use self::krate::Crate;
pub use self::model::Model;
pub use self::user::User;
pub use self::version::Version;

use std::sync::Arc;
use std::error::Error;

use conduit_router::RouteBuilder;
use conduit_middleware::MiddlewareBuilder;

use util::{C, R, R404};

pub mod app;
pub mod config;
pub mod db;
pub mod dependency;
pub mod dist;
pub mod download;
pub mod git;
pub mod keyword;
pub mod krate;
pub mod model;
pub mod upload;
pub mod user;
pub mod util;
pub mod version;
mod licenses;

#[derive(PartialEq, Eq, Clone, Copy)]
pub enum Env {
    Development,
    Test,
    Production,
}

pub fn middleware(app: Arc<App>) -> MiddlewareBuilder {
    let mut api_router = RouteBuilder::new();

    api_router.get("/crates", C(krate::index));
    api_router.get("/crates/:crate_id", C(krate::show));
    api_router.put("/crates/new", C(krate::new));
    api_router.get("/crates/:crate_id/:version", C(version::show));
    api_router.get("/crates/:crate_id/:version/download", C(krate::download));
    api_router.get("/crates/:crate_id/:version/dependencies", C(version::dependencies));
    api_router.get("/crates/:crate_id/:version/downloads", C(version::downloads));
    api_router.get("/crates/:crate_id/:version/authors", C(version::authors));
    api_router.get("/crates/:crate_id/downloads", C(krate::downloads));
    api_router.get("/crates/:crate_id/versions", C(krate::versions));
    api_router.put("/crates/:crate_id/follow", C(krate::follow));
    api_router.delete("/crates/:crate_id/follow", C(krate::unfollow));
    api_router.get("/crates/:crate_id/following", C(krate::following));
    api_router.get("/crates/:crate_id/owners", C(krate::owners));
    api_router.put("/crates/:crate_id/owners", C(krate::add_owners));
    api_router.delete("/crates/:crate_id/owners", C(krate::remove_owners));
    api_router.delete("/crates/:crate_id/:version/yank", C(version::yank));
    api_router.put("/crates/:crate_id/:version/unyank", C(version::unyank));
    api_router.get("/crates/:crate_id/reverse_dependencies", C(krate::reverse_dependencies));
    api_router.get("/versions", C(version::index));
    api_router.get("/versions/:version_id", C(version::show));
    api_router.get("/keywords", C(keyword::index));
    api_router.get("/keywords/:keyword_id", C(keyword::show));
    let api_router = Arc::new(R404(api_router));

    let mut router = RouteBuilder::new();

    // Mount the router under the /api/v1 path so we're at least somewhat at the
    // liberty to change things in the future!
    router.get("/api/v1/*path", R(api_router.clone()));
    router.put("/api/v1/*path", R(api_router.clone()));
    router.post("/api/v1/*path", R(api_router.clone()));
    router.head("/api/v1/*path", R(api_router.clone()));
    router.delete("/api/v1/*path", R(api_router));

    router.get("/authorize_url", C(user::github_authorize));
    router.get("/authorize", C(user::github_access_token));
    router.get("/logout", C(user::logout));
    router.get("/me", C(user::me));
    router.put("/me/reset_token", C(user::reset_token));
    router.get("/me/updates", C(user::updates));
    router.get("/summary", C(krate::summary));

    let env = app.config.env;
    if env == Env::Development {
        let s1 = conduit_git_http_backend::Serve(app.git_repo_checkout.clone());
        let s2 = conduit_git_http_backend::Serve(app.git_repo_checkout.clone());
        router.get("/git/index/*path", s1);
        router.post("/git/index/*path", s2);
    }

    let mut m = MiddlewareBuilder::new(R404(router));
    if env == Env::Development {
        m.add(DebugMiddleware);
    }
    if env != Env::Test {
        m.add(conduit_log_requests::LogRequests(log::LogLevel::Info));
    }
    m.around(util::Head::new());
    m.add(conduit_conditional_get::ConditionalGet);
    m.add(conduit_cookie::Middleware::new(app.session_key.as_bytes()));
    m.add(conduit_cookie::SessionMiddleware::new("cargo_session",
                                                 env == Env::Production));
    m.add(app::AppMiddleware::new(app));
    m.add(db::TransactionMiddleware);
    m.add(user::Middleware);
    if env != Env::Test {
        m.around(dist::Middleware::new());
    }

    return m;

    struct DebugMiddleware;

    impl conduit_middleware::Middleware for DebugMiddleware {
        fn before(&self, req: &mut conduit::Request)
                  -> Result<(), Box<Error+Send>> {
            println!("  version: {}", req.http_version());
            println!("  method: {:?}", req.method());
            println!("  scheme: {:?}", req.scheme());
            println!("  host: {:?}", req.host());
            println!("  path: {}", req.path());
            println!("  query_string: {:?}", req.query_string());
            for &(k, ref v) in req.headers().all().iter() {
                println!("  hdr: {}={:?}", k, v);
            }
            Ok(())
        }
        fn after(&self, _req: &mut conduit::Request,
                 res: Result<conduit::Response, Box<Error+Send>>)
                 -> Result<conduit::Response, Box<Error+Send>> {
            res.map(|res| {
                println!("  <- {:?}", res.status);
                for (k, v) in res.headers.iter() {
                    println!("  <- {} {:?}", k, v);
                }
                res
            })
        }
    }
}

pub fn now() -> time::Timespec {
    time::now_utc().to_timespec()
}

pub fn encode_time(ts: time::Timespec) -> String {
    time::at_utc(ts).rfc3339().to_string()
}
<commit_msg>Add a missing feature<commit_after>#![feature(collections, core, std_misc, io, str_char)]

#[macro_use] extern crate log;
extern crate "postgres" as pg;
extern crate "rustc-serialize" as rustc_serialize;
extern crate curl;
extern crate flate2;
extern crate git2;
extern crate oauth2;
extern crate openssl;
extern crate r2d2;
extern crate r2d2_postgres;
extern crate rand;
extern crate s3;
extern crate semver;
extern crate time;
extern crate url;

extern crate "conduit-conditional-get" as conduit_conditional_get;
extern crate "conduit-cookie" as conduit_cookie;
extern crate "conduit-git-http-backend" as conduit_git_http_backend;
extern crate "conduit-json-parser" as conduit_json_parser;
extern crate "conduit-log-requests" as conduit_log_requests;
extern crate "conduit-middleware" as conduit_middleware;
extern crate "conduit-router" as conduit_router;
extern crate "conduit-static" as conduit_static;
extern crate conduit;

pub use app::App;
pub use config::Config;
pub use self::dependency::Dependency;
pub use self::download::{CrateDownload, VersionDownload};
pub use self::keyword::Keyword;
pub use self::krate::Crate;
pub use self::model::Model;
pub use self::user::User;
pub use self::version::Version;

use std::sync::Arc;
use std::error::Error;

use conduit_router::RouteBuilder;
use conduit_middleware::MiddlewareBuilder;

use util::{C, R, R404};

pub mod app;
pub mod config;
pub mod db;
pub mod dependency;
pub mod dist;
pub mod download;
pub mod git;
pub mod keyword;
pub mod krate;
pub mod model;
pub mod upload;
pub mod user;
pub mod util;
pub mod version;
mod licenses;

#[derive(PartialEq, Eq, Clone, Copy)]
pub enum Env {
    Development,
    Test,
    Production,
}

pub fn middleware(app: Arc<App>) -> MiddlewareBuilder {
    let mut api_router = RouteBuilder::new();

    api_router.get("/crates", C(krate::index));
    api_router.get("/crates/:crate_id", C(krate::show));
    api_router.put("/crates/new", C(krate::new));
    api_router.get("/crates/:crate_id/:version", C(version::show));
    api_router.get("/crates/:crate_id/:version/download", C(krate::download));
    api_router.get("/crates/:crate_id/:version/dependencies", C(version::dependencies));
    api_router.get("/crates/:crate_id/:version/downloads", C(version::downloads));
    api_router.get("/crates/:crate_id/:version/authors", C(version::authors));
    api_router.get("/crates/:crate_id/downloads", C(krate::downloads));
    api_router.get("/crates/:crate_id/versions", C(krate::versions));
    api_router.put("/crates/:crate_id/follow", C(krate::follow));
    api_router.delete("/crates/:crate_id/follow", C(krate::unfollow));
    api_router.get("/crates/:crate_id/following", C(krate::following));
    api_router.get("/crates/:crate_id/owners", C(krate::owners));
    api_router.put("/crates/:crate_id/owners", C(krate::add_owners));
    api_router.delete("/crates/:crate_id/owners", C(krate::remove_owners));
    api_router.delete("/crates/:crate_id/:version/yank", C(version::yank));
    api_router.put("/crates/:crate_id/:version/unyank", C(version::unyank));
    api_router.get("/crates/:crate_id/reverse_dependencies", C(krate::reverse_dependencies));
    api_router.get("/versions", C(version::index));
    api_router.get("/versions/:version_id", C(version::show));
    api_router.get("/keywords", C(keyword::index));
    api_router.get("/keywords/:keyword_id", C(keyword::show));
    let api_router = Arc::new(R404(api_router));

    let mut router = RouteBuilder::new();

    // Mount the router under the /api/v1 path so we're at least somewhat at the
    // liberty to change things in the future!
    router.get("/api/v1/*path", R(api_router.clone()));
    router.put("/api/v1/*path", R(api_router.clone()));
    router.post("/api/v1/*path", R(api_router.clone()));
    router.head("/api/v1/*path", R(api_router.clone()));
    router.delete("/api/v1/*path", R(api_router));

    router.get("/authorize_url", C(user::github_authorize));
    router.get("/authorize", C(user::github_access_token));
    router.get("/logout", C(user::logout));
    router.get("/me", C(user::me));
    router.put("/me/reset_token", C(user::reset_token));
    router.get("/me/updates", C(user::updates));
    router.get("/summary", C(krate::summary));

    let env = app.config.env;
    if env == Env::Development {
        let s1 = conduit_git_http_backend::Serve(app.git_repo_checkout.clone());
        let s2 = conduit_git_http_backend::Serve(app.git_repo_checkout.clone());
        router.get("/git/index/*path", s1);
        router.post("/git/index/*path", s2);
    }

    let mut m = MiddlewareBuilder::new(R404(router));
    if env == Env::Development {
        m.add(DebugMiddleware);
    }
    if env != Env::Test {
        m.add(conduit_log_requests::LogRequests(log::LogLevel::Info));
    }
    m.around(util::Head::new());
    m.add(conduit_conditional_get::ConditionalGet);
    m.add(conduit_cookie::Middleware::new(app.session_key.as_bytes()));
    m.add(conduit_cookie::SessionMiddleware::new("cargo_session",
                                                 env == Env::Production));
    m.add(app::AppMiddleware::new(app));
    m.add(db::TransactionMiddleware);
    m.add(user::Middleware);
    if env != Env::Test {
        m.around(dist::Middleware::new());
    }

    return m;

    struct DebugMiddleware;

    impl conduit_middleware::Middleware for DebugMiddleware {
        fn before(&self, req: &mut conduit::Request)
                  -> Result<(), Box<Error+Send>> {
            println!("  version: {}", req.http_version());
            println!("  method: {:?}", req.method());
            println!("  scheme: {:?}", req.scheme());
            println!("  host: {:?}", req.host());
            println!("  path: {}", req.path());
            println!("  query_string: {:?}", req.query_string());
            for &(k, ref v) in req.headers().all().iter() {
                println!("  hdr: {}={:?}", k, v);
            }
            Ok(())
        }
        fn after(&self, _req: &mut conduit::Request,
                 res: Result<conduit::Response, Box<Error+Send>>)
                 -> Result<conduit::Response, Box<Error+Send>> {
            res.map(|res| {
                println!("  <- {:?}", res.status);
                for (k, v) in res.headers.iter() {
                    println!("  <- {} {:?}", k, v);
                }
                res
            })
        }
    }
}

pub fn now() -> time::Timespec {
    time::now_utc().to_timespec()
}

pub fn encode_time(ts: time::Timespec) -> String {
    time::at_utc(ts).rfc3339().to_string()
}
<|endoftext|>


<commit_before>extern crate regex;

extern crate rusoto_core;
extern crate rusoto_sts;
extern crate rusoto_dynamodb;

pub mod SessionHandler {
    use std::fs::File;
    use std::io::BufReader;
    use std::io::prelude::*;
    use std::env;

    use rusoto_core::{DefaultCredentialsProvider, Region};
    use rusoto_core::{default_tls_client, ProfileProvider, ProvideAwsCredentials};
    use rusoto_sts::{StsClient, StsAssumeRoleSessionCredentialsProvider};
    use rusoto_dynamodb::{DynamoDb, DynamoDbClient, ListTablesInput};

    use regex::Regex;

    struct AWSConfig {
        profiles: Option<Vec<AWSProfile>>,
    }

    struct AWSProfile {
        source_profile: Option<String>,
        region: Option<String>,
        output: Option<String>,
        role_arn: Option<String>,
        mfa_serial: Option<String>
    }

    pub fn create(profile_name: &str) {
        println!("Creating session for profile \"{}\"...", profile_name);

        // let mut profile = ProfileProvider::new().unwrap();
        // profile.set_profile(profile_name);

        // let sts = StsClient::new(default_tls_client().unwrap(), profile, Region::EuWest1);
        // let provider = StsAssumeRoleSessionCredentialsProvider::new(
        //     sts,
        //     "arn:aws:iam::247901982038:role/CloudreachAdminRole".to_owned(),
        //     "default".to_owned(),
        //     None, None, None, None
        // );
        // let client = DynamoDbClient::new(default_tls_client().unwrap(), profile, Region::EuWest1);
        // let list_tables_input: ListTablesInput = Default::default();

        // match client.list_tables(&list_tables_input) {
        //     Ok(output) => {
        //         match output.table_names {
        //             Some(table_name_list) => {
        //                 println!("Tables in database:");

        //                 for table_name in table_name_list {
        //                     println!("{}", table_name);
        //                 }
        //             }
        //             None => println!("No tables in database!"),
        //         }
        //     }
        //     Err(error) => {
        //         println!("Error: {:?}", error);
        //     }
        // }
    }

    pub fn show() {
        println!("Showing sessions...");
        let aws_config_file = read_aws_config_file();
        println!("{}", aws_config_file);

        println!("Spliting...");
        let profiles = split_config_file(aws_config_file);
        for profile in &profiles {
            println!("{}", profile);
        }

    }

    pub fn refresh() {
        println!("Refreshing sessions...");
    }

    pub fn clean() {
        println!("Cleaning sessions...");
    }

    fn read_aws_config_file() -> String {
        let path = match env::home_dir() {
            Some(path) => path,
            None => panic!("Could not retrieve user's home directory."),
        };
        let config_file_path = format!("{}/.aws/config", path.display());

        let f = File::open(config_file_path).expect("Could not find AWS config file.");
        let mut buf_reader = BufReader::new(f);
        let mut contents = String::new();
        buf_reader.read_to_string(&mut contents).expect("Found config file but could not read it.");

        contents
    }

    // fn get_profile_names(aws_config: String) -> Vec<String>{
    //     let re = Regex::new(r"(?m)(\[profile+.+\])").unwrap();
    //     let caps = re.captures(&aws_config).unwrap();

    //     println!("{:?}", caps);
    //     let profile1 = caps.get(0).map_or("", |m| m.as_str());
    //     let profile2 = caps.get(1).map_or("", |m| m.as_str());

    //     let mut profiles = Vec::new();
    //     profiles.push(profile1.to_string());
    //     profiles.push(profile2.to_string());

    //     profiles
    // }

    fn split_config_file(aws_config: String) -> Vec<String> {
        let split = aws_config.split("\n\n");
        let profiles: Vec<String> = split.map(|s| s.to_string()).collect();;

        profiles
    }
}
<commit_msg>Add function that loads the aws config<commit_after>extern crate regex;

extern crate rusoto_core;
extern crate rusoto_sts;
extern crate rusoto_dynamodb;

pub mod SessionHandler {
    use std::fs::File;
    use std::io::BufReader;
    use std::io::prelude::*;
    use std::env;

    use rusoto_core::{DefaultCredentialsProvider, Region};
    use rusoto_core::{default_tls_client, ProfileProvider, ProvideAwsCredentials};
    use rusoto_sts::{StsClient, StsAssumeRoleSessionCredentialsProvider};
    use rusoto_dynamodb::{DynamoDb, DynamoDbClient, ListTablesInput};

    use regex::Regex;

    struct AWSConfig {
        profiles: Option<Vec<AWSProfile>>,
    }

    struct AWSProfile {
        source_profile: Option<String>,
        region: Option<String>,
        output: Option<String>,
        role_arn: Option<String>,
        mfa_serial: Option<String>
    }

    pub fn create(profile_name: &str) {
        println!("Creating session for profile \"{}\"...", profile_name);

        // let mut profile = ProfileProvider::new().unwrap();
        // profile.set_profile(profile_name);

        // let sts = StsClient::new(default_tls_client().unwrap(), profile, Region::EuWest1);
        // let provider = StsAssumeRoleSessionCredentialsProvider::new(
        //     sts,
        //     "arn:aws:iam::247901982038:role/CloudreachAdminRole".to_owned(),
        //     "default".to_owned(),
        //     None, None, None, None
        // );
        // let client = DynamoDbClient::new(default_tls_client().unwrap(), profile, Region::EuWest1);
        // let list_tables_input: ListTablesInput = Default::default();

        // match client.list_tables(&list_tables_input) {
        //     Ok(output) => {
        //         match output.table_names {
        //             Some(table_name_list) => {
        //                 println!("Tables in database:");

        //                 for table_name in table_name_list {
        //                     println!("{}", table_name);
        //                 }
        //             }
        //             None => println!("No tables in database!"),
        //         }
        //     }
        //     Err(error) => {
        //         println!("Error: {:?}", error);
        //     }
        // }
    }

    pub fn show() {
        println!("Showing sessions...");
        load_config();
    }

    pub fn refresh() {
        println!("Refreshing sessions...");
    }

    pub fn clean() {
        println!("Cleaning sessions...");
    }

    fn load_config() {
        let aws_config_file = read_aws_config_file();

        println!("Spliting...");
        let profiles = split_config_file(aws_config_file);

        // Iterate over profile chunks
        for profile in &profiles {
            // Create a vector of string that holds each line
            let split = profile.split("\n");
            let lines: Vec<String> = split.map(|s| s.to_string()).collect();

            // Get the profile name from the first line
            let profile_line = lines[0].to_owned();
            let split2 = profile_line.split(" ");
            let mut words: Vec<String> = split2.map(|s| s.to_string()).collect();
            if words.len() > 1 {
                words[1].pop();
                println!("{}", words[1].trim_right());
            }

            for i in 1..lines.len() {
                println!("{}", lines[i]);
            }
        }
    }
    fn read_aws_config_file() -> String {
        let path = match env::home_dir() {
            Some(path) => path,
            None => panic!("Could not retrieve user's home directory."),
        };
        let config_file_path = format!("{}/.aws/config", path.display());

        let f = File::open(config_file_path).expect("Could not find AWS config file.");
        let mut buf_reader = BufReader::new(f);
        let mut contents = String::new();
        buf_reader.read_to_string(&mut contents).expect("Found config file but could not read it.");

        contents
    }

    // fn get_profile_names(aws_config: String) -> Vec<String>{
    //     let re = Regex::new(r"(?m)(\[profile+.+\])").unwrap();
    //     let caps = re.captures(&aws_config).unwrap();

    //     println!("{:?}", caps);
    //     let profile1 = caps.get(0).map_or("", |m| m.as_str());
    //     let profile2 = caps.get(1).map_or("", |m| m.as_str());

    //     let mut profiles = Vec::new();
    //     profiles.push(profile1.to_string());
    //     profiles.push(profile2.to_string());

    //     profiles
    // }

    fn split_config_file(aws_config: String) -> Vec<String> {
        let split = aws_config.split("\n\n");
        let profiles: Vec<String> = split.map(|s| s.to_string()).collect();;

        profiles
    }
}
<|endoftext|>


<commit_before>//! Simple crate that parses athenahealth Watertown's Nourish menu, and posts it to a slack channel
//! of choice.

#![warn(missing_docs)]

#[macro_use]
extern crate lazy_static;

extern crate chrono;
extern crate inflector;
extern crate linked_hash_map;
extern crate regex;
extern crate select;
extern crate url;

use std::cmp;
use std::fmt::{self, Display, Write};

use chrono::{Datelike, Duration, NaiveDate};
use inflector::Inflector;
use linked_hash_map::LinkedHashMap;
use regex::Regex;
use select::document::Document;
use select::predicate::{Attr, Name};
use url::Url;

lazy_static! {
    /// Extracts the ingredients out of the text of a menu item.
    static ref INGREDIENTS_RE: Regex =
        Regex::new(r"Ingredients: (.*?)(?: - Serving Size.*)?\s*$").unwrap();

    /// This Regex matches nutritional information, to filter it out from the menu output.
    static ref NUTRITION_RE: Regex = Regex::new(r"Cal.*Fat.*Sat.*Sod.*Carbs.*Fib.*Pro").unwrap();

    /// This Regex matches the price information, to filter it out from the menu output.
    static ref PRICE_RE: Regex = Regex::new(r"^\d+\.\d+( ?/ ?(\d+\.\d+|[a-z]+))?$").unwrap();
}

/// The Nourish menu for a given date.
#[derive(Debug, Clone, Default)]
pub struct Menu(LinkedHashMap<String, Entry>);

/// A section of the menu.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Entry {
    /// The section header.
    pub heading: String,

    /// The food items listed under the header.
    pub items: Vec<String>,

    /// Information indicating what ingredients are in the food, including whether it's vegetarian,
    /// vegan, etc.
    pub dietary_info: Option<String>,
}

impl Display for Entry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "*{}*\n", self.heading)?;

        for item in &self.items {
            let dietary_info = if let Some(ref info) = self.dietary_info {
                format!(" (_{}_)", info)
            } else {
                String::default()
            };

            writeln!(f, "• {}{}", item, dietary_info)?;
        }

        Ok(())
    }
}

impl Menu {
    /// Returns the entries of the menu.
    pub fn entries(&mut self) -> Vec<Entry> {
        self.0.values().cloned().collect()
    }

    /// Renders the menu as a Markdown string.
    pub fn to_markdown(&mut self) -> Option<String> {
        let mut output = String::default();

        for entry in self.entries() {
            writeln!(output, "{}", entry).unwrap();
        }

        if output.is_empty() {
            return None;
        }

        writeln!(
            output,
            "> Made with :btb: by @anrussell. Please direct feature requests and bug reports to \
             https://github.com/euclio/nourishbot."
        ).unwrap();

        Some(output)
    }
}

/// Returns the URL pointing to the Nourish menu for a given day. On the weekends, returns Friday's
/// menu.
pub fn url_for_date(date: &NaiveDate) -> Url {
    let days_from_monday = cmp::min(date.weekday().num_days_from_monday(), 4);
    let monday = *date - Duration::days(date.weekday().num_days_from_monday() as i64);

    Url::parse(&format!(
        "http://dining.guckenheimer.com/clients/athenahealth/fss/fss.\
                       nsf/weeklyMenuLaunch/8DURSE~{}/$file/day{}.htm",
        monday.format("%m-%d-%Y"),
        days_from_monday + 1
    )).unwrap()
}

/// Parses the menu information out of HTML.
pub fn parse_menu(html: &str) -> Menu {
    let document = Document::from(html);
    let menu_node = document.find(Attr("id", "center_text")).next().unwrap();

    let mut menu = LinkedHashMap::new();
    let mut current_heading = None;

    for node in menu_node.children() {
        let text = node.text().trim().to_owned();

        if let Some("font-weight:bold;") = node.attr("style") {
            current_heading = Some(text.to_lowercase().to_title_case());
        } else {
            if let Some(ref heading) = current_heading {
                // Skip breakfast
                if heading == "Breakfast Special" {
                    continue;
                }

                // Skip nutrition and price information.
                if NUTRITION_RE.is_match(&text) || PRICE_RE.is_match(&text) {
                    continue;
                }

                let mut entry = menu.entry(heading.clone()).or_insert_with(|| {
                    Entry {
                        heading: heading.to_owned(),
                        items: Vec::new(),
                        dietary_info: None,
                    }
                });

                if node.is(Name("div")) {
                    entry.items.push(text);
                } else if let Some(caps) = INGREDIENTS_RE.captures(&text) {
                    entry.dietary_info = Some(caps[1].to_owned());
                }
            } else {
                println!("encountered entry without a heading");
                continue;
            }
        }
    }

    Menu(menu)
}

#[cfg(test)]
mod tests {
    use chrono::NaiveDate;

    #[test]
    fn monday() {
        let expected_url = "http://dining.guckenheimer.com/clients/athenahealth/fss/fss.\
                            nsf/weeklyMenuLaunch/8DURSE~04-18-2016/$file/day1.htm";

        assert_eq!(
            expected_url,
            &super::url_for_date(&NaiveDate::from_ymd(2016, 4, 18)).to_string()
        );
    }

    #[test]
    fn saturday() {
        let expected_url = "http://dining.guckenheimer.com/clients/athenahealth/fss/fss.\
                            nsf/weeklyMenuLaunch/8DURSE~04-11-2016/$file/day5.htm";

        assert_eq!(
            expected_url,
            &super::url_for_date(&NaiveDate::from_ymd(2016, 4, 16)).to_string()
        );

    }

    #[test]
    fn ingredients_regex() {
        use super::INGREDIENTS_RE;

        let test_cases = [
            ("Ingredients: Pork, Egg, Spicy", "Pork, Egg, Spicy"),
            (
                "Ingredients: Vegetarian - Contains Wheat, Dairy",
                "Vegetarian - Contains Wheat, Dairy",
            ),
            ("Ingredients: Vegan - Serving Size 12oz", "Vegan"),
        ];

        for &(text, ingredients) in &test_cases {
            let capture = INGREDIENTS_RE.captures(text).unwrap().get(1).unwrap();
            assert_eq!(capture.as_str(), ingredients);
        }
    }

    #[test]
    fn price_regex() {
        use super::PRICE_RE;

        let patterns = ["7.50", "0.25/oz", "2.15 / 2.65"];

        for pattern in &patterns {
            assert!(PRICE_RE.is_match(pattern), pattern.clone());
        }
    }
}
<commit_msg>do not display empty menu entries<commit_after>//! Simple crate that parses athenahealth Watertown's Nourish menu, and posts it to a slack channel
//! of choice.

#![warn(missing_docs)]

#[macro_use]
extern crate lazy_static;

extern crate chrono;
extern crate inflector;
extern crate linked_hash_map;
extern crate regex;
extern crate select;
extern crate url;

use std::cmp;
use std::fmt::{self, Display, Write};

use chrono::{Datelike, Duration, NaiveDate};
use inflector::Inflector;
use linked_hash_map::LinkedHashMap;
use regex::Regex;
use select::document::Document;
use select::predicate::{Attr, Name};
use url::Url;

lazy_static! {
    /// Extracts the ingredients out of the text of a menu item.
    static ref INGREDIENTS_RE: Regex =
        Regex::new(r"Ingredients: (.*?)(?: - Serving Size.*)?\s*$").unwrap();

    /// This Regex matches nutritional information, to filter it out from the menu output.
    static ref NUTRITION_RE: Regex = Regex::new(r"Cal.*Fat.*Sat.*Sod.*Carbs.*Fib.*Pro").unwrap();

    /// This Regex matches the price information, to filter it out from the menu output.
    static ref PRICE_RE: Regex = Regex::new(r"^\d+\.\d+( ?/ ?(\d+\.\d+|[a-z]+))?$").unwrap();
}

/// The Nourish menu for a given date.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Menu(LinkedHashMap<String, Entry>);

/// A section of the menu.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Entry {
    /// The section header.
    pub heading: String,

    /// The food items listed under the header.
    pub items: Vec<String>,

    /// Information indicating what ingredients are in the food, including whether it's vegetarian,
    /// vegan, etc.
    pub dietary_info: Option<String>,
}

impl Display for Entry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "*{}*\n", self.heading)?;

        let items = if self.items.is_empty() {
            vec![String::from("Nothing today")]
        } else {
            self.items.clone()
        };

        for item in &items {
            let dietary_info = if let Some(ref info) = self.dietary_info {
                format!(" (_{}_)", info)
            } else {
                String::default()
            };

            writeln!(f, "• {}{}", item, dietary_info)?;
        }

        Ok(())
    }
}

impl Menu {
    /// Returns the entries of the menu.
    pub fn entries(&mut self) -> Vec<Entry> {
        self.0.values().cloned().collect()
    }

    /// Renders the menu as a Markdown string.
    pub fn to_markdown(&mut self) -> Option<String> {
        let mut output = String::default();

        for entry in self.entries() {
            writeln!(output, "{}", entry).unwrap();
        }

        if output.is_empty() {
            return None;
        }

        writeln!(
            output,
            "> Made with :btb: by @anrussell. Please direct feature requests and bug reports to \
             https://github.com/euclio/nourishbot."
        ).unwrap();

        Some(output)
    }
}

/// Returns the URL pointing to the Nourish menu for a given day. On the weekends, returns Friday's
/// menu.
pub fn url_for_date(date: &NaiveDate) -> Url {
    let days_from_monday = cmp::min(date.weekday().num_days_from_monday(), 4);
    let monday = *date - Duration::days(date.weekday().num_days_from_monday() as i64);

    Url::parse(&format!(
        "http://dining.guckenheimer.com/clients/athenahealth/fss/fss.nsf\
        /weeklyMenuLaunch/8DURSE~{}/$file/day{}.htm",
        monday.format("%m-%d-%Y"),
        days_from_monday + 1
    )).unwrap()
}

/// Parses the menu information out of HTML.
pub fn parse_menu(html: &str) -> Menu {
    let document = Document::from(html);
    let menu_node = document.find(Attr("id", "center_text")).next().unwrap();

    let mut menu = LinkedHashMap::new();
    let mut current_heading = None;

    for node in menu_node.children() {
        let text = node.text().trim().to_owned();

        if let Some("font-weight:bold;") = node.attr("style") {
            current_heading = Some(text.to_lowercase().to_title_case());
        } else {
            if let Some(ref heading) = current_heading {
                // Skip breakfast
                if heading == "Breakfast Special" {
                    continue;
                }

                // Skip nutrition and price information.
                if NUTRITION_RE.is_match(&text) || PRICE_RE.is_match(&text) {
                    continue;
                }

                let entry = menu.entry(heading.clone()).or_insert_with(|| {
                    Entry {
                        heading: heading.to_owned(),
                        items: Vec::new(),
                        dietary_info: None,
                    }
                });

                if node.is(Name("div")) && !text.is_empty() {
                    entry.items.push(text);
                } else if let Some(caps) = INGREDIENTS_RE.captures(&text) {
                    entry.dietary_info = Some(caps[1].to_owned());
                }
            } else {
                println!("encountered entry without a heading");
                continue;
            }
        }
    }

    Menu(menu)
}

#[cfg(test)]
mod tests {
    use chrono::NaiveDate;
    use linked_hash_map::LinkedHashMap;

    use super::{Entry, Menu};

    #[test]
    fn parse_menu() {
        let html = r#"

CHEF'S SPECIAL PIZZA

        "#;

        let menu = super::parse_menu(html);
        let expected = {
            let mut menu = LinkedHashMap::new();
            menu.insert(String::from("Chef's Special Pizza"), Entry {
                heading: String::from("Chef's Special Pizza"),
                items: vec![],
                dietary_info: None,
            });
            Menu(menu)
        };

        assert_eq!(menu, expected);
    }

    #[test]
    fn monday() {
        let expected_url = "http://dining.guckenheimer.com/clients/athenahealth/fss/fss.\
                            nsf/weeklyMenuLaunch/8DURSE~04-18-2016/$file/day1.htm";

        assert_eq!(
            expected_url,
            &super::url_for_date(&NaiveDate::from_ymd(2016, 4, 18)).to_string()
        );
    }

    #[test]
    fn saturday() {
        let expected_url = "http://dining.guckenheimer.com/clients/athenahealth/fss/fss.\
                            nsf/weeklyMenuLaunch/8DURSE~04-11-2016/$file/day5.htm";

        assert_eq!(
            expected_url,
            &super::url_for_date(&NaiveDate::from_ymd(2016, 4, 16)).to_string()
        );

    }

    #[test]
    fn ingredients_regex() {
        use super::INGREDIENTS_RE;

        let test_cases = [
            ("Ingredients: Pork, Egg, Spicy", "Pork, Egg, Spicy"),
            (
                "Ingredients: Vegetarian - Contains Wheat, Dairy",
                "Vegetarian - Contains Wheat, Dairy",
            ),
            ("Ingredients: Vegan - Serving Size 12oz", "Vegan"),
        ];

        for &(text, ingredients) in &test_cases {
            let capture = INGREDIENTS_RE.captures(text).unwrap().get(1).unwrap();
            assert_eq!(capture.as_str(), ingredients);
        }
    }

    #[test]
    fn price_regex() {
        use super::PRICE_RE;

        let patterns = ["7.50", "0.25/oz", "2.15 / 2.65"];

        for pattern in &patterns {
            assert!(PRICE_RE.is_match(pattern), pattern.clone());
        }
    }
}
<|endoftext|>


<commit_before>//! This crate provides a simple interface to liblzma.  LZMA is more commonly known as XZ or 7zip,
//! (as in, files with the `.xz` or `.7z` file extension). LZMA compression is fast and aggressive,
//! compressing better than bzip2.  liblzma implements the XZ variant, so it can read and write
//! `.xz` files/streams.
//!
//! Two interfaces are provided.  `LzmaReader`/`LzmaWriter` are generic Readers and Writers that
//! can be composed with other `Read`/`Write` interfaces.  For example, wrap them around a `File`
//! and you can write data to a file while compressing it on the fly, or stream in an `xz` file
//! from disk.
//!
//! `compress`/`decompress` are easy to use functions for simple use cases.
//!
//! See the `LzmaReader` and `LzmaWriter` documentation for further details on that interface.
//! `compress` and `decompress` are documented here.
//!
//! # Examples
//!
//! ```
//! let test_string = "Like tears in rain".to_string();
//! let mut compressed = lzma::compress(&test_string.into_bytes(), 6).unwrap();
//! let decompressed = lzma::decompress(&mut compressed).unwrap();
//! let decompressed_str = String::from_utf8(decompressed).unwrap();
//!
//! assert_eq!("Like tears in rain", decompressed_str);
//! ```

pub enum Direction {
	Compress,
	Decompress,
}

mod lzma_sys;
pub mod lzma_stream_wrapper;
pub mod reader;
pub mod writer;
pub mod error;

use std::io::Read;
pub use reader::LzmaReader;
pub use writer::LzmaWriter;
pub use error::LzmaError;

pub const EXTREME_PRESET: u32 = (1 << 31);

/// Compress `buf` and return the result.
///
/// preset is [0-9] and corresponds to xz's presets.
/// Binary-or with EXTREME_PRESET for --extreme (e.g. 9 | EXTREME_PRESET).
pub fn compress(buf: &[u8], preset: u32) -> Result<Vec<u8>, LzmaError> {
	let mut output: Vec<u8> = Vec::new();

	{
		let mut reader = try!(LzmaReader::new_compressor(buf, preset));

		try!(reader.read_to_end(&mut output));
	}

	Ok(output)
}

/// Decompress `buf` and return the result.
pub fn decompress(buf: &[u8]) -> Result<Vec<u8>, LzmaError> {
	let mut output: Vec<u8> = Vec::new();

	{
		let mut reader = try!(LzmaReader::new_decompressor(buf));

		try!(reader.read_to_end(&mut output));
	}

	Ok(output)
}
<commit_msg>Improve documentation test.<commit_after>//! This crate provides a simple interface to liblzma.  LZMA is more commonly known as XZ or 7zip,
//! (as in, files with the `.xz` or `.7z` file extension). LZMA compression is fast and aggressive,
//! compressing better than bzip2.  liblzma implements the XZ variant, so it can read and write
//! `.xz` files/streams.
//!
//! Two interfaces are provided.  `LzmaReader`/`LzmaWriter` are generic Readers and Writers that
//! can be composed with other `Read`/`Write` interfaces.  For example, wrap them around a `File`
//! and you can write data to a file while compressing it on the fly, or stream in an `xz` file
//! from disk.
//!
//! `compress`/`decompress` are easy to use functions for simple use cases.
//!
//! See the `LzmaReader` and `LzmaWriter` documentation for further details on that interface.
//! `compress` and `decompress` are documented here.
//!
//! # Examples
//!
//! ```
//! let test_string = "Like tears in rain";
//! let mut compressed = lzma::compress(test_string.as_bytes(), 6).unwrap();
//! let decompressed = lzma::decompress(&mut compressed).unwrap();
//! let decompressed_str = String::from_utf8(decompressed).unwrap();
//!
//! assert_eq!(test_string, decompressed_str);
//! ```

pub enum Direction {
	Compress,
	Decompress,
}

mod lzma_sys;
pub mod lzma_stream_wrapper;
pub mod reader;
pub mod writer;
pub mod error;

use std::io::Read;
pub use reader::LzmaReader;
pub use writer::LzmaWriter;
pub use error::LzmaError;

pub const EXTREME_PRESET: u32 = (1 << 31);

/// Compress `buf` and return the result.
///
/// preset is [0-9] and corresponds to xz's presets.
/// Binary-or with EXTREME_PRESET for --extreme (e.g. 9 | EXTREME_PRESET).
pub fn compress(buf: &[u8], preset: u32) -> Result<Vec<u8>, LzmaError> {
	let mut output: Vec<u8> = Vec::new();

	{
		let mut reader = try!(LzmaReader::new_compressor(buf, preset));

		try!(reader.read_to_end(&mut output));
	}

	Ok(output)
}

/// Decompress `buf` and return the result.
pub fn decompress(buf: &[u8]) -> Result<Vec<u8>, LzmaError> {
	let mut output: Vec<u8> = Vec::new();

	{
		let mut reader = try!(LzmaReader::new_decompressor(buf));

		try!(reader.read_to_end(&mut output));
	}

	Ok(output)
}
<|endoftext|>


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

//! Semantic version parsing and comparison.
//!
//! Semantic versioning (see http://semver.org/) is a set of rules for
//! assigning version numbers.
//!
//! ## SemVer overview
//!
//! Given a version number MAJOR.MINOR.PATCH, increment the:
//!
//! 1. MAJOR version when you make incompatible API changes,
//! 2. MINOR version when you add functionality in a backwards-compatible
//!    manner, and
//! 3. PATCH version when you make backwards-compatible bug fixes.
//!
//! Additional labels for pre-release and build metadata are available as
//! extensions to the MAJOR.MINOR.PATCH format.
//!
//! Any references to 'the spec' in this documentation refer to [version 2.0 of
//! the SemVer spec](http://semver.org/spec/v2.0.0.html).
//!
//! ## SemVer and the Rust ecosystem
//!
//! Rust itself follows the SemVer specification, as does its standard
//! libraries. The two are not tied together.
//!
//! [Cargo](http://crates.io), Rust's package manager, uses SemVer to determine
//! which versions of packages you need installed.
//!
//! ## Versions
//!
//! At its simplest, the `semver` crate allows you to construct `Version`
//! objects using the `parse` method:
//!
//! ```{rust}
//! use semver::Version;
//!
//! assert!(Version::parse("1.2.3") == Ok(Version {
//!    major: 1,
//!    minor: 2,
//!    patch: 3,
//!    pre: vec!(),
//!    build: vec!(),
//! }));
//! ```
//!
//! If you have multiple `Version`s, you can use the usual comparison operators
//! to compare them:
//!
//! ```{rust}
//! use semver::Version;
//!
//! assert!(Version::parse("1.2.3-alpha") != Version::parse("1.2.3-beta"));
//! assert!(Version::parse("1.2.3-alpha2") >  Version::parse("1.2.0"));
//! ```
//!
//! If you explicitly need to modify a Version, SemVer also allows you to
//! increment the major, minor, and patch numbers in accordance with the spec.
//!
//! Please note that in order to do this, you must use a mutable Version:
//!
//! ```{rust}
//! use semver::Version;
//! use std::error::Error;
//!
//! fn try_main() -> Result<(), Box<Error>> {
//!
//!     let mut bugfix_release = Version::parse("1.0.0")?;
//!     bugfix_release.increment_patch();
//!
//!     assert_eq!(Ok(bugfix_release), Version::parse("1.0.1"));
//!
//!     Ok(())
//! }
//!
//! fn main() {
//!     try_main().unwrap();
//! }
//! ```
//!
//! When incrementing the minor version number, the patch number resets to zero
//! (in accordance with section 7 of the spec)
//!
//! ```{rust}
//! use semver::Version;
//! use std::error::Error;
//!
//! fn try_main() -> Result<(), Box<Error>> {
//!
//!    let mut feature_release = Version::parse("1.4.6")?;
//!    feature_release.increment_minor();
//!
//!    assert_eq!(Ok(feature_release), Version::parse("1.5.0"));
//!
//!    Ok(())
//! }
//!
//! fn main() {
//!     try_main().unwrap();
//! }
//! ```
//!
//! Similarly, when incrementing the major version number, the patch and minor
//! numbers reset to zero (in accordance with section 8 of the spec)
//!
//! ```{rust}
//! use semver::Version;
//! use std::error::Error;
//!
//! fn try_main() -> Result<(), Box<Error>> {
//!
//!     let mut chrome_release = Version::parse("41.5.5377")?;
//!     chrome_release.increment_major();
//!
//!     assert_eq!(Ok(chrome_release), Version::parse("42.0.0"));
//!
//!     Ok(())
//! }
//!
//! fn main() {
//!     try_main().unwrap();
//! }
//! ```
//!
//! ## Requirements
//!
//! The `semver` crate also provides the ability to compare requirements, which
//! are more complex comparisons.
//!
//! For example, creating a requirement that only matches versions greater than
//! or equal to 1.0.0:
//!
//! ```{rust}
//! # #![allow(unstable)]
//! use semver::Version;
//! use semver::VersionReq;
//! use std::error::Error;
//!
//! fn try_main() -> Result<(), Box<Error>> {
//!
//!     let r = VersionReq::parse(">= 1.0.0")?;
//!     let v = Version::parse("1.0.0")?;
//!
//!     assert!(r.to_string() == ">= 1.0.0".to_string());
//!     assert!(r.matches(&v));
//!
//!     Ok(())
//! }
//!
//! fn main() {
//!     try_main().unwrap();
//! }
//! ```
//!
//! It also allows parsing of `~x.y.z` and `^x.y.z` requirements as defined at
//! https://www.npmjs.org/doc/misc/semver.html
//!
//! **Tilde requirements** specify a minimal version with some updates:
//!
//! ```notrust
//! ~1.2.3 := >=1.2.3 <1.3.0
//! ~1.2   := >=1.2.0 <1.3.0
//! ~1     := >=1.0.0 <2.0.0
//! ```
//!
//! **Caret requirements** allow SemVer compatible updates to a specified
//! verion, `0.x` and `0.x+1` are not considered compatible, but `1.x` and
//! `1.x+1` are.
//!
//! `0.0.x` is not considered compatible with any other version.
//! Missing minor and patch versions are desugared to `0` but allow flexibility
//! for that value.
//!
//! ```notrust
//! ^1.2.3 := >=1.2.3 <2.0.0
//! ^0.2.3 := >=0.2.3 <0.3.0
//! ^0.0.3 := >=0.0.3 <0.0.4
//! ^0.0   := >=0.0.0 <0.1.0
//! ^0     := >=0.0.0 <1.0.0
//! ```
//!
//! **Wildcard requirements** allows parsing of version requirements of the
//! formats `*`, `x.*` and `x.y.*`.
//!
//! ```notrust
//! *     := >=0.0.0
//! 1.*   := >=1.0.0 <2.0.0
//! 1.2.* := >=1.2.0 <1.3.0
//! ```

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
       html_root_url = "https://docs.rs/semver")]
#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]

extern crate semver_parser;

// Serialization and deserialization support for version numbers
#[cfg(feature = "serde")]
extern crate serde;

// We take the common approach of keeping our own module system private, and
// just re-exporting the interface that we want.

pub use version::{Identifier, SemVerError, Version};
pub use version::Identifier::{AlphaNumeric, Numeric};
pub use version_req::{ReqParseError, VersionReq};

// SemVer-compliant versions.
mod version;

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

//! Semantic version parsing and comparison.
//!
//! Semantic versioning (see http://semver.org/) is a set of rules for
//! assigning version numbers.
//!
//! ## SemVer overview
//!
//! Given a version number MAJOR.MINOR.PATCH, increment the:
//!
//! 1. MAJOR version when you make incompatible API changes,
//! 2. MINOR version when you add functionality in a backwards-compatible
//!    manner, and
//! 3. PATCH version when you make backwards-compatible bug fixes.
//!
//! Additional labels for pre-release and build metadata are available as
//! extensions to the MAJOR.MINOR.PATCH format.
//!
//! Any references to 'the spec' in this documentation refer to [version 2.0 of
//! the SemVer spec](http://semver.org/spec/v2.0.0.html).
//!
//! ## SemVer and the Rust ecosystem
//!
//! Rust itself follows the SemVer specification, as does its standard
//! libraries. The two are not tied together.
//!
//! [Cargo](http://crates.io), Rust's package manager, uses SemVer to determine
//! which versions of packages you need installed.
//!
//! ## Versions
//!
//! At its simplest, the `semver` crate allows you to construct `Version`
//! objects using the `parse` method:
//!
//! ```{rust}
//! use semver::Version;
//!
//! assert!(Version::parse("1.2.3") == Ok(Version {
//!    major: 1,
//!    minor: 2,
//!    patch: 3,
//!    pre: vec!(),
//!    build: vec!(),
//! }));
//! ```
//!
//! If you have multiple `Version`s, you can use the usual comparison operators
//! to compare them:
//!
//! ```{rust}
//! use semver::Version;
//!
//! assert!(Version::parse("1.2.3-alpha") != Version::parse("1.2.3-beta"));
//! assert!(Version::parse("1.2.3-alpha2") >  Version::parse("1.2.0"));
//! ```
//!
//! If you explicitly need to modify a Version, SemVer also allows you to
//! increment the major, minor, and patch numbers in accordance with the spec.
//!
//! Please note that in order to do this, you must use a mutable Version:
//!
//! ```{rust}
//! use semver::Version;
//! use std::error::Error;
//!
//! fn try_increment_patch() -> Result<(), Box<Error>> {
//!
//!     let mut bugfix_release = Version::parse("1.0.0")?;
//!     bugfix_release.increment_patch();
//!
//!     assert_eq!(Ok(bugfix_release), Version::parse("1.0.1"));
//!
//!     Ok(())
//! }
//!
//! fn main() {
//!     try_increment_patch().unwrap();
//! }
//! ```
//!
//! When incrementing the minor version number, the patch number resets to zero
//! (in accordance with section 7 of the spec)
//!
//! ```{rust}
//! use semver::Version;
//! use std::error::Error;
//!
//! fn try_increment_minor() -> Result<(), Box<Error>> {
//!
//!    let mut feature_release = Version::parse("1.4.6")?;
//!    feature_release.increment_minor();
//!
//!    assert_eq!(Ok(feature_release), Version::parse("1.5.0"));
//!
//!    Ok(())
//! }
//!
//! fn main() {
//!     try_increment_minor().unwrap();
//! }
//! ```
//!
//! Similarly, when incrementing the major version number, the patch and minor
//! numbers reset to zero (in accordance with section 8 of the spec)
//!
//! ```{rust}
//! use semver::Version;
//! use std::error::Error;
//!
//! fn try_increment_major() -> Result<(), Box<Error>> {
//!
//!     let mut chrome_release = Version::parse("41.5.5377")?;
//!     chrome_release.increment_major();
//!
//!     assert_eq!(Ok(chrome_release), Version::parse("42.0.0"));
//!
//!     Ok(())
//! }
//!
//! fn main() {
//!     try_increment_major().unwrap();
//! }
//! ```
//!
//! ## Requirements
//!
//! The `semver` crate also provides the ability to compare requirements, which
//! are more complex comparisons.
//!
//! For example, creating a requirement that only matches versions greater than
//! or equal to 1.0.0:
//!
//! ```{rust}
//! # #![allow(unstable)]
//! use semver::Version;
//! use semver::VersionReq;
//! use std::error::Error;
//!
//! fn try_compare() -> Result<(), Box<Error>> {
//!
//!     let r = VersionReq::parse(">= 1.0.0")?;
//!     let v = Version::parse("1.0.0")?;
//!
//!     assert!(r.to_string() == ">= 1.0.0".to_string());
//!     assert!(r.matches(&v));
//!
//!     Ok(())
//! }
//!
//! fn main() {
//!     try_compare().unwrap();
//! }
//! ```
//!
//! It also allows parsing of `~x.y.z` and `^x.y.z` requirements as defined at
//! https://www.npmjs.org/doc/misc/semver.html
//!
//! **Tilde requirements** specify a minimal version with some updates:
//!
//! ```notrust
//! ~1.2.3 := >=1.2.3 <1.3.0
//! ~1.2   := >=1.2.0 <1.3.0
//! ~1     := >=1.0.0 <2.0.0
//! ```
//!
//! **Caret requirements** allow SemVer compatible updates to a specified
//! verion, `0.x` and `0.x+1` are not considered compatible, but `1.x` and
//! `1.x+1` are.
//!
//! `0.0.x` is not considered compatible with any other version.
//! Missing minor and patch versions are desugared to `0` but allow flexibility
//! for that value.
//!
//! ```notrust
//! ^1.2.3 := >=1.2.3 <2.0.0
//! ^0.2.3 := >=0.2.3 <0.3.0
//! ^0.0.3 := >=0.0.3 <0.0.4
//! ^0.0   := >=0.0.0 <0.1.0
//! ^0     := >=0.0.0 <1.0.0
//! ```
//!
//! **Wildcard requirements** allows parsing of version requirements of the
//! formats `*`, `x.*` and `x.y.*`.
//!
//! ```notrust
//! *     := >=0.0.0
//! 1.*   := >=1.0.0 <2.0.0
//! 1.2.* := >=1.2.0 <1.3.0
//! ```

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
       html_root_url = "https://docs.rs/semver")]
#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]

extern crate semver_parser;

// Serialization and deserialization support for version numbers
#[cfg(feature = "serde")]
extern crate serde;

// We take the common approach of keeping our own module system private, and
// just re-exporting the interface that we want.

pub use version::{Identifier, SemVerError, Version};
pub use version::Identifier::{AlphaNumeric, Numeric};
pub use version_req::{ReqParseError, VersionReq};

// SemVer-compliant versions.
mod version;

// advanced version comparisons
mod version_req;
<|endoftext|>