<commit_before>//! Force (base unit newton, kg · m · s-2).

quantity! {
    /// Force (base unit newton, kg · m · s-2).
    quantity: Force; "force";
    /// Force dimension, kg · m · s-2.
    dimension: ISQ<
        P1,     // length
        P1,     // mass
        N2,     // time
        Z0,     // electric current
        Z0,     // thermodynamic temperature
        Z0,     // amount of substance
        Z0>;    // luminous intensity
    units {
        @yottanewton: prefix!(yotta); "YN", "yottanewton", "yottanewtons";
        @zettanewton: prefix!(zetta); "ZN", "zettanewton", "zettanewtons";
        @exanewton: prefix!(exa); "EN", "exanewton", "exanewtons";
        @petanewton: prefix!(peta); "PN", "petanewton", "petanewtons";
        @teranewton: prefix!(tera); "TN", "teranewton", "teranewtons";
        @giganewton: prefix!(giga); "GN", "giganewton", "giganewtons";
        @meganewton: prefix!(mega); "MN", "meganewton", "meganewtons";
        @kilonewton: prefix!(kilo); "kN", "kilonewton", "kilonewtons";
        @hectonewton: prefix!(hecto); "hN", "hectonewton", "hectonewtons";
        @decanewton: prefix!(deca); "daN", "decanewton", "decanewtons";
        /// Derived unit of force.
        @newton: prefix!(none); "N", "newton", "newtons";
        @decinewton: prefix!(deci); "dN", "decinewton", "decinewtons";
        @centinewton: prefix!(centi); "cN", "centinewton", "centinewtons";
        @millinewton: prefix!(milli); "mN", "millinewton", "millinewtons";
        @micronewton: prefix!(micro); "µN", "micronewton", "micronewtons";
        @nanonewton: prefix!(nano); "nN", "nanonewton", "nanonewtons";
        @piconewton: prefix!(pico); "pN", "piconewton", "piconewtons";
        @femtonewton: prefix!(femto); "fN", "femtonewton", "femtonewtons";
        @attonewton: prefix!(atto); "aN", "attonewton", "attonewtons";
        @zeptonewton: prefix!(zepto); "zN", "zeptonewton", "zeptonewtons";
        @yoctonewton: prefix!(yocto); "yN", "yoctonewton", "yoctonewtons";
    }
}

#[cfg(test)]
mod tests {
    storage_types! {
        use num::One;
        use si::quantities::*;
        use si::force as f;
        use si::length as l;
        use si::mass as m;
        use si::time as t;
        use tests::Test;

        #[test]
        fn check_dimension() {
            let _: Force<V> = (Mass::new::<m::kilogram>(V::one())
                    * Length::new::<l::meter>(V::one()))
                / (Time::new::<t::second>(V::one()) * Time::new::<t::second>(V::one()));
        }

        #[test]
        fn check_units() {
            test::<m::yottagram, l::meter, t::second, f::zettanewton>();
            test::<m::zettagram, l::meter, t::second, f::exanewton>();
            test::<m::exagram, l::meter, t::second, f::petanewton>();
            test::<m::petagram, l::meter, t::second, f::teranewton>();
            test::<m::teragram, l::meter, t::second, f::giganewton>();
            test::<m::gigagram, l::meter, t::second, f::meganewton>();
            test::<m::megagram, l::meter, t::second, f::kilonewton>();
            test::<m::kilogram, l::meter, t::second, f::newton>();
            test::<m::gram, l::meter, t::second, f::millinewton>();
            test::<m::milligram, l::meter, t::second, f::micronewton>();
            test::<m::microgram, l::meter, t::second, f::nanonewton>();
            test::<m::nanogram, l::meter, t::second, f::piconewton>();
            test::<m::picogram, l::meter, t::second, f::femtonewton>();
            test::<m::femtogram, l::meter, t::second, f::attonewton>();
            test::<m::attogram, l::meter, t::second, f::zeptonewton>();
            test::<m::zeptogram, l::meter, t::second, f::yoctonewton>();

            test::<m::kilogram, l::yottameter, t::second, f::yottanewton>();
            test::<m::kilogram, l::zettameter, t::second, f::zettanewton>();
            test::<m::kilogram, l::exameter, t::second, f::exanewton>();
            test::<m::kilogram, l::petameter, t::second, f::petanewton>();
            test::<m::kilogram, l::terameter, t::second, f::teranewton>();
            test::<m::kilogram, l::gigameter, t::second, f::giganewton>();
            test::<m::kilogram, l::megameter, t::second, f::meganewton>();
            test::<m::kilogram, l::kilometer, t::second, f::kilonewton>();
            test::<m::kilogram, l::hectometer, t::second, f::hectonewton>();
            test::<m::kilogram, l::decameter, t::second, f::decanewton>();
            test::<m::kilogram, l::meter, t::second, f::newton>();
            test::<m::kilogram, l::decimeter, t::second, f::decinewton>();
            test::<m::kilogram, l::centimeter, t::second, f::centinewton>();
            test::<m::kilogram, l::millimeter, t::second, f::millinewton>();
            test::<m::kilogram, l::micrometer, t::second, f::micronewton>();
            test::<m::kilogram, l::nanometer, t::second, f::nanonewton>();
            test::<m::kilogram, l::picometer, t::second, f::piconewton>();
            test::<m::kilogram, l::femtometer, t::second, f::femtonewton>();
            test::<m::kilogram, l::attometer, t::second, f::attonewton>();
            test::<m::kilogram, l::zeptometer, t::second, f::zeptonewton>();
            test::<m::kilogram, l::meter, t::second, f::newton>();

            test::<m::kilogram, l::meter, t::terasecond, f::yoctonewton>();
            test::<m::kilogram, l::meter, t::gigasecond, f::attonewton>();
            test::<m::kilogram, l::meter, t::megasecond, f::piconewton>();
            test::<m::kilogram, l::meter, t::kilosecond, f::micronewton>();
            test::<m::kilogram, l::meter, t::decasecond, f::centinewton>();
            test::<m::kilogram, l::meter, t::decisecond, f::hectonewton>();
            test::<m::kilogram, l::meter, t::millisecond, f::meganewton>();
            test::<m::kilogram, l::meter, t::microsecond, f::teranewton>();
            test::<m::kilogram, l::meter, t::nanosecond, f::exanewton>();
            test::<m::kilogram, l::meter, t::picosecond, f::yottanewton>();

            fn test<
                M: m::Conversion<V>,
                L: l::Conversion<V>,
                T: t::Conversion<V>,
                F: f::Conversion<V>>()
            {
                Test::assert_approx_eq(&Force::new::<F>(V::one()),
                    &((Mass::new::<M>(V::one())
                        * Length::new::<L>(V::one()))
                        / (Time::new::<T>(V::one()) * Time::new::<T>(V::one()))));
            }
        }
    }
}
<commit_msg>Add missing superscripts to `Force` documentation.<commit_after>//! Force (base unit newton, kg1 · m1 · s-2).

quantity! {
    /// Force (base unit newton, kg1 · m1 · s-2).
    quantity: Force; "force";
    /// Force dimension, kg1 · m1 · s-2.
    dimension: ISQ<
        P1,     // length
        P1,     // mass
        N2,     // time
        Z0,     // electric current
        Z0,     // thermodynamic temperature
        Z0,     // amount of substance
        Z0>;    // luminous intensity
    units {
        @yottanewton: prefix!(yotta); "YN", "yottanewton", "yottanewtons";
        @zettanewton: prefix!(zetta); "ZN", "zettanewton", "zettanewtons";
        @exanewton: prefix!(exa); "EN", "exanewton", "exanewtons";
        @petanewton: prefix!(peta); "PN", "petanewton", "petanewtons";
        @teranewton: prefix!(tera); "TN", "teranewton", "teranewtons";
        @giganewton: prefix!(giga); "GN", "giganewton", "giganewtons";
        @meganewton: prefix!(mega); "MN", "meganewton", "meganewtons";
        @kilonewton: prefix!(kilo); "kN", "kilonewton", "kilonewtons";
        @hectonewton: prefix!(hecto); "hN", "hectonewton", "hectonewtons";
        @decanewton: prefix!(deca); "daN", "decanewton", "decanewtons";
        /// Derived unit of force.
        @newton: prefix!(none); "N", "newton", "newtons";
        @decinewton: prefix!(deci); "dN", "decinewton", "decinewtons";
        @centinewton: prefix!(centi); "cN", "centinewton", "centinewtons";
        @millinewton: prefix!(milli); "mN", "millinewton", "millinewtons";
        @micronewton: prefix!(micro); "µN", "micronewton", "micronewtons";
        @nanonewton: prefix!(nano); "nN", "nanonewton", "nanonewtons";
        @piconewton: prefix!(pico); "pN", "piconewton", "piconewtons";
        @femtonewton: prefix!(femto); "fN", "femtonewton", "femtonewtons";
        @attonewton: prefix!(atto); "aN", "attonewton", "attonewtons";
        @zeptonewton: prefix!(zepto); "zN", "zeptonewton", "zeptonewtons";
        @yoctonewton: prefix!(yocto); "yN", "yoctonewton", "yoctonewtons";
    }
}

