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

// Objects for seeking through a char stream for occurrences of TODO and FIXME.
// Depending on the loaded configuration, may also check that these have an
// associated issue number.

use std::fmt;

use config::ReportTactic;

const TO_DO_CHARS: &[char] = &['t', 'o', 'd', 'o'];
const FIX_ME_CHARS: &[char] = &['f', 'i', 'x', 'm', 'e'];

// Enabled implementation detail is here because it is
// irrelevant outside the issues module
fn is_enabled(report_tactic: ReportTactic) -> bool {
    report_tactic != ReportTactic::Never
}

#[derive(Clone, Copy)]
enum Seeking {
    Issue { todo_idx: usize, fixme_idx: usize },
    Number { issue: Issue, part: NumberPart },
}

#[derive(Clone, Copy)]
enum NumberPart {
    OpenParen,
    Pound,
    Number,
    CloseParen,
}

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub struct Issue {
    issue_type: IssueType,
    // Indicates whether we're looking for issues with missing numbers, or
    // all issues of this type.
    missing_number: bool,
}

impl fmt::Display for Issue {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let msg = match self.issue_type {
            IssueType::Todo => "TODO",
            IssueType::Fixme => "FIXME",
        };
        let details = if self.missing_number {
            " without issue number"
        } else {
            ""
        };

        write!(fmt, "{}{}", msg, details)
    }
}

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
enum IssueType {
    Todo,
    Fixme,
}

enum IssueClassification {
    Good,
    Bad(Issue),
    None,
}

pub struct BadIssueSeeker {
    state: Seeking,
    report_todo: ReportTactic,
    report_fixme: ReportTactic,
}

impl BadIssueSeeker {
    pub fn new(report_todo: ReportTactic, report_fixme: ReportTactic) -> BadIssueSeeker {
        BadIssueSeeker {
            state: Seeking::Issue {
                todo_idx: 0,
                fixme_idx: 0,
            },
            report_todo,
            report_fixme,
        }
    }

    // Check whether or not the current char is conclusive evidence for an
    // unnumbered TO-DO or FIX-ME.
    pub fn inspect(&mut self, c: char) -> Option<Issue> {
        match self.state {
            Seeking::Issue {
                todo_idx,
                fixme_idx,
            } => {
                self.state = self.inspect_issue(c, todo_idx, fixme_idx);
            }
            Seeking::Number { issue, part } => {
                let result = self.inspect_number(c, issue, part);

                if let IssueClassification::None = result {
                    return None;
                }

                self.state = Seeking::Issue {
                    todo_idx: 0,
                    fixme_idx: 0,
                };

                if let IssueClassification::Bad(issue) = result {
                    return Some(issue);
                }
            }
        }

        None
    }

    fn inspect_issue(&mut self, c: char, mut todo_idx: usize, mut fixme_idx: usize) -> Seeking {
        if let Some(lower_case_c) = c.to_lowercase().next() {
            if is_enabled(self.report_todo) && lower_case_c == TO_DO_CHARS[todo_idx] {
                todo_idx += 1;
                if todo_idx == TO_DO_CHARS.len() {
                    return Seeking::Number {
                        issue: Issue {
                            issue_type: IssueType::Todo,
                            missing_number: if let ReportTactic::Unnumbered = self.report_todo {
                                true
                            } else {
                                false
                            },
                        },
                        part: NumberPart::OpenParen,
                    };
                }
                fixme_idx = 0;
            } else if is_enabled(self.report_fixme) && lower_case_c == FIX_ME_CHARS[fixme_idx] {
                // Exploit the fact that the character sets of todo and fixme
                // are disjoint by adding else.
                fixme_idx += 1;
                if fixme_idx == FIX_ME_CHARS.len() {
                    return Seeking::Number {
                        issue: Issue {
                            issue_type: IssueType::Fixme,
                            missing_number: if let ReportTactic::Unnumbered = self.report_fixme {
                                true
                            } else {
                                false
                            },
                        },
                        part: NumberPart::OpenParen,
                    };
                }
                todo_idx = 0;
            } else {
                todo_idx = 0;
                fixme_idx = 0;
            }
        }

        Seeking::Issue {
            todo_idx,
            fixme_idx,
        }
    }

    fn inspect_number(
        &mut self,
        c: char,
        issue: Issue,
        mut part: NumberPart,
    ) -> IssueClassification {
        if !issue.missing_number || c == '\n' {
            return IssueClassification::Bad(issue);
        } else if c == ')' {
            return if let NumberPart::CloseParen = part {
                IssueClassification::Good
            } else {
                IssueClassification::Bad(issue)
            };
        }

        match part {
            NumberPart::OpenParen => {
                if c != '(' {
                    return IssueClassification::Bad(issue);
                } else {
                    part = NumberPart::Pound;
                }
            }
            NumberPart::Pound => {
                if c == '#' {
                    part = NumberPart::Number;
                }
            }
            NumberPart::Number => {
                if c >= '0' && c <= '9' {
                    part = NumberPart::CloseParen;
                } else {
                    return IssueClassification::Bad(issue);
                }
            }
            NumberPart::CloseParen => {}
        }

        self.state = Seeking::Number { part, issue };

        IssueClassification::None
    }
}

#[test]
fn find_unnumbered_issue() {
    fn check_fail(text: &str, failing_pos: usize) {
        let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered, ReportTactic::Unnumbered);
        assert_eq!(
            Some(failing_pos),
            text.chars().position(|c| seeker.inspect(c).is_some())
        );
    }

    fn check_pass(text: &str) {
        let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered, ReportTactic::Unnumbered);
        assert_eq!(None, text.chars().position(|c| seeker.inspect(c).is_some()));
    }

    check_fail("TODO\n", 4);
    check_pass(" TO FIX DOME\n");
    check_fail(" \n FIXME\n", 8);
    check_fail("FIXME(\n", 6);
    check_fail("FIXME(#\n", 7);
    check_fail("FIXME(#1\n", 8);
    check_fail("FIXME(#)1\n", 7);
    check_pass("FIXME(#1222)\n");
    check_fail("FIXME(#12\n22)\n", 9);
    check_pass("FIXME(@maintainer, #1222, hello)\n");
    check_fail("TODO(#22) FIXME\n", 15);
}

#[test]
fn find_issue() {
    fn is_bad_issue(text: &str, report_todo: ReportTactic, report_fixme: ReportTactic) -> bool {
        let mut seeker = BadIssueSeeker::new(report_todo, report_fixme);
        text.chars().any(|c| seeker.inspect(c).is_some())
    }

    assert!(is_bad_issue(
        "TODO(@maintainer, #1222, hello)\n",
        ReportTactic::Always,
        ReportTactic::Never,
    ));

    assert!(!is_bad_issue(
        "TODO: no number\n",
        ReportTactic::Never,
        ReportTactic::Always,
    ));

    assert!(!is_bad_issue(
        "Todo: mixed case\n",
        ReportTactic::Never,
        ReportTactic::Always,
    ));

    assert!(is_bad_issue(
        "This is a FIXME(#1)\n",
        ReportTactic::Never,
        ReportTactic::Always,
    ));

    assert!(is_bad_issue(
        "This is a FixMe(#1) mixed case\n",
        ReportTactic::Never,
        ReportTactic::Always,
    ));

    assert!(!is_bad_issue(
        "bad FIXME\n",
        ReportTactic::Always,
        ReportTactic::Never,
    ));
}

#[test]
fn issue_type() {
    let mut seeker = BadIssueSeeker::new(ReportTactic::Always, ReportTactic::Never);
    let expected = Some(Issue {
        issue_type: IssueType::Todo,
        missing_number: false,
    });

    assert_eq!(
        expected,
        "TODO(#100): more awesomeness"
            .chars()
            .map(|c| seeker.inspect(c))
            .find(Option::is_some)
            .unwrap()
    );

    let mut seeker = BadIssueSeeker::new(ReportTactic::Never, ReportTactic::Unnumbered);
    let expected = Some(Issue {
        issue_type: IssueType::Fixme,
        missing_number: true,
    });

    assert_eq!(
        expected,
        "Test. FIXME: bad, bad, not good"
            .chars()
            .map(|c| seeker.inspect(c))
            .find(Option::is_some)
            .unwrap()
    );
}
<commit_msg>Do not inspect code for issues or todos when not required<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Objects for seeking through a char stream for occurrences of TODO and FIXME.
// Depending on the loaded configuration, may also check that these have an
// associated issue number.

use std::fmt;

use config::ReportTactic;

const TO_DO_CHARS: &[char] = &['t', 'o', 'd', 'o'];
const FIX_ME_CHARS: &[char] = &['f', 'i', 'x', 'm', 'e'];

// Enabled implementation detail is here because it is
// irrelevant outside the issues module
fn is_enabled(report_tactic: ReportTactic) -> bool {
    report_tactic != ReportTactic::Never
}

#[derive(Clone, Copy)]
enum Seeking {
    Issue { todo_idx: usize, fixme_idx: usize },
    Number { issue: Issue, part: NumberPart },
}

#[derive(Clone, Copy)]
enum NumberPart {
    OpenParen,
    Pound,
    Number,
    CloseParen,
}

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub struct Issue {
    issue_type: IssueType,
    // Indicates whether we're looking for issues with missing numbers, or
    // all issues of this type.
    missing_number: bool,
}

impl fmt::Display for Issue {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let msg = match self.issue_type {
            IssueType::Todo => "TODO",
            IssueType::Fixme => "FIXME",
        };
        let details = if self.missing_number {
            " without issue number"
        } else {
            ""
        };

        write!(fmt, "{}{}", msg, details)
    }
}

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
enum IssueType {
    Todo,
    Fixme,
}

enum IssueClassification {
    Good,
    Bad(Issue),
    None,
}

pub struct BadIssueSeeker {
    state: Seeking,
    report_todo: ReportTactic,
    report_fixme: ReportTactic,
}

impl BadIssueSeeker {
    pub fn new(report_todo: ReportTactic, report_fixme: ReportTactic) -> BadIssueSeeker {
        BadIssueSeeker {
            state: Seeking::Issue {
                todo_idx: 0,
                fixme_idx: 0,
            },
            report_todo,
            report_fixme,
        }
    }

    fn is_disabled(&self) -> bool {
        !is_enabled(self.report_todo) && !is_enabled(self.report_fixme)
    }

    // Check whether or not the current char is conclusive evidence for an
    // unnumbered TO-DO or FIX-ME.
    pub fn inspect(&mut self, c: char) -> Option<Issue> {
        if self.is_disabled() {
            return None;
        }

        match self.state {
            Seeking::Issue {
                todo_idx,
                fixme_idx,
            } => {
                self.state = self.inspect_issue(c, todo_idx, fixme_idx);
            }
            Seeking::Number { issue, part } => {
                let result = self.inspect_number(c, issue, part);

                if let IssueClassification::None = result {
                    return None;
                }

                self.state = Seeking::Issue {
                    todo_idx: 0,
                    fixme_idx: 0,
                };

                if let IssueClassification::Bad(issue) = result {
                    return Some(issue);
                }
            }
        }

        None
    }

