<commit_before>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
//
// 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.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.

use crate::utils;

use clap::{ArgEnum, Parser};

#[test]
fn doc_comments() {
    /// Lorem ipsum
    #[derive(Parser, PartialEq, Debug)]
    struct LoremIpsum {
        /// Fooify a bar
        /// and a baz
        #[clap(short, long)]
        foo: bool,
    }

    let help = utils::get_long_help::<LoremIpsum>();
    assert!(help.contains("Lorem ipsum"));
    assert!(help.contains("Fooify a bar and a baz"));
}

#[test]
fn help_is_better_than_comments() {
    /// Lorem ipsum
    #[derive(Parser, PartialEq, Debug)]
    #[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
    struct LoremIpsum {
        /// Fooify a bar
        #[clap(short, long, help = "DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES")]
        foo: bool,
    }

    let help = utils::get_long_help::<LoremIpsum>();
    assert!(help.contains("Dolor sit amet"));
    assert!(!help.contains("Lorem ipsum"));
    assert!(help.contains("DO NOT PASS A BAR"));
}

#[test]
fn empty_line_in_doc_comment_is_double_linefeed() {
    /// Foo.
    ///
    /// Bar
    #[derive(Parser, PartialEq, Debug)]
    #[clap(name = "lorem-ipsum")]
    struct LoremIpsum {}

    let help = utils::get_long_help::<LoremIpsum>();
    assert!(help.starts_with("lorem-ipsum \n\nFoo.\n\nBar\n\nUSAGE:"));
}

#[test]
fn field_long_doc_comment_both_help_long_help() {
    /// Lorem ipsumclap
    #[derive(Parser, PartialEq, Debug)]
    #[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
    struct LoremIpsum {
        /// Dot is removed from multiline comments.
        ///
        /// Long help
        #[clap(long)]
        foo: bool,

        /// Dot is removed from one short comment.
        #[clap(long)]
        bar: bool,
    }

    let short_help = utils::get_help::<LoremIpsum>();
    let long_help = utils::get_long_help::<LoremIpsum>();

    assert!(short_help.contains("Dot is removed from one short comment"));
    assert!(!short_help.contains("Dot is removed from one short comment."));
    assert!(short_help.contains("Dot is removed from multiline comments"));
    assert!(!short_help.contains("Dot is removed from multiline comments."));
    assert!(long_help.contains("Long help"));
    assert!(!short_help.contains("Long help"));
}

#[test]
fn top_long_doc_comment_both_help_long_help() {
    /// Lorem ipsumclap
    #[derive(Parser, Debug)]
    #[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
    struct LoremIpsum {
        #[clap(subcommand)]
        foo: SubCommand,
    }

    #[derive(Parser, Debug)]
    pub enum SubCommand {
        /// DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES
        ///
        /// Or something else
        Foo {
            #[clap(help = "foo")]
            bars: String,
        },
    }

    let short_help = utils::get_help::<LoremIpsum>();
    let long_help = utils::get_subcommand_long_help::<LoremIpsum>("foo");

    assert!(!short_help.contains("Or something else"));
    assert!(long_help.contains("DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES"));
    assert!(long_help.contains("Or something else"));
}

#[test]
fn verbatim_doc_comment() {
    /// DANCE!
    ///
    ///                    ()
    ///                    |
    ///               (   ()   )
    ///     ) ________    //  )
    ///  ()  |\       \  //
    /// ( \\__ \ ______\//
    ///    \__) |       |
    ///      |  |       |
    ///       \ |       |
    ///        \|_______|
    ///        //    \\
    ///       ((     ||
    ///        \\    ||
    ///      ( ()    ||
    ///       (      () ) )
    #[derive(Parser, Debug)]
    #[clap(verbatim_doc_comment)]
    struct SeeFigure1 {
        #[clap(long)]
        foo: bool,
    }

    let help = utils::get_long_help::<SeeFigure1>();
    let sample = r#"
                   ()
                   |
              (   ()   )
    ) ________    //  )
 ()  |\       \  //