#[cfg(test)]
mod tests {
    storage_types! {
        use num::One;
        use si::quantities::*;
        use si::force as f;
        use si::length as l;
        use si::mass as m;
        use si::time as t;
        use tests::Test;

        #[test]
        fn check_dimension() {
            let _: Force<V> = (Mass::new::<m::kilogram>(V::one())
                    * Length::new::<l::meter>(V::one()))
                / (Time::new::<t::second>(V::one()) * Time::new::<t::second>(V::one()));
        }

        #[test]
        fn check_units() {
            test::<m::yottagram, l::meter, t::second, f::zettanewton>();
            test::<m::zettagram, l::meter, t::second, f::exanewton>();
            test::<m::exagram, l::meter, t::second, f::petanewton>();
            test::<m::petagram, l::meter, t::second, f::teranewton>();
            test::<m::teragram, l::meter, t::second, f::giganewton>();
            test::<m::gigagram, l::meter, t::second, f::meganewton>();
            test::<m::megagram, l::meter, t::second, f::kilonewton>();
            test::<m::kilogram, l::meter, t::second, f::newton>();
            test::<m::gram, l::meter, t::second, f::millinewton>();
            test::<m::milligram, l::meter, t::second, f::micronewton>();
            test::<m::microgram, l::meter, t::second, f::nanonewton>();
            test::<m::nanogram, l::meter, t::second, f::piconewton>();
            test::<m::picogram, l::meter, t::second, f::femtonewton>();
            test::<m::femtogram, l::meter, t::second, f::attonewton>();
            test::<m::attogram, l::meter, t::second, f::zeptonewton>();
            test::<m::zeptogram, l::meter, t::second, f::yoctonewton>();

            test::<m::kilogram, l::yottameter, t::second, f::yottanewton>();
            test::<m::kilogram, l::zettameter, t::second, f::zettanewton>();
            test::<m::kilogram, l::exameter, t::second, f::exanewton>();
            test::<m::kilogram, l::petameter, t::second, f::petanewton>();
            test::<m::kilogram, l::terameter, t::second, f::teranewton>();
            test::<m::kilogram, l::gigameter, t::second, f::giganewton>();
            test::<m::kilogram, l::megameter, t::second, f::meganewton>();
            test::<m::kilogram, l::kilometer, t::second, f::kilonewton>();
            test::<m::kilogram, l::hectometer, t::second, f::hectonewton>();
            test::<m::kilogram, l::decameter, t::second, f::decanewton>();
            test::<m::kilogram, l::meter, t::second, f::newton>();
            test::<m::kilogram, l::decimeter, t::second, f::decinewton>();
            test::<m::kilogram, l::centimeter, t::second, f::centinewton>();
            test::<m::kilogram, l::millimeter, t::second, f::millinewton>();
            test::<m::kilogram, l::micrometer, t::second, f::micronewton>();
            test::<m::kilogram, l::nanometer, t::second, f::nanonewton>();
            test::<m::kilogram, l::picometer, t::second, f::piconewton>();
            test::<m::kilogram, l::femtometer, t::second, f::femtonewton>();
            test::<m::kilogram, l::attometer, t::second, f::attonewton>();
            test::<m::kilogram, l::zeptometer, t::second, f::zeptonewton>();
            test::<m::kilogram, l::meter, t::second, f::newton>();

            test::<m::kilogram, l::meter, t::terasecond, f::yoctonewton>();
            test::<m::kilogram, l::meter, t::gigasecond, f::attonewton>();
            test::<m::kilogram, l::meter, t::megasecond, f::piconewton>();
            test::<m::kilogram, l::meter, t::kilosecond, f::micronewton>();
            test::<m::kilogram, l::meter, t::decasecond, f::centinewton>();
            test::<m::kilogram, l::meter, t::decisecond, f::hectonewton>();
            test::<m::kilogram, l::meter, t::millisecond, f::meganewton>();
            test::<m::kilogram, l::meter, t::microsecond, f::teranewton>();
            test::<m::kilogram, l::meter, t::nanosecond, f::exanewton>();
            test::<m::kilogram, l::meter, t::picosecond, f::yottanewton>();

            fn test<
                M: m::Conversion<V>,
                L: l::Conversion<V>,
                T: t::Conversion<V>,
                F: f::Conversion<V>>()
            {
                Test::assert_approx_eq(&Force::new::<F>(V::one()),
                    &((Mass::new::<M>(V::one())
                        * Length::new::<L>(V::one()))
                        / (Time::new::<T>(V::one()) * Time::new::<T>(V::one()))));
            }
        }
    }
}
<|endoftext|>


<commit_before>// This module implements the snapshot files used by zakopane.
// ATOW a snapshot file is pretty much the output of the ``sha256sum''
// command with three extra lines atop.

use std::collections::HashMap;
use std::result::Result;
use std::str::Lines;
use std::string::String;

use crate::structs::ZakopaneError;

// Defines the number of lines preceding the actual checksum content.
const HEADER_LINES: usize = 3;

// Defines the number of hex characters in a sha256sum.
const CHECKSUM_CHARS: usize = 64;

// Defines a valid zakopane snapshot header.
const SNAPSHOT_HEADER_FOR_TESTING: &'static str = r#"simple-zakopane.sh: 2019-07-27-090032
simple-zakopane.sh: /home/kalvin
# this line is typically empty but must be present
"#;

// Accepts a borrowed string representation of some zakopane
// checksums, prepends the standard zakopane snapshot header to the
// same, and returns the owned result.
pub fn snapshot_string_for_testing(checksums: &str) -> String {
    let mut snapshot: String = SNAPSHOT_HEADER_FOR_TESTING.to_string();
    snapshot.push_str(checksums);
    snapshot
}

// Defines a zakopane snapshot, which maps paths to checksums.
#[derive(Debug)]
pub struct Snapshot {
    contents: HashMap<String, String>,
}

// Borrows the string representation of a line in a zakopane snapshot
// and returns sliced str's in a tuple of (checksum, path).
fn parse_snapshot_line(line: &str) -> Result<(&str, &str), ZakopaneError> {
    let bad_line = ZakopaneError::Snapshot(format!("malformed snapshot line: ``{}''", line));
    // A snapshot line should consist of the checksum, two spaces, and a
    // non-empty pathname.
    if line.len() < CHECKSUM_CHARS + 3
        || !line.is_char_boundary(CHECKSUM_CHARS)
        || !line.is_char_boundary(CHECKSUM_CHARS + 1)
        || !line.is_char_boundary(CHECKSUM_CHARS + 2)
    {
        return Err(bad_line);
    }

    let (checksum, path_with_leading_space) = line.split_at(CHECKSUM_CHARS);
    if !path_with_leading_space.starts_with("  ") {
        return Err(bad_line);
    }
    Ok((checksum, &path_with_leading_space[2..]))
}

impl Snapshot {
    // Borrows the string representation of a zakopane snapshot and
    // returns the corresponding Snapshot struct.
    pub fn new(snapshot: &str) -> Result<Snapshot, ZakopaneError> {
        let mut lines: Lines = snapshot.lines();

        // A zakopane snapshot starts with three extra lines intended
        // for human readers. zakopane doesn't care about this header.
        let mut header_drain: usize = HEADER_LINES;
        while header_drain > 0 {
            match lines.next() {
                Some(_) => (),
                None => {
                    return Err(ZakopaneError::Snapshot(
                        "truncated zakopane snapshot".to_string(),
                    ))
                }
            };
            header_drain -= 1;
        }

        // Ingests the rest of the snapshot representation.
        let mut contents: HashMap<String, String> = HashMap::new();
        for line in lines {
            let (checksum, path) = parse_snapshot_line(line)?;
            match contents.insert(path.to_string(), checksum.to_string()) {
                None => (),
                Some(old_checksum) => {
                    return Err(ZakopaneError::Snapshot(format!(
                        "path collision: {} (was already {}, is now {})",
                        path, old_checksum, checksum
                    )))
                }
            };
        }
        Ok(Snapshot { contents: contents })
    }

    // Passes the inner struct's iterator straight out.
    pub fn iter(&self) -> std::collections::hash_map::Iter<String, String> {
        self.contents.iter()
    }

    // Returns a reference to the checksum of the file (if present).
    pub fn get(&self, key: &str) -> std::option::Option<&String> {
        self.contents.get(key)
    }
}

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

    // Consumes a ZakopaneError and borrows a string slice. Asserts that
    // the error is of the Snapshot variant and starts with the string
    // slice.
    fn assert_snapshot_error(error: ZakopaneError, prefix: &str) {
        match error {
            ZakopaneError::Snapshot(message) => assert!(message.starts_with(prefix)),
            _ => panic!("expected ZakopaneError::Snapshot"),
        };
    }

    #[test]
    fn snapshot_must_have_proper_header() {
        let snapshot = Snapshot::new(SNAPSHOT_HEADER_FOR_TESTING).unwrap();
        assert_eq!(snapshot.contents.len(), 0);

        let snapshot_without_proper_header = r#"simple-zakopane.sh: 2019-07-27-090032
simple-zakopane.sh: /home/kalvin
"#;
        assert_snapshot_error(
            Snapshot::new(snapshot_without_proper_header).unwrap_err(),
            "truncated",
        );
    }

    #[test]
    fn snapshot_checksum_is_hex() {
        let checksum_ok =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5  ./hello/there.txt";
        let snapshot = Snapshot::new(&snapshot_string_for_testing(checksum_ok)).unwrap();
        assert_eq!(snapshot.contents.len(), 1);

        // Defines the literal string "malformed" that appears
        // repeatedly in the following tests.
        let malformed: &str = "malformed";

        // Oh no! This checksum dropped a character off the end.
        let checksum_short =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a  ./hello/there.txt";
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing(checksum_short)).unwrap_err(),
            malformed,
        );

        // Oh no! This checksum line does not refer to a path.
        let checksum_without_path =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5  ";
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing(checksum_without_path)).unwrap_err(),
            malformed,
        );

        // Checksum lines may not be empty or too short.
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing("\n")).unwrap_err(),
            malformed,
        );
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing("Hello there!")).unwrap_err(),
            malformed,
        );
    }

    #[test]
    fn snapshot_paths_may_not_repeat() {
        let checksums = r#"4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5  ./hello/there.txt
0000000000000000000000000000000000000000000000000000000000000000  ./hello/there.txt"#;
        // The point of this test is not to catch identical checksums
        // (which occur naturally if you ever accidentally duplicate
        // files), but to catch repeated paths (which should not be
        // possible in a well-formed snapshot file).
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing(checksums)).unwrap_err(),
            "path collision",
        );
    }

    #[test]
    fn snapshot_get() {
        // Creates a snapshot describing two files, each with contrived
        // but valid-looking checksums.
        let snapshot = Snapshot::new(&snapshot_string_for_testing(
            r#"0000000000000000000000000000000000000000000000000000000000000001  ./hello/there.txt
0000000000000000000000000000000000000000000000000000000000000002  ./general/kenobi.txt
00000000000000000000000000000000000000000000000000000000000000ff  ./you/are.txt
00000000000000000000000000000000000000000000000000000000000001ff  ./a/bold-one.txt
"#,
        ))
        .unwrap();
        assert_eq!(
            snapshot.get("./hello/there.txt").unwrap(),
            "0000000000000000000000000000000000000000000000000000000000000001"
        );
        assert_eq!(
            snapshot.get("./general/kenobi.txt").unwrap(),
            "0000000000000000000000000000000000000000000000000000000000000002"
        );
        assert_eq!(
            snapshot.get("./you/are.txt").unwrap(),
            "00000000000000000000000000000000000000000000000000000000000000ff"
        );
        assert_eq!(
            snapshot.get("./a/bold-one.txt").unwrap(),
            "00000000000000000000000000000000000000000000000000000000000001ff"
        );

        assert!(snapshot.get("blah-blah-nonexistent-key").is_none());
        // Note that Snapshots don't perform any path comprehension - as
        // far as a Snapshot key is concerned, it's an arbitrary
        // sequence of bytes.
        assert!(snapshot.get("a/bold-one.txt").is_none());
    }
}
<commit_msg>snapshot: Refactor `Snapshot::new()`<commit_after>// This module implements the snapshot files used by zakopane.
// ATOW a snapshot file is pretty much the output of the ``sha256sum''
// command with three extra lines atop.