    fn inspect_issue(&mut self, c: char, mut todo_idx: usize, mut fixme_idx: usize) -> Seeking {
        if let Some(lower_case_c) = c.to_lowercase().next() {
            if is_enabled(self.report_todo) && lower_case_c == TO_DO_CHARS[todo_idx] {
                todo_idx += 1;
                if todo_idx == TO_DO_CHARS.len() {
                    return Seeking::Number {
                        issue: Issue {
                            issue_type: IssueType::Todo,
                            missing_number: if let ReportTactic::Unnumbered = self.report_todo {
                                true
                            } else {
                                false
                            },
                        },
                        part: NumberPart::OpenParen,
                    };
                }
                fixme_idx = 0;
            } else if is_enabled(self.report_fixme) && lower_case_c == FIX_ME_CHARS[fixme_idx] {
                // Exploit the fact that the character sets of todo and fixme
                // are disjoint by adding else.
                fixme_idx += 1;
                if fixme_idx == FIX_ME_CHARS.len() {
                    return Seeking::Number {
                        issue: Issue {
                            issue_type: IssueType::Fixme,
                            missing_number: if let ReportTactic::Unnumbered = self.report_fixme {
                                true
                            } else {
                                false
                            },
                        },
                        part: NumberPart::OpenParen,
                    };
                }
                todo_idx = 0;
            } else {
                todo_idx = 0;
                fixme_idx = 0;
            }
        }

        Seeking::Issue {
            todo_idx,
            fixme_idx,
        }
    }

    fn inspect_number(
        &mut self,
        c: char,
        issue: Issue,
        mut part: NumberPart,
    ) -> IssueClassification {
        if !issue.missing_number || c == '\n' {
            return IssueClassification::Bad(issue);
        } else if c == ')' {
            return if let NumberPart::CloseParen = part {
                IssueClassification::Good
            } else {
                IssueClassification::Bad(issue)
            };
        }

        match part {
            NumberPart::OpenParen => {
                if c != '(' {
                    return IssueClassification::Bad(issue);
                } else {
                    part = NumberPart::Pound;
                }
            }
            NumberPart::Pound => {
                if c == '#' {
                    part = NumberPart::Number;
                }
            }
            NumberPart::Number => {
                if c >= '0' && c <= '9' {
                    part = NumberPart::CloseParen;
                } else {
                    return IssueClassification::Bad(issue);
                }
            }
            NumberPart::CloseParen => {}
        }

        self.state = Seeking::Number { part, issue };

        IssueClassification::None
    }
}

#[test]
fn find_unnumbered_issue() {
    fn check_fail(text: &str, failing_pos: usize) {
        let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered, ReportTactic::Unnumbered);
        assert_eq!(
            Some(failing_pos),
            text.chars().position(|c| seeker.inspect(c).is_some())
        );
    }

    fn check_pass(text: &str) {
        let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered, ReportTactic::Unnumbered);
        assert_eq!(None, text.chars().position(|c| seeker.inspect(c).is_some()));
    }

    check_fail("TODO\n", 4);
    check_pass(" TO FIX DOME\n");
    check_fail(" \n FIXME\n", 8);
    check_fail("FIXME(\n", 6);
    check_fail("FIXME(#\n", 7);
    check_fail("FIXME(#1\n", 8);
    check_fail("FIXME(#)1\n", 7);
    check_pass("FIXME(#1222)\n");
    check_fail("FIXME(#12\n22)\n", 9);
    check_pass("FIXME(@maintainer, #1222, hello)\n");
    check_fail("TODO(#22) FIXME\n", 15);
}

#[test]
fn find_issue() {
    fn is_bad_issue(text: &str, report_todo: ReportTactic, report_fixme: ReportTactic) -> bool {
        let mut seeker = BadIssueSeeker::new(report_todo, report_fixme);
        text.chars().any(|c| seeker.inspect(c).is_some())
    }

    assert!(is_bad_issue(
        "TODO(@maintainer, #1222, hello)\n",
        ReportTactic::Always,
        ReportTactic::Never,
    ));

    assert!(!is_bad_issue(
        "TODO: no number\n",
        ReportTactic::Never,
        ReportTactic::Always,
    ));

    assert!(!is_bad_issue(
        "Todo: mixed case\n",
        ReportTactic::Never,
        ReportTactic::Always,
    ));

    assert!(is_bad_issue(
        "This is a FIXME(#1)\n",
        ReportTactic::Never,
        ReportTactic::Always,
    ));

    assert!(is_bad_issue(
        "This is a FixMe(#1) mixed case\n",
        ReportTactic::Never,
        ReportTactic::Always,
    ));

    assert!(!is_bad_issue(
        "bad FIXME\n",
        ReportTactic::Always,
        ReportTactic::Never,
    ));
}

#[test]
fn issue_type() {
    let mut seeker = BadIssueSeeker::new(ReportTactic::Always, ReportTactic::Never);
    let expected = Some(Issue {
        issue_type: IssueType::Todo,
        missing_number: false,
    });

    assert_eq!(
        expected,
        "TODO(#100): more awesomeness"
            .chars()
            .map(|c| seeker.inspect(c))
            .find(Option::is_some)
            .unwrap()
    );

    let mut seeker = BadIssueSeeker::new(ReportTactic::Never, ReportTactic::Unnumbered);
    let expected = Some(Issue {
        issue_type: IssueType::Fixme,
        missing_number: true,
    });

    assert_eq!(
        expected,
        "Test. FIXME: bad, bad, not good"
            .chars()
            .map(|c| seeker.inspect(c))
            .find(Option::is_some)
            .unwrap()
    );
}
<|endoftext|>