( \\__ \ ______\//
   \__) |       |
     |  |       |
      \ |       |
       \|_______|
       //    \\
      ((     ||
       \\    ||
     ( ()    ||
      (      () ) )"#;

    assert!(help.contains(sample))
}

#[test]
fn verbatim_doc_comment_field() {
    #[derive(Parser, Debug)]
    struct App {
        /// This help ends in a period.
        #[clap(long, verbatim_doc_comment)]
        foo: bool,
        /// This help does not end in a period.
        #[clap(long)]
        bar: bool,
    }

    let help = utils::get_long_help::<App>();

    assert!(help.contains("This help ends in a period."));
    assert!(help.contains("This help does not end in a period"));
}

#[test]
fn multiline_separates_default() {
    #[derive(Parser, Debug)]
    struct App {
        /// Multiline
        ///
        /// Doc comment
        #[clap(long, default_value = "x")]
        x: String,
    }

    let help = utils::get_long_help::<App>();
    assert!(!help.contains("Doc comment [default"));
    assert!(help.lines().any(|s| s.trim().starts_with("[default")));

    // The short help should still have the default on the same line
    let help = utils::get_help::<App>();
    assert!(help.contains("Multiline [default"));
}

#[test]
fn argenum_multiline_doc_comment() {
    #[derive(ArgEnum, Clone)]
    enum LoremIpsum {
        /// Multiline
        ///
        /// Doc comment
        Bar,
    }
}
<commit_msg>test(derive): Show current doc comment behavior<commit_after>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
//
// 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.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.

use crate::utils;

use clap::{ArgEnum, IntoApp, Parser};

#[test]
fn doc_comments() {
    /// Lorem ipsum
    #[derive(Parser, PartialEq, Debug)]
    struct LoremIpsum {
        /// Fooify a bar
        /// and a baz
        #[clap(short, long)]
        foo: bool,
    }

    let help = utils::get_long_help::<LoremIpsum>();
    assert!(help.contains("Lorem ipsum"));
    assert!(help.contains("Fooify a bar and a baz"));
}

#[test]
fn help_is_better_than_comments() {
    /// Lorem ipsum
    #[derive(Parser, PartialEq, Debug)]
    #[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
    struct LoremIpsum {
        /// Fooify a bar
        #[clap(short, long, help = "DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES")]
        foo: bool,
    }

    let help = utils::get_long_help::<LoremIpsum>();
    assert!(help.contains("Dolor sit amet"));
    assert!(!help.contains("Lorem ipsum"));
    assert!(help.contains("DO NOT PASS A BAR"));
}

#[test]
fn empty_line_in_doc_comment_is_double_linefeed() {
    /// Foo.
    ///
    /// Bar
    #[derive(Parser, PartialEq, Debug)]
    #[clap(name = "lorem-ipsum")]
    struct LoremIpsum {}

    let help = utils::get_long_help::<LoremIpsum>();
    assert!(help.starts_with("lorem-ipsum \n\nFoo.\n\nBar\n\nUSAGE:"));
}

#[test]
fn field_long_doc_comment_both_help_long_help() {
    /// Lorem ipsumclap
    #[derive(Parser, PartialEq, Debug)]
    #[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
    struct LoremIpsum {
        /// Dot is removed from multiline comments.
        ///
        /// Long help
        #[clap(long)]
        foo: bool,

        /// Dot is removed from one short comment.
        #[clap(long)]
        bar: bool,
    }

    let short_help = utils::get_help::<LoremIpsum>();
    let long_help = utils::get_long_help::<LoremIpsum>();

    assert!(short_help.contains("Dot is removed from one short comment"));
    assert!(!short_help.contains("Dot is removed from one short comment."));
    assert!(short_help.contains("Dot is removed from multiline comments"));
    assert!(!short_help.contains("Dot is removed from multiline comments."));
    assert!(long_help.contains("Long help"));
    assert!(!short_help.contains("Long help"));
}

#[test]
fn top_long_doc_comment_both_help_long_help() {
    /// Lorem ipsumclap
    #[derive(Parser, Debug)]
    #[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
    struct LoremIpsum {
        #[clap(subcommand)]
        foo: SubCommand,
    }

    #[derive(Parser, Debug)]
    pub enum SubCommand {
        /// DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES
        ///
        /// Or something else
        Foo {
            #[clap(help = "foo")]
            bars: String,
        },
    }

    let short_help = utils::get_help::<LoremIpsum>();
    let long_help = utils::get_subcommand_long_help::<LoremIpsum>("foo");

    assert!(!short_help.contains("Or something else"));
    assert!(long_help.contains("DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES"));
    assert!(long_help.contains("Or something else"));
}

#[test]
fn verbatim_doc_comment() {
    /// DANCE!
    ///
    ///                    ()
    ///                    |
    ///               (   ()   )
    ///     ) ________    //  )
    ///  ()  |\       \  //
    /// ( \\__ \ ______\//
    ///    \__) |       |
    ///      |  |       |
    ///       \ |       |
    ///        \|_______|
    ///        //    \\
    ///       ((     ||
    ///        \\    ||
    ///      ( ()    ||
    ///       (      () ) )
    #[derive(Parser, Debug)]
    #[clap(verbatim_doc_comment)]
    struct SeeFigure1 {
        #[clap(long)]
        foo: bool,
    }

    let help = utils::get_long_help::<SeeFigure1>();
    let sample = r#"
                   ()
                   |
              (   ()   )
    ) ________    //  )
 ()  |\       \  //
( \\__ \ ______\//
   \__) |       |
     |  |       |
      \ |       |
       \|_______|
       //    \\
      ((     ||
       \\    ||
     ( ()    ||
      (      () ) )"#;

    assert!(help.contains(sample))
}

#[test]
fn verbatim_doc_comment_field() {
    #[derive(Parser, Debug)]
    struct App {
        /// This help ends in a period.
        #[clap(long, verbatim_doc_comment)]
        foo: bool,
        /// This help does not end in a period.
        #[clap(long)]
        bar: bool,
    }

    let help = utils::get_long_help::<App>();

    assert!(help.contains("This help ends in a period."));
    assert!(help.contains("This help does not end in a period"));
}

#[test]
fn multiline_separates_default() {
    #[derive(Parser, Debug)]
    struct App {
        /// Multiline
        ///
        /// Doc comment
        #[clap(long, default_value = "x")]
        x: String,
    }

    let help = utils::get_long_help::<App>();
    assert!(!help.contains("Doc comment [default"));
    assert!(help.lines().any(|s| s.trim().starts_with("[default")));

    // The short help should still have the default on the same line
    let help = utils::get_help::<App>();
    assert!(help.contains("Multiline [default"));
}

#[test]
fn argenum_multiline_doc_comment() {
    #[derive(ArgEnum, Clone)]
    enum LoremIpsum {
        /// Multiline
        ///
        /// Doc comment
        Bar,
    }
}

#[test]
fn doc_comment_about_handles_both_abouts() {
    /// Opts doc comment summary
    #[derive(Parser, Debug)]
    pub struct Opts {
        #[clap(subcommand)]
        pub cmd: Sub,
    }

    /// Sub doc comment summary
    ///
    /// Sub doc comment body
    #[derive(Parser, PartialEq, Eq, Debug)]
    pub enum Sub {
        Compress { output: String },
    }

    let app = Opts::into_app();
    assert_eq!(app.get_about(), Some("Opts doc comment summary"));
    assert_eq!(
        app.get_long_about(),
        Some("Sub doc comment summary\n\nSub doc comment body")
    );
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate itertools;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Tile {
    Safe,
    Trap,
}

impl Tile {
    fn is_safe(&self) -> bool {
        match *self {
            Tile::Safe => true,
            Tile::Trap => false,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct Row {
    tiles: Vec<Tile>,
}

impl Row {
    fn next_row(&self) -> Row {
        use Tile::*;
        let mut new_tiles = Vec::with_capacity(self.tiles.len());
        for idx in 0..self.tiles.len() {
            let left = if idx == 0 { Safe } else { self.tiles[idx - 1] };
            let mid = self.tiles[idx];
            let right = if idx == self.tiles.len() - 1 {
                Safe
            } else {
                self.tiles[idx + 1]
            };
            match (left, mid, right) {
                (Trap, Trap, Safe) |
                (Safe, Trap, Trap) |
                (Trap, Safe, Safe) |
                (Safe, Safe, Trap) => new_tiles.push(Trap),
                _ => new_tiles.push(Safe),
            }
        }
        Row { tiles: new_tiles }
    }
    fn generate_rows(self, len: usize) -> Vec<Row> {
        itertools::iterate(self, |r| r.next_row()).take(len).collect()
    }
}

impl std::str::FromStr for Row {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut tiles = Vec::with_capacity(s.len());
        for ch in s.chars() {
            match ch {
                '.' => tiles.push(Tile::Safe),
                '^' => tiles.push(Tile::Trap),
                _ => return Err(format!("Bad char '{}' in row: {}", ch, s)),
            }
        }
        Ok(Row { tiles: tiles })
    }
}

impl std::fmt::Display for Row {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        use std::fmt::Write;
        for tile in &self.tiles {
            match *tile {
                Tile::Safe => f.write_char('.')?,
                Tile::Trap => f.write_char('^')?,
            }
        }
        Ok(())
    }
}

fn parse_args() -> Result<(Row, bool), String> {
    let matches = clap::App::new("Day 18")
        .author("Devon Hollowood")
        .arg(clap::Arg::with_name("initial")
            .index(1)
            .help("initial row for generating room")
            .required(true))
        .arg(clap::Arg::with_name("show")
            .short("s")
            .long("show"))
        .get_matches();
    Ok((matches.value_of("initial").unwrap().parse()?, matches.is_present("show")))
}

fn main() {
    let (initial, show) = parse_args()
        .unwrap_or_else(|e| panic!("Error parsing initial row: {}", e));
    let room = initial.generate_rows(40);
    if show {
        for row in &room {
            println!("{}", row);
        }
    }
    let n_safe = room.iter().flat_map(|row| row.tiles.iter()).filter(|t| t.is_safe()).count();
    println!("Number of safe tiles: {}", n_safe);
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn example_room() {
        assert_eq!("..^^.".parse::<Row>().unwrap().generate_rows(3),
                   vec!["..^^.".parse().unwrap(),
                        ".^^^^".parse().unwrap(),
                        "^^..^".parse().unwrap()])
    }

    #[test]
    fn example_count() {
        let room = ".^^.^.^^^^".parse::<Row>().unwrap().generate_rows(10);
        assert_eq!(room.iter().flat_map(|row| row.tiles.iter()).filter(|t| t.is_safe()).count(),
                   38);
    }
}
<commit_msg>Day 18 part 2<commit_after>extern crate clap;
extern crate itertools;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Tile {
    Safe,
    Trap,
}

impl Tile {
    fn is_safe(&self) -> bool {
        match *self {
            Tile::Safe => true,
            Tile::Trap => false,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct Row {
    tiles: Vec<Tile>,
}

impl Row {
    fn next_row(&self) -> Row {
        use Tile::*;
        let mut new_tiles = Vec::with_capacity(self.tiles.len());
        for idx in 0..self.tiles.len() {
            let left = if idx == 0 { Safe } else { self.tiles[idx - 1] };
            let mid = self.tiles[idx];
            let right = if idx == self.tiles.len() - 1 {
                Safe
            } else {
                self.tiles[idx + 1]
            };
            match (left, mid, right) {
                (Trap, Trap, Safe) |
                (Safe, Trap, Trap) |
                (Trap, Safe, Safe) |
                (Safe, Safe, Trap) => new_tiles.push(Trap),
                _ => new_tiles.push(Safe),
            }
        }
        Row { tiles: new_tiles }
    }
    fn generate_rows(self, len: usize) -> Vec<Row> {
        itertools::iterate(self, |r| r.next_row()).take(len).collect()
    }
}

impl std::str::FromStr for Row {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut tiles = Vec::with_capacity(s.len());
        for ch in s.chars() {
            match ch {
                '.' => tiles.push(Tile::Safe),
                '^' => tiles.push(Tile::Trap),
                _ => return Err(format!("Bad char '{}' in row: {}", ch, s)),
            }
        }
        Ok(Row { tiles: tiles })
    }
}

impl std::fmt::Display for Row {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        use std::fmt::Write;
        for tile in &self.tiles {
            match *tile {
                Tile::Safe => f.write_char('.')?,
                Tile::Trap => f.write_char('^')?,
            }
        }
        Ok(())
    }
}

fn parse_args() -> Result<(Row, bool), String> {
    let matches = clap::App::new("Day 18")
        .author("Devon Hollowood")
        .arg(clap::Arg::with_name("initial")
            .index(1)
            .help("initial row for generating room")
            .required(true))
        .arg(clap::Arg::with_name("show")
            .short("s")
            .long("show"))
        .get_matches();
    Ok((matches.value_of("initial").unwrap().parse()?, matches.is_present("show")))
}

fn main() {
    let (initial, show) = parse_args()
        .unwrap_or_else(|e| panic!("Error parsing initial row: {}", e));
    let room = initial.clone().generate_rows(40);
    if show {
        for row in &room {
            println!("{}", row);
        }
    }
    let n_safe_40 = room.iter().flat_map(|row| row.tiles.iter()).filter(|t| t.is_safe()).count();
    println!("Number of safe tiles in 40 rows: {}", n_safe_40);
    let n_safe_400000 = itertools::iterate(initial, |row| row.next_row())
        .take(400000)
        .fold(0, |count, row| {
            count +
            row.tiles
                .iter()
                .filter(|tile| tile.is_safe())
                .count()
        });
    println!("Number of safe tiles in 400000 rows: {}", n_safe_400000);
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn example_room() {
        assert_eq!("..^^.".parse::<Row>().unwrap().generate_rows(3),
                   vec!["..^^.".parse().unwrap(),
                        ".^^^^".parse().unwrap(),
                        "^^..^".parse().unwrap()])
    }

    #[test]
    fn example_count() {
        let room = ".^^.^.^^^^".parse::<Row>().unwrap().generate_rows(10);
        assert_eq!(room.iter().flat_map(|row| row.tiles.iter()).filter(|t| t.is_safe()).count(),
                   38);
    }
}
<|endoftext|>


<commit_before>//! Provides functions for maintaining database schema.
//!
//! A database migration always provides procedures to update the schema, as well as to revert
//! itself. Diesel's migrations are versioned, and run in order. Diesel also takes care of tracking
//! which migrations have already been run automatically. Your migrations don't need to be
//! idempotent, as Diesel will ensure no migration is run twice unless it has been reverted.
//!
//! Migrations should be placed in a `/migrations` directory at the root of your project (the same
//! directory as `Cargo.toml`). When any of these functions are run, Diesel will search for the
//! migrations directory in the current directory and its parents, stopping when it finds the
//! directory containing `Cargo.toml`.
//!
//! Individual migrations should be a folder containing exactly two files, `up.sql` and `down.sql`.
//! `up.sql` will be used to run the migration, while `down.sql` will be used for reverting it. The
//! folder itself should have the structure `{version}_{migration_name}`. It is recommended that
//! you use the timestamp of creation for the version.
//!
//! ## Example
//!
//! ```text
//! # Directory Structure
//! - 20151219180527_create_users
//!     - up.sql
//!     - down.sql
//! - 20160107082941_create_posts
//!     - up.sql
//!     - down.sql
//! ```
//!
//! ```sql
//! -- 20151219180527_create_users/up.sql
//! CREATE TABLE users (
//!   id SERIAL PRIMARY KEY,
//!   name VARCHAR NOT NULL,
//!   hair_color VARCHAR
//! );
//! ```
//!
//! ```sql
//! -- 20151219180527_create_users/down.sql
//! DROP TABLE users;
//! ```
//!
//! ```sql
//! -- 20160107082941_create_posts/up.sql
//! CREATE TABLE posts (
//!   id SERIAL PRIMARY KEY,
//!   user_id INTEGER NOT NULL,
//!   title VARCHAR NOT NULL,
//!   body TEXT
//! );
//! ```
//!
//! ```sql
//! -- 20160107082941_create_posts/down.sql
//! DROP TABLE posts;
//! ```
mod migration;
mod migration_error;
mod schema;

pub use self::migration_error::*;

use ::expression::expression_methods::*;
use ::query_dsl::*;
use self::migration::*;
use self::migration_error::MigrationError::*;
use self::schema::NewMigration;
use self::schema::__diesel_schema_migrations::dsl::*;
use {Connection, QueryResult};

use std::collections::HashSet;
use std::env;
use std::path::{PathBuf, Path};

/// Runs all migrations that have not yet been run. This function will print all progress to
/// stdout. This function will return an `Err` if some error occurs reading the migrations, or if
/// any migration fails to run. Each migration is run in its own transaction, so some migrations
/// may be committed, even if a later migration fails to run.
///
/// It should be noted that this runs all migrations that have not already been run, regardless of
/// whether or not their version is later than the latest run migration. This is generally not a
/// problem, and eases the more common case of two developers generating independent migrations on
/// a branch. Whoever created the second one will eventually need to run the first when both
/// branches are merged.
///
/// See the [module level documentation](index.html) for information on how migrations should be
/// structured, and where Diesel will look for them by default.
pub fn run_pending_migrations<Conn: Connection>(conn: &Conn) -> Result<(), RunMigrationsError> {
    try!(create_schema_migrations_table_if_needed(conn));
    let already_run = try!(previously_run_migration_versions(conn));
    let migrations_dir = try!(find_migrations_directory());
    let all_migrations = try!(migrations_in_directory(&migrations_dir));
    let pending_migrations = all_migrations.into_iter().filter(|m| {
        !already_run.contains(m.version())
    });
    run_migrations(conn, pending_migrations)
}

/// Reverts the last migration that was run. Returns the version that was reverted. Returns an
/// `Err` if no migrations have ever been run.
///
/// See the [module level documentation](index.html) for information on how migrations should be
/// structured, and where Diesel will look for them by default.
pub fn revert_latest_migration<Conn: Connection>(conn: &Conn) -> Result<String, RunMigrationsError> {
    try!(create_schema_migrations_table_if_needed(conn));
    let latest_migration_version = try!(latest_run_migration_version(conn));
    revert_migration_with_version(conn, &latest_migration_version)
        .map(|_| latest_migration_version)
}

#[doc(hidden)]
pub fn revert_migration_with_version<Conn: Connection>(conn: &Conn, ver: &str) -> Result<(), RunMigrationsError> {
    migration_with_version(ver)
        .map_err(|e| e.into())
        .and_then(|m| revert_migration(conn, m))
}

#[doc(hidden)]
pub fn run_migration_with_version<Conn: Connection>(conn: &Conn, ver: &str) -> Result<(), RunMigrationsError> {
    migration_with_version(ver)
        .map_err(|e| e.into())
        .and_then(|m| run_migration(conn, m))
}

fn migration_with_version(ver: &str) -> Result<Box<Migration>, MigrationError> {
    let migrations_dir = try!(find_migrations_directory());
    let all_migrations = try!(migrations_in_directory(&migrations_dir));
    let migration = all_migrations.into_iter().find(|m| {
        m.version() == ver
    });
    match migration {
        Some(m) => Ok(m),
        None => Err(UnknownMigrationVersion(ver.into())),
    }
}

#[doc(hidden)]
pub fn create_schema_migrations_table_if_needed<Conn: Connection>(conn: &Conn) -> QueryResult<usize> {
    conn.silence_notices(|| {
        conn.execute("CREATE TABLE IF NOT EXISTS __diesel_schema_migrations (
            version VARCHAR PRIMARY KEY NOT NULL,
            run_on TIMESTAMP NOT NULL DEFAULT NOW()
        )")
    })
}

fn previously_run_migration_versions<Conn: Connection>(conn: &Conn) -> QueryResult<HashSet<String>> {
    __diesel_schema_migrations.select(version)
        .load(conn)
        .map(|r| r.collect())
}

fn latest_run_migration_version<Conn: Connection>(conn: &Conn) -> QueryResult<String> {
    use ::expression::dsl::max;
    __diesel_schema_migrations.select(max(version))
        .first(conn)
}

fn migrations_in_directory(path: &Path) -> Result<Vec<Box<Migration>>, MigrationError> {
    use self::migration::migration_from;

    try!(path.read_dir())
        .filter_map(|entry| {
            let entry = match entry {
                Ok(e) => e,
                Err(e) => return Some(Err(e.into())),
            };
            if !entry.file_name().to_string_lossy().starts_with(".") {
                Some(migration_from(entry.path()))
            } else {
                None
            }
        }).collect()
}

fn run_migrations<T, Conn: Connection>(conn: &Conn, migrations: T)
    -> Result<(), RunMigrationsError> where
        T: Iterator<Item=Box<Migration>>
{
    for migration in migrations {
        try!(run_migration(conn, migration));
    }
    Ok(())
}

fn run_migration<Conn: Connection>(conn: &Conn, migration: Box<Migration>)
    -> Result<(), RunMigrationsError>
{
    conn.transaction(|| {
        println!("Running migration {}", migration.version());
        try!(migration.run(conn));
        try!(::insert(&NewMigration(migration.version()))
             .into(__diesel_schema_migrations)
             .execute(conn));
        Ok(())
    }).map_err(|e| e.into())
}

fn revert_migration<Conn: Connection>(conn: &Conn, migration: Box<Migration>)
    -> Result<(), RunMigrationsError>
{
    try!(conn.transaction(|| {
        println!("Rolling back migration {}", migration.version());
        try!(migration.revert(conn));
        let target = __diesel_schema_migrations.filter(version.eq(migration.version()));
        try!(::delete(target).execute(conn));
        Ok(())
    }));
    Ok(())
}

/// Returns the directory containing migrations. Will look at for
/// $PWD/migrations. If it is not found, it will search the parents of the
/// current directory, until it reaches the root directory.  Returns
/// `MigrationError::MigrationDirectoryNotFound` if no directory is found.
pub fn find_migrations_directory() -> Result<PathBuf, MigrationError> {
    search_for_migrations_directory(&try!(env::current_dir()))
}

fn search_for_migrations_directory(path: &Path) -> Result<PathBuf, MigrationError> {
    let migration_path = path.join("migrations");
    if migration_path.is_dir() {
        Ok(migration_path)
    } else {
        path.parent().map(search_for_migrations_directory)
            .unwrap_or(Err(MigrationError::MigrationDirectoryNotFound))
    }
}

#[cfg(test)]
mod tests {
    extern crate tempdir;

    use super::*;
    use super::search_for_migrations_directory;

    use self::tempdir::TempDir;
    use std::fs;

    #[test]
    fn migration_directory_not_found_if_no_migration_dir_exists() {
        let dir = TempDir::new("diesel").unwrap();

        assert_eq!(Err(MigrationError::MigrationDirectoryNotFound),
            search_for_migrations_directory(dir.path()));
    }

    #[test]
    fn migration_directory_defaults_to_pwd_slash_migrations() {
        let dir = TempDir::new("diesel").unwrap();
        let temp_path = dir.path().canonicalize().unwrap();
        let migrations_path = temp_path.join("migrations");

        fs::create_dir(&migrations_path).unwrap();

        assert_eq!(Ok(migrations_path), search_for_migrations_directory(&temp_path));
    }

    #[test]
    fn migration_directory_checks_parents() {
        let dir = TempDir::new("diesel").unwrap();
        let temp_path = dir.path().canonicalize().unwrap();
        let migrations_path = temp_path.join("migrations");
        let child_path = temp_path.join("child");

        fs::create_dir(&child_path).unwrap();
        fs::create_dir(&migrations_path).unwrap();

        assert_eq!(Ok(migrations_path), search_for_migrations_directory(&child_path));
    }
}
<commit_msg> Respect order of migrations<commit_after>//! Provides functions for maintaining database schema.
//!
//! A database migration always provides procedures to update the schema, as well as to revert
//! itself. Diesel's migrations are versioned, and run in order. Diesel also takes care of tracking
//! which migrations have already been run automatically. Your migrations don't need to be
//! idempotent, as Diesel will ensure no migration is run twice unless it has been reverted.
//!
//! Migrations should be placed in a `/migrations` directory at the root of your project (the same
//! directory as `Cargo.toml`). When any of these functions are run, Diesel will search for the
//! migrations directory in the current directory and its parents, stopping when it finds the
//! directory containing `Cargo.toml`.
//!
//! Individual migrations should be a folder containing exactly two files, `up.sql` and `down.sql`.
//! `up.sql` will be used to run the migration, while `down.sql` will be used for reverting it. The
//! folder itself should have the structure `{version}_{migration_name}`. It is recommended that
//! you use the timestamp of creation for the version.
//!
//! ## Example
//!
//! ```text
//! # Directory Structure
//! - 20151219180527_create_users
//!     - up.sql
//!     - down.sql
//! - 20160107082941_create_posts
//!     - up.sql
//!     - down.sql
//! ```
//!
//! ```sql
//! -- 20151219180527_create_users/up.sql
//! CREATE TABLE users (
//!   id SERIAL PRIMARY KEY,
//!   name VARCHAR NOT NULL,
//!   hair_color VARCHAR
//! );
//! ```
//!
//! ```sql
//! -- 20151219180527_create_users/down.sql
//! DROP TABLE users;
//! ```
//!
//! ```sql
//! -- 20160107082941_create_posts/up.sql
//! CREATE TABLE posts (
//!   id SERIAL PRIMARY KEY,
//!   user_id INTEGER NOT NULL,
//!   title VARCHAR NOT NULL,
//!   body TEXT
//! );
//! ```
//!
//! ```sql
//! -- 20160107082941_create_posts/down.sql
//! DROP TABLE posts;
//! ```
mod migration;
mod migration_error;
mod schema;

pub use self::migration_error::*;

use ::expression::expression_methods::*;
use ::query_dsl::*;
use self::migration::*;
use self::migration_error::MigrationError::*;
use self::schema::NewMigration;
use self::schema::__diesel_schema_migrations::dsl::*;
use {Connection, QueryResult};

use std::collections::HashSet;
use std::env;
use std::path::{PathBuf, Path};

/// Runs all migrations that have not yet been run. This function will print all progress to
/// stdout. This function will return an `Err` if some error occurs reading the migrations, or if
/// any migration fails to run. Each migration is run in its own transaction, so some migrations
/// may be committed, even if a later migration fails to run.
///
/// It should be noted that this runs all migrations that have not already been run, regardless of
/// whether or not their version is later than the latest run migration. This is generally not a
/// problem, and eases the more common case of two developers generating independent migrations on
/// a branch. Whoever created the second one will eventually need to run the first when both
/// branches are merged.
///
/// See the [module level documentation](index.html) for information on how migrations should be
/// structured, and where Diesel will look for them by default.
pub fn run_pending_migrations<Conn: Connection>(conn: &Conn) -> Result<(), RunMigrationsError> {
    try!(create_schema_migrations_table_if_needed(conn));
    let already_run = try!(previously_run_migration_versions(conn));
    let migrations_dir = try!(find_migrations_directory());
    let all_migrations = try!(migrations_in_directory(&migrations_dir));
    let pending_migrations = all_migrations.into_iter().filter(|m| {
        !already_run.contains(m.version())
    });
    run_migrations(conn, pending_migrations.collect())
}

/// Reverts the last migration that was run. Returns the version that was reverted. Returns an
/// `Err` if no migrations have ever been run.
///
/// See the [module level documentation](index.html) for information on how migrations should be
/// structured, and where Diesel will look for them by default.
pub fn revert_latest_migration<Conn: Connection>(conn: &Conn) -> Result<String, RunMigrationsError> {
    try!(create_schema_migrations_table_if_needed(conn));
    let latest_migration_version = try!(latest_run_migration_version(conn));
    revert_migration_with_version(conn, &latest_migration_version)
        .map(|_| latest_migration_version)
}

#[doc(hidden)]
pub fn revert_migration_with_version<Conn: Connection>(conn: &Conn, ver: &str) -> Result<(), RunMigrationsError> {
    migration_with_version(ver)
        .map_err(|e| e.into())
        .and_then(|m| revert_migration(conn, m))
}

#[doc(hidden)]
pub fn run_migration_with_version<Conn: Connection>(conn: &Conn, ver: &str) -> Result<(), RunMigrationsError> {
    migration_with_version(ver)
        .map_err(|e| e.into())
        .and_then(|m| run_migration(conn, m))
}

fn migration_with_version(ver: &str) -> Result<Box<Migration>, MigrationError> {
    let migrations_dir = try!(find_migrations_directory());
    let all_migrations = try!(migrations_in_directory(&migrations_dir));
    let migration = all_migrations.into_iter().find(|m| {
        m.version() == ver
    });
    match migration {
        Some(m) => Ok(m),
        None => Err(UnknownMigrationVersion(ver.into())),
    }
}

#[doc(hidden)]
pub fn create_schema_migrations_table_if_needed<Conn: Connection>(conn: &Conn) -> QueryResult<usize> {
    conn.silence_notices(|| {
        conn.execute("CREATE TABLE IF NOT EXISTS __diesel_schema_migrations (
            version VARCHAR PRIMARY KEY NOT NULL,
            run_on TIMESTAMP NOT NULL DEFAULT NOW()
        )")
    })
}

fn previously_run_migration_versions<Conn: Connection>(conn: &Conn) -> QueryResult<HashSet<String>> {
    __diesel_schema_migrations.select(version)
        .load(conn)
        .map(|r| r.collect())
}

fn latest_run_migration_version<Conn: Connection>(conn: &Conn) -> QueryResult<String> {
    use ::expression::dsl::max;
    __diesel_schema_migrations.select(max(version))
        .first(conn)
}

fn migrations_in_directory(path: &Path) -> Result<Vec<Box<Migration>>, MigrationError> {
    use self::migration::migration_from;

    try!(path.read_dir())
        .filter_map(|entry| {
            let entry = match entry {
                Ok(e) => e,
                Err(e) => return Some(Err(e.into())),
            };
            if !entry.file_name().to_string_lossy().starts_with(".") {
                Some(migration_from(entry.path()))
            } else {
                None
            }
        }).collect()
}

fn run_migrations<Conn: Connection>(conn: &Conn, mut migrations: Vec<Box<Migration>>)
    -> Result<(), RunMigrationsError>
{
    migrations.sort_by(|a, b| a.version().cmp(b.version()));
    for migration in migrations {
        try!(run_migration(conn, migration));
    }
    Ok(())
}

fn run_migration<Conn: Connection>(conn: &Conn, migration: Box<Migration>)
    -> Result<(), RunMigrationsError>
{
    conn.transaction(|| {
        println!("Running migration {}", migration.version());
        try!(migration.run(conn));
        try!(::insert(&NewMigration(migration.version()))
             .into(__diesel_schema_migrations)
             .execute(conn));
        Ok(())
    }).map_err(|e| e.into())
}

fn revert_migration<Conn: Connection>(conn: &Conn, migration: Box<Migration>)
    -> Result<(), RunMigrationsError>
{
    try!(conn.transaction(|| {
        println!("Rolling back migration {}", migration.version());
        try!(migration.revert(conn));
        let target = __diesel_schema_migrations.filter(version.eq(migration.version()));
        try!(::delete(target).execute(conn));
        Ok(())
    }));
    Ok(())
}

/// Returns the directory containing migrations. Will look at for
/// $PWD/migrations. If it is not found, it will search the parents of the
/// current directory, until it reaches the root directory.  Returns
/// `MigrationError::MigrationDirectoryNotFound` if no directory is found.
pub fn find_migrations_directory() -> Result<PathBuf, MigrationError> {
    search_for_migrations_directory(&try!(env::current_dir()))
}

fn search_for_migrations_directory(path: &Path) -> Result<PathBuf, MigrationError> {
    let migration_path = path.join("migrations");
    if migration_path.is_dir() {
        Ok(migration_path)
    } else {
        path.parent().map(search_for_migrations_directory)
            .unwrap_or(Err(MigrationError::MigrationDirectoryNotFound))
    }
}

#[cfg(test)]
mod tests {
    extern crate tempdir;

    use super::*;
    use super::search_for_migrations_directory;

    use self::tempdir::TempDir;
    use std::fs;

    #[test]
    fn migration_directory_not_found_if_no_migration_dir_exists() {
        let dir = TempDir::new("diesel").unwrap();

        assert_eq!(Err(MigrationError::MigrationDirectoryNotFound),
            search_for_migrations_directory(dir.path()));
    }

    #[test]
    fn migration_directory_defaults_to_pwd_slash_migrations() {
        let dir = TempDir::new("diesel").unwrap();
        let temp_path = dir.path().canonicalize().unwrap();
        let migrations_path = temp_path.join("migrations");

        fs::create_dir(&migrations_path).unwrap();

        assert_eq!(Ok(migrations_path), search_for_migrations_directory(&temp_path));
    }

    #[test]
    fn migration_directory_checks_parents() {
        let dir = TempDir::new("diesel").unwrap();
        let temp_path = dir.path().canonicalize().unwrap();
        let migrations_path = temp_path.join("migrations");
        let child_path = temp_path.join("child");

        fs::create_dir(&child_path).unwrap();
        fs::create_dir(&migrations_path).unwrap();

        assert_eq!(Ok(migrations_path), search_for_migrations_directory(&child_path));
    }
}
<|endoftext|>


<commit_before>extern mod std;

use std::{io, vec, ptr, os};
use std::libc::{c_uint};

mod crypto {
  use std::libc::{c_uint};
#[link_args = "-lcrypto"]
  extern {
    fn SHA1(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn MD5(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA224(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA256(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA384(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA512(src: *u8, sz: c_uint, out: *u8) -> *u8;
  }
}

enum HashMethod { MD5, SHA1, SHA224, SHA256, SHA384, SHA512 }

struct HashEngine {
  engine:      (extern "C" unsafe fn(*u8, c_uint, *u8) -> *u8),
  digest_size: uint,
  block_size:  uint
}

impl HashEngine {
  fn new(engine: HashMethod) -> ~HashEngine {
    match engine {
      MD5    => ~HashEngine{ engine: crypto::MD5,    digest_size: 16, block_size: 64  },
      SHA1   => ~HashEngine{ engine: crypto::SHA1,   digest_size: 20, block_size: 64  },
      SHA224 => ~HashEngine{ engine: crypto::SHA224, digest_size: 28, block_size: 64  },
      SHA256 => ~HashEngine{ engine: crypto::SHA256, digest_size: 32, block_size: 64  },
      SHA384 => ~HashEngine{ engine: crypto::SHA384, digest_size: 48, block_size: 64  },
      SHA512 => ~HashEngine{ engine: crypto::SHA512, digest_size: 64, block_size: 128 }
    }
  }

  fn hash(&self, data: ~[u8]) -> ~Digest {
    let hash_func = self.engine;
    Digest::new(
      unsafe {
        vec::from_buf(
          hash_func(
            vec::raw::to_ptr(data),
            data.len() as c_uint, ptr::null()
          ),
          self.digest_size
        )
      }
    )
  }

  fn hmac(&self, key: ~[u8], message: ~[u8]) -> ~Digest {
    let computed_key = match key.len() {
      _ if key.len() > self.block_size => self.zero_pad(self.hash(key).digest),
      _ if key.len() < self.block_size => self.zero_pad(key),
      _ => key
    };

    let o_key_pad = HashEngine::xor_with(computed_key, 0x5c);
    let i_key_pad = HashEngine::xor_with(computed_key, 0x36);

    self.hash(o_key_pad + self.hash(i_key_pad + message).digest)
  }

  priv fn xor_with(subject: &[u8], val: u8) -> ~[u8] {
    do subject.map |&c| { val ^ c }
  }

  priv fn zero_pad(&self, subject: ~[u8]) -> ~[u8] {
    subject + vec::from_elem(self.block_size - subject.len(), 0)
  }
}

struct Digest { digest: ~[u8] }

impl Digest {
  fn new(digest: ~[u8]) -> ~Digest { return ~Digest{ digest: digest } }

  fn hexdigest(&self) -> ~str {
    let mut acc = ~"";
    for self.digest.iter().advance |&byte| { acc = acc.append(fmt!("%02x", byte as uint)); }
    acc
  }
}

fn sha1(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA1).hash(data)
}

fn sha224(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA224).hash(data)
}

fn sha256(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA256).hash(data)
}

fn sha384(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA384).hash(data)
}

fn sha512(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA512).hash(data)
}

fn md5(data: ~[u8]) -> ~Digest {
  HashEngine::new(MD5).hash(data)
}

fn hmac_md5(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(MD5).hmac(key, message)
}

fn hmac_sha1(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(SHA1).hmac(key, message)
}

fn hmac_sha256(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(SHA256).hmac(key, message)
}

fn hmac_sha512(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(SHA512).hmac(key, message)
}

fn main() {
  if os::args().len() < 2 { fail!(~"Nothing to hash: supply an argument") }
  let args = ~os::args();
  io::println(sha1(args[1].as_bytes().to_owned()).hexdigest());
}

#[test]
fn test_sha1() {
  assert_eq!(sha1("testing SHA1".as_bytes().to_owned()).hexdigest(), ~"a07bb2f7f56b7b47e54fd9e29b4629c8967dde4c");
}

#[test]
fn test_md5() {
  assert_eq!(md5("testing MD5".as_bytes().to_owned()).hexdigest(), ~"da35df95d34f166890fa759f5e00e94c");
}

#[test]
fn test_hmac_sha1_with_a_64_byte_key() {
  assert_eq!(
    hmac_sha1("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"d6264ce636b593e6c5b43c90ec4ca3550032c99f"
  )
}

#[test]
fn test_hmac_sha1_with_a_too_long_key() {
  assert_eq!(
    hmac_sha1("keyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"4361f0a10ae7fc1ce33d2795e998d0e526a38815"
  )
}

#[test]
fn test_hmac_sha1_with_a_too_short_key() {
  assert_eq!(
    hmac_sha1("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"331c9bf4d7dc8ad7e9bab2f566c8612042a9f4e2"
  )
}

#[test]
fn test_hmac_sha256() {
  assert_eq!(
    hmac_sha256("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"17764a8a2e8c23ae3b9e1781c5ebeb5754c7920806021032e9133051e10118be"
  );
}

#[test]
fn test_hmac_sha512() {
  assert_eq!(
    hmac_sha512("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"b54973feba2e436c4f0911855c80e7320d72edd37b359d6474d714718c52775f163832b79214e9fa9d400208d21372a465b2cda2a1d5ab488bbc02b0daea9626"
  );
}

#[test]
fn test_hmac_md5() {
  assert_eq!(hmac_md5("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(), ~"befd13e00ee07549520ade402c664a3a");
}
<commit_msg>Don't call key.len() multiple times in the hmac implementation.<commit_after>extern mod std;

use std::{io, vec, ptr, os};
use std::libc::{c_uint};

mod crypto {
  use std::libc::{c_uint};
#[link_args = "-lcrypto"]
  extern {
    fn SHA1(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn MD5(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA224(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA256(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA384(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA512(src: *u8, sz: c_uint, out: *u8) -> *u8;
  }
}

enum HashMethod { MD5, SHA1, SHA224, SHA256, SHA384, SHA512 }

struct HashEngine {
  engine:      (extern "C" unsafe fn(*u8, c_uint, *u8) -> *u8),
  digest_size: uint,
  block_size:  uint
}

impl HashEngine {
  fn new(engine: HashMethod) -> ~HashEngine {
    match engine {
      MD5    => ~HashEngine{ engine: crypto::MD5,    digest_size: 16, block_size: 64  },
      SHA1   => ~HashEngine{ engine: crypto::SHA1,   digest_size: 20, block_size: 64  },
      SHA224 => ~HashEngine{ engine: crypto::SHA224, digest_size: 28, block_size: 64  },
      SHA256 => ~HashEngine{ engine: crypto::SHA256, digest_size: 32, block_size: 64  },
      SHA384 => ~HashEngine{ engine: crypto::SHA384, digest_size: 48, block_size: 64  },
      SHA512 => ~HashEngine{ engine: crypto::SHA512, digest_size: 64, block_size: 128 }
    }
  }

  fn hash(&self, data: ~[u8]) -> ~Digest {
    let hash_func = self.engine;
    Digest::new(
      unsafe {
        vec::from_buf(
          hash_func(
            vec::raw::to_ptr(data),
            data.len() as c_uint, ptr::null()
          ),
          self.digest_size
        )
      }
    )
  }

  fn hmac(&self, key: ~[u8], message: ~[u8]) -> ~Digest {
    let computed_key = match key.len() {
      len if len > self.block_size => self.zero_pad(self.hash(key).digest),
      len if len < self.block_size => self.zero_pad(key),
      _ => key
    };

    let o_key_pad = HashEngine::xor_with(computed_key, 0x5c);
    let i_key_pad = HashEngine::xor_with(computed_key, 0x36);

    self.hash(o_key_pad + self.hash(i_key_pad + message).digest)
  }

  priv fn xor_with(subject: &[u8], val: u8) -> ~[u8] {
    do subject.map |&c| { val ^ c }
  }

  priv fn zero_pad(&self, subject: ~[u8]) -> ~[u8] {
    subject + vec::from_elem(self.block_size - subject.len(), 0)
  }
}

struct Digest { digest: ~[u8] }

impl Digest {
  fn new(digest: ~[u8]) -> ~Digest { return ~Digest{ digest: digest } }

  fn hexdigest(&self) -> ~str {
    let mut acc = ~"";
    for self.digest.iter().advance |&byte| { acc = acc.append(fmt!("%02x", byte as uint)); }
    acc
  }
}

fn sha1(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA1).hash(data)
}

fn sha224(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA224).hash(data)
}

fn sha256(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA256).hash(data)
}

fn sha384(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA384).hash(data)
}

fn sha512(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA512).hash(data)
}

fn md5(data: ~[u8]) -> ~Digest {
  HashEngine::new(MD5).hash(data)
}

fn hmac_md5(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(MD5).hmac(key, message)
}

fn hmac_sha1(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(SHA1).hmac(key, message)
}

fn hmac_sha256(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(SHA256).hmac(key, message)
}

fn hmac_sha512(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(SHA512).hmac(key, message)
}

fn main() {
  if os::args().len() < 2 { fail!(~"Nothing to hash: supply an argument") }
  let args = ~os::args();
  io::println(sha1(args[1].as_bytes().to_owned()).hexdigest());
}

#[test]
fn test_sha1() {
  assert_eq!(sha1("testing SHA1".as_bytes().to_owned()).hexdigest(), ~"a07bb2f7f56b7b47e54fd9e29b4629c8967dde4c");
}

#[test]
fn test_md5() {
  assert_eq!(md5("testing MD5".as_bytes().to_owned()).hexdigest(), ~"da35df95d34f166890fa759f5e00e94c");
}

#[test]
fn test_hmac_sha1_with_a_64_byte_key() {
  assert_eq!(
    hmac_sha1("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"d6264ce636b593e6c5b43c90ec4ca3550032c99f"
  )
}

#[test]
fn test_hmac_sha1_with_a_too_long_key() {
  assert_eq!(
    hmac_sha1("keyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"4361f0a10ae7fc1ce33d2795e998d0e526a38815"
  )
}

#[test]
fn test_hmac_sha1_with_a_too_short_key() {
  assert_eq!(
    hmac_sha1("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"331c9bf4d7dc8ad7e9bab2f566c8612042a9f4e2"
  )
}

#[test]
fn test_hmac_sha256() {
  assert_eq!(
    hmac_sha256("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"17764a8a2e8c23ae3b9e1781c5ebeb5754c7920806021032e9133051e10118be"
  );
}

#[test]
fn test_hmac_sha512() {
  assert_eq!(
    hmac_sha512("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"b54973feba2e436c4f0911855c80e7320d72edd37b359d6474d714718c52775f163832b79214e9fa9d400208d21372a465b2cda2a1d5ab488bbc02b0daea9626"
  );
}

#[test]
fn test_hmac_md5() {
  assert_eq!(hmac_md5("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(), ~"befd13e00ee07549520ade402c664a3a");
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::path::Path;
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::result::Result as RResult;

use toml::Value;

use libimagerror::into::IntoError;
use libimagerror::trace::trace_error;
use libimagstore::hook::Hook;
use libimagstore::hook::accessor::StoreIdAccessor;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::error::HookError as HE;
use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::result::HookResult;
use libimagstore::storeid::StoreId;
use libimagutil::debug_result::*;

use vcs::git::error::GitHookError as GHE;
use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::error::MapIntoHookError;
use vcs::git::result::Result;
use vcs::git::runtime::Runtime as GRuntime;

pub struct UpdateHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl UpdateHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook {
        UpdateHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Debug for UpdateHook {
    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "UpdateHook(storepath={:?}, repository={}, pos={:?}, cfg={:?}",
               self.storepath,
               (if self.runtime.has_repository() { "Some(_)" } else { "None" }),
               self.position,
               self.runtime.has_config())
    }
}

impl Hook for UpdateHook {

    fn name(&self) -> &'static str {
        "stdhook_git_update"
    }

    fn set_config(&mut self, config: &Value) {
        if let Err(e) = self.runtime.set_config(config) {
            trace_error(&e);
        }
    }

}

impl HookDataAccessorProvider for UpdateHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for UpdateHook {

    /// The implementation of the UpdateHook
    ///
    /// # Scope
    ///
    /// This hook takes the git index and commits it either interactively or with a default message,
    /// if there is no configuration for an interactive commit.
    ///
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use vcs::git::action::StoreAction;
        use vcs::git::config::commit_message;
        use vcs::git::error::MapIntoHookError;
        use vcs::git::util::fetch_index;
        use git2::{Reference as GitReference, Repository, Error as Git2Error};
        use git2::{ADD_DEFAULT, STATUS_WT_NEW, STATUS_WT_MODIFIED, IndexMatchedPath};

        debug!("[GIT UPDATE HOOK]: {:?}", id);

        let action    = StoreAction::Update;
        let cfg       = try!(self.runtime.config_value_or_err(&action));
        let repo      = try!(self.runtime.repository(&action));
        let mut index = try!(fetch_index(repo, &action));

        let path = try!(
            id.clone()
            .into_pathbuf()
            .map_err_into(GHEK::StoreIdHandlingError)
            .map_dbg_err_str("Failed to StoreId.into_pathbuf()")
            .map_into_hook_error()
        );

        let signature = try!(
            repo.signature()
                .map_err_into(GHEK::MkSignature)
                .map_dbg_err_str("Failed to fetch signature")
                .map_into_hook_error()
        );

        let head = try!(
            repo.head()
                .map_err_into(GHEK::HeadFetchError)
                .map_dbg_err_str("Failed to fetch HEAD")
                .map_into_hook_error()
        );

        let file_status = try!(
            repo
                .status_file(&path)
                .map_dbg_err_str("Failed to fetch file status")
                .map_dbg_err(|e| format!("\t->  {:?}", e))
                .map_err_into(GHEK::RepositoryFileStatusError)
                .map_into_hook_error()
        );

        let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 {
            if file_status.contains(STATUS_WT_NEW) || file_status.contains(STATUS_WT_MODIFIED) {
                debug!("[GIT CREATE HOOK]: File is modified/new: {}", path.display());
                0
            } else {
                debug!("[GIT CREATE HOOK]: Ignoring file: {}", path.display());
                1
            }
        };

        try!(
            index.add_all(&[path], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath))
                .map_err_into(GHEK::RepositoryPathAddingError)
                .map_dbg_err_str("Failed to add to index")
                .map_into_hook_error()
        );

        let mut parents = Vec::new();
        {
            let commit = try!(
                repo.find_commit(head.target().unwrap())
                    .map_err_into(GHEK::RepositoryParentFetchingError)
                    .map_dbg_err_str("Failed to find commit HEAD")
                    .map_into_hook_error()
            );
            parents.push(commit);
        }

        // for converting from Vec<Commit> to Vec<&Commit>
        let parents = parents.iter().collect::<Vec<_>>();

        let tree_id = try!(
            index.write_tree()
                .map_err_into(GHEK::RepositoryIndexWritingError)
                .map_dbg_err_str("Failed to write tree")
                .map_into_hook_error()
        );

        let tree = try!(
            repo.find_tree(tree_id)
                .map_err_into(GHEK::RepositoryParentFetchingError)
                .map_dbg_err_str("Failed to find tree")
                .map_into_hook_error()
        );

        let message = try!(commit_message(cfg, StoreAction::Update)
                .map_dbg_err_str("Failed to get commit message"));

        repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents)
            .map_dbg_err_str("Failed to commit")
            .map_err_into(GHEK::RepositoryCommittingError)
            .map_into_hook_error()
            .map(|_| ())
    }

}

<commit_msg>We only need to pass the local part to git2 here<commit_after>use std::path::PathBuf;
use std::path::Path;
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::result::Result as RResult;

use toml::Value;

use libimagerror::into::IntoError;
use libimagerror::trace::trace_error;
use libimagstore::hook::Hook;
use libimagstore::hook::accessor::StoreIdAccessor;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::error::HookError as HE;
use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::result::HookResult;
use libimagstore::storeid::StoreId;
use libimagutil::debug_result::*;

use vcs::git::error::GitHookError as GHE;
use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::error::MapIntoHookError;
use vcs::git::result::Result;
use vcs::git::runtime::Runtime as GRuntime;

pub struct UpdateHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl UpdateHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook {
        UpdateHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Debug for UpdateHook {
    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "UpdateHook(storepath={:?}, repository={}, pos={:?}, cfg={:?}",
               self.storepath,
               (if self.runtime.has_repository() { "Some(_)" } else { "None" }),
               self.position,
               self.runtime.has_config())
    }
}

impl Hook for UpdateHook {

    fn name(&self) -> &'static str {
        "stdhook_git_update"
    }

    fn set_config(&mut self, config: &Value) {
        if let Err(e) = self.runtime.set_config(config) {
            trace_error(&e);
        }
    }

}

impl HookDataAccessorProvider for UpdateHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for UpdateHook {

    /// The implementation of the UpdateHook
    ///
    /// # Scope
    ///
    /// This hook takes the git index and commits it either interactively or with a default message,
    /// if there is no configuration for an interactive commit.
    ///
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use vcs::git::action::StoreAction;
        use vcs::git::config::commit_message;
        use vcs::git::error::MapIntoHookError;
        use vcs::git::util::fetch_index;
        use git2::{Reference as GitReference, Repository, Error as Git2Error};
        use git2::{ADD_DEFAULT, STATUS_WT_NEW, STATUS_WT_MODIFIED, IndexMatchedPath};

        debug!("[GIT UPDATE HOOK]: {:?}", id);

        let action    = StoreAction::Update;
        let cfg       = try!(self.runtime.config_value_or_err(&action));
        let repo      = try!(self.runtime.repository(&action));
        let mut index = try!(fetch_index(repo, &action));

        let signature = try!(
            repo.signature()
                .map_err_into(GHEK::MkSignature)
                .map_dbg_err_str("Failed to fetch signature")
                .map_into_hook_error()
        );

        let head = try!(
            repo.head()
                .map_err_into(GHEK::HeadFetchError)
                .map_dbg_err_str("Failed to fetch HEAD")
                .map_into_hook_error()
        );

        let file_status = try!(
            repo
                .status_file(id.local())
                .map_dbg_err_str("Failed to fetch file status")
                .map_dbg_err(|e| format!("\t->  {:?}", e))
                .map_err_into(GHEK::RepositoryFileStatusError)
                .map_into_hook_error()
        );

        let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 {
            if file_status.contains(STATUS_WT_NEW) || file_status.contains(STATUS_WT_MODIFIED) {
                debug!("[GIT CREATE HOOK]: File is modified/new: {}", path.display());
                0
            } else {
                debug!("[GIT CREATE HOOK]: Ignoring file: {}", path.display());
                1
            }
        };

        try!(
            index.add_all(&[id.local()], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath))
                .map_err_into(GHEK::RepositoryPathAddingError)
                .map_dbg_err_str("Failed to add to index")
                .map_into_hook_error()
        );

        let mut parents = Vec::new();
        {
            let commit = try!(
                repo.find_commit(head.target().unwrap())
                    .map_err_into(GHEK::RepositoryParentFetchingError)
                    .map_dbg_err_str("Failed to find commit HEAD")
                    .map_into_hook_error()
            );
            parents.push(commit);
        }

        // for converting from Vec<Commit> to Vec<&Commit>
        let parents = parents.iter().collect::<Vec<_>>();

        let tree_id = try!(
            index.write_tree()
                .map_err_into(GHEK::RepositoryIndexWritingError)
                .map_dbg_err_str("Failed to write tree")
                .map_into_hook_error()
        );

        let tree = try!(
            repo.find_tree(tree_id)
                .map_err_into(GHEK::RepositoryParentFetchingError)
                .map_dbg_err_str("Failed to find tree")
                .map_into_hook_error()
        );

        let message = try!(commit_message(cfg, StoreAction::Update)
                .map_dbg_err_str("Failed to get commit message"));

        repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents)
            .map_dbg_err_str("Failed to commit")
            .map_err_into(GHEK::RepositoryCommittingError)
            .map_into_hook_error()
            .map(|_| ())
    }

}

<|endoftext|>


<commit_before>//! A private parser implementation of IPv4, IPv6, and socket addresses.
//!
//! This module is "publicly exported" through the `FromStr` implementations
//! below.

#[cfg(test)]
mod tests;

use crate::convert::TryInto as _;
use crate::error::Error;
use crate::fmt;
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use crate::str::FromStr;

trait ReadNumberHelper: crate::marker::Sized + crate::cmp::PartialEq {
    const ZERO: Self;
    fn checked_mul(&self, other: u32) -> Option<Self>;
    fn checked_add(&self, other: u32) -> Option<Self>;
}

macro_rules! impl_helper {
    ($($t:ty)*) => ($(impl ReadNumberHelper for $t {
        const ZERO: Self = 0;
        #[inline]
        fn checked_mul(&self, other: u32) -> Option<Self> {
            Self::checked_mul(*self, other.try_into().ok()?)
        }
        #[inline]
        fn checked_add(&self, other: u32) -> Option<Self> {
            Self::checked_add(*self, other.try_into().ok()?)
        }
    })*)
}

impl_helper! { u8 u16 u32 }

struct Parser<'a> {
    // Parsing as ASCII, so can use byte array.
    state: &'a [u8],
}

impl<'a> Parser<'a> {
    fn new(input: &'a str) -> Parser<'a> {
        Parser { state: input.as_bytes() }
    }

    /// Run a parser, and restore the pre-parse state if it fails.
    fn read_atomically<T, F>(&mut self, inner: F) -> Option<T>
    where
        F: FnOnce(&mut Parser<'_>) -> Option<T>,
    {
        let state = self.state;
        let result = inner(self);
        if result.is_none() {
            self.state = state;
        }
        result
    }

    /// Run a parser, but fail if the entire input wasn't consumed.
    /// Doesn't run atomically.
    fn parse_with<T, F>(&mut self, inner: F) -> Result<T, AddrParseError>
    where
        F: FnOnce(&mut Parser<'_>) -> Option<T>,
    {
        let result = inner(self);
        if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(()))
    }

    /// Peek the next character from the input
    fn peek_char(&self) -> Option<char> {
        self.state.first().map(|&b| char::from(b))
    }

    /// Read the next character from the input
    fn read_char(&mut self) -> Option<char> {
        self.state.split_first().map(|(&b, tail)| {
            self.state = tail;
            char::from(b)
        })
    }

    #[must_use]
    /// Read the next character from the input if it matches the target.
    fn read_given_char(&mut self, target: char) -> Option<()> {
        self.read_atomically(|p| {
            p.read_char().and_then(|c| if c == target { Some(()) } else { None })
        })
    }

    /// Helper for reading separators in an indexed loop. Reads the separator
    /// character iff index > 0, then runs the parser. When used in a loop,
    /// the separator character will only be read on index > 0 (see
    /// read_ipv4_addr for an example)
    fn read_separator<T, F>(&mut self, sep: char, index: usize, inner: F) -> Option<T>
    where
        F: FnOnce(&mut Parser<'_>) -> Option<T>,
    {
        self.read_atomically(move |p| {
            if index > 0 {
                p.read_given_char(sep)?;
            }
            inner(p)
        })
    }

    // Read a number off the front of the input in the given radix, stopping
    // at the first non-digit character or eof. Fails if the number has more
    // digits than max_digits or if there is no number.
    fn read_number<T: ReadNumberHelper>(
        &mut self,
        radix: u32,
        max_digits: Option<usize>,
        allow_zero_prefix: bool,
    ) -> Option<T> {
        self.read_atomically(move |p| {
            let mut result = T::ZERO;
            let mut digit_count = 0;
            let has_leading_zero = p.peek_char() == Some('0');

            while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) {
                result = result.checked_mul(radix)?;
                result = result.checked_add(digit)?;
                digit_count += 1;
                if let Some(max_digits) = max_digits {
                    if digit_count > max_digits {
                        return None;
                    }
                }
            }

            if digit_count == 0 {
                None
            } else if !allow_zero_prefix
                && has_leading_zero
                && (result != T::ZERO || result == T::ZERO && digit_count > 1)
            {
                None
            } else {
                Some(result)
            }
        })
    }

    /// Read an IPv4 address.
    fn read_ipv4_addr(&mut self) -> Option<Ipv4Addr> {
        self.read_atomically(|p| {
            let mut groups = [0; 4];

            for (i, slot) in groups.iter_mut().enumerate() {
                *slot = p.read_separator('.', i, |p| {
                    // Disallow octal number in IP string.
                    // https://tools.ietf.org/html/rfc6943#section-3.1.1
                    p.read_number(10, None, false)
                })?;
            }

            Some(groups.into())
        })
    }

    /// Read an IPv6 Address.
    fn read_ipv6_addr(&mut self) -> Option<Ipv6Addr> {
        /// Read a chunk of an IPv6 address into `groups`. Returns the number
        /// of groups read, along with a bool indicating if an embedded
        /// trailing IPv4 address was read. Specifically, read a series of
        /// colon-separated IPv6 groups (0x0000 - 0xFFFF), with an optional
        /// trailing embedded IPv4 address.
        fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) {
            let limit = groups.len();

            for (i, slot) in groups.iter_mut().enumerate() {
                // Try to read a trailing embedded IPv4 address. There must be
                // at least two groups left.
                if i < limit - 1 {
                    let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr());

                    if let Some(v4_addr) = ipv4 {
                        let [one, two, three, four] = v4_addr.octets();
                        groups[i + 0] = u16::from_be_bytes([one, two]);
                        groups[i + 1] = u16::from_be_bytes([three, four]);
                        return (i + 2, true);
                    }
                }

                let group = p.read_separator(':', i, |p| p.read_number(16, Some(4), true));

                match group {
                    Some(g) => *slot = g,
                    None => return (i, false),
                }
            }
            (groups.len(), false)
        }

        self.read_atomically(|p| {
            // Read the front part of the address; either the whole thing, or up
            // to the first ::
            let mut head = [0; 8];
            let (head_size, head_ipv4) = read_groups(p, &mut head);

            if head_size == 8 {
                return Some(head.into());
            }

            // IPv4 part is not allowed before `::`
            if head_ipv4 {
                return None;
            }

            // Read `::` if previous code parsed less than 8 groups.
            // `::` indicates one or more groups of 16 bits of zeros.
            p.read_given_char(':')?;
            p.read_given_char(':')?;

            // Read the back part of the address. The :: must contain at least one
            // set of zeroes, so our max length is 7.
            let mut tail = [0; 7];
            let limit = 8 - (head_size + 1);
            let (tail_size, _) = read_groups(p, &mut tail[..limit]);

            // Concat the head and tail of the IP address
            head[(8 - tail_size)..8].copy_from_slice(&tail[..tail_size]);

            Some(head.into())
        })
    }

    /// Read an IP Address, either IPv4 or IPv6.
    fn read_ip_addr(&mut self) -> Option<IpAddr> {
        self.read_ipv4_addr().map(IpAddr::V4).or_else(move || self.read_ipv6_addr().map(IpAddr::V6))
    }

    /// Read a `:` followed by a port in base 10.
    fn read_port(&mut self) -> Option<u16> {
        self.read_atomically(|p| {
            p.read_given_char(':')?;
            p.read_number(10, None, true)
        })
    }

    /// Read a `%` followed by a scope ID in base 10.
    fn read_scope_id(&mut self) -> Option<u32> {
        self.read_atomically(|p| {
            p.read_given_char('%')?;
            p.read_number(10, None, true)
        })
    }

    /// Read an IPv4 address with a port.
    fn read_socket_addr_v4(&mut self) -> Option<SocketAddrV4> {
        self.read_atomically(|p| {
            let ip = p.read_ipv4_addr()?;
            let port = p.read_port()?;
            Some(SocketAddrV4::new(ip, port))
        })
    }

    /// Read an IPv6 address with a port.
    fn read_socket_addr_v6(&mut self) -> Option<SocketAddrV6> {
        self.read_atomically(|p| {
            p.read_given_char('[')?;
            let ip = p.read_ipv6_addr()?;
            let scope_id = p.read_scope_id().unwrap_or(0);
            p.read_given_char(']')?;

            let port = p.read_port()?;
            Some(SocketAddrV6::new(ip, port, 0, scope_id))
        })
    }

    /// Read an IP address with a port
    fn read_socket_addr(&mut self) -> Option<SocketAddr> {
        self.read_socket_addr_v4()
            .map(SocketAddr::V4)
            .or_else(|| self.read_socket_addr_v6().map(SocketAddr::V6))
    }
}

#[stable(feature = "ip_addr", since = "1.7.0")]
impl FromStr for IpAddr {
    type Err = AddrParseError;
    fn from_str(s: &str) -> Result<IpAddr, AddrParseError> {
        Parser::new(s).parse_with(|p| p.read_ip_addr())
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl FromStr for Ipv4Addr {
    type Err = AddrParseError;
    fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> {
        // don't try to parse if too long
        if s.len() > 15 {
            Err(AddrParseError(()))
        } else {
            Parser::new(s).parse_with(|p| p.read_ipv4_addr())
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl FromStr for Ipv6Addr {
    type Err = AddrParseError;
    fn from_str(s: &str) -> Result<Ipv6Addr, AddrParseError> {
        Parser::new(s).parse_with(|p| p.read_ipv6_addr())
    }
}

#[stable(feature = "socket_addr_from_str", since = "1.5.0")]
impl FromStr for SocketAddrV4 {
    type Err = AddrParseError;
    fn from_str(s: &str) -> Result<SocketAddrV4, AddrParseError> {
        Parser::new(s).parse_with(|p| p.read_socket_addr_v4())
    }
}

#[stable(feature = "socket_addr_from_str", since = "1.5.0")]
impl FromStr for SocketAddrV6 {
    type Err = AddrParseError;
    fn from_str(s: &str) -> Result<SocketAddrV6, AddrParseError> {
        Parser::new(s).parse_with(|p| p.read_socket_addr_v6())
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl FromStr for SocketAddr {
    type Err = AddrParseError;
    fn from_str(s: &str) -> Result<SocketAddr, AddrParseError> {
        Parser::new(s).parse_with(|p| p.read_socket_addr())
    }
}

/// An error which can be returned when parsing an IP address or a socket address.
///
/// This error is used as the error type for the [`FromStr`] implementation for
/// [`IpAddr`], [`Ipv4Addr`], [`Ipv6Addr`], [`SocketAddr`], [`SocketAddrV4`], and
/// [`SocketAddrV6`].
///
/// # Potential causes
///
/// `AddrParseError` may be thrown because the provided string does not parse as the given type,
/// often because it includes information only handled by a different address type.
///
/// ```should_panic
/// use std::net::IpAddr;
/// let _foo: IpAddr = "127.0.0.1:8080".parse().expect("Cannot handle the socket port");
/// ```
///
/// [`IpAddr`] doesn't handle the port. Use [`SocketAddr`] instead.
///
/// ```
/// use std::net::SocketAddr;
///
/// // No problem, the `panic!` message has disappeared.
/// let _foo: SocketAddr = "127.0.0.1:8080".parse().expect("unreachable panic");
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddrParseError(());

#[stable(feature = "addr_parse_error_error", since = "1.4.0")]
impl fmt::Display for AddrParseError {
    #[allow(deprecated, deprecated_in_future)]
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.write_str(self.description())
    }
}

#[stable(feature = "addr_parse_error_error", since = "1.4.0")]
impl Error for AddrParseError {
    #[allow(deprecated)]
    fn description(&self) -> &str {
        "invalid IP address syntax"
    }
}
<commit_msg>Simplify leading zero checks<commit_after>//! A private parser implementation of IPv4, IPv6, and socket addresses.
//!
//! This module is "publicly exported" through the `FromStr` implementations
//! below.

#[cfg(test)]
mod tests;

use crate::convert::TryInto as _;
use crate::error::Error;
use crate::fmt;
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use crate::str::FromStr;

trait ReadNumberHelper: crate::marker::Sized {
    const ZERO: Self;
    fn checked_mul(&self, other: u32) -> Option<Self>;
    fn checked_add(&self, other: u32) -> Option<Self>;
}

macro_rules! impl_helper {
    ($($t:ty)*) => ($(impl ReadNumberHelper for $t {
        const ZERO: Self = 0;
        #[inline]
        fn checked_mul(&self, other: u32) -> Option<Self> {
            Self::checked_mul(*self, other.try_into().ok()?)
        }
        #[inline]
        fn checked_add(&self, other: u32) -> Option<Self> {
            Self::checked_add(*self, other.try_into().ok()?)
        }
    })*)
}

impl_helper! { u8 u16 u32 }

struct Parser<'a> {
    // Parsing as ASCII, so can use byte array.
    state: &'a [u8],
}

impl<'a> Parser<'a> {
    fn new(input: &'a str) -> Parser<'a> {
        Parser { state: input.as_bytes() }
    }

    /// Run a parser, and restore the pre-parse state if it fails.
    fn read_atomically<T, F>(&mut self, inner: F) -> Option<T>
    where
        F: FnOnce(&mut Parser<'_>) -> Option<T>,
    {
        let state = self.state;
        let result = inner(self);
        if result.is_none() {
            self.state = state;
        }
        result
    }

    /// Run a parser, but fail if the entire input wasn't consumed.
    /// Doesn't run atomically.
    fn parse_with<T, F>(&mut self, inner: F) -> Result<T, AddrParseError>
    where
        F: FnOnce(&mut Parser<'_>) -> Option<T>,
    {
        let result = inner(self);
        if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(()))
    }

    /// Peek the next character from the input
    fn peek_char(&self) -> Option<char> {
        self.state.first().map(|&b| char::from(b))
    }

    /// Read the next character from the input
    fn read_char(&mut self) -> Option<char> {
        self.state.split_first().map(|(&b, tail)| {
            self.state = tail;
            char::from(b)
        })
    }

    #[must_use]
    /// Read the next character from the input if it matches the target.
    fn read_given_char(&mut self, target: char) -> Option<()> {
        self.read_atomically(|p| {
            p.read_char().and_then(|c| if c == target { Some(()) } else { None })
        })
    }

    /// Helper for reading separators in an indexed loop. Reads the separator
    /// character iff index > 0, then runs the parser. When used in a loop,
    /// the separator character will only be read on index > 0 (see
    /// read_ipv4_addr for an example)
    fn read_separator<T, F>(&mut self, sep: char, index: usize, inner: F) -> Option<T>
    where
        F: FnOnce(&mut Parser<'_>) -> Option<T>,
    {
        self.read_atomically(move |p| {
            if index > 0 {
                p.read_given_char(sep)?;
            }
            inner(p)
        })
    }

    // Read a number off the front of the input in the given radix, stopping
    // at the first non-digit character or eof. Fails if the number has more
    // digits than max_digits or if there is no number.
    fn read_number<T: ReadNumberHelper>(
        &mut self,
        radix: u32,
        max_digits: Option<usize>,
        allow_zero_prefix: bool,
    ) -> Option<T> {
        self.read_atomically(move |p| {
            let mut result = T::ZERO;
            let mut digit_count = 0;
            let has_leading_zero = p.peek_char() == Some('0');

            while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) {
                result = result.checked_mul(radix)?;
                result = result.checked_add(digit)?;
                digit_count += 1;
                if let Some(max_digits) = max_digits {
                    if digit_count > max_digits {
                        return None;
                    }
                }
            }

            if digit_count == 0 {
                None
            } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 {
                None
            } else {
                Some(result)
            }
        })
    }

    /// Read an IPv4 address.
    fn read_ipv4_addr(&mut self) -> Option<Ipv4Addr> {
        self.read_atomically(|p| {
            let mut groups = [0; 4];

            for (i, slot) in groups.iter_mut().enumerate() {
                *slot = p.read_separator('.', i, |p| {
                    // Disallow octal number in IP string.
                    // https://tools.ietf.org/html/rfc6943#section-3.1.1
                    p.read_number(10, None, false)
                })?;
            }

            Some(groups.into())
        })
    }

    /// Read an IPv6 Address.
    fn read_ipv6_addr(&mut self) -> Option<Ipv6Addr> {
        /// Read a chunk of an IPv6 address into `groups`. Returns the number
        /// of groups read, along with a bool indicating if an embedded
        /// trailing IPv4 address was read. Specifically, read a series of
        /// colon-separated IPv6 groups (0x0000 - 0xFFFF), with an optional
        /// trailing embedded IPv4 address.
        fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) {
            let limit = groups.len();

            for (i, slot) in groups.iter_mut().enumerate() {
                // Try to read a trailing embedded IPv4 address. There must be
                // at least two groups left.
                if i < limit - 1 {
                    let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr());

                    if let Some(v4_addr) = ipv4 {
                        let [one, two, three, four] = v4_addr.octets();
                        groups[i + 0] = u16::from_be_bytes([one, two]);
                        groups[i + 1] = u16::from_be_bytes([three, four]);
                        return (i + 2, true);
                    }
                }

                let group = p.read_separator(':', i, |p| p.read_number(16, Some(4), true));

                match group {
                    Some(g) => *slot = g,
                    None => return (i, false),
                }
            }
            (groups.len(), false)
        }

        self.read_atomically(|p| {
            // Read the front part of the address; either the whole thing, or up
            // to the first ::
            let mut head = [0; 8];
            let (head_size, head_ipv4) = read_groups(p, &mut head);

            if head_size == 8 {
                return Some(head.into());
            }

            // IPv4 part is not allowed before `::`
            if head_ipv4 {
                return None;
            }

            // Read `::` if previous code parsed less than 8 groups.
            // `::` indicates one or more groups of 16 bits of zeros.
            p.read_given_char(':')?;
            p.read_given_char(':')?;

            // Read the back part of the address. The :: must contain at least one
            // set of zeroes, so our max length is 7.
            let mut tail = [0; 7];
            let limit = 8 - (head_size + 1);
            let (tail_size, _) = read_groups(p, &mut tail[..limit]);

            // Concat the head and tail of the IP address
            head[(8 - tail_size)..8].copy_from_slice(&tail[..tail_size]);

            Some(head.into())
        })
    }

    /// Read an IP Address, either IPv4 or IPv6.
    fn read_ip_addr(&mut self) -> Option<IpAddr> {
        self.read_ipv4_addr().map(IpAddr::V4).or_else(move || self.read_ipv6_addr().map(IpAddr::V6))
    }

    /// Read a `:` followed by a port in base 10.
    fn read_port(&mut self) -> Option<u16> {
        self.read_atomically(|p| {
            p.read_given_char(':')?;
            p.read_number(10, None, true)
        })
    }

    /// Read a `%` followed by a scope ID in base 10.
    fn read_scope_id(&mut self) -> Option<u32> {
        self.read_atomically(|p| {
            p.read_given_char('%')?;
            p.read_number(10, None, true)
        })
    }

    /// Read an IPv4 address with a port.
    fn read_socket_addr_v4(&mut self) -> Option<SocketAddrV4> {
        self.read_atomically(|p| {
            let ip = p.read_ipv4_addr()?;
            let port = p.read_port()?;
            Some(SocketAddrV4::new(ip, port))
        })
    }

    /// Read an IPv6 address with a port.
    fn read_socket_addr_v6(&mut self) -> Option<SocketAddrV6> {
        self.read_atomically(|p| {
            p.read_given_char('[')?;
            let ip = p.read_ipv6_addr()?;
            let scope_id = p.read_scope_id().unwrap_or(0);
            p.read_given_char(']')?;

            let port = p.read_port()?;
            Some(SocketAddrV6::new(ip, port, 0, scope_id))
        })
    }

    /// Read an IP address with a port
    fn read_socket_addr(&mut self) -> Option<SocketAddr> {
        self.read_socket_addr_v4()
            .map(SocketAddr::V4)
            .or_else(|| self.read_socket_addr_v6().map(SocketAddr::V6))
    }
}

#[stable(feature = "ip_addr", since = "1.7.0")]
impl FromStr for IpAddr {
    type Err = AddrParseError;
    fn from_str(s: &str) -> Result<IpAddr, AddrParseError> {
        Parser::new(s).parse_with(|p| p.read_ip_addr())
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl FromStr for Ipv4Addr {
    type Err = AddrParseError;
    fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> {
        // don't try to parse if too long
        if s.len() > 15 {
            Err(AddrParseError(()))
        } else {
            Parser::new(s).parse_with(|p| p.read_ipv4_addr())
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl FromStr for Ipv6Addr {
    type Err = AddrParseError;
    fn from_str(s: &str) -> Result<Ipv6Addr, AddrParseError> {
        Parser::new(s).parse_with(|p| p.read_ipv6_addr())
    }
}

#[stable(feature = "socket_addr_from_str", since = "1.5.0")]
impl FromStr for SocketAddrV4 {
    type Err = AddrParseError;
    fn from_str(s: &str) -> Result<SocketAddrV4, AddrParseError> {
        Parser::new(s).parse_with(|p| p.read_socket_addr_v4())
    }
}

#[stable(feature = "socket_addr_from_str", since = "1.5.0")]
impl FromStr for SocketAddrV6 {
    type Err = AddrParseError;
    fn from_str(s: &str) -> Result<SocketAddrV6, AddrParseError> {
        Parser::new(s).parse_with(|p| p.read_socket_addr_v6())
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl FromStr for SocketAddr {
    type Err = AddrParseError;
    fn from_str(s: &str) -> Result<SocketAddr, AddrParseError> {
        Parser::new(s).parse_with(|p| p.read_socket_addr())
    }
}

/// An error which can be returned when parsing an IP address or a socket address.
///
/// This error is used as the error type for the [`FromStr`] implementation for
/// [`IpAddr`], [`Ipv4Addr`], [`Ipv6Addr`], [`SocketAddr`], [`SocketAddrV4`], and
/// [`SocketAddrV6`].
///
/// # Potential causes
///
/// `AddrParseError` may be thrown because the provided string does not parse as the given type,
/// often because it includes information only handled by a different address type.
///
/// ```should_panic
/// use std::net::IpAddr;
/// let _foo: IpAddr = "127.0.0.1:8080".parse().expect("Cannot handle the socket port");
/// ```
///
/// [`IpAddr`] doesn't handle the port. Use [`SocketAddr`] instead.
///
/// ```
/// use std::net::SocketAddr;
///
/// // No problem, the `panic!` message has disappeared.
/// let _foo: SocketAddr = "127.0.0.1:8080".parse().expect("unreachable panic");
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddrParseError(());

#[stable(feature = "addr_parse_error_error", since = "1.4.0")]
impl fmt::Display for AddrParseError {
    #[allow(deprecated, deprecated_in_future)]
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.write_str(self.description())
    }
}

#[stable(feature = "addr_parse_error_error", since = "1.4.0")]
impl Error for AddrParseError {
    #[allow(deprecated)]
    fn description(&self) -> &str {
        "invalid IP address syntax"
    }
}
<|endoftext|>


<commit_before>
extern crate nccl;

use nccl::{Pair, Scanner, NcclError, ErrorKind, Parser};

use ::std::fs::File;
use ::std::path::Path;
use ::std::io::Read;

#[test]
fn pair_new() {
    assert_eq!(Pair::new("hello"), Pair {
        key: "hello".into(),
        value: vec![]
    });
}

#[test]
fn pair_add() {
    let mut p = Pair::new("top_level");
    p.add("key");
    assert_eq!(p, Pair {
        key: "top_level".into(),
        value: vec![Pair {
            key: "key".into(),
            value: vec![]
        }]
    });
}

#[test]
fn pair_get() {
    let mut p = Pair::new("top_level");
    p.add("key");
    assert_eq!(p.get("key").unwrap(), &mut Pair {
        key: "key".into(),
        value: vec![]
    });
}

#[test]
fn pair_index() {
    let mut p = Pair::new("top_level");
    p.add("key");
    assert_eq!(p["key"], Pair {
        key: "key".into(),
        value: vec![]
    });
}

#[test]
fn pair_keys() {
    let mut p = Pair::new("top");
    p.add("numbers");
    p["numbers"].add("1");
    p["numbers"].add("2");
    p["numbers"].add("3");
    p["numbers"].add("4");
    p["numbers"].add("5");
    assert_eq!(p["numbers"].keys(), vec!["1", "2", "3", "4", "5"]);
}

#[test]
fn pair_value_parse() {
    let mut p = Pair::new("top");
    p.add("bools");
    p["bools"].add("true");
    assert!(p["bools"].value_as::<bool>().unwrap());
}

#[test]
fn scanner_literal() {
    let mut s = Scanner::new("\"this: is neato\": burrito 64\n    yes.".into());
    s.scan_tokens().unwrap();
}

#[test]
#[should_panic]
fn error_key_not_found() {
    let mut p = Pair::new("jjj");
    p.get("jwiofiojwaef jio").unwrap();
}

#[test]
fn scan_file() {
    assert!(nccl::parse_file("examples/config.nccl").is_ok());
}

#[test]
fn dos_unix_lines() {
    assert_eq!(nccl::parse_file("examples/config.nccl"), nccl::parse_file("examples/config_dos.nccl"));
}

#[test]
fn string_escape() {
    let mut s = Scanner::new("\"\\\"hello\\\"\\n\"".into());
    assert_eq!(s.scan_tokens().unwrap()[0].lexeme, "\"hello\"\n");
}

#[test]
fn add_pair() {
    // create a new pair
     let mut p1 = Pair::new("happy birthday");

     p1.add("Bobby");
     p1["Bobby"].add("Today!");

     // we think Ron's birthday is the 3rd...
     p1.add("Ron");
     p1["Ron"].add("March 3rd");

     // whoops, we were wrong
     let mut p2 = Pair::new("Ron");
     p2.add("March 2nd");

     // there you go Ron, happy belated birthday
     p1.add_pair(p2);
}

#[test]
fn traverse_path() {
    let mut p = Pair::new("top");
    p.add_slice(&["a".into(), "b".into(), "c".into()]);
    p.traverse_path(&["a".into(), "b".into()]).add("happy");
    assert_eq!(p.traverse_path(&["a".into(), "b".into(), "happy".into()]), &mut Pair::new("happy"));
}

#[test]
fn add_slice() {
    let mut config = Pair::new("top_level");
    config.add("server");
    config["server"].add("domain");
    config["server"].add("port");
    config["server"].add("root");
    config["server"]["domain"].add("example.com");
    config["server"]["domain"].add("www.example.com");
    config["server"]["port"].add("80");
    config["server"]["port"].add("443");
    config["server"]["root"].add("/var/www/html");

    config.add_slice(&["server".into(), "port".into(), "22".into()]);
    assert_eq!(config["server"]["port"].keys(), vec!["80", "443", "22"]);
}

#[test]
#[should_panic]
fn multiple_errors() {
    let mut s = Scanner::new("hey: momma\n   test\n\tjeii\n    oh no!\n".into());
    s.scan_tokens().unwrap();
}

#[test]
fn add_vec() {
    let mut p = Pair::new("__top_level__");
    p.add("a");
    p.add_slice(&["a".into(), "hello".into(), "world".into()]);
    p.add_slice(&["a".into(), "hello".into(), "world".into()]);
    assert_eq!(p["a"]["hello"].value.len(), 1);
}

#[test]
fn long() {
    let oh_dear = nccl::parse_file("examples/long.nccl").unwrap();
    oh_dear.pretty_print();
}

#[test]
fn inherit2() {
    let schemas = nccl::parse_file("examples/inherit.nccl").unwrap();
    let user = nccl::parse_file_with("examples/inherit2.nccl", schemas).unwrap();
    assert_eq!(user["sandwich"]["meat"].value.len(), 3);
    assert_eq!(user["hello"]["world"].value.len(), 3);
}

#[test]
fn readme() {
    let config = nccl::parse_file("examples/config.nccl").unwrap();
    let ports = config["server"]["port"].keys_as::<u32>().unwrap();
    assert_eq!(ports, vec![80, 443]);
}

<commit_msg>update tests<commit_after>
extern crate nccl;

use nccl::{Pair, Scanner};

#[test]
fn pair_new() {
    assert_eq!(Pair::new("hello"), Pair {
        key: "hello".into(),
        value: vec![]
    });
}

#[test]
fn pair_add() {
    let mut p = Pair::new("top_level");
    p.add("key");
    assert_eq!(p, Pair {
        key: "top_level".into(),
        value: vec![Pair {
            key: "key".into(),
            value: vec![]
        }]
    });
}

#[test]
fn pair_get() {
    let mut p = Pair::new("top_level");
    p.add("key");
    assert_eq!(p.get("key").unwrap(), &mut Pair {
        key: "key".into(),
        value: vec![]
    });
}

#[test]
fn pair_index() {
    let mut p = Pair::new("top_level");
    p.add("key");
    assert_eq!(p["key"], Pair {
        key: "key".into(),
        value: vec![]
    });
}

#[test]
fn pair_keys() {
    let mut p = Pair::new("top");
    p.add("numbers");
    p["numbers"].add("1");
    p["numbers"].add("2");
    p["numbers"].add("3");
    p["numbers"].add("4");
    p["numbers"].add("5");
    assert_eq!(p["numbers"].keys(), vec!["1", "2", "3", "4", "5"]);
}

#[test]
fn pair_value_parse() {
    let mut p = Pair::new("top");
    p.add("bools");
    p["bools"].add("true");
    assert!(p["bools"].value_as::<bool>().unwrap());
}

#[test]
fn scanner_literal() {
    let mut s = Scanner::new("\"this: is neato\": burrito 64\n    yes.".into());
    s.scan_tokens().unwrap();
}

#[test]
#[should_panic]
fn error_key_not_found() {
    let mut p = Pair::new("jjj");
    p.get("jwiofiojwaef jio").unwrap();
}

#[test]
fn scan_file() {
    assert!(nccl::parse_file("examples/config.nccl").is_ok());
}

#[test]
fn dos_unix_lines() {
    assert!(nccl::parse_file("examples/config.nccl").is_ok());
    assert!(nccl::parse_file("examples/config_dos.nccl").is_ok());
}

#[test]
fn string_escape() {
    let mut s = Scanner::new("\"\\\"hello\\\"\\n\"".into());
    assert_eq!(s.scan_tokens().unwrap()[0].lexeme, "\"hello\"\n");
}

#[test]
fn add_pair() {
    // create a new pair
     let mut p1 = Pair::new("happy birthday");

     p1.add("Bobby");
     p1["Bobby"].add("Today!");

     // we think Ron's birthday is the 3rd...
     p1.add("Ron");
     p1["Ron"].add("March 3rd");

     // whoops, we were wrong
     let mut p2 = Pair::new("Ron");
     p2.add("March 2nd");

     // there you go Ron, happy belated birthday
     p1.add_pair(p2);
}

#[test]
fn traverse_path() {
    let mut p = Pair::new("top");
    p.add_slice(&["a".into(), "b".into(), "c".into()]);
    p.traverse_path(&["a".into(), "b".into()]).add("happy");
    assert_eq!(p.traverse_path(&["a".into(), "b".into(), "happy".into()]), &mut Pair::new("happy"));
}

#[test]
fn add_slice() {
    let mut config = Pair::new("top_level");
    config.add("server");
    config["server"].add("domain");
    config["server"].add("port");
    config["server"].add("root");
    config["server"]["domain"].add("example.com");
    config["server"]["domain"].add("www.example.com");
    config["server"]["port"].add("80");
    config["server"]["port"].add("443");
    config["server"]["root"].add("/var/www/html");

    config.add_slice(&["server".into(), "port".into(), "22".into()]);
    assert_eq!(config["server"]["port"].keys(), vec!["80", "443", "22"]);
}

#[test]
#[should_panic]
fn multiple_errors() {
    let mut s = Scanner::new("hey: momma\n   test\n\tjeii\n    oh no!\n".into());
    s.scan_tokens().unwrap();
}

#[test]
fn add_vec() {
    let mut p = Pair::new("__top_level__");
    p.add("a");
    p.add_slice(&["a".into(), "hello".into(), "world".into()]);
    p.add_slice(&["a".into(), "hello".into(), "world".into()]);
    assert_eq!(p["a"]["hello"].value.len(), 1);
}

#[test]
fn long() {
    let oh_dear = nccl::parse_file("examples/long.nccl").unwrap();
    oh_dear.pretty_print();
}

#[test]
fn inherit2() {
    let schemas = nccl::parse_file("examples/inherit.nccl").unwrap();
    let user = nccl::parse_file_with("examples/inherit2.nccl", schemas).unwrap();
    assert_eq!(user["sandwich"]["meat"].value.len(), 3);
    assert_eq!(user["hello"]["world"].value.len(), 3);
}

#[test]
fn readme() {
    let config = nccl::parse_file("examples/config.nccl").unwrap();
    let ports = config["server"]["port"].keys_as::<u32>().unwrap();
    assert_eq!(ports, vec![80, 443]);
}

<|endoftext|>