use std::collections::HashMap;
use std::result::Result;
use std::string::String;

use crate::structs::ZakopaneError;

// Defines the number of lines preceding the actual checksum content.
const HEADER_LINES: usize = 3;

// Defines the number of hex characters in a sha256sum.
const CHECKSUM_CHARS: usize = 64;

// Defines a valid zakopane snapshot header.
const SNAPSHOT_HEADER_FOR_TESTING: &'static str = r#"simple-zakopane.sh: 2019-07-27-090032
simple-zakopane.sh: /home/kalvin
# this line is typically empty but must be present
"#;

// Accepts a borrowed string representation of some zakopane
// checksums, prepends the standard zakopane snapshot header to the
// same, and returns the owned result.
pub fn snapshot_string_for_testing(checksums: &str) -> String {
    let mut snapshot: String = SNAPSHOT_HEADER_FOR_TESTING.to_string();
    snapshot.push_str(checksums);
    snapshot
}

// Defines a zakopane snapshot, which maps paths to checksums.
#[derive(Debug)]
pub struct Snapshot {
    contents: HashMap<String, String>,
}

// Borrows the string representation of a line in a zakopane snapshot
// and returns sliced str's in a tuple of (checksum, path).
fn parse_snapshot_line(line: &str) -> Result<(&str, &str), ZakopaneError> {
    let bad_line = ZakopaneError::Snapshot(format!("malformed snapshot line: ``{}''", line));
    // A snapshot line should consist of the checksum, two spaces, and a
    // non-empty pathname.
    if line.len() < CHECKSUM_CHARS + 3
        || !line.is_char_boundary(CHECKSUM_CHARS)
        || !line.is_char_boundary(CHECKSUM_CHARS + 1)
        || !line.is_char_boundary(CHECKSUM_CHARS + 2)
    {
        return Err(bad_line);
    }

    let (checksum, path_with_leading_space) = line.split_at(CHECKSUM_CHARS);
    if !path_with_leading_space.starts_with("  ") {
        return Err(bad_line);
    }
    Ok((checksum, &path_with_leading_space[2..]))
}

impl Snapshot {
    // Borrows the string representation of a zakopane snapshot and
    // returns the corresponding Snapshot struct.
    pub fn new(snapshot: &str) -> Result<Snapshot, ZakopaneError> {
        // A zakopane snapshot starts with three extra lines intended
        // for human readers. zakopane doesn't care about this header.
        let mut header_drain: usize = HEADER_LINES;

        let mut contents: HashMap<String, String> = HashMap::new();
        for line in snapshot.lines() {
            if header_drain > 0 {
                header_drain -= 1;
                continue;
            }

            let (checksum, path) = parse_snapshot_line(line)?;
            if let Some(old_checksum) = contents.insert(path.to_string(), checksum.to_string()) {
                return Err(ZakopaneError::Snapshot(format!(
                    "path collision: {} (was already {}, is now {})",
                    path, old_checksum, checksum
                )));
            };
        }

        if header_drain > 0 {
            return Err(ZakopaneError::Snapshot(String::from(
                "truncated zakopane snapshot",
            )));
        }
        Ok(Snapshot { contents: contents })
    }

    // Passes the inner struct's iterator straight out.
    pub fn iter(&self) -> std::collections::hash_map::Iter<String, String> {
        self.contents.iter()
    }

    // Returns a reference to the checksum of the file (if present).
    pub fn get(&self, key: &str) -> std::option::Option<&String> {
        self.contents.get(key)
    }
}

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

    // Consumes a ZakopaneError and borrows a string slice. Asserts that
    // the error is of the Snapshot variant and starts with the string
    // slice.
    fn assert_snapshot_error(error: ZakopaneError, prefix: &str) {
        match error {
            ZakopaneError::Snapshot(message) => assert!(message.starts_with(prefix)),
            _ => panic!("expected ZakopaneError::Snapshot"),
        };
    }

    #[test]
    fn snapshot_must_have_proper_header() {
        let snapshot = Snapshot::new(SNAPSHOT_HEADER_FOR_TESTING).unwrap();
        assert_eq!(snapshot.contents.len(), 0);

        let snapshot_without_proper_header = r#"simple-zakopane.sh: 2019-07-27-090032
simple-zakopane.sh: /home/kalvin
"#;
        assert_snapshot_error(
            Snapshot::new(snapshot_without_proper_header).unwrap_err(),
            "truncated",
        );
    }

    #[test]
    fn snapshot_checksum_is_hex() {
        let checksum_ok =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5  ./hello/there.txt";
        let snapshot = Snapshot::new(&snapshot_string_for_testing(checksum_ok)).unwrap();
        assert_eq!(snapshot.contents.len(), 1);

        // Defines the literal string "malformed" that appears
        // repeatedly in the following tests.
        let malformed: &str = "malformed";

        // Oh no! This checksum dropped a character off the end.
        let checksum_short =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a  ./hello/there.txt";
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing(checksum_short)).unwrap_err(),
            malformed,
        );

        // Oh no! This checksum line does not refer to a path.
        let checksum_without_path =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5  ";
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing(checksum_without_path)).unwrap_err(),
            malformed,
        );

        // Checksum lines may not be empty or too short.
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing("\n")).unwrap_err(),
            malformed,
        );
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing("Hello there!")).unwrap_err(),
            malformed,
        );
    }

    #[test]
    fn snapshot_paths_may_not_repeat() {
        let checksums = r#"4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5  ./hello/there.txt
0000000000000000000000000000000000000000000000000000000000000000  ./hello/there.txt"#;
        // The point of this test is not to catch identical checksums
        // (which occur naturally if you ever accidentally duplicate
        // files), but to catch repeated paths (which should not be
        // possible in a well-formed snapshot file).
        assert_snapshot_error(
            Snapshot::new(&snapshot_string_for_testing(checksums)).unwrap_err(),
            "path collision",
        );
    }

    #[test]
    fn snapshot_get() {
        // Creates a snapshot describing two files, each with contrived
        // but valid-looking checksums.
        let snapshot = Snapshot::new(&snapshot_string_for_testing(
            r#"0000000000000000000000000000000000000000000000000000000000000001  ./hello/there.txt
0000000000000000000000000000000000000000000000000000000000000002  ./general/kenobi.txt
00000000000000000000000000000000000000000000000000000000000000ff  ./you/are.txt
00000000000000000000000000000000000000000000000000000000000001ff  ./a/bold-one.txt
"#,
        ))
        .unwrap();
        assert_eq!(
            snapshot.get("./hello/there.txt").unwrap(),
            "0000000000000000000000000000000000000000000000000000000000000001"
        );
        assert_eq!(
            snapshot.get("./general/kenobi.txt").unwrap(),
            "0000000000000000000000000000000000000000000000000000000000000002"
        );
        assert_eq!(
            snapshot.get("./you/are.txt").unwrap(),
            "00000000000000000000000000000000000000000000000000000000000000ff"
        );
        assert_eq!(
            snapshot.get("./a/bold-one.txt").unwrap(),
            "00000000000000000000000000000000000000000000000000000000000001ff"
        );

        assert!(snapshot.get("blah-blah-nonexistent-key").is_none());
        // Note that Snapshots don't perform any path comprehension - as
        // far as a Snapshot key is concerned, it's an arbitrary
        // sequence of bytes.
        assert!(snapshot.get("a/bold-one.txt").is_none());
    }
}
<|endoftext|>


<commit_before>use utils::is_stop_char;
use std::collections::HashMap;

pub fn get_trigrams_with_positions(text : &str) -> HashMap<String, u32> {
    let counter_hash = count(text);

    let mut count_vec: Vec<_> = counter_hash.iter().collect();
    count_vec.sort_by_key(|key| key.1 );
    count_vec.reverse();

    let mut result: HashMap<String, u32> = HashMap::new();

    // TODO: extract 600 as LANG_PROFILE_LENGTH * 2
    for (i, trigram) in count_vec.iter().take(600).map(|x| x.0).enumerate() {
        // TODO: find a way not to clone it
        result.insert((*trigram).clone(), i as u32);
    }

    result
}

#[inline(always)]
fn count(text : &str) -> HashMap<String, u32> {
    let mut s = text.to_lowercase();
    s.push(' '); // add space to the end

    let mut counter_hash : HashMap<String, u32> = HashMap::new();

    // iterate through the string and count trigrams
    let mut chars_iter = s.chars();
    let mut c1 = ' ';
    let mut c2 = to_trigram_char(chars_iter.next().unwrap());
    for cur_char in chars_iter {
        let c3 = to_trigram_char(cur_char);
        if !(c2 == ' ' && (c1 == ' ' || c3 == ' ')) {
            let mut trigram = String::with_capacity(3);
            trigram.push(c1);
            trigram.push(c2);
            trigram.push(c3);
            let count = counter_hash.entry(trigram).or_insert(0);
            *count += 1;
        }
        c1 = c2;
        c2 = c3;
    }

    counter_hash
}

// Convert punctuations and digits to a space.
#[inline(always)]
fn to_trigram_char(ch : char) -> char {
    if is_stop_char(ch) { ' ' } else { ch }
}

#[cfg(test)]
mod tests {
    use super::to_trigram_char;
    use super::count;
    use super::get_trigrams_with_positions;

    fn assert_valuable_trigram_chars(chars : &[char]) {
        for &ch in chars.iter() {
            assert_eq!(to_trigram_char(ch), ch);
        }
    }

    fn assert_not_valuable_trigram_chars(chars : &[char]) {
        for &ch in chars.iter() {
            assert_eq!(to_trigram_char(ch), ' ');
        }
    }