<commit_before>// Fake module for documentation
//! Documentation about the various scripts contained herein
//!
//! - [check-graphite](#check-graphite)
//! - [check-cpu](#check-cpu)
//! - [check-container-cpu](#check-container-cpu)
//! - [check-load](#check-load)
//! - [check-container-ram](#check-container-ram)
//! - [check-ram](#check-ram)
//! - [check-fs-writeable](#check-fs-writeable)
//! - [check-disk](#check-disk)
//!
//! check-graphite
//! ==============
//!
//! Usage looks like:
//!
//! ```plain
//! $ check-graphite --help
//! check-graphite 0.1.0
//! Brandon W Maister <quodlibetor@gmail.com>
//! Query graphite and exit based on predicates
//!
//! USAGE:
//!         check-graphite [FLAGS] [OPTIONS] <URL> <PATH> <ASSERTION>... [--]
//!
//! FLAGS:
//!     -h, --help                 Prints help information
//!         --print-url            Unconditionally print the graphite url queried
//!     -V, --version              Prints version information
//!         --verify-assertions    Just check assertion syntax, do not query urls
//!
//! OPTIONS:
//!         --retries <COUNT>      How many times to retry reaching graphite. Default 4
//!         --graphite-error <GRAPHITE_ERROR_STATUS>    What to do with no data.
//!                               Choices: ok, warn, critical, unknown.
//!                               What to say if graphite returns a 500 or invalid JSON
//!                               Default: unknown. [values: ok warning critical unknown]
//!     -w, --window <MINUTES>                          How many minutes of data to test. Default 10.
//!         --no-data <NO_DATA_STATUS>                  What to do with no data.
//!                               Choices: ok, warn, critical, unknown.
//!                               This is the value to use for the assertion
//!                               'if all values are null'
//!                               Default: warn. [values: ok warning critical unknown]
//!
//! ARGS:
//!     URL             The domain to query graphite. Must include scheme (http/s)
//!     PATH            The graphite path to query. For example: "collectd.*.cpu"
//!     ASSERTION...    The assertion to make against the PATH. See Below.
//!
//! About Assertions:
//!
//!     Assertions look like 'critical if any point in any series is > 5'.
//!
//!     They describe what you care about in your graphite data. The structure of
//!     an assertion is as follows:
//!
//!         <errorkind> if <point spec> [in <series spec>] is|are [not] <operator> <threshold>
//!
//!     Where:
//!
//!         - `errorkind` is either `critical` or `warning`
//!         - `point spec` can be one of:
//!             - `any point`
//!             - `all points`
//!             - `at least <N>% of points`
//!             - `most recent point`
//!         - `series spec` (optional) can be one of:
//!             - `any series`
//!             - `all series`
//!             - `at least <N>% of series`
//!         - `not` is optional, and inverts the following operator
//!         - `operator` is one of: `==` `!=` `<` `>` `<=` `>=`
//!         - `threshold` is a floating-point value (e.g. 100, 78.0)
//!
//!     Here are some example assertions:
//!
//!         - `critical if any point is > 0`
//!         - `critical if any point in at least 40% of series is > 0`
//!         - `critical if any point is not > 0`
//!         - `warning if any point is == 9`
//!         - `critical if all points are > 100.0`
//!         - `critical if at least 20% of points are > 100`
//!         - `critical if most recent point is > 5`
//!         - `critical if most recent point in all series are == 0`
//! ```
//!
//! check-cpu
//! =========
//!
//! Linux-only.
//!
//! ```plain
//! Usage:
//!     check-cpu [options] [--type=<work-source>...] [--show-hogs=<count>]
//!     check-cpu (-h | --help)
//!
//! Options:
//!     -h, --help               Show this help message
//!
//!     -s, --sample=<seconds>   Seconds to spent collecting   [default: 1]
//!     -w, --warn=<percent>     Percent to warn at            [default: 80]
//!     -c, --crit=<percent>     Percent to critical at        [default: 95]
//!
//!     --per-cpu                Gauge values per-cpu instead of across the
//!                              entire machine
//!     --cpu-count=<num>        If --per-cpu is specified, this is how many
//!                              CPUs need to be at a threshold to trigger.
//!                              [default: 1]
//!
//!     --show-hogs=<count>      Show most cpu-hungry procs    [default: 0]
//!
//! CPU Work Types:
//!
//!     Specifying one of the CPU kinds checks that kind of utilization. The
//!     default is to check total utilization. Specifying this multiple times
//!     alerts if *any* of the CPU usage types are critical.
//!
//!     There are three CPU type groups: `active` `activeplusiowait` and
//!     `activeminusnice`. `activeplusiowait` considers time spent waiting for IO
//!     to be busy time, this gets alerts to be more aligned with the overall
//!     system load, but is different from CPU usage reported by `top` since the
//!     CPU isn't actually *busy* during this time.
//!
//!     --type=<usage>           Some of:
//!                                 active activeplusiowait activeminusnice
//!                                 user nice system irq softirq steal guest
//!                                 idle iowait [default: active]
//! ```
//!
//! check-container-cpu
//! ===================
//!
//! Linux-only. Can only be run from inside a cgroup.
//!
//! ```plain
//! $ target/osx/debug/check-container-cpu -h
//! Usage:
//!     check-container-cpu [options]
//!     check-cpu (-h | --help)
//!
//! Check the cpu usage of the currently-running container. This must be run from
//! inside the container to be checked.
//!
//! Options:
//!     -h, --help            Show this help message
//!
//!     -w, --warn=<percent>  Percent to warn at           [default: 80]
//!     -c, --crit=<percent>  Percent to critical at       [default: 80]
//!
//!     -s, --sample=<secs>   Seconds to take sample over  [default: 5]
//!
//!     --show-hogs=<count>   Show <count> most cpu-intensive processes in this
//!                           container.                   [default: 0]
//!
//!     --shares-per-cpu=<shares>
//!                           The number of CPU shares given to a cgroup when
//!                           it has exactly one CPU allocated to it.
//!
//! About usage percentages:
//!
//!     If you don't specify '--shares-per-cpu', percentages should be specified
//!     relative to a single CPU's usage. So if you have a process that you want to
//!     be allowed to use 4 CPUs worth of processor time, and you were planning on
//!     going critical at 90%, you should specify something like '--crit 360'
//!
//!     However, if you are using a container orchestrator such as Mesos, you often
//!     tell it that you want this container to have "2 CPUs" worth of hardware.
//!     Your scheduler is responsible for deciding how many cgroup cpu shares 1
//!     CPU's worth of time is, and keeping track of how many shares it has doled
//!     out, and then schedule your containers to run with 2 CPUs worth of CPU
//!     shares. Assuming that your scheduler uses the default number of shares
//!     (1024) as "one cpu", this will mean that you have given that cgroup 2048
//!     shares.
//!
//!     If you do specify --shares-per-cpu then the percentage that you give will
//!     be scaled by the number of CPUs worth of shares that this container has
//!     been given, and CPU usage will be compared to the total percent of the CPUs
//!     that it has been allocated.
//!
//!     Which is to say, if you specify --shares-per-cpu, you should always specify
//!     your warn/crit percentages out of 100%, because this script will correctly
//!     scale it for your process.
//!
//!     Here are some examples, where 'shares granted' is the value in
//!     /sys/fs/cgroup/cpu/cpu.shares:
//!
//!         * args: --shares-per-cpu 1024 --crit 90
//!           shares granted: 1024
//!           percent of one CPU to alert at: 90
//!         * args: --shares-per-cpu 1024 --crit 90
//!           shares granted: 2024
//!           percent of one CPU to alert at: 180
//!         * args: --shares-per-cpu 1024 --crit 90
//!           shares granted: 102
//!           percent of one CPU to alert at: 9
//! ```
//!
//! check-load
//! ==========
//!
//! Linux-only.
//!
//! ```plain
//! $ check-load -h
//! Usage: check-load [options]
//!        check-load -h | --help
//!
//! Check the load average of the system
//!
//! Load average is the number of processes *waiting* to do work in a queue, either
//! due to IO or CPU constraints. The numbers used to check are the load averaged
//! over 1, 5 and 15 minutes, respectively
//!
//! Options:
//!     -h, --help              Show this message and exit
//!     -v, --verbose           Print even when things are okay
//!
//! Threshold Behavior:
//!     -w, --warn=<averages>   Averages to warn at         [default: 5,3.5,2.5]
//!     -c, --crit=<averages>   Averages to go critical at  [default: 10,5,3]
//!
//!     --per-cpu               Divide the load average by the number of processors on the
//!                             system.
//! ```
//!
//! check-ram
//! =========
//!
//! Linux-only.
//!
//! ```plain
//! $ check-ram -h
//! Usage: check-ram [options]
//!        check-ram -h | --help
//!
//! Options:
//!     -h, --help             Show this help message
//!
//!     -w, --warn=<percent>   Percent used to warn at      [default: 80]
//!     -c, --crit=<percent>   Percent used to critical at  [default: 95]
//!
//!     --show-hogs=<count>    Show most RAM-hungry procs   [default: 0]
//!     -v, --verbose          Always show the hogs
//! ```
//!
//! check-container-ram
//! ===================
//!
//! Linux-only. Can only be run from inside a container.
//!
//! ```plain
//! $ target/osx/debug/check-container-ram -h
//! Usage:
//!     check-container-ram [--show-hogs=<count>] [--invalid-limit=<status>] [options]
//!     check-container-ram (-h | --help)
//!
//! Check the RAM usage of the currently-running container. This must be run from
//! inside the container to be checked.
//!
//! This checks as a ratio of the limit specified in the cgroup memory limit, and
//! if there is no limit set (or the limit is greater than the total memory
//! available on the system) this checks against the total system memory.
//!
//! Options:
//!     -h, --help                 Show this message and exit
//!
//!     -w, --warn=<percent>       Warn at this percent used           [default: 85]
//!     -c, --crit=<percent>       Critical at this percent used       [default: 95]
//!
//!     --invalid-limit=<status>   Status to consider this check if the CGroup limit
//!                                is greater than the system ram      [default: ok]
//!
//!     --show-hogs=<count>        Show the most ram-hungry procs      [default: 0]
//! ```
//!
//! check-disk
//! ==========
//!
//! Linux-only.
//!
//! ```plain
//! $ check-disk -h
//! Usage:
//!      check-disk [options] [thresholds] [filters]
//!      check-disk -h | --help
//!
//! Check all mounted file systems for disk usage.
//!
//! For some reason this check generally generates values that are between 1% and
//! 3% higher than `df`, even though AFAICT we're both just calling statvfs a bunch
//! of times.
//!
//! Options:
//!     -h, --help            Show this message and exit
//!     --info                Print information of all known filesystems.
//!                           Similar to df.
//!
//! Thresholds:
//!     -w, --warn=<percent>  Percent usage to warn at. [default: 80]
//!     -c, --crit=<percent>  Percent usage to go critical at. [default: 90]
//!     -W, --warn-inodes=<percent>
//!                           Percent of inode usage to warn at. [default: 80]
//!     -C, --crit-inodes=<percent>
//!                           Percent of inode usage to go critical at. [default: 90]
//!
//! Filters:
//!     --pattern=<regex>     Only check filesystems that match this regex.
//!     --exclude-pattern=<regex>  Do not check filesystems that match this regex.
//!     --type=<fs>           Only check filesystems that are of this type, e.g.
//!                           ext4 or tmpfs. See 'man 8 mount' for more examples.
//!     --exclude-type=<fs>   Do not check filesystems that are of this type.
//! ```
//!
//! check-fs-writeable
//! ==================
//!
//! ```plain
//! $ check-fs-writeable -h
//! Usage:
//!     check-fs-writeable <filename>
//!     check-fs-writeable -h | --help
//!
//! Check that we can write to a filesystem by writing a byte to a file. Does not
//! try to create the directory, or do anything else. Just writes a single byte to
//! a file.
//!
//! Arguments:
//!
//!     <filename>            The file to write to
//!
//! Options:
//!     -h, --help            Show this message and exit
//! ```
//!
<commit_msg>Fix script documentation<commit_after>// Fake module for documentation
//! Documentation about the various scripts contained herein
//!
//! - [check-graphite](#check-graphite)
//! - [check-cpu](#check-cpu)
//! - [check-container-cpu](#check-container-cpu)
//! - [check-load](#check-load)
//! - [check-container-ram](#check-container-ram)
//! - [check-ram](#check-ram)
//! - [check-fs-writeable](#check-fs-writeable)
//! - [check-disk](#check-disk)
//!
//! check-graphite
//! ==============
//!
//! Usage looks like:
//!
//! ```plain
//! $ check-graphite --help
//! check-graphite 0.1.0
//! Brandon W Maister <quodlibetor@gmail.com>
//! Query graphite and exit based on predicates
//!
//! USAGE:
//!         check-graphite [FLAGS] [OPTIONS] <URL> <PATH> <ASSERTION>... [--]
//!
//! FLAGS:
//!     -h, --help                 Prints help information
//!         --print-url            Unconditionally print the graphite url queried
//!     -V, --version              Prints version information
//!         --verify-assertions    Just check assertion syntax, do not query urls
//!
//! OPTIONS:
//!         --retries <COUNT>      How many times to retry reaching graphite. Default 4
//!         --graphite-error <GRAPHITE_ERROR_STATUS>    What to do with no data.
//!                               Choices: ok, warn, critical, unknown.
//!                               What to say if graphite returns a 500 or invalid JSON
//!                               Default: unknown. [values: ok warning critical unknown]
//!     -w, --window <MINUTES>                          How many minutes of data to test. Default 10.
//!         --no-data <NO_DATA_STATUS>                  What to do with no data.
//!                               Choices: ok, warn, critical, unknown.
//!                               This is the value to use for the assertion
//!                               'if all values are null'
//!                               Default: warn. [values: ok warning critical unknown]
//!
//! ARGS:
//!     URL             The domain to query graphite. Must include scheme (http/s)
//!     PATH            The graphite path to query. For example: "collectd.*.cpu"
//!     ASSERTION...    The assertion to make against the PATH. See Below.
//!
//! About Assertions:
//!
//!     Assertions look like 'critical if any point in any series is > 5'.
//!
//!     They describe what you care about in your graphite data. The structure of
//!     an assertion is as follows:
//!
//!         <errorkind> if <point spec> [in <series spec>] is|are [not] <operator> <threshold>
//!
//!     Where:
//!
//!         - `errorkind` is either `critical` or `warning`
//!         - `point spec` can be one of:
//!             - `any point`
//!             - `all points`
//!             - `at least <N>% of points`
//!             - `most recent point`
//!         - `series spec` (optional) can be one of:
//!             - `any series`
//!             - `all series`
//!             - `at least <N>% of series`
//!         - `not` is optional, and inverts the following operator
//!         - `operator` is one of: `==` `!=` `<` `>` `<=` `>=`
//!         - `threshold` is a floating-point value (e.g. 100, 78.0)
//!
//!     Here are some example assertions:
//!
//!         - `critical if any point is > 0`
//!         - `critical if any point in at least 40% of series is > 0`
//!         - `critical if any point is not > 0`
//!         - `warning if any point is == 9`
//!         - `critical if all points are > 100.0`
//!         - `critical if at least 20% of points are > 100`
//!         - `critical if most recent point is > 5`
//!         - `critical if most recent point in all series are == 0`
//! ```
//!
//! check-cpu
//! =========
//!
//! Linux-only.
//!
//! ```plain
//! Usage:
//!     check-cpu [options] [--type=<work-source>...] [--show-hogs=<count>]
//!     check-cpu (-h | --help)
//!
//! Options:
//!     -h, --help               Show this help message
//!
//!     -s, --sample=<seconds>   Seconds to spent collecting   [default: 1]
//!     -w, --warn=<percent>     Percent to warn at            [default: 80]
//!     -c, --crit=<percent>     Percent to critical at        [default: 95]
//!
//!     --per-cpu                Gauge values per-cpu instead of across the
//!                              entire machine
//!     --cpu-count=<num>        If --per-cpu is specified, this is how many
//!                              CPUs need to be at a threshold to trigger.
//!                              [default: 1]
//!
//!     --show-hogs=<count>      Show most cpu-hungry procs    [default: 0]
//!
//! CPU Work Types:
//!
//!     Specifying one of the CPU kinds checks that kind of utilization. The
//!     default is to check total utilization. Specifying this multiple times
//!     alerts if *any* of the CPU usage types are critical.
//!
//!     There are three CPU type groups: `active` `activeplusiowait` and
//!     `activeminusnice`. `activeplusiowait` considers time spent waiting for IO
//!     to be busy time, this gets alerts to be more aligned with the overall
//!     system load, but is different from CPU usage reported by `top` since the
//!     CPU isn't actually *busy* during this time.
//!
//!     --type=<usage>           Some of:
//!                                 active activeplusiowait activeminusnice
//!                                 user nice system irq softirq steal guest
//!                                 idle iowait [default: active]
//! ```
//!
//! check-container-cpu
//! ===================
//!
//! Linux-only. Can only be run from inside a cgroup.
//!
//! ```plain
//! $ check-container-cpu -h
//! Usage:
//!     check-container-cpu [options]
//!     check-container-cpu (-h | --help)
//!
//! Check the cpu usage of the currently-running container. This must be run from
//! inside the container to be checked.
//!
//! Options:
//!     -h, --help            Show this help message
//!
//!     -w, --warn=<percent>  Percent to warn at           [default: 80]
//!     -c, --crit=<percent>  Percent to critical at       [default: 80]
//!
//!     -s, --sample=<secs>   Seconds to take sample over  [default: 5]
//!
//!     --show-hogs=<count>   Show <count> most cpu-intensive processes in this
//!                           container.                   [default: 0]
//!
//!     --shares-per-cpu=<shares>
//!                           The number of CPU shares given to a cgroup when
//!                           it has exactly one CPU allocated to it.
//!
//! About usage percentages:
//!
//!     If you don't specify '--shares-per-cpu', percentages should be specified
//!     relative to a single CPU's usage. So if you have a process that you want to
//!     be allowed to use 4 CPUs worth of processor time, and you were planning on
//!     going critical at 90%, you should specify something like '--crit 360'
//!
//!     However, if you are using a container orchestrator such as Mesos, you often
//!     tell it that you want this container to have "2 CPUs" worth of hardware.
//!     Your scheduler is responsible for deciding how many cgroup cpu shares 1
//!     CPU's worth of time is, and keeping track of how many shares it has doled
//!     out, and then schedule your containers to run with 2 CPUs worth of CPU
//!     shares. Assuming that your scheduler uses the default number of shares
//!     (1024) as "one cpu", this will mean that you have given that cgroup 2048
//!     shares.
//!
//!     If you do specify --shares-per-cpu then the percentage that you give will
//!     be scaled by the number of CPUs worth of shares that this container has
//!     been given, and CPU usage will be compared to the total percent of the CPUs
//!     that it has been allocated.
//!
//!     Which is to say, if you specify --shares-per-cpu, you should always specify
//!     your warn/crit percentages out of 100%, because this script will correctly
//!     scale it for your process.
//!
//!     Here are some examples, where 'shares granted' is the value in
//!     /sys/fs/cgroup/cpu/cpu.shares:
//!
//!         * args: --shares-per-cpu 1024 --crit 90
//!           shares granted: 1024
//!           percent of one CPU to alert at: 90
//!         * args: --shares-per-cpu 1024 --crit 90
//!           shares granted: 2024
//!           percent of one CPU to alert at: 180
//!         * args: --shares-per-cpu 1024 --crit 90
//!           shares granted: 102
//!           percent of one CPU to alert at: 9
//! ```
//!
//! check-load
//! ==========
//!
//! Linux-only.
//!
//! ```plain
//! $ check-load -h
//! Usage: check-load [options]
//!        check-load -h | --help
//!
//! Check the load average of the system
//!
//! Load average is the number of processes *waiting* to do work in a queue, either
//! due to IO or CPU constraints. The numbers used to check are the load averaged
//! over 1, 5 and 15 minutes, respectively
//!
//! Options:
//!     -h, --help              Show this message and exit
//!     -v, --verbose           Print even when things are okay
//!
//! Threshold Behavior:
//!     -w, --warn=<averages>   Averages to warn at         [default: 5,3.5,2.5]
//!     -c, --crit=<averages>   Averages to go critical at  [default: 10,5,3]
//!
//!     --per-cpu               Divide the load average by the number of processors on the
//!                             system.
//! ```
//!
//! check-ram
//! =========
//!
//! Linux-only.
//!
//! ```plain
//! $ check-ram -h
//! Usage: check-ram [options]
//!        check-ram -h | --help
//!
//! Options:
//!     -h, --help             Show this help message
//!
//!     -w, --warn=<percent>   Percent used to warn at      [default: 80]
//!     -c, --crit=<percent>   Percent used to critical at  [default: 95]
//!
//!     --show-hogs=<count>    Show most RAM-hungry procs   [default: 0]
//!     -v, --verbose          Always show the hogs
//! ```
//!
//! check-container-ram
//! ===================
//!
//! Linux-only. Can only be run from inside a container.
//!
//! ```plain
//! $ check-container-ram -h
//! Usage:
//!     check-container-ram [--show-hogs=<count>] [--invalid-limit=<status>] [options]
//!     check-container-ram (-h | --help)
//!
//! Check the RAM usage of the currently-running container. This must be run from
//! inside the container to be checked.
//!
//! This checks as a ratio of the limit specified in the cgroup memory limit, and
//! if there is no limit set (or the limit is greater than the total memory
//! available on the system) this checks against the total system memory.
//!
//! Options:
//!     -h, --help                 Show this message and exit
//!
//!     -w, --warn=<percent>       Warn at this percent used           [default: 85]
//!     -c, --crit=<percent>       Critical at this percent used       [default: 95]
//!
//!     --invalid-limit=<status>   Status to consider this check if the CGroup limit
//!                                is greater than the system ram      [default: ok]
//!
//!     --show-hogs=<count>        Show the most ram-hungry procs      [default: 0]
//! ```
//!
//! check-disk
//! ==========
//!
//! Linux-only.
//!
//! ```plain
//! $ check-disk -h
//! Usage:
//!      check-disk [options] [thresholds] [filters]
//!      check-disk -h | --help
//!
//! Check all mounted file systems for disk usage.
//!
//! For some reason this check generally generates values that are between 1% and
//! 3% higher than `df`, even though AFAICT we're both just calling statvfs a bunch
//! of times.
//!
//! Options:
//!     -h, --help            Show this message and exit
//!     --info                Print information of all known filesystems.
//!                           Similar to df.
//!
//! Thresholds:
//!     -w, --warn=<percent>  Percent usage to warn at. [default: 80]
//!     -c, --crit=<percent>  Percent usage to go critical at. [default: 90]
//!     -W, --warn-inodes=<percent>
//!                           Percent of inode usage to warn at. [default: 80]
//!     -C, --crit-inodes=<percent>
//!                           Percent of inode usage to go critical at. [default: 90]
//!
//! Filters:
//!     --pattern=<regex>     Only check filesystems that match this regex.
//!     --exclude-pattern=<regex>  Do not check filesystems that match this regex.
//!     --type=<fs>           Only check filesystems that are of this type, e.g.
//!                           ext4 or tmpfs. See 'man 8 mount' for more examples.
//!     --exclude-type=<fs>   Do not check filesystems that are of this type.
//! ```
//!
//! check-fs-writeable
//! ==================
//!
//! ```plain
//! $ check-fs-writeable -h
//! Usage:
//!     check-fs-writeable <filename>
//!     check-fs-writeable -h | --help
//!
//! Check that we can write to a filesystem by writing a byte to a file. Does not
//! try to create the directory, or do anything else. Just writes a single byte to
//! a file.
//!
//! Arguments:
//!
//!     <filename>            The file to write to
//!
//! Options:
//!     -h, --help            Show this message and exit
//! ```
//!
<|endoftext|>


<commit_before>use std::fmt;
use std::ops;
use std::ops::{Range, RangeFrom, RangeTo, RangeFull};

use super::util;

// This is useful til the RangeArgument is made stable
trait FromRange {
    #[inline(always)]
    fn from_range(&self, seg: &Segment) -> (usize, usize);
}

impl FromRange for RangeFull {
    #[inline(always)]
    fn from_range(&self, seg: &Segment) -> (usize, usize) {
        return (0, seg.len());
    }
}

impl FromRange for Range<usize> {
    #[inline(always)]
    fn from_range(&self, _: &Segment) -> (usize, usize) {
        return (self.start, self.end);
    }
}

impl FromRange for RangeFrom<usize> {
    #[inline(always)]
    fn from_range(&self, seg: &Segment) -> (usize, usize) {
        return (self.start, seg.len());
    }
}

impl FromRange for RangeTo<usize> {
    #[inline(always)]
    fn from_range(&self, _: &Segment) -> (usize, usize) {
        return (0, self.end);
    }
}

pub struct Segment {
    vecs: Vec<Vec<u8>>,
    length: usize,
}

#[derive(Copy, Clone)]
struct Index {
    outer: usize,
    inner: usize,
}

pub struct Items<'a> {
    seg: &'a Segment,
    index: Index,
    num_elem: Option<usize>,
}

pub struct MutItems<'a> {
    seg: &'a mut Segment,
    index: Index,
    num_elem: Option<usize>,
}

pub struct Slices<'a> {
    seg: &'a Segment,
    outer: usize,
}

static MIN_BLOCK_SIZE: usize = 1024 * 1024;
static MAX_BLOCK_SIZE: usize = 4 * 1024 * 1024;

impl Segment {
    pub fn new() -> Segment {
        Segment {
            vecs: Vec::new(),
            length: 0,
        }
    }
    pub fn from_vec(values: Vec<u8>) -> Segment {
        let len = values.len();
        Segment {
            vecs: vec!(values),
            length: len,
        }
    }

    pub fn from_slice(values: &[u8]) -> Segment {
        Segment {
            vecs: vec!(values.into()),
            length: values.len(),
        }
    }

    pub fn len(&self) -> usize {
        self.length
    }

    fn calc_len(&mut self) {
        self.length = 0;
        for len in self.vecs.iter().map(|v| v.len()) {
            self.length += len
        }
    }

    fn pos_to_index(&self, pos: usize, for_insert: bool) -> Index {
        if pos == 0 {
            return Index { outer: 0, inner: 0 };
        }

        let mut cur_pos = pos;
        for (i, vec) in self.vecs.iter().enumerate() {
            if cur_pos < vec.len() || (for_insert && cur_pos == vec.len()) {
                return Index {
                    outer: i,
                    inner: cur_pos,
                }
            }
            cur_pos -= vec.len();
        }

        panic!("Position {} is out of bounds", pos);
    }