    #[test]
    fn test_to_trigram_char() {
        // valuable chars, that metters
        assert_valuable_trigram_chars(&['a', 'z', 'A', 'Z', 'Ж', 'ß']);

        // punctuations, digits, etc..
        //
        // 0x00 - 0x40
        assert_not_valuable_trigram_chars(&['\t', '\n', ' ', '.', '0', '9', ',', '@']);
        // 0x5B - 0x60
        assert_not_valuable_trigram_chars(&['[', ']', '^', '\\', '`']);
        // 0x7B - 0x7E
        assert_not_valuable_trigram_chars(&['[', '|', '{', '}', '~']);
    }

    fn assert_count(text : &str, pairs : &[(&str, u32)]) {
        let result = count(&text.to_string());
        for &(k, v) in pairs.iter() {
            let &actual_val = result.get(k).unwrap_or(&0);
            assert_eq!(actual_val, v, "trigram '{}' expected to occur {} times, got {}", k, v, actual_val);
        }
        assert_eq!(result.len(), pairs.len());
    }

    #[test]
    fn test_count() {
        assert_count("", &[]);
        assert_count(",", &[]);
        assert_count("a", &[(" a ", 1)]);
        assert_count("-a-", &[(" a ", 1)]);
        assert_count("yes", &[(" ye", 1), ("yes", 1), ("es ", 1)]);
        assert_count("Give - IT...", &[(" gi", 1), ("giv", 1), ("ive", 1), ("ve ", 1), (" it", 1), ("it ", 1)]);
    }

    #[test]
    fn test_get_trigrams_with_positions() {
        let res = get_trigrams_with_positions(&"xaaaaabbbbd".to_string());
        assert_eq!(*res.get("aaa").unwrap(), 0);
        assert_eq!(*res.get("bbb").unwrap(), 1);
    }
}
<commit_msg>Fix bug: sort trigrams by number of occurences and alphabetic order<commit_after>use utils::is_stop_char;
use std::collections::HashMap;

pub fn get_trigrams_with_positions(text : &str) -> HashMap<String, u32> {
    let counter_hash = count(text);

    // Sort in descending order by number of occurrences and trigrams
    let mut count_vec: Vec<_> = counter_hash.iter().map(|(trigram, count)| (count, trigram)).collect();
    count_vec.sort_by(|a, b| b.cmp(a));

    let mut result: HashMap<String, u32> = HashMap::new();

    // TODO: extract 600 as LANG_PROFILE_LENGTH * 2
    for (i, trigram) in count_vec.iter().take(600).map(|x| x.1).enumerate() {
        // TODO: find a way not to clone it
        result.insert((*trigram).clone(), i as u32);
    }

    result
}

#[inline(always)]
fn count(text : &str) -> HashMap<String, u32> {
    let mut s = text.to_lowercase();
    s.push(' '); // add space to the end

    let mut counter_hash : HashMap<String, u32> = HashMap::new();

    // iterate through the string and count trigrams
    let mut chars_iter = s.chars();
    let mut c1 = ' ';
    let mut c2 = to_trigram_char(chars_iter.next().unwrap());
    for cur_char in chars_iter {
        let c3 = to_trigram_char(cur_char);
        if !(c2 == ' ' && (c1 == ' ' || c3 == ' ')) {
            let mut trigram = String::with_capacity(3);
            trigram.push(c1);
            trigram.push(c2);
            trigram.push(c3);
            let count = counter_hash.entry(trigram).or_insert(0);
            *count += 1;
        }
        c1 = c2;
        c2 = c3;
    }

    counter_hash
}

// Convert punctuations and digits to a space.
#[inline(always)]
fn to_trigram_char(ch : char) -> char {
    if is_stop_char(ch) { ' ' } else { ch }
}

#[cfg(test)]
mod tests {
    use super::to_trigram_char;
    use super::count;
    use super::get_trigrams_with_positions;

    fn assert_valuable_trigram_chars(chars : &[char]) {
        for &ch in chars.iter() {
            assert_eq!(to_trigram_char(ch), ch);
        }
    }

    fn assert_not_valuable_trigram_chars(chars : &[char]) {
        for &ch in chars.iter() {
            assert_eq!(to_trigram_char(ch), ' ');
        }
    }

    #[test]
    fn test_to_trigram_char() {
        // valuable chars, that metters
        assert_valuable_trigram_chars(&['a', 'z', 'A', 'Z', 'Ж', 'ß']);

        // punctuations, digits, etc..
        //
        // 0x00 - 0x40
        assert_not_valuable_trigram_chars(&['\t', '\n', ' ', '.', '0', '9', ',', '@']);
        // 0x5B - 0x60
        assert_not_valuable_trigram_chars(&['[', ']', '^', '\\', '`']);
        // 0x7B - 0x7E
        assert_not_valuable_trigram_chars(&['[', '|', '{', '}', '~']);
    }

    fn assert_count(text : &str, pairs : &[(&str, u32)]) {
        let result = count(&text.to_string());
        for &(k, v) in pairs.iter() {
            let &actual_val = result.get(k).unwrap_or(&0);
            assert_eq!(actual_val, v, "trigram '{}' expected to occur {} times, got {}", k, v, actual_val);
        }
        assert_eq!(result.len(), pairs.len());
    }

    #[test]
    fn test_count() {
        assert_count("", &[]);
        assert_count(",", &[]);
        assert_count("a", &[(" a ", 1)]);
        assert_count("-a-", &[(" a ", 1)]);
        assert_count("yes", &[(" ye", 1), ("yes", 1), ("es ", 1)]);
        assert_count("Give - IT...", &[(" gi", 1), ("giv", 1), ("ive", 1), ("ve ", 1), (" it", 1), ("it ", 1)]);
    }

    #[test]
    fn test_get_trigrams_with_positions() {
        let res = get_trigrams_with_positions(&"xaaaaabbbbd".to_string());
        assert_eq!(*res.get("aaa").unwrap(), 0);
        assert_eq!(*res.get("bbb").unwrap(), 1);
    }
}
<|endoftext|>


<commit_before>#![allow(non_camel_case_types, non_snake_case)]
extern crate ncurses;

use ncurses::ll::{box_, chtype, attr_t, WINDOW, wattrset};

use ncurses::NCURSES_ATTR_SHIFT;

pub fn NCURSES_BITS(mask: u32, shift: u32) -> u32 {
    mask << (shift + NCURSES_ATTR_SHIFT) as usize
}

pub const COLOR_BLACK: i16 = 0;
pub const COLOR_RED: i16 = 1;
pub const COLOR_GREEN: i16 = 2;
pub const COLOR_YELLOW: i16 = 3;
pub const COLOR_BLUE: i16 = 4;
pub const COLOR_MAGENTA: i16 = 5;
pub const COLOR_CYAN: i16 = 6;
pub const COLOR_WHITE: i16 = 7;