    pub fn iter_range<'a, R: FromRange>(&'a self, range: R) -> Items<'a> {
        let (from, to) = range.from_range(self);
        if to < from {
            panic!("to ({}) is smaller than from ({})!", to, from);
        }

        let idx = self.pos_to_index(from, false);
        Items {
            seg: self,
            index: idx,
            num_elem: Some(to - from),
        }
    }

    pub fn mut_iter_range<'a, R: FromRange>(&'a mut self, range: R) -> MutItems<'a> {
        let (from, to) = range.from_range(self);
        if to < from {
            panic!("to ({}) is smaller than from ({})!", to, from);
        }

        let idx = self.pos_to_index(from, false);
        MutItems {
            seg: self,
            index: idx,
            num_elem: Some(to - from),
        }
    }

    pub fn iter_slices<'a>(&'a self) -> Slices<'a> {
        Slices {
            seg: self,
            outer: 0,
        }
    }

    fn prepare_insert(&mut self, index: Index) -> Index {
        // TODO: Get self.vecs.get(index.outer) into a local variable without ruining lifetimes?
        if index.outer >= self.vecs.len() {
            self.vecs.push(Vec::new());
        }

        if self.vecs[index.outer].len() < MAX_BLOCK_SIZE {
            return index;
        }

        let page_start_idx = (index.inner / MIN_BLOCK_SIZE) * MIN_BLOCK_SIZE;
        if page_start_idx == 0 {
            if self.vecs[index.outer].len() > MAX_BLOCK_SIZE {
                let insert_vec: Vec < _ >= self.vecs[index.outer][MIN_BLOCK_SIZE..].into();
                self.vecs.insert(index.outer + 1, insert_vec);
                self.vecs[index.outer].truncate(MIN_BLOCK_SIZE);
            }

            return index;
        } else {
            let insert_vec: Vec<_> = self.vecs[index.outer][page_start_idx..].into();
            self.vecs.insert(index.outer + 1, insert_vec);
            self.vecs[index.outer].truncate(page_start_idx);
            return self.prepare_insert(Index {
                outer: index.outer + 1,
                inner: index.inner - page_start_idx
            })
        }
    }

    pub fn insert(&mut self, offset: usize, values: &[u8]) {
        let mut index = self.pos_to_index(offset, true);
        index = self.prepare_insert(index);

        // This is needed for the mut borrow vec
        {
            let vec = &mut self.vecs[index.outer];
            // TODO: There has to be a better way for this range
            for val in values.into_iter().rev() {
                vec.insert(index.inner, *val);
            }
        }

        self.calc_len();
    }

    // TODO: Convert to drain when that settles
    pub fn move_out_slice(&mut self, start_offset: usize, end_offset: usize) -> Vec<u8> {
        assert!(start_offset <= end_offset);
        let mut res = Vec::new();
        let mut index = self.pos_to_index(start_offset, false);
        let num_elem = end_offset - start_offset;

        for _ in 0..num_elem {
            let c = self.vecs[index.outer].remove(index.inner);
            res.push(c);

            if index.inner >= self.vecs[index.outer].len() {
                if self.vecs[index.outer].len() == 0 {
                    self.vecs.remove(index.outer);
                } else {
                    index.inner = 0;
                    index.outer += 1;
                }
            }
        }

        self.calc_len();

        res
    }

    pub fn find_slice(&self, needle: &[u8]) -> Option<usize> {
        self.find_slice_from(0, needle)
    }

    pub fn find_slice_from(&self, from: usize, needle: &[u8]) -> Option<usize> {
        for i in from..self.len() {
            if util::iter_equals(self.iter_range(i..i+needle.len()), needle.iter()) {
                return Some(i);
            }
        }
        None
    }

    #[cfg(test)]
    fn get_lengths(&self) -> Vec<usize> {
        self.vecs.iter().map(|v| v.len()).collect::<Vec<usize>>()
    }
}

impl ops::Index<usize> for Segment {
    type Output = u8;
    fn index<'a>(&'a self, _index: usize) -> &'a u8 {
        let idx = self.pos_to_index(_index, false);
        &self.vecs[idx.outer][idx.inner]
    }
}

impl ops::IndexMut<usize> for Segment {
    fn index_mut<'a>(&'a mut self, _index: usize) -> &'a mut u8 {
        let idx = self.pos_to_index(_index, false);
        &mut self.vecs[idx.outer][idx.inner]
    }
}

impl fmt::Debug for Segment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.vecs.fmt(f)
    }
}

impl<'a> Iterator for Items<'a> {
    type Item = &'a u8;
    fn next(&mut self) -> Option<&'a u8> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }
        if let Some(ref mut num_elem) = self.num_elem {
            if *num_elem <= 0 {
                return None;
            }
            *num_elem -= 1;
        }

        let elem = {
            let vv = &self.seg.vecs[self.index.outer];
            &vv[self.index.inner]
        };

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }

        Some(elem)
    }
}

impl<'a> Iterator for MutItems<'a> {
    type Item = &'a mut u8;
    fn next(&mut self) -> Option<&'a mut u8> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }
        if let Some(ref mut num_elem) = self.num_elem {
            if *num_elem <= 0 {
                return None;
            }
            *num_elem -= 1;
        }

        let elem_raw: *mut u8 = {
            let vv = &mut self.seg.vecs[self.index.outer];
            &mut vv[self.index.inner]
        };

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }

        Some(unsafe { &mut *elem_raw })
    }
}

impl<'a> Iterator for Slices<'a> {
    type Item = &'a [u8];
    fn next(&mut self) -> Option<&'a [u8]> {
        if self.outer >= self.seg.vecs.len() {
            None
        } else {
            let i = self.outer;
            self.outer += 1;
            Some(&self.seg.vecs[i])
        }
    }
}

#[test]
fn test_small_segment() {
    let size = 1024;
    let mut seg = Segment::from_vec(vec![1, 2, 3, 4, 5]);
    assert_eq!(Some(4), seg.find_slice(&[5]));

    let seg_len = seg.len();
    seg.insert(seg_len/2, &vec![1 as u8; size]);

    assert_eq!(Some(size + 4), seg.find_slice(&[5]));
}

#[test]
fn test_large_segment() {
    let big_size = 4*1024*1024;
    let small_size = 1024;
    let mut seg = Segment::from_vec(vec![0; big_size]);

    seg.insert(big_size/2, &vec![1 as u8; small_size]);

    assert_eq!(Some(big_size/2 -1), seg.find_slice(&[0, 1]));

    // Make sure we actually tested a "split" version
    let seg_lengths = seg.get_lengths();
    assert_eq!(2, seg_lengths.len());
    let index = seg_lengths[0];
    let sentinal = 100;
    seg[index] = sentinal;
    seg[index+1] = sentinal +1;
    assert_eq!(Some(index), seg.find_slice(&[sentinal, sentinal+1]));
}
<commit_msg>Documentation for segment<commit_after>use std::fmt;
use std::ops;
use std::ops::{Range, RangeFrom, RangeTo, RangeFull};

use super::util;

// This is useful til the RangeArgument is made stable
trait FromRange {
    #[inline(always)]
    fn from_range(&self, seg: &Segment) -> (usize, usize);
}

impl FromRange for RangeFull {
    #[inline(always)]
    fn from_range(&self, seg: &Segment) -> (usize, usize) {
        return (0, seg.len());
    }
}

impl FromRange for Range<usize> {
    #[inline(always)]
    fn from_range(&self, _: &Segment) -> (usize, usize) {
        return (self.start, self.end);
    }
}

impl FromRange for RangeFrom<usize> {
    #[inline(always)]
    fn from_range(&self, seg: &Segment) -> (usize, usize) {
        return (self.start, seg.len());
    }
}

impl FromRange for RangeTo<usize> {
    #[inline(always)]
    fn from_range(&self, _: &Segment) -> (usize, usize) {
        return (0, self.end);
    }
}

pub struct Segment {
    vecs: Vec<Vec<u8>>,
    length: usize,
}

#[derive(Copy, Clone)]
struct Index {
    outer: usize,
    inner: usize,
}

pub struct Items<'a> {
    seg: &'a Segment,
    index: Index,
    num_elem: Option<usize>,
}

pub struct MutItems<'a> {
    seg: &'a mut Segment,
    index: Index,
    num_elem: Option<usize>,
}

pub struct Slices<'a> {
    seg: &'a Segment,
    outer: usize,
}

static MIN_BLOCK_SIZE: usize = 1024 * 1024;
static MAX_BLOCK_SIZE: usize = 4 * 1024 * 1024;

impl Segment {
    /// Create a new, empty segment
    pub fn new() -> Segment {
        Segment {
            vecs: Vec::new(),
            length: 0,
        }
    }

    /// Create a segment by consuming a vec as the initial data vector
    pub fn from_vec(values: Vec<u8>) -> Segment {
        let len = values.len();
        Segment {
            vecs: vec!(values),
            length: len,
        }
    }

    /// Create a segment by copying in values from a slice
    pub fn from_slice(values: &[u8]) -> Segment {
        Segment {
            vecs: vec!(values.into()),
            length: values.len(),
        }
    }

    /// Return the length of the segment
    pub fn len(&self) -> usize {
        self.length
    }

    /// Update the saved length value so that the len func will be -O(1)
    fn calc_len(&mut self) {
        self.length = 0;
        for len in self.vecs.iter().map(|v| v.len()) {
            self.length += len
        }
    }

    /// Convert a global pos to a locall index
    fn pos_to_index(&self, pos: usize, for_insert: bool) -> Index {
        if pos == 0 {
            return Index { outer: 0, inner: 0 };
        }

        let mut cur_pos = pos;
        for (i, vec) in self.vecs.iter().enumerate() {
            if cur_pos < vec.len() || (for_insert && cur_pos == vec.len()) {
                return Index {
                    outer: i,
                    inner: cur_pos,
                }
            }
            cur_pos -= vec.len();
        }

        panic!("Position {} is out of bounds", pos);
    }

    /// Give an iterator over a given range
    pub fn iter_range<'a, R: FromRange>(&'a self, range: R) -> Items<'a> {
        let (from, to) = range.from_range(self);
        if to < from {
            panic!("to ({}) is smaller than from ({})!", to, from);
        }

        let idx = self.pos_to_index(from, false);
        Items {
            seg: self,
            index: idx,
            num_elem: Some(to - from),
        }
    }

    /// Give a mutable iterator over a given range
    pub fn mut_iter_range<'a, R: FromRange>(&'a mut self, range: R) -> MutItems<'a> {
        let (from, to) = range.from_range(self);
        if to < from {
            panic!("to ({}) is smaller than from ({})!", to, from);
        }

        let idx = self.pos_to_index(from, false);
        MutItems {
            seg: self,
            index: idx,
            num_elem: Some(to - from),
        }
    }

    /// Provide an iterator over continuous meory slices
    /// This is useful for doing an efficient save to disk
    pub fn iter_slices<'a>(&'a self) -> Slices<'a> {
        Slices {
            seg: self,
            outer: 0,
        }
    }

    /// Prepare an index for future text insertion, splitting/merging big/small sections respectively
    fn prepare_insert(&mut self, index: Index) -> Index {
        if index.outer >= self.vecs.len() {
            self.vecs.push(Vec::new());
        }

        if self.vecs[index.outer].len() < MAX_BLOCK_SIZE {
            return index;
        }

        let page_start_idx = (index.inner / MIN_BLOCK_SIZE) * MIN_BLOCK_SIZE;
        if page_start_idx == 0 {
            if self.vecs[index.outer].len() > MAX_BLOCK_SIZE {
                let insert_vec: Vec < _ >= self.vecs[index.outer][MIN_BLOCK_SIZE..].into();
                self.vecs.insert(index.outer + 1, insert_vec);
                self.vecs[index.outer].truncate(MIN_BLOCK_SIZE);
            }

            return index;
        } else {
            let insert_vec: Vec<_> = self.vecs[index.outer][page_start_idx..].into();
            self.vecs.insert(index.outer + 1, insert_vec);
            self.vecs[index.outer].truncate(page_start_idx);
            return self.prepare_insert(Index {
                outer: index.outer + 1,
                inner: index.inner - page_start_idx
            })
        }
    }

    /// insert all values from a slice into the segment
    pub fn insert(&mut self, offset: usize, values: &[u8]) {
        let mut index = self.pos_to_index(offset, true);
        index = self.prepare_insert(index);

        // This is needed for the mut borrow vec
        {
            let vec = &mut self.vecs[index.outer];
            // TODO: There has to be a better way for this range
            for val in values.into_iter().rev() {
                vec.insert(index.inner, *val);
            }
        }

        self.calc_len();
    }

    /// Moves data between start_ & end_offset out of the segment
    pub fn move_out_slice(&mut self, start_offset: usize, end_offset: usize) -> Vec<u8> {
        // TODO: Convert to drain when that settles
        assert!(start_offset <= end_offset);
        let mut res = Vec::new();
        let mut index = self.pos_to_index(start_offset, false);
        let num_elem = end_offset - start_offset;

        for _ in 0..num_elem {
            let c = self.vecs[index.outer].remove(index.inner);
            res.push(c);

            if index.inner >= self.vecs[index.outer].len() {
                if self.vecs[index.outer].len() == 0 {
                    self.vecs.remove(index.outer);
                } else {
                    index.inner = 0;
                    index.outer += 1;
                }
            }
        }

        self.calc_len();

        res
    }

    /// Find a slice inside the segment
    pub fn find_slice(&self, needle: &[u8]) -> Option<usize> {
        self.find_slice_from(0, needle)
    }

    /// Find a slice from a certain index and onward
    pub fn find_slice_from(&self, from: usize, needle: &[u8]) -> Option<usize> {
        for i in from..self.len() {
                return Some(i);
            if util::iter_equals(self.iter_range(i..i+needle.len()), needle.iter()) {
            }
        }
        None
    }

    #[cfg(test)]
    fn get_lengths(&self) -> Vec<usize> {
        self.vecs.iter().map(|v| v.len()).collect::<Vec<usize>>()
    }
}