pub const A_NORMAL: attr_t = 0u32 as attr_t;
pub const A_ATTRIBUTES: attr_t = (!0u32 << (0u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_CHARTEXT: attr_t = (1u32 << (0u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_COLOR: attr_t = ((((1u32) << 8) - 1u32) << (0u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_BLINK: attr_t = (1u32 << (11u32 + NCURSES_ATTR_SHIFT)) as attr_t;

// pub fn A_STANDOUT() -> attr_t
// { NCURSES_BITS(1u32, 8u32) as attr_t }
//
// pub fn A_UNDERLINE() -> attr_t
// { NCURSES_BITS(1u32, 9u32) as attr_t }
//
// pub fn A_REVERSE() -> attr_t
// { NCURSES_BITS(1u32, 10u32) as attr_t }
//
// pub fn A_DIM() -> attr_t
// { NCURSES_BITS(1u32, 12u32) as attr_t }
//

pub const A_BOLD: attr_t = (1u32 << (13u32 + NCURSES_ATTR_SHIFT)) as attr_t;

pub const KEY_RESIZE: i32 = 0632;

pub fn COLOR_PAIR(n: chtype) -> attr_t {
    NCURSES_BITS(n as u32, 0u32) as attr_t
}

pub fn _attron(w: WINDOW, attributes: chtype) -> i32 {
    unsafe { wattron(w, attributes as i32) }
}

pub fn _attrset(w: WINDOW, attributes: chtype) -> i32 {
    unsafe { wattrset(w, attributes as i32) }
}

pub fn _draw_box(w: WINDOW, verch: chtype, horch: chtype) -> i32 {
    unsafe { box_(w, verch, horch) }
}

pub fn _resize_term(_nlines: i32, _ncols: i32) -> i32 {
    error!("resize_term is not implemented in ncurses-rs");
    -1
}

use input::Input;

const KEY_OFFSET: i32 = 0o0400;
const KEY_F15: i32 = (KEY_OFFSET + 0x17);
const KEY_EVENT: i32 = (KEY_OFFSET + 0o633);

const SPECIAL_KEY_CODES: [Input; 108] = [Input::KeyCodeYes,

                                         Input::KeyBreak,
                                         Input::KeyDown,
                                         Input::KeyUp,
                                         Input::KeyLeft,
                                         Input::KeyRight,
                                         Input::KeyHome,
                                         Input::KeyBackspace,

                                         Input::KeyF0,
                                         Input::KeyF1,
                                         Input::KeyF2,
                                         Input::KeyF3,
                                         Input::KeyF4,
                                         Input::KeyF5,
                                         Input::KeyF6,
                                         Input::KeyF7,
                                         Input::KeyF8,
                                         Input::KeyF9,
                                         Input::KeyF10,
                                         Input::KeyF11,
                                         Input::KeyF12,
                                         Input::KeyF13,
                                         Input::KeyF14,
                                         Input::KeyF15,
                                         // ncurses reserves space for 64 function keys, but we've
                                         // only implemented 15. This has to be taken into account
                                         // when converting the integer into an index of this array
                                         Input::KeyDL,
                                         Input::KeyIL,
                                         Input::KeyDC,
                                         Input::KeyIC,
                                         Input::KeyEIC,
                                         Input::KeyClear,
                                         Input::KeyEOS,
                                         Input::KeyEOL,
                                         Input::KeySF,
                                         Input::KeySR,
                                         Input::KeyNPage,
                                         Input::KeyPPage,
                                         Input::KeySTab,
                                         Input::KeyCTab,
                                         Input::KeyCATab,
                                         Input::KeyEnter,
                                         Input::KeySReset,
                                         Input::KeyReset,
                                         Input::KeyPrint,
                                         Input::KeyLL,
                                         Input::KeyA1,
                                         Input::KeyA3,
                                         Input::KeyB2,
                                         Input::KeyC1,
                                         Input::KeyC3,
                                         Input::KeyBTab,
                                         Input::KeyBeg,
                                         Input::KeyCancel,
                                         Input::KeyClose,
                                         Input::KeyCommand,
                                         Input::KeyCopy,
                                         Input::KeyCreate,
                                         Input::KeyEnd,
                                         Input::KeyExit,
                                         Input::KeyFind,
                                         Input::KeyHelp,
                                         Input::KeyMark,
                                         Input::KeyMessage,
                                         Input::KeyMove,
                                         Input::KeyNext,
                                         Input::KeyOpen,
                                         Input::KeyOptions,
                                         Input::KeyPrevious,
                                         Input::KeyRedo,
                                         Input::KeyReference,
                                         Input::KeyRefresh,
                                         Input::KeyReplace,
                                         Input::KeyRestart,
                                         Input::KeyResume,
                                         Input::KeySave,
                                         Input::KeySBeg,
                                         Input::KeySCancel,
                                         Input::KeySCommand,
                                         Input::KeySCopy,
                                         Input::KeySCreate,
                                         Input::KeySDC,
                                         Input::KeySDL,
                                         Input::KeySelect,
                                         Input::KeySEnd,
                                         Input::KeySEOL,
                                         Input::KeySExit,
                                         Input::KeySFind,
                                         Input::KeySHelp,
                                         Input::KeySHome,
                                         Input::KeySIC,

                                         Input::KeySLeft,
                                         Input::KeySMessage,
                                         Input::KeySMove,
                                         Input::KeySNext,
                                         Input::KeySOptions,
                                         Input::KeySPrevious,
                                         Input::KeySPrint,
                                         Input::KeySRedo,
                                         Input::KeySReplace,
                                         Input::KeySRight,
                                         Input::KeySResume,
                                         Input::KeySSave,
                                         Input::KeySSuspend,
                                         Input::KeySUndo,
                                         Input::KeySuspend,
                                         Input::KeyUndo,
                                         Input::KeyMouse,
                                         Input::KeyResize,
                                         Input::KeyEvent,
];

/// Converts an integer returned by getch() to a Input value
pub fn to_special_keycode(i: i32) -> Input {
    assert!(i >= KEY_OFFSET && i <= KEY_EVENT);
    let i = if i <= KEY_F15 {
        i - KEY_OFFSET
    } else {
        i - KEY_OFFSET - 48
    };
    SPECIAL_KEY_CODES[i as usize]
}

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

    #[test]
    fn test_key_dl_to_special_keycode() {
        let keyDl = 0o510;
        assert_eq!(Input::KeyDL, to_special_keycode(keyDl));
    }

    #[test]
    fn test_key_f15_to_input() {
        let keyF15 = 0o410 + 15;
        assert_eq!(Input::KeyF15, to_special_keycode(keyF15));
    }

    #[test]
    fn test_key_up_to_input() {
        let keyUp = 0o403;
        assert_eq!(Input::KeyUp, to_special_keycode(keyUp));
    }
}
<commit_msg>Added use ll::wattron<commit_after>#![allow(non_camel_case_types, non_snake_case)]
extern crate ncurses;

use ncurses::ll::{box_, chtype, attr_t, WINDOW, wattron, wattrset};

use ncurses::NCURSES_ATTR_SHIFT;

pub fn NCURSES_BITS(mask: u32, shift: u32) -> u32 {
    mask << (shift + NCURSES_ATTR_SHIFT) as usize
}

pub const COLOR_BLACK: i16 = 0;
pub const COLOR_RED: i16 = 1;
pub const COLOR_GREEN: i16 = 2;
pub const COLOR_YELLOW: i16 = 3;
pub const COLOR_BLUE: i16 = 4;
pub const COLOR_MAGENTA: i16 = 5;
pub const COLOR_CYAN: i16 = 6;
pub const COLOR_WHITE: i16 = 7;

pub const A_NORMAL: attr_t = 0u32 as attr_t;
pub const A_ATTRIBUTES: attr_t = (!0u32 << (0u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_CHARTEXT: attr_t = (1u32 << (0u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_COLOR: attr_t = ((((1u32) << 8) - 1u32) << (0u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_BLINK: attr_t = (1u32 << (11u32 + NCURSES_ATTR_SHIFT)) as attr_t;

// pub fn A_STANDOUT() -> attr_t
// { NCURSES_BITS(1u32, 8u32) as attr_t }
//
// pub fn A_UNDERLINE() -> attr_t
// { NCURSES_BITS(1u32, 9u32) as attr_t }
//
// pub fn A_REVERSE() -> attr_t
// { NCURSES_BITS(1u32, 10u32) as attr_t }
//
// pub fn A_DIM() -> attr_t
// { NCURSES_BITS(1u32, 12u32) as attr_t }
//

pub const A_BOLD: attr_t = (1u32 << (13u32 + NCURSES_ATTR_SHIFT)) as attr_t;

pub const KEY_RESIZE: i32 = 0632;

pub fn COLOR_PAIR(n: chtype) -> attr_t {
    NCURSES_BITS(n as u32, 0u32) as attr_t
}

pub fn _attron(w: WINDOW, attributes: chtype) -> i32 {
    unsafe { wattron(w, attributes as i32) }
}

pub fn _attrset(w: WINDOW, attributes: chtype) -> i32 {
    unsafe { wattrset(w, attributes as i32) }
}

pub fn _draw_box(w: WINDOW, verch: chtype, horch: chtype) -> i32 {
    unsafe { box_(w, verch, horch) }
}

pub fn _resize_term(_nlines: i32, _ncols: i32) -> i32 {
    error!("resize_term is not implemented in ncurses-rs");
    -1
}

use input::Input;

const KEY_OFFSET: i32 = 0o0400;
const KEY_F15: i32 = (KEY_OFFSET + 0x17);
const KEY_EVENT: i32 = (KEY_OFFSET + 0o633);

const SPECIAL_KEY_CODES: [Input; 108] = [Input::KeyCodeYes,

                                         Input::KeyBreak,
                                         Input::KeyDown,
                                         Input::KeyUp,
                                         Input::KeyLeft,
                                         Input::KeyRight,
                                         Input::KeyHome,
                                         Input::KeyBackspace,

                                         Input::KeyF0,
                                         Input::KeyF1,
                                         Input::KeyF2,
                                         Input::KeyF3,
                                         Input::KeyF4,
                                         Input::KeyF5,
                                         Input::KeyF6,
                                         Input::KeyF7,
                                         Input::KeyF8,
                                         Input::KeyF9,
                                         Input::KeyF10,
                                         Input::KeyF11,
                                         Input::KeyF12,
                                         Input::KeyF13,
                                         Input::KeyF14,
                                         Input::KeyF15,
                                         // ncurses reserves space for 64 function keys, but we've
                                         // only implemented 15. This has to be taken into account
                                         // when converting the integer into an index of this array
                                         Input::KeyDL,
                                         Input::KeyIL,
                                         Input::KeyDC,
                                         Input::KeyIC,
                                         Input::KeyEIC,
                                         Input::KeyClear,
                                         Input::KeyEOS,
                                         Input::KeyEOL,
                                         Input::KeySF,
                                         Input::KeySR,
                                         Input::KeyNPage,
                                         Input::KeyPPage,
                                         Input::KeySTab,
                                         Input::KeyCTab,
                                         Input::KeyCATab,
                                         Input::KeyEnter,
                                         Input::KeySReset,
                                         Input::KeyReset,
                                         Input::KeyPrint,
                                         Input::KeyLL,
                                         Input::KeyA1,
                                         Input::KeyA3,
                                         Input::KeyB2,
                                         Input::KeyC1,
                                         Input::KeyC3,
                                         Input::KeyBTab,
                                         Input::KeyBeg,
                                         Input::KeyCancel,
                                         Input::KeyClose,
                                         Input::KeyCommand,
                                         Input::KeyCopy,
                                         Input::KeyCreate,
                                         Input::KeyEnd,
                                         Input::KeyExit,
                                         Input::KeyFind,
                                         Input::KeyHelp,
                                         Input::KeyMark,
                                         Input::KeyMessage,
                                         Input::KeyMove,
                                         Input::KeyNext,
                                         Input::KeyOpen,
                                         Input::KeyOptions,
                                         Input::KeyPrevious,
                                         Input::KeyRedo,
                                         Input::KeyReference,
                                         Input::KeyRefresh,
                                         Input::KeyReplace,
                                         Input::KeyRestart,
                                         Input::KeyResume,
                                         Input::KeySave,
                                         Input::KeySBeg,
                                         Input::KeySCancel,
                                         Input::KeySCommand,
                                         Input::KeySCopy,
                                         Input::KeySCreate,
                                         Input::KeySDC,
                                         Input::KeySDL,
                                         Input::KeySelect,
                                         Input::KeySEnd,
                                         Input::KeySEOL,
                                         Input::KeySExit,
                                         Input::KeySFind,
                                         Input::KeySHelp,
                                         Input::KeySHome,
                                         Input::KeySIC,

                                         Input::KeySLeft,
                                         Input::KeySMessage,
                                         Input::KeySMove,
                                         Input::KeySNext,
                                         Input::KeySOptions,
                                         Input::KeySPrevious,
                                         Input::KeySPrint,
                                         Input::KeySRedo,
                                         Input::KeySReplace,
                                         Input::KeySRight,
                                         Input::KeySResume,
                                         Input::KeySSave,
                                         Input::KeySSuspend,
                                         Input::KeySUndo,
                                         Input::KeySuspend,
                                         Input::KeyUndo,
                                         Input::KeyMouse,
                                         Input::KeyResize,
                                         Input::KeyEvent,
];

/// Converts an integer returned by getch() to a Input value
pub fn to_special_keycode(i: i32) -> Input {
    assert!(i >= KEY_OFFSET && i <= KEY_EVENT);
    let i = if i <= KEY_F15 {
        i - KEY_OFFSET
    } else {
        i - KEY_OFFSET - 48
    };
    SPECIAL_KEY_CODES[i as usize]
}

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

    #[test]
    fn test_key_dl_to_special_keycode() {
        let keyDl = 0o510;
        assert_eq!(Input::KeyDL, to_special_keycode(keyDl));
    }

    #[test]
    fn test_key_f15_to_input() {
        let keyF15 = 0o410 + 15;
        assert_eq!(Input::KeyF15, to_special_keycode(keyF15));
    }

    #[test]
    fn test_key_up_to_input() {
        let keyUp = 0o403;
        assert_eq!(Input::KeyUp, to_special_keycode(keyUp));
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2017 The imgui-rs Developers
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::time::{Duration, Instant};

use glium::glutin;
use glium::{Display, Surface};
use imgui::{FontGlyphRange, ImFontConfig, ImGui, Ui};
use imgui_glium_renderer::Renderer;

const FRAME_DURATION: Duration = Duration::from_millis(50);

#[derive(Copy, Clone, PartialEq, Debug, Default)]
struct MouseState {
    pos: (i32, i32),
    pressed: (bool, bool, bool),
    wheel: f32,
}

pub fn run<F: FnMut(&Ui, (f32, f32)) -> bool>(
    title: String,
    dimensions: (u32, u32),
    clear_color: [f32; 4],
    mut events_loop: glutin::EventsLoop,
    mut run_ui: F,
) {
    let window = glutin::WindowBuilder::new()
        .with_title(title)
        .with_dimensions(dimensions.0, dimensions.1);
    let context = glutin::ContextBuilder::new().with_vsync(true);
    let display = Display::new(window, context, &events_loop).unwrap();

    let mut imgui = ImGui::init();
    imgui.set_ini_filename(None);

    let config = ImFontConfig::new()
        .oversample_h(4)
        .oversample_v(4)
        .size_pixels(21.0)
        .rasterizer_multiply(1.05);

    config.add_font(
        &mut imgui.fonts(),
        include_bytes!("../fonts/FiraSans-Regular.ttf"),
        &FontGlyphRange::default(),
    );

    let mut renderer = Renderer::init(&mut imgui, &display).expect("Failed to initialize renderer");

    let mut last_frame = Instant::now();
    let mut mouse_state = MouseState::default();

    let mut render = |imgui: &mut ImGui, last_frame: &mut Instant| {
        let now = Instant::now();
        let delta = now - *last_frame;
        let delta_s = delta.as_secs() as f32 + delta.subsec_nanos() as f32 / 1_000_000_000.0;
        *last_frame = now;

        let gl_window = display.gl_window();
        let size_pixels = gl_window.get_inner_size().unwrap();
        let hidpi = gl_window.hidpi_factor();
        let size_points = (
            (size_pixels.0 as f32 / hidpi) as u32,
            (size_pixels.1 as f32 / hidpi) as u32,
        );

        let ui = imgui.frame(size_points, size_pixels, delta_s);
        if !run_ui(&ui, (size_points.0 as f32, size_points.1 as f32)) {
            return false;
        }

        let mut target = display.draw();
        target.clear_color(
            clear_color[0],
            clear_color[1],
            clear_color[2],
            clear_color[3],
        );
        renderer.render(&mut target, ui).expect("Rendering failed");
        target.finish().unwrap();
        true
    };

    // Render one frame before the event loop so the screen isn't empty
    render(&mut imgui, &mut last_frame);

    events_loop.run_forever(|event| {
        use glium::glutin::ElementState::Pressed;
        use glium::glutin::WindowEvent::*;
        use glium::glutin::{ControlFlow, Event, MouseButton, VirtualKeyCode};

        if let Event::Awakened = event {
            // Render twice to immediately show the new AI move
            if !render(&mut imgui, &mut last_frame) {
                return ControlFlow::Break;
            }
            if !render(&mut imgui, &mut last_frame) {
                return ControlFlow::Break;
            }
        } else if let Event::WindowEvent { event, .. } = event {
            match event {
                Closed => return ControlFlow::Break,
                KeyboardInput { input, .. } => {
                    if let Some(VirtualKeyCode::Q) = input.virtual_keycode {
                        if cfg!(target_os = "macos") && input.modifiers.logo {
                            return ControlFlow::Break;
                        }
                    }
                }
                Refresh | Resized(_, _) | HiDPIFactorChanged(_) => {
                    if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                CursorMoved {
                    position: (x, y), ..
                } => {
                    mouse_state.pos = (x as i32, y as i32);
                    update_mouse(&mut imgui, &mut mouse_state);

                    if Instant::now() - last_frame < FRAME_DURATION {
                        return ControlFlow::Continue;
                    } else if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                MouseInput { state, button, .. } => if MouseButton::Left == button {
                    mouse_state.pressed.0 = state == Pressed;
                    update_mouse(&mut imgui, &mut mouse_state);

                    // Render twice to immediately show the results of the click
                    if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                    if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                },
                _ => (),
            }
        }
        ControlFlow::Continue
    });
}

fn update_mouse(imgui: &mut ImGui, mouse_state: &mut MouseState) {
    let scale = imgui.display_framebuffer_scale();
    imgui.set_mouse_pos(
        mouse_state.pos.0 as f32 / scale.0,
        mouse_state.pos.1 as f32 / scale.1,
    );
    imgui.set_mouse_down(&[
        mouse_state.pressed.0,
        mouse_state.pressed.1,
        mouse_state.pressed.2,
        false,
        false,
    ]);
    imgui.set_mouse_wheel(mouse_state.wheel / scale.1);
    mouse_state.wheel = 0.0;
}
<commit_msg>Bump FPS back up to 60fps<commit_after>/*
 * Copyright (C) 2015-2017 The imgui-rs Developers
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::time::{Duration, Instant};

use glium::glutin;
use glium::{Display, Surface};
use imgui::{FontGlyphRange, ImFontConfig, ImGui, Ui};
use imgui_glium_renderer::Renderer;

const FRAME_DURATION: Duration = Duration::from_millis(16);

#[derive(Copy, Clone, PartialEq, Debug, Default)]
struct MouseState {
    pos: (i32, i32),
    pressed: (bool, bool, bool),
    wheel: f32,
}

pub fn run<F: FnMut(&Ui, (f32, f32)) -> bool>(
    title: String,
    dimensions: (u32, u32),
    clear_color: [f32; 4],
    mut events_loop: glutin::EventsLoop,
    mut run_ui: F,
) {
    let window = glutin::WindowBuilder::new()
        .with_title(title)
        .with_dimensions(dimensions.0, dimensions.1);
    let context = glutin::ContextBuilder::new().with_vsync(true);
    let display = Display::new(window, context, &events_loop).unwrap();

    let mut imgui = ImGui::init();
    imgui.set_ini_filename(None);

    let config = ImFontConfig::new()
        .oversample_h(4)
        .oversample_v(4)
        .size_pixels(21.0)
        .rasterizer_multiply(1.05);

    config.add_font(
        &mut imgui.fonts(),
        include_bytes!("../fonts/FiraSans-Regular.ttf"),
        &FontGlyphRange::default(),
    );

    let mut renderer = Renderer::init(&mut imgui, &display).expect("Failed to initialize renderer");

    let mut last_frame = Instant::now();
    let mut mouse_state = MouseState::default();

    let mut render = |imgui: &mut ImGui, last_frame: &mut Instant| {
        let now = Instant::now();
        let delta = now - *last_frame;
        let delta_s = delta.as_secs() as f32 + delta.subsec_nanos() as f32 / 1_000_000_000.0;
        *last_frame = now;

        let gl_window = display.gl_window();
        let size_pixels = gl_window.get_inner_size().unwrap();
        let hidpi = gl_window.hidpi_factor();
        let size_points = (
            (size_pixels.0 as f32 / hidpi) as u32,
            (size_pixels.1 as f32 / hidpi) as u32,
        );

        let ui = imgui.frame(size_points, size_pixels, delta_s);
        if !run_ui(&ui, (size_points.0 as f32, size_points.1 as f32)) {
            return false;
        }

        let mut target = display.draw();
        target.clear_color(
            clear_color[0],
            clear_color[1],
            clear_color[2],
            clear_color[3],
        );
        renderer.render(&mut target, ui).expect("Rendering failed");
        target.finish().unwrap();
        true
    };

    // Render one frame before the event loop so the screen isn't empty
    render(&mut imgui, &mut last_frame);

    events_loop.run_forever(|event| {
        use glium::glutin::ElementState::Pressed;
        use glium::glutin::WindowEvent::*;
        use glium::glutin::{ControlFlow, Event, MouseButton, VirtualKeyCode};

        if let Event::Awakened = event {
            // Render twice to immediately show the new AI move
            if !render(&mut imgui, &mut last_frame) {
                return ControlFlow::Break;
            }
            if !render(&mut imgui, &mut last_frame) {
                return ControlFlow::Break;
            }
        } else if let Event::WindowEvent { event, .. } = event {
            match event {
                Closed => return ControlFlow::Break,
                KeyboardInput { input, .. } => {
                    if let Some(VirtualKeyCode::Q) = input.virtual_keycode {
                        if cfg!(target_os = "macos") && input.modifiers.logo {
                            return ControlFlow::Break;
                        }
                    }
                }
                Refresh | Resized(_, _) | HiDPIFactorChanged(_) => {
                    if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                CursorMoved {
                    position: (x, y), ..
                } => {
                    mouse_state.pos = (x as i32, y as i32);
                    update_mouse(&mut imgui, &mut mouse_state);

                    if Instant::now() - last_frame < FRAME_DURATION {
                        return ControlFlow::Continue;
                    } else if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                MouseInput { state, button, .. } => if MouseButton::Left == button {
                    mouse_state.pressed.0 = state == Pressed;
                    update_mouse(&mut imgui, &mut mouse_state);

                    // Render twice to immediately show the results of the click
                    if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                    if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                },
                _ => (),
            }
        }
        ControlFlow::Continue
    });
}

fn update_mouse(imgui: &mut ImGui, mouse_state: &mut MouseState) {
    let scale = imgui.display_framebuffer_scale();
    imgui.set_mouse_pos(
        mouse_state.pos.0 as f32 / scale.0,
        mouse_state.pos.1 as f32 / scale.1,
    );
    imgui.set_mouse_down(&[
        mouse_state.pressed.0,
        mouse_state.pressed.1,
        mouse_state.pressed.2,
        false,
        false,
    ]);
    imgui.set_mouse_wheel(mouse_state.wheel / scale.1);
    mouse_state.wheel = 0.0;
}
<|endoftext|>


<commit_before>// This module implements simple request and response objects.

use std::fmt;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::io::{Read, Write};

use hyper;
use hyper::uri::RequestUri::{AbsolutePath, AbsoluteUri, Authority, Star};
use hyper::header::{Headers, ContentLength, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::method::Method;
use url;
use url::UrlParser;
use url::form_urlencoded;

use app::Pencil;
use datastructures::MultiDict;
use httputils::{get_name_by_http_code, get_content_type, get_host_value};
use httputils::get_status_from_code;
use routing::Rule;
use types::ViewArgs;
use http_errors::{HTTPError, NotFound};

/// Request type.
pub struct Request<'r, 'a, 'b: 'a> {
    pub app: &'r Pencil,
    /// The original hyper request object.
    pub request: hyper::server::request::Request<'a, 'b>,
    url: Option<url::Url>,
    /// The URL rule that matched the request.  This is
    /// going to be `None` if nothing matched.
    pub url_rule: Option<Rule>,
    /// A dict of view arguments that matched the request.
    pub view_args: ViewArgs,
    /// If matching the URL failed, this will be the error.
    pub routing_error: Option<HTTPError>,
    args: Option<MultiDict>,
    form: Option<MultiDict>,
}

impl<'r, 'a, 'b: 'a> Request<'r, 'a, 'b> {
    /// Create a `Request`.
    pub fn new(app: &'r Pencil, request: hyper::server::request::Request<'a, 'b>) -> Request<'r, 'a, 'b> {
        let url = match request.uri {
            AbsolutePath(ref url) => {
                let host: Option<&hyper::header::Host> = request.headers.get();
                match host {
                    Some(host) => {
                        let full_url = String::from("http://") + &get_host_value(host) +
                            "/" + &url.trim_left_matches('/');
                        match url::Url::parse(&full_url) {
                            Ok(url) => Some(url),
                            Err(_) => None,
                        }
                    },
                    None => None,
                }
            },
            _ => None,
        };
        Request {
            app: app,
            request: request,
            url: url,
            url_rule: None,
            view_args: HashMap::new(),
            routing_error: None,
            args: None,
            form: None,
        }
    }

    /// Match the request, set the `url_rule` and `view_args` field.
    pub fn match_request(&mut self) {
        match self.path() {
            Some(path) => {
                let url_adapter = self.app.url_map.bind(path, self.method());
                match url_adapter.matched() {
                    Ok(caps) => {
                        let (rule, view_args) = caps;
                        self.url_rule = Some(rule);
                        self.view_args = view_args;
                    },
                    Err(e) => {
                        self.routing_error = Some(e);
                    },
                }
            },
            None => {
                self.routing_error = Some(NotFound);
            }
        }
    }

    /// The endpoint that matched the request.
    pub fn endpoint(&self) -> Option<String> {
        match self.url_rule {
            Some(ref rule) => Some(rule.endpoint.clone()),
            None => None,
        }
    }

    /// The parsed URL parameters.
    pub fn args(&mut self) -> &MultiDict {
        if self.args.is_none() {
            let mut args = MultiDict::new();
            let query_pairs = match self.request.uri {
                AbsolutePath(ref path) => {
                    match UrlParser::new().parse_path(path) {
                        Ok((_, query, _)) => {
                            query.map(|query| form_urlencoded::parse(query.as_bytes()))
                        },
                        Err(_) => None
                    }
                },
                AbsoluteUri(ref url) => {
                    url.query_pairs()
                },
                Authority(_) | Star => None
            };
            match query_pairs {
                Some(pairs) => {
                    for &(ref k, ref v) in pairs.iter() {
                        args.add(&k, &v);
                    }
                },
                None => {},
            }
            self.args = Some(args);
        }
        return self.args.as_ref().unwrap();
    }

    /// Get content type.
    fn content_type(&self) -> Option<ContentType> {
        let content_type: Option<&ContentType> = self.request.headers.get();
        content_type.map(|c| c.clone())
    }

    /// This method is used internally to retrieve submitted data.
    fn load_form_data(&mut self) {
        if self.form.is_some() {
            return
        }
        let form = match self.content_type() {
            Some(ContentType(Mime(toplevel, sublevel, _))) => {
                if toplevel == TopLevel::Application && sublevel == SubLevel::WwwFormUrlEncoded {
                    let mut body: Vec<u8> = Vec::new();
                    self.request.read_to_end(&mut body).unwrap();
                    let mut form = MultiDict::new();
                    for &(ref k, ref v) in form_urlencoded::parse(&body).iter() {
                        form.add(&k, &v);
                    }
                    form
                } else {
                    MultiDict::new()
                }
            },
            None => {
                MultiDict::new()
            }
        };
        self.form = Some(form);
    }

    /// The form parameters.
    pub fn form(&mut self) -> &MultiDict {
        self.load_form_data();
        self.form.as_ref().unwrap()
    }

    /// The headers.
    pub fn headers(&self) -> &Headers {
        &self.request.headers
    }

    /// Requested path.
    pub fn path(&self) -> Option<String> {
        match self.request.uri {
            AbsolutePath(ref path) => {
                Some(path.splitn(2, '?').next().unwrap().to_string())
            },
            AbsoluteUri(ref url) => {
                url.serialize_path()
            },
            Authority(_) | Star => None
        }
    }

    /// Requested path including the query string.
    pub fn full_path(&self) -> Option<String> {
        let path = self.path();
        let query_string = self.query_string();
        if path.is_some() && query_string.is_some() {
            return Some(path.unwrap() + "?" + &query_string.unwrap());
        } else  {
            return path;
        }
    }

    /// The host including the port if available.
    pub fn host(&self) -> Option<String> {
        let host: Option<&hyper::header::Host> = self.request.headers.get();
        host.map(|host| get_host_value(host))
    }

    /// The URL parameters as raw String.
    pub fn query_string(&self) -> Option<String> {
        if self.url.is_some() {
            return self.url.as_ref().unwrap().query.clone();
        } else {
            return None;
        }
    }

    /// The requested method.
    pub fn method(&self) -> String {
        match self.request.method {
            Method::Options => "OPTIONS".to_string(),
            Method::Get => "GET".to_string(),
            Method::Post => "POST".to_string(),
            Method::Put => "PUT".to_string(),
            Method::Delete => "DELETE".to_string(),
            Method::Head => "HEAD".to_string(),
            Method::Trace => "TRACE".to_string(),
            Method::Connect => "CONNECT".to_string(),
            Method::Patch => "PATCH".to_string(),
            Method::Extension(ref method) => method.clone(),
        }
    }

    /// The remote address of the client.
    pub fn remote_addr(&self) -> SocketAddr {
        self.request.remote_addr.clone()
    }

    /// URL scheme (http or https), currently I do not know how to get
    /// this, the result will always be http.
    pub fn scheme(&self) -> String {
        String::from("http")
    }

    /// Just the host with scheme.
    pub fn host_url(&self) -> Option<String> {
        match self.host() {
            Some(host) => {
                Some(self.scheme() + "://" + &host + "/")
            },
            None => None,
        }
    }

    /// The current url.
    pub fn url(&self) -> Option<String> {
        let host_url = self.host_url();
        let full_path = self.full_path();
        if host_url.is_some() && full_path.is_some() {
            Some(host_url.unwrap() + &(full_path.unwrap()).trim_left_matches('/'))
        } else {
            None
        }
    }

    /// The current url without the query string.
    pub fn base_url(&self) -> Option<String> {
        let host_url = self.host_url();
        let path = self.path();
        if host_url.is_some() && path.is_some() {
            Some(host_url.unwrap() + &path.unwrap().trim_left_matches('/'))
        } else {
            None
        }
    }

    /// Whether the request is secure (https).
    pub fn is_secure(&self) -> bool {
        self.scheme() == "https".to_string()
    }
}

impl<'r, 'a, 'b: 'a> fmt::Debug for Request<'r, 'a, 'b> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.url() {
            Some(url) => {
                write!(f, "Pencil Request '{}' {}", url, self.method())
            },
            None => {
                write!(f, "Pencil Request")
            }
        }
    }
}

/// Response type.  It is just one container with a couple of parameters
/// (headers, body, status code etc).
#[derive(Clone)]
pub struct Response {
    pub status_code: isize,
    pub headers: Headers,
    pub body: String,
}

impl Response {
    /// Create a `Response`.
    pub fn new(body: String) -> Response {
        let mut response = Response {
            status_code: 200,
            headers: Headers::new(),
            body: body,
        };
        let mime: Mime = "text/html; charset=UTF-8".parse().unwrap();
        let content_type = ContentType(mime);
        let content_length = ContentLength(response.body.len() as u64);
        response.headers.set(content_type);
        response.headers.set(content_length);
        return response;
    }

    /// Get status name.
    pub fn status_name(&self) -> &str {
        match get_name_by_http_code(self.status_code) {
            Some(name) => name,
            None => "UNKNOWN",
        }
    }

    /// Sets a new string as response body.  The content length is set
    /// automatically.
    pub fn set_data(&mut self, value: String) {
        self.body = value;
        let content_length = self.body.len();
        self.set_content_length(content_length);
    }

    /// Returns the response content type if available.
    pub fn content_type(&self) -> Option<&ContentType> {
        self.headers.get()
    }

    /// Set response content type.
    pub fn set_content_type(&mut self, mimetype: &str) {
        let mimetype = get_content_type(mimetype, "UTF-8");
        let mime: Mime = (&mimetype).parse().unwrap();
        let content_type = ContentType(mime);
        self.headers.set(content_type);
    }

    /// Returns the response content length if available.
    pub fn content_length(&self) -> Option<usize> {
        let content_length: Option<&ContentLength> = self.headers.get();
        match content_length {
            Some(&ContentLength(length)) => Some(length as usize),
            None => None,
        }
    }

    /// Set content length.
    pub fn set_content_length(&mut self, value: usize) {
        let content_length = ContentLength(value as u64);
        self.headers.set(content_length);
    }

    /// Sets a cookie(TODO).
    pub fn set_cookie(&mut self) {
    }

    /// Delete a cookie(TODO).
    pub fn delete_cookie(&mut self) {
    }

    /// Return `true` if the response is streamed(currently we do not support
    /// streaming yet, always return `false`).
    pub fn is_streamed(&self) -> bool {
        false
    }

    /// Write the response out.  Mostly you shouldn't use this directly.
    pub fn write(self, request_method: String, mut res: hyper::server::Response) {
        // write status.
        let status_code = self.status_code;
        *res.status_mut() = get_status_from_code(status_code);

        // write headers.
        *res.headers_mut() = self.headers;

        // write data.
        if request_method == String::from("HEAD") ||
           (100 <= status_code && status_code < 200) ||
           status_code == 204 || status_code == 304 {
            res.headers_mut().set(ContentLength(0));
            try_return!(res.start().and_then(|w| w.end()));
        } else {
            let mut res = try_return!(res.start());
            try_return!(res.write_all(self.body.as_bytes()));
            try_return!(res.end());
        }
    }
}
<commit_msg>Added scheme for request wrapper<commit_after>// This module implements simple request and response objects.

use std::fmt;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::io::{Read, Write};

use hyper;
use hyper::uri::RequestUri::{AbsolutePath, AbsoluteUri, Authority, Star};
use hyper::header::{Headers, ContentLength, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::method::Method;
use url::UrlParser;
use url::form_urlencoded;

use app::Pencil;
use datastructures::MultiDict;
use httputils::{get_name_by_http_code, get_content_type, get_host_value};
use httputils::get_status_from_code;
use routing::Rule;
use types::ViewArgs;
use http_errors::{HTTPError, NotFound};

/// Request type.
pub struct Request<'r, 'a, 'b: 'a> {
    pub app: &'r Pencil,
    /// The original hyper request object.
    pub request: hyper::server::request::Request<'a, 'b>,
    /// The URL rule that matched the request.  This is
    /// going to be `None` if nothing matched.
    pub url_rule: Option<Rule>,
    /// A dict of view arguments that matched the request.
    pub view_args: ViewArgs,
    /// If matching the URL failed, this will be the error.
    pub routing_error: Option<HTTPError>,
    args: Option<MultiDict>,
    form: Option<MultiDict>,
}

impl<'r, 'a, 'b: 'a> Request<'r, 'a, 'b> {
    /// Create a `Request`.
    pub fn new(app: &'r Pencil, request: hyper::server::request::Request<'a, 'b>) -> Request<'r, 'a, 'b> {
        Request {
            app: app,
            request: request,
            url_rule: None,
            view_args: HashMap::new(),
            routing_error: None,
            args: None,
            form: None,
        }
    }

    /// Match the request, set the `url_rule` and `view_args` field.
    pub fn match_request(&mut self) {
        match self.path() {
            Some(path) => {
                let url_adapter = self.app.url_map.bind(path, self.method());
                match url_adapter.matched() {
                    Ok(caps) => {
                        let (rule, view_args) = caps;
                        self.url_rule = Some(rule);
                        self.view_args = view_args;
                    },
                    Err(e) => {
                        self.routing_error = Some(e);
                    },
                }
            },
            None => {
                self.routing_error = Some(NotFound);
            }
        }
    }

    /// The endpoint that matched the request.
    pub fn endpoint(&self) -> Option<String> {
        match self.url_rule {
            Some(ref rule) => Some(rule.endpoint.clone()),
            None => None,
        }
    }

    /// The parsed URL parameters.
    pub fn args(&mut self) -> &MultiDict {
        if self.args.is_none() {
            let mut args = MultiDict::new();
            let query_pairs = self.query_string().map(|query| form_urlencoded::parse(query.as_bytes()));
            match query_pairs {
                Some(pairs) => {
                    for &(ref k, ref v) in pairs.iter() {
                        args.add(&k, &v);
                    }
                },
                None => {},
            }
            self.args = Some(args);
        }
        return self.args.as_ref().unwrap();
    }

    /// Get content type.
    fn content_type(&self) -> Option<ContentType> {
        let content_type: Option<&ContentType> = self.request.headers.get();
        content_type.map(|c| c.clone())
    }

    /// This method is used internally to retrieve submitted data.
    fn load_form_data(&mut self) {
        if self.form.is_some() {
            return
        }
        let form = match self.content_type() {
            Some(ContentType(Mime(toplevel, sublevel, _))) => {
                if toplevel == TopLevel::Application && sublevel == SubLevel::WwwFormUrlEncoded {
                    let mut body: Vec<u8> = Vec::new();
                    self.request.read_to_end(&mut body).unwrap();
                    let mut form = MultiDict::new();
                    for &(ref k, ref v) in form_urlencoded::parse(&body).iter() {
                        form.add(&k, &v);
                    }
                    form
                } else {
                    MultiDict::new()
                }
            },
            None => {
                MultiDict::new()
            }
        };
        self.form = Some(form);
    }

    /// The form parameters.
    pub fn form(&mut self) -> &MultiDict {
        self.load_form_data();
        self.form.as_ref().unwrap()
    }

    /// The headers.
    pub fn headers(&self) -> &Headers {
        &self.request.headers
    }

    /// Requested path.
    pub fn path(&self) -> Option<String> {
        match self.request.uri {
            AbsolutePath(ref path) => {
                Some(path.splitn(2, '?').next().unwrap().to_string())
            },
            AbsoluteUri(ref url) => {
                url.serialize_path()
            },
            Authority(_) | Star => None
        }
    }

    /// Requested path including the query string.
    pub fn full_path(&self) -> Option<String> {
        let path = self.path();
        let query_string = self.query_string();
        if path.is_some() && query_string.is_some() {
            return Some(path.unwrap() + "?" + &query_string.unwrap());
        } else  {
            return path;
        }
    }

    /// The host including the port if available.
    pub fn host(&self) -> Option<String> {
        let host: Option<&hyper::header::Host> = self.request.headers.get();
        host.map(|host| get_host_value(host))
    }

    /// The query string.
    pub fn query_string(&self) -> Option<String> {
        match self.request.uri {
            AbsolutePath(ref path) => {
                match UrlParser::new().parse_path(path) {
                    Ok((_, query, _)) => {
                        query
                    },
                    Err(_) => None
                }
            },
            AbsoluteUri(ref url) => {
                url.query.clone()
            },
            Authority(_) | Star => None
        }
    }

    /// The requested method.
    pub fn method(&self) -> String {
        match self.request.method {
            Method::Options => "OPTIONS".to_string(),
            Method::Get => "GET".to_string(),
            Method::Post => "POST".to_string(),
            Method::Put => "PUT".to_string(),
            Method::Delete => "DELETE".to_string(),
            Method::Head => "HEAD".to_string(),
            Method::Trace => "TRACE".to_string(),
            Method::Connect => "CONNECT".to_string(),
            Method::Patch => "PATCH".to_string(),
            Method::Extension(ref method) => method.clone(),
        }
    }

    /// The remote address of the client.
    pub fn remote_addr(&self) -> SocketAddr {
        self.request.remote_addr.clone()
    }

    /// URL scheme (http or https)
    pub fn scheme(&self) -> String {
        match self.request.uri {
            AbsoluteUri(ref url) => {
                return url.scheme.clone();
            },
            _ => {}
        }
        String::from("http")
    }

    /// Just the host with scheme.
    pub fn host_url(&self) -> Option<String> {
        match self.host() {
            Some(host) => {
                Some(self.scheme() + "://" + &host + "/")
            },
            None => None,
        }
    }

    /// The current url.
    pub fn url(&self) -> Option<String> {
        let host_url = self.host_url();
        let full_path = self.full_path();
        if host_url.is_some() && full_path.is_some() {
            Some(host_url.unwrap() + &(full_path.unwrap()).trim_left_matches('/'))
        } else {
            None
        }
    }

    /// The current url without the query string.
    pub fn base_url(&self) -> Option<String> {
        let host_url = self.host_url();
        let path = self.path();
        if host_url.is_some() && path.is_some() {
            Some(host_url.unwrap() + &path.unwrap().trim_left_matches('/'))
        } else {
            None
        }
    }

    /// Whether the request is secure (https).
    pub fn is_secure(&self) -> bool {
        self.scheme() == "https".to_string()
    }
}

impl<'r, 'a, 'b: 'a> fmt::Debug for Request<'r, 'a, 'b> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.url() {
            Some(url) => {
                write!(f, "Pencil Request '{}' {}", url, self.method())
            },
            None => {
                write!(f, "Pencil Request")
            }
        }
    }
}

/// Response type.  It is just one container with a couple of parameters
/// (headers, body, status code etc).
#[derive(Clone)]
pub struct Response {
    pub status_code: isize,
    pub headers: Headers,
    pub body: String,
}

impl Response {
    /// Create a `Response`.
    pub fn new(body: String) -> Response {
        let mut response = Response {
            status_code: 200,
            headers: Headers::new(),
            body: body,
        };
        let mime: Mime = "text/html; charset=UTF-8".parse().unwrap();
        let content_type = ContentType(mime);
        let content_length = ContentLength(response.body.len() as u64);
        response.headers.set(content_type);
        response.headers.set(content_length);
        return response;
    }

    /// Get status name.
    pub fn status_name(&self) -> &str {
        match get_name_by_http_code(self.status_code) {
            Some(name) => name,
            None => "UNKNOWN",
        }
    }

    /// Sets a new string as response body.  The content length is set
    /// automatically.
    pub fn set_data(&mut self, value: String) {
        self.body = value;
        let content_length = self.body.len();
        self.set_content_length(content_length);
    }

    /// Returns the response content type if available.
    pub fn content_type(&self) -> Option<&ContentType> {
        self.headers.get()
    }

    /// Set response content type.
    pub fn set_content_type(&mut self, mimetype: &str) {
        let mimetype = get_content_type(mimetype, "UTF-8");
        let mime: Mime = (&mimetype).parse().unwrap();
        let content_type = ContentType(mime);
        self.headers.set(content_type);
    }

    /// Returns the response content length if available.
    pub fn content_length(&self) -> Option<usize> {
        let content_length: Option<&ContentLength> = self.headers.get();
        match content_length {
            Some(&ContentLength(length)) => Some(length as usize),
            None => None,
        }
    }

    /// Set content length.
    pub fn set_content_length(&mut self, value: usize) {
        let content_length = ContentLength(value as u64);
        self.headers.set(content_length);
    }

    /// Sets a cookie(TODO).
    pub fn set_cookie(&mut self) {
    }

    /// Delete a cookie(TODO).
    pub fn delete_cookie(&mut self) {
    }

    /// Return `true` if the response is streamed(currently we do not support
    /// streaming yet, always return `false`).
    pub fn is_streamed(&self) -> bool {
        false
    }

    /// Write the response out.  Mostly you shouldn't use this directly.
    pub fn write(self, request_method: String, mut res: hyper::server::Response) {
        // write status.
        let status_code = self.status_code;
        *res.status_mut() = get_status_from_code(status_code);

        // write headers.
        *res.headers_mut() = self.headers;

        // write data.
        if request_method == String::from("HEAD") ||
           (100 <= status_code && status_code < 200) ||
           status_code == 204 || status_code == 304 {
            res.headers_mut().set(ContentLength(0));
            try_return!(res.start().and_then(|w| w.end()));
        } else {
            let mut res = try_return!(res.start());
            try_return!(res.write_all(self.body.as_bytes()));
            try_return!(res.end());
        }
    }
}
<|endoftext|>