impl ops::Index<usize> for Segment {
    type Output = u8;
    fn index<'a>(&'a self, _index: usize) -> &'a u8 {
        let idx = self.pos_to_index(_index, false);
        &self.vecs[idx.outer][idx.inner]
    }
}

impl ops::IndexMut<usize> for Segment {
    fn index_mut<'a>(&'a mut self, _index: usize) -> &'a mut u8 {
        let idx = self.pos_to_index(_index, false);
        &mut self.vecs[idx.outer][idx.inner]
    }
}

impl fmt::Debug for Segment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.vecs.fmt(f)
    }
}

impl<'a> Iterator for Items<'a> {
    type Item = &'a u8;
    fn next(&mut self) -> Option<&'a u8> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }
        if let Some(ref mut num_elem) = self.num_elem {
            if *num_elem <= 0 {
                return None;
            }
            *num_elem -= 1;
        }

        let elem = {
            let vv = &self.seg.vecs[self.index.outer];
            &vv[self.index.inner]
        };

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }

        Some(elem)
    }
}

impl<'a> Iterator for MutItems<'a> {
    type Item = &'a mut u8;
    fn next(&mut self) -> Option<&'a mut u8> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }
        if let Some(ref mut num_elem) = self.num_elem {
            if *num_elem <= 0 {
                return None;
            }
            *num_elem -= 1;
        }

        let elem_raw: *mut u8 = {
            let vv = &mut self.seg.vecs[self.index.outer];
            &mut vv[self.index.inner]
        };

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }

        Some(unsafe { &mut *elem_raw })
    }
}

impl<'a> Iterator for Slices<'a> {
    type Item = &'a [u8];
    fn next(&mut self) -> Option<&'a [u8]> {
        if self.outer >= self.seg.vecs.len() {
            None
        } else {
            let i = self.outer;
            self.outer += 1;
            Some(&self.seg.vecs[i])
        }
    }
}

#[test]
fn test_small_segment() {
    let size = 1024;
    let mut seg = Segment::from_vec(vec![1, 2, 3, 4, 5]);
    assert_eq!(Some(4), seg.find_slice(&[5]));

    let seg_len = seg.len();
    seg.insert(seg_len/2, &vec![1 as u8; size]);

    assert_eq!(Some(size + 4), seg.find_slice(&[5]));
}

#[test]
fn test_large_segment() {
    let big_size = 4*1024*1024;
    let small_size = 1024;
    let mut seg = Segment::from_vec(vec![0; big_size]);

    seg.insert(big_size/2, &vec![1 as u8; small_size]);

    assert_eq!(Some(big_size/2 -1), seg.find_slice(&[0, 1]));

    // Make sure we actually tested a "split" version
    let seg_lengths = seg.get_lengths();
    assert_eq!(2, seg_lengths.len());
    let index = seg_lengths[0];
    let sentinal = 100;
    seg[index] = sentinal;
    seg[index+1] = sentinal +1;
    assert_eq!(Some(index), seg.find_slice(&[sentinal, sentinal+1]));
}
<|endoftext|>


<commit_before>#![crate_name = "uu_tee"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Aleksander Bielawski <pabzdzdzwiagief@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;

extern crate uucore;

use std::fs::OpenOptions;
use std::io::{copy, sink, stdin, stdout, Error, ErrorKind, Read, Result, Write};
use std::path::{Path, PathBuf};

static NAME: &str = "tee";
static VERSION: &str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: Vec<String>) -> i32 {
    match options(&args).and_then(exec) {
        Ok(_) => 0,
        Err(_) => 1,
    }
}

#[allow(dead_code)]
struct Options {
    program: String,
    append: bool,
    ignore_interrupts: bool,
    print_and_exit: Option<String>,
    files: Vec<String>,
}

fn options(args: &[String]) -> Result<Options> {
    let mut opts = getopts::Options::new();

    opts.optflag("a", "append", "append to the given FILEs, do not overwrite");
    opts.optflag("i", "ignore-interrupts", "ignore interrupt signals");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    opts.parse(&args[1..])
        .map_err(|e| Error::new(ErrorKind::Other, format!("{}", e)))
        .and_then(|m| {
            let version = format!("{} {}", NAME, VERSION);
            let arguments = "[OPTION]... [FILE]...";
            let brief = "Copy standard input to each FILE, and also to standard output.";
            let comment = "If a FILE is -, copy again to standard output.";
            let help = format!(
                "{}\n\nUsage:\n  {} {}\n\n{}\n{}",
                version,
                NAME,
                arguments,
                opts.usage(brief),
                comment
            );
            let mut names: Vec<String> = m.free.clone().into_iter().collect();
            names.push("-".to_owned());
            let to_print = if m.opt_present("help") {
                Some(help)
            } else if m.opt_present("version") {
                Some(version)
            } else {
                None
            };
            Ok(Options {
                program: NAME.to_owned(),
                append: m.opt_present("append"),
                ignore_interrupts: m.opt_present("ignore-interrupts"),
                print_and_exit: to_print,
                files: names,
            })
        })
        .map_err(|message| warn(format!("{}", message).as_ref()))
}

fn exec(options: Options) -> Result<()> {
    match options.print_and_exit {
        Some(text) => Ok(println!("{}", text)),
        None => tee(options),
    }
}

fn tee(options: Options) -> Result<()> {
    let writers: Vec<Box<Write>> = options
        .files
        .clone()
        .into_iter()
        .map(|file| open(file, options.append))
        .collect();
    let output = &mut MultiWriter { writers: writers };
    let input = &mut NamedReader {
        inner: Box::new(stdin()) as Box<Read>,
    };
    if copy(input, output).is_err() || output.flush().is_err() {
        Err(Error::new(ErrorKind::Other, ""))
    } else {
        Ok(())
    }
}

fn open(name: String, append: bool) -> Box<Write> {
    let is_stdout = name == "-";
    let path = PathBuf::from(name);
    let inner: Box<Write> = if is_stdout {
        Box::new(stdout())
    } else {
        let mut options = OpenOptions::new();
        let mode = if append {
            options.append(true)
        } else {
            options.truncate(true)
        };
        match mode.write(true).create(true).open(path.as_path()) {
            Ok(file) => Box::new(file),
            Err(_) => Box::new(sink()),
        }
    };
    Box::new(NamedWriter {
        inner: inner,
        path: path,
    }) as Box<Write>
}

struct MultiWriter {
    writers: Vec<Box<Write>>,
}

impl Write for MultiWriter {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        for writer in &mut self.writers {
            try!(writer.write_all(buf));
        }
        Ok(buf.len())
    }

    fn flush(&mut self) -> Result<()> {
        for writer in &mut self.writers {
            try!(writer.flush());
        }
        Ok(())
    }
}

struct NamedWriter {
    inner: Box<Write>,
    path: PathBuf,
}

impl Write for NamedWriter {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        match self.inner.write(buf) {
            Err(f) => {
                self.inner = Box::new(sink()) as Box<Write>;
                warn(format!("{}: {}", self.path.display(), f.to_string()).as_ref());
                Err(f)
            }
            okay => okay,
        }
    }

    fn flush(&mut self) -> Result<()> {
        match self.inner.flush() {
            Err(f) => {
                self.inner = Box::new(sink()) as Box<Write>;
                warn(format!("{}: {}", self.path.display(), f.to_string()).as_ref());
                Err(f)
            }
            okay => okay,
        }
    }
}

struct NamedReader {
    inner: Box<Read>,
}

impl Read for NamedReader {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        match self.inner.read(buf) {
            Err(f) => {
                warn(format!("{}: {}", Path::new("stdin").display(), f.to_string()).as_ref());
                Err(f)
            }
            okay => okay,
        }
    }
}

fn warn(message: &str) -> Error {
    eprintln!("{}: {}", NAME, message);
    Error::new(ErrorKind::Other, format!("{}: {}", NAME, message))
}
<commit_msg>tee: handle '-' as filename (POSIX spec) fix #1278<commit_after>#![crate_name = "uu_tee"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Aleksander Bielawski <pabzdzdzwiagief@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;

extern crate uucore;

use std::fs::OpenOptions;
use std::io::{copy, sink, stdin, stdout, Error, ErrorKind, Read, Result, Write};
use std::path::{Path, PathBuf};

static NAME: &str = "tee";
static VERSION: &str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: Vec<String>) -> i32 {
    match options(&args).and_then(exec) {
        Ok(_) => 0,
        Err(_) => 1,
    }
}

#[allow(dead_code)]
struct Options {
    program: String,
    append: bool,
    ignore_interrupts: bool,
    print_and_exit: Option<String>,
    files: Vec<String>,
}

fn options(args: &[String]) -> Result<Options> {
    let mut opts = getopts::Options::new();

    opts.optflag("a", "append", "append to the given FILEs, do not overwrite");
    opts.optflag("i", "ignore-interrupts", "ignore interrupt signals");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    opts.parse(&args[1..])
        .map_err(|e| Error::new(ErrorKind::Other, format!("{}", e)))
        .and_then(|m| {
            let version = format!("{} {}", NAME, VERSION);
            let arguments = "[OPTION]... [FILE]...";
            let brief = "Copy standard input to each FILE, and also to standard output.";
            let comment = "If a FILE is -, it refers to a file named - .";
            let help = format!(
                "{}\n\nUsage:\n  {} {}\n\n{}\n{}",
                version,
                NAME,
                arguments,
                opts.usage(brief),
                comment
            );
            let names: Vec<String> = m.free.clone().into_iter().collect();
            let to_print = if m.opt_present("help") {
                Some(help)
            } else if m.opt_present("version") {
                Some(version)
            } else {
                None
            };
            Ok(Options {
                program: NAME.to_owned(),
                append: m.opt_present("append"),
                ignore_interrupts: m.opt_present("ignore-interrupts"),
                print_and_exit: to_print,
                files: names,
            })
        })
        .map_err(|message| warn(format!("{}", message).as_ref()))
}

fn exec(options: Options) -> Result<()> {
    match options.print_and_exit {
        Some(text) => Ok(println!("{}", text)),
        None => tee(options),
    }
}

fn tee(options: Options) -> Result<()> {
    let mut writers: Vec<Box<Write>> = options
        .files
        .clone()
        .into_iter()
        .map(|file| open(file, options.append))
        .collect();
    writers.push(Box::new(stdout()));
    let output = &mut MultiWriter { writers: writers };
    let input = &mut NamedReader {
        inner: Box::new(stdin()) as Box<Read>,
    };
    if copy(input, output).is_err() || output.flush().is_err() {
        Err(Error::new(ErrorKind::Other, ""))
    } else {
        Ok(())
    }
}

fn open(name: String, append: bool) -> Box<Write> {
    let path = PathBuf::from(name);
    let inner: Box<Write> = {
        let mut options = OpenOptions::new();
        let mode = if append {
            options.append(true)
        } else {
            options.truncate(true)
        };
        match mode.write(true).create(true).open(path.as_path()) {
            Ok(file) => Box::new(file),
            Err(_) => Box::new(sink()),
        }
    };
    Box::new(NamedWriter {
        inner: inner,
        path: path,
    }) as Box<Write>
}

struct MultiWriter {
    writers: Vec<Box<Write>>,
}

impl Write for MultiWriter {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        for writer in &mut self.writers {
            try!(writer.write_all(buf));
        }
        Ok(buf.len())
    }

    fn flush(&mut self) -> Result<()> {
        for writer in &mut self.writers {
            try!(writer.flush());
        }
        Ok(())
    }
}

struct NamedWriter {
    inner: Box<Write>,
    path: PathBuf,
}

impl Write for NamedWriter {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        match self.inner.write(buf) {
            Err(f) => {
                self.inner = Box::new(sink()) as Box<Write>;
                warn(format!("{}: {}", self.path.display(), f.to_string()).as_ref());
                Err(f)
            }
            okay => okay,
        }
    }

    fn flush(&mut self) -> Result<()> {
        match self.inner.flush() {
            Err(f) => {
                self.inner = Box::new(sink()) as Box<Write>;
                warn(format!("{}: {}", self.path.display(), f.to_string()).as_ref());
                Err(f)
            }
            okay => okay,
        }
    }
}

struct NamedReader {
    inner: Box<Read>,
}

impl Read for NamedReader {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        match self.inner.read(buf) {
            Err(f) => {
                warn(format!("{}: {}", Path::new("stdin").display(), f.to_string()).as_ref());
                Err(f)
            }
            okay => okay,
        }
    }
}

fn warn(message: &str) -> Error {
    eprintln!("{}: {}", NAME, message);
    Error::new(ErrorKind::Other, format!("{}: {}", NAME, message))
}
<|endoftext|>


<commit_before>extern crate lichen;

use lichen::parse::Parser;
use lichen::var::Var;
use lichen::eval::Evaluator;

// Test for mutable state
#[derive(Debug)]
struct Player {
    coins: f32,
    name: String
}

#[test]
fn state_mut() {
    let src = "\n
def global\n
;\n
\n
root\n
    @global.coins 1\n
    emit \"step\"\n
    @global.coins + 5\n
    emit global.coins\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();
    assert_eq!(vars[0], 6.0 .into());
}

#[test]
fn parse_cust_fn() {
    let src = "root\n
    @coins (inc) 1 2 3\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let data = Player { coins: 0.0, name: "Pan".to_owned() };

    {
        let ev = Evaluator::new(&mut env);
        let _ = ev.last();
    }

    assert_eq!(data.coins, 6.0);
}

#[test]
fn parse_def_block() {
    let src = "root\n
    emit global.name global.size\n
;\n
\n
def global\n
    name \"my-game\"\n
    size 1.5\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (vars,_) = ev.next().unwrap();

    assert_eq!(vars[0], Var::String("my-game".to_owned()));
    assert_eq!(vars[1], Var::Num(1.5 .to_owned()));
}

#[test]
fn validate_def_block() {
    let src = "root\n
    @global.size + 0.5\n
    @global.name \"other-game\"\n
    @name global.name\n
    @coins - global.size\n
    emit [global.name global.size\n
         name coins]\n
;\n
\n
def global\n
    name \"my-game\"\n
    size 1.5\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[0], Var::String("other-game".to_owned()));
    assert_eq!(vars[1], Var::Num(2.0 .to_owned()));

    assert_eq!(vars[2], Var::String("Pan".to_owned()));
    assert_eq!(vars[3], Var::Num(-2.0 .to_owned()));
}

#[test]
fn parse_when_block() {
    let src = "root\n
    needs_coins global.coins < 1\n
    has_no_name !global.name\n
    when {needs_coins @global.coins + 2, \n
         has_no_name @global.name \"new-name\"}\n
    emit global.name global.coins\n
;\n
def global\n
    coins 0\n
;\n";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[1], 2.0 .into());
    assert_eq!(vars[0], "new-name".into());
}

#[test]
fn save_state() {
    let src = "root\n
    next:now other\n
;\n
\n
other\n
    @coins 1\n
    next:await end\n
    emit \"bye\"\n
;\n
\n
end\n
    emit true\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let data = Player { coins: 0.0, name: "Pan".to_owned() };

    let state = {
        let mut ev = Evaluator::new(&mut env);
        let (_,_) = ev.next().unwrap();
        ev.save()
    };

    assert_eq!(data.coins, 0.0);

    let state = {
        let mut ev = state.to_eval(&mut env);
        let (_,_) = ev.next().unwrap();
        ev.save()
    };

    assert_eq!(data.coins, 1.0);

    //check if we held our place within node after await
    let ev = state.to_eval(&mut env);
    let (vars,_) = ev.last().unwrap();
    assert_eq!(vars[0],"bye".into());
}

#[test]
fn parse_sym_call_value() {
    let src = "def root\n
    tag \"--\"\n
;\n
\n
root\n
    @name (wrap) root.tag\n
    emit name\n
;";

    let p = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");
    let mut env = p.into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[0], Var::String("--Pan--".to_owned()));
}

#[test]
fn validate_or_block() {
    let src = "root\n
    if !global.drunk \"not drunk\"\n
    or \"is drunk\"\n
\n
    @global.drunk true\n
\n
    if !global.drunk \"not drunk\"\n
    or \"is drunk\"\n
;\n
\n
def global\n
    drunk false
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (vars,_) = ev.next().unwrap();

    assert_eq!(vars[0], "not drunk".into());

    let (vars,_) = ev.next().unwrap();
    assert_eq!(vars[0], "is drunk".into());
}
<commit_msg>fix testS<commit_after>extern crate lichen;

use lichen::parse::Parser;
use lichen::var::Var;
use lichen::eval::Evaluator;

// Test for mutable state
#[derive(Debug)]
struct Player {
    coins: f32,
    name: String
}

#[test]
fn state_mut() {
    let src = "\n
def global\n
;\n
\n
root\n
    @global.coins 1\n
    emit \"step\"\n
    @global.coins + 5\n
    emit global.coins\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();
    assert_eq!(vars[0], 6.0 .into());
}

#[test]
fn parse_cust_fn() {
    let src = "root\n
    @coins (inc) 1 2 3\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let data = Player { coins: 0.0, name: "Pan".to_owned() };

    {
        let ev = Evaluator::new(&mut env);
        let _ = ev.last();
    }

    assert_eq!(data.coins, 6.0);
}

#[test]
fn parse_def_block() {
    let src = "root\n
    emit global.name global.size\n
;\n
\n
def global\n
    name \"my-game\"\n
    size 1.5\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (vars,_) = ev.next().unwrap();

    assert_eq!(vars[0], Var::String("my-game".to_owned()));
    assert_eq!(vars[1], Var::Num(1.5 .to_owned()));
}

#[test]
fn validate_def_block() {
    let src = "root\n
    @global.size + 0.5\n
    @global.name \"other-game\"\n
    emit global.name global.size\n\n
;\n
\n
def global\n
    name \"my-game\"\n
    size 1.5\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[0], Var::String("other-game".to_owned()));
    assert_eq!(vars[1], Var::Num(2.0 .to_owned()));
}

#[test]
fn parse_when_block() {
    let src = "root\n
    needs_coins global.coins < 1\n
    has_no_name !global.name\n
    when {needs_coins @global.coins + 2, \n
         has_no_name @global.name \"new-name\"}\n
    emit global.name global.coins\n
;\n
def global\n
    coins 0\n
;\n";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[1], 2.0 .into());
    assert_eq!(vars[0], "new-name".into());
}

#[test]
fn save_state() {
    let src = "root\n
    next:now other\n
;\n
\n
other\n
    @coins 1\n
    next:await end\n
    emit \"bye\"\n
;\n
\n
end\n
    emit true\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let data = Player { coins: 0.0, name: "Pan".to_owned() };

    let state = {
        let mut ev = Evaluator::new(&mut env);
        let (_,_) = ev.next().unwrap();
        ev.save()
    };

    assert_eq!(data.coins, 0.0);

    let state = {
        let mut ev = state.to_eval(&mut env);
        let (_,_) = ev.next().unwrap();
        ev.save()
    };

    assert_eq!(data.coins, 1.0);

    //check if we held our place within node after await
    let ev = state.to_eval(&mut env);
    let (vars,_) = ev.last().unwrap();
    assert_eq!(vars[0],"bye".into());
}

#[test]
fn parse_sym_call_value() {
    let src = "def root\n
    tag \"--\"\n
;\n
\n
root\n
    @name (wrap) root.tag\n
    emit name\n
;";

    let p = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");
    let mut env = p.into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[0], Var::String("--Pan--".to_owned()));
}

#[test]
fn validate_or_block() {
    let src = "root\n
    if !global.drunk \"not drunk\"\n
    or \"is drunk\"\n
\n
    @global.drunk true\n
\n
    if !global.drunk \"not drunk\"\n
    or \"is drunk\"\n
;\n
\n
def global\n
    drunk false
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (vars,_) = ev.next().unwrap();

    assert_eq!(vars[0], "not drunk".into());

    let (vars,_) = ev.next().unwrap();
    assert_eq!(vars[0], "is drunk".into());
}
<|endoftext|>


<commit_before>//! Create textures and build texture atlas.

use gfx::CommandBuffer;
use gfx::Device;
use image;
use image::{ GenericImage, ImageBuffer, RgbaImage, Pixel, SubImage };
use std::num::FloatMath;
use std::collections::HashMap;
use std::collections::hash_map::{ Occupied, Vacant };
use std::mem;

pub use gfx_texture::Texture;

/// Loads RGBA image from path.
fn load_rgba8(path: &Path) -> Result<RgbaImage, String> {
    Ok(match image::open(path) {
        Ok(image::ImageRgba8(img)) => img,
        Ok(image::ImageRgb8(img)) => {
            let (w, h) = img.dimensions();
            ImageBuffer::from_fn(w, h, |x, y| img.get_pixel(x, y).to_rgba())
        }
        Ok(img) => {
            return Err(format!("Unsupported color type {} in '{}'",
                img.color(), path.display()));
        }
        Err(e)  => {
            return Err(format!("Could not load '{}': {}", path.display(), e));
        }
    })
}

/// A 256x256 image that stores colors.
pub struct ColorMap {
    image: RgbaImage
}

impl ColorMap {
    /// Creates a new `ColorMap` from path.
    pub fn from_path(path: &Path) -> Result<ColorMap, String> {
        let img = try!(load_rgba8(path));

        match img.dimensions() {
            (256, 256) => Ok(ColorMap {image: img}),
            (w, h) => Err(format!("ColorMap expected 256x256, found {}x{} in '{}'",
                                  w, h, path.display()))
        }
    }

    /// Gets RGB color from the color map.
    pub fn get(&self, x: f32, y: f32) -> [u8, ..3] {
        // Clamp to [0.0, 1.0].
        let x = x.max(0.0).min(1.0);
        let y = y.max(0.0).min(1.0);

        // Scale y from [0.0, 1.0] to [0.0, x], forming a triangle.
        let y = x * y;

        // Origin is in the bottom-right corner.
        let x = ((1.0 - x) * 255.0) as u8;
        let y = ((1.0 - y) * 255.0) as u8;

        let col = self.image.get_pixel(x as u32, y as u32).channels();
        [col[0], col[1], col[2]]
    }
}

/// Builds an atlas of textures.
pub struct AtlasBuilder {
    image: RgbaImage,
    // Base path for loading tiles.
    path: Path,
    // Size of an individual tile.
    unit_width: u32,
    unit_height: u32,
    // Size of the entirely occupied square, in tiles.
    completed_tiles_size: u32,
    // Position in the current strip.
    position: u32,
    // Position cache for loaded tiles (in pixels).
    tile_positions: HashMap<String, (u32, u32)>,
    // Lowest-alpha cache for rectangles in the atlas.
    min_alpha_cache: HashMap<(u32, u32, u32, u32), u8>
}

impl AtlasBuilder {
    /// Creates a new `AtlasBuilder`.
    pub fn new(path: Path, unit_width: u32, unit_height: u32) -> AtlasBuilder {
        AtlasBuilder {
            image: ImageBuffer::new(unit_width * 4, unit_height * 4),
            path: path,
            unit_width: unit_width,
            unit_height: unit_height,
            completed_tiles_size: 0,
            position: 0,
            tile_positions: HashMap::new(),
            min_alpha_cache: HashMap::new()
        }
    }

    /// Loads a file into the texture atlas.
    /// Checks if the file is loaded and returns position within the atlas.
    /// The name should be specified without file extension.
    /// PNG is the only supported format.
    pub fn load(&mut self, name: &str) -> (u32, u32) {
        match self.tile_positions.get(name) {
            Some(pos) => return *pos,
            None => {}
        }

        let mut path = self.path.join(name);
        path.set_extension("png");
        let img = load_rgba8(&path).unwrap();

        let (iw, ih) = img.dimensions();
        assert!(iw == self.unit_width);
        assert!((ih % self.unit_height) == 0);
        if ih > self.unit_height {
            println!("ignoring {} extra frames in '{}'", (ih / self.unit_height) - 1, name);
        }

        let (uw, uh) = (self.unit_width, self.unit_height);
        let (w, h) = self.image.dimensions();
        let size = self.completed_tiles_size;

        // Expand the image buffer if necessary.
        if self.position == 0 && (uw * size >= w || uh * size >= h) {
            let old = mem::replace(&mut self.image, ImageBuffer::new(w * 2, h * 2));
            let mut dest = SubImage::new(&mut self.image, 0, 0, w, h);
            for ((_, _, a), b) in dest.pixels_mut().zip(old.pixels()) {
                *a = *b;
            }
        }

        let (x, y) = if self.position < size {
            (self.position, size)
        } else {
            (size, self.position - size)
        };

        self.position += 1;
        if self.position >= size * 2 + 1 {
            self.position = 0;
            self.completed_tiles_size += 1;
        }

        let mut dest = SubImage::new(&mut self.image, x * uw, y * uh, uw, uh);
        for ((_, _, a), b) in dest.pixels_mut().zip(img.pixels()) {
            *a = *b;
        }

        *match self.tile_positions.entry(name.to_string()) {
            Occupied(entry) => entry.into_mut(),
            Vacant(entry) => entry.set((x * uw, y * uh))
        }
    }

    /// Finds the minimum alpha value in a given sub texture of the image.
    pub fn min_alpha(&mut self, rect: [u32, ..4]) -> u8 {
        let [x, y, w, h] = rect;
        match self.min_alpha_cache.get(&(x, y, w, h)) {
            Some(alpha) => return *alpha,
            None => {}
        }

        let tile = SubImage::new(&mut self.image, x, y, w, h);
        let min_alpha = tile.pixels().map(|(_, _, p)| p[3])
            .min().unwrap_or(0);
        self.min_alpha_cache.insert((x, y, w, h), min_alpha);
        min_alpha
    }

    /// Returns the complete texture atlas as a texture.
    pub fn complete<D: Device<C>, C: CommandBuffer>(self, d: &mut D) -> Texture {
        Texture::from_rgba8(self.image, d)
    }
}
<commit_msg>Manually replacing pixels<commit_after>//! Create textures and build texture atlas.

use gfx::CommandBuffer;
use gfx::Device;
use image;
use image::{ GenericImage, ImageBuffer, RgbaImage, Pixel, SubImage };
use std::num::FloatMath;
use std::collections::HashMap;
use std::collections::hash_map::{ Occupied, Vacant };
use std::mem;

pub use gfx_texture::Texture;

/// Loads RGBA image from path.
fn load_rgba8(path: &Path) -> Result<RgbaImage, String> {
    Ok(match image::open(path) {
        Ok(image::ImageRgba8(img)) => img,
        Ok(image::ImageRgb8(img)) => {
            let (w, h) = img.dimensions();
            ImageBuffer::from_fn(w, h, |x, y| img.get_pixel(x, y).to_rgba())
        }
        Ok(img) => {
            return Err(format!("Unsupported color type {} in '{}'",
                img.color(), path.display()));
        }
        Err(e)  => {
            return Err(format!("Could not load '{}': {}", path.display(), e));
        }
    })
}

/// A 256x256 image that stores colors.
pub struct ColorMap {
    image: RgbaImage
}

impl ColorMap {
    /// Creates a new `ColorMap` from path.
    pub fn from_path(path: &Path) -> Result<ColorMap, String> {
        let img = try!(load_rgba8(path));

        match img.dimensions() {
            (256, 256) => Ok(ColorMap {image: img}),
            (w, h) => Err(format!("ColorMap expected 256x256, found {}x{} in '{}'",
                                  w, h, path.display()))
        }
    }

    /// Gets RGB color from the color map.
    pub fn get(&self, x: f32, y: f32) -> [u8, ..3] {
        // Clamp to [0.0, 1.0].
        let x = x.max(0.0).min(1.0);
        let y = y.max(0.0).min(1.0);

        // Scale y from [0.0, 1.0] to [0.0, x], forming a triangle.
        let y = x * y;

        // Origin is in the bottom-right corner.
        let x = ((1.0 - x) * 255.0) as u8;
        let y = ((1.0 - y) * 255.0) as u8;

        let col = self.image.get_pixel(x as u32, y as u32).channels();
        [col[0], col[1], col[2]]
    }
}

/// Builds an atlas of textures.
pub struct AtlasBuilder {
    image: RgbaImage,
    // Base path for loading tiles.
    path: Path,
    // Size of an individual tile.
    unit_width: u32,
    unit_height: u32,
    // Size of the entirely occupied square, in tiles.
    completed_tiles_size: u32,
    // Position in the current strip.
    position: u32,
    // Position cache for loaded tiles (in pixels).
    tile_positions: HashMap<String, (u32, u32)>,
    // Lowest-alpha cache for rectangles in the atlas.
    min_alpha_cache: HashMap<(u32, u32, u32, u32), u8>
}

impl AtlasBuilder {
    /// Creates a new `AtlasBuilder`.
    pub fn new(path: Path, unit_width: u32, unit_height: u32) -> AtlasBuilder {
        AtlasBuilder {
            image: ImageBuffer::new(unit_width * 4, unit_height * 4),
            path: path,
            unit_width: unit_width,
            unit_height: unit_height,
            completed_tiles_size: 0,
            position: 0,
            tile_positions: HashMap::new(),
            min_alpha_cache: HashMap::new()
        }
    }

    /// Loads a file into the texture atlas.
    /// Checks if the file is loaded and returns position within the atlas.
    /// The name should be specified without file extension.
    /// PNG is the only supported format.
    pub fn load(&mut self, name: &str) -> (u32, u32) {
        match self.tile_positions.get(name) {
            Some(pos) => return *pos,
            None => {}
        }

        let mut path = self.path.join(name);
        path.set_extension("png");
        let img = load_rgba8(&path).unwrap();

        let (iw, ih) = img.dimensions();
        assert!(iw == self.unit_width);
        assert!((ih % self.unit_height) == 0);
        if ih > self.unit_height {
            println!("ignoring {} extra frames in '{}'", (ih / self.unit_height) - 1, name);
        }

        let (uw, uh) = (self.unit_width, self.unit_height);
        let (w, h) = self.image.dimensions();
        let size = self.completed_tiles_size;

        // Expand the image buffer if necessary.
        if self.position == 0 && (uw * size >= w || uh * size >= h) {
            let old = mem::replace(&mut self.image, ImageBuffer::new(w * 2, h * 2));
            for ix in range(0, w) {
                for iy in range(0, h) {
                    *self.image.get_pixel_mut(ix, iy) = old[(ix, iy)];
                }
            }

            /*
            let mut dest = SubImage::new(&mut self.image, 0, 0, w, h);
            for ((_, _, a), b) in dest.pixels_mut().zip(old.pixels()) {
                *a = *b;
            }
            */
        }

        let (x, y) = if self.position < size {
            (self.position, size)
        } else {
            (size, self.position - size)
        };

        self.position += 1;
        if self.position >= size * 2 + 1 {
            self.position = 0;
            self.completed_tiles_size += 1;
        }

        {
            let [x, y, w, h] = [x * uw, y * uh, uw, uh];
            for ix in range(0, w) {
                for iy in range(0, h) {
                    *self.image.get_pixel_mut(ix + x, iy + y) = img[(ix, iy)];
                }
            }
        }

        /*
        let mut dest = SubImage::new(&mut self.image, x * uw, y * uh, uw, uh);
        for ((_, _, a), b) in dest.pixels_mut().zip(img.pixels()) {
            *a = *b;
        }
        */

        *match self.tile_positions.entry(name.to_string()) {
            Occupied(entry) => entry.into_mut(),
            Vacant(entry) => entry.set((x * uw, y * uh))
        }
    }

    /// Finds the minimum alpha value in a given sub texture of the image.
    pub fn min_alpha(&mut self, rect: [u32, ..4]) -> u8 {
        let [x, y, w, h] = rect;
        match self.min_alpha_cache.get(&(x, y, w, h)) {
            Some(alpha) => return *alpha,
            None => {}
        }

        let tile = SubImage::new(&mut self.image, x, y, w, h);
        let min_alpha = tile.pixels().map(|(_, _, p)| p[3])
            .min().unwrap_or(0);
        self.min_alpha_cache.insert((x, y, w, h), min_alpha);
        min_alpha
    }

    /// Returns the complete texture atlas as a texture.
    pub fn complete<D: Device<C>, C: CommandBuffer>(self, d: &mut D) -> Texture {
        Texture::from_rgba8(self.image, d)
    }
}
<|endoftext|>