<commit_before>//
// Sysinfo
//
// Copyright (c) 2015 Guillaume Gomez
//

#![allow(clippy::too_many_arguments)]

use std::collections::HashSet;
use std::fs::File;
use std::io::Read;

use crate::ProcessorExt;

/// Struct containing values to compute a CPU usage.
#[derive(Clone, Copy)]
pub struct CpuValues {
    user: u64,
    nice: u64,
    system: u64,
    idle: u64,
    iowait: u64,
    irq: u64,
    softirq: u64,
    steal: u64,
    guest: u64,
    guest_nice: u64,
}

impl CpuValues {
    /// Creates a new instance of `CpuValues` with everything set to `0`.
    pub fn new() -> CpuValues {
        CpuValues {
            user: 0,
            nice: 0,
            system: 0,
            idle: 0,
            iowait: 0,
            irq: 0,
            softirq: 0,
            steal: 0,
            guest: 0,
            guest_nice: 0,
        }
    }

    /// Creates a new instance of `CpuValues` with everything set to the corresponding argument.
    pub fn new_with_values(
        user: u64,
        nice: u64,
        system: u64,
        idle: u64,
        iowait: u64,
        irq: u64,
        softirq: u64,
        steal: u64,
        guest: u64,
        guest_nice: u64,
    ) -> CpuValues {
        CpuValues {
            user,
            nice,
            system,
            idle,
            iowait,
            irq,
            softirq,
            steal,
            guest,
            guest_nice,
        }
    }

    /*pub fn is_zero(&self) -> bool {
        self.user == 0 && self.nice == 0 && self.system == 0 && self.idle == 0 &&
        self.iowait == 0 && self.irq == 0 && self.softirq == 0 && self.steal == 0 &&
        self.guest == 0 && self.guest_nice == 0
    }*/

    /// Sets the given argument to the corresponding fields.
    pub fn set(
        &mut self,
        user: u64,
        nice: u64,
        system: u64,
        idle: u64,
        iowait: u64,
        irq: u64,
        softirq: u64,
        steal: u64,
        guest: u64,
        guest_nice: u64,
    ) {
        self.user = user;
        self.nice = nice;
        self.system = system;
        self.idle = idle;
        self.iowait = iowait;
        self.irq = irq;
        self.softirq = softirq;
        self.steal = steal;
        self.guest = guest;
        self.guest_nice = guest_nice;
    }

    /// Returns work time.
    pub fn work_time(&self) -> u64 {
        self.user + self.nice + self.system + self.irq + self.softirq + self.steal
    }

    /// Returns total time.
    pub fn total_time(&self) -> u64 {
        // `guest` is already included in `user`
        // `guest_nice` is already included in `nice`
        self.work_time() + self.idle + self.iowait
    }
}

/// Struct containing a processor information.
pub struct Processor {
    old_values: CpuValues,
    new_values: CpuValues,
    pub(crate) name: String,
    cpu_usage: f32,
    total_time: u64,
    old_total_time: u64,
    pub(crate) frequency: u64,
    pub(crate) vendor_id: String,
    pub(crate) brand: String,
}

impl Processor {
    pub(crate) fn new_with_values(
        name: &str,
        user: u64,
        nice: u64,
        system: u64,
        idle: u64,
        iowait: u64,
        irq: u64,
        softirq: u64,
        steal: u64,
        guest: u64,
        guest_nice: u64,
        frequency: u64,
        vendor_id: String,
        brand: String,
    ) -> Processor {
        Processor {
            name: name.to_owned(),
            old_values: CpuValues::new(),
            new_values: CpuValues::new_with_values(
                user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice,
            ),
            cpu_usage: 0f32,
            total_time: 0,
            old_total_time: 0,
            frequency,
            vendor_id,
            brand,
        }
    }

    pub(crate) fn set(
        &mut self,
        user: u64,
        nice: u64,
        system: u64,
        idle: u64,
        iowait: u64,
        irq: u64,
        softirq: u64,
        steal: u64,
        guest: u64,
        guest_nice: u64,
    ) {
        macro_rules! min {
            ($a:expr, $b:expr) => {
                if $a > $b {
                    ($a - $b) as f32
                } else {
                    1.
                }
            };
        }
        self.old_values = self.new_values;
        self.new_values.set(
            user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice,
        );
        self.total_time = self.new_values.total_time();
        self.old_total_time = self.old_values.total_time();
        self.cpu_usage = min!(self.new_values.work_time(), self.old_values.work_time())
            / min!(self.total_time, self.old_total_time)
            * 100.;
        if self.cpu_usage > 100. {
            self.cpu_usage = 100.; // to prevent the pourcentage to go above 100%
        }
    }
}

impl ProcessorExt for Processor {
    fn get_cpu_usage(&self) -> f32 {
        self.cpu_usage
    }

    fn get_name(&self) -> &str {
        &self.name
    }

    /// Returns the CPU frequency in MHz.
    fn get_frequency(&self) -> u64 {
        self.frequency
    }

    fn get_vendor_id(&self) -> &str {
        &self.vendor_id
    }

    fn get_brand(&self) -> &str {
        &self.brand
    }
}

pub fn get_raw_times(p: &Processor) -> (u64, u64) {
    (p.total_time, p.old_total_time)
}

pub fn get_cpu_frequency(cpu_core_index: usize) -> u64 {
    let mut s = String::new();
    if File::open(format!(
        "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq",
        cpu_core_index
    ))
    .and_then(|mut f| f.read_to_string(&mut s))
    .is_ok()
    {
        let freq_option = s.trim().split('\n').next();
        if let Some(freq_string) = freq_option {
            if let Ok(freq) = freq_string.parse::<u64>() {
                return freq / 1000;
            }
        }
    }
    s.clear();
    if File::open("/proc/cpuinfo")
        .and_then(|mut f| f.read_to_string(&mut s))
        .is_err()
    {
        return 0;
    }
    let find_cpu_mhz = s.split('\n').find(|line| {
        line.starts_with("cpu MHz\t")
            || line.starts_with("BogoMIPS")
            || line.starts_with("clock\t")
            || line.starts_with("bogomips per cpu")
    });
    find_cpu_mhz
        .and_then(|line| line.split(':').last())
        .and_then(|val| val.replace("MHz", "").trim().parse::<f64>().ok())
        .map(|speed| speed as u64)
        .unwrap_or_default()
}

pub fn get_physical_core_count() -> Option<usize> {
    let mut s = String::new();
    if File::open("/proc/cpuinfo")
        .and_then(|mut f| f.read_to_string(&mut s))
        .is_err()
    {
        return None;
    }

    let mut core_ids_and_physical_ids: HashSet<String> = HashSet::new();
    let mut core_id = "";
    let mut physical_id = "";
    for line in s.lines() {
        if line.starts_with("core id") {
            core_id = line
                .splitn(2, ':')
                .last()
                .map(|x| x.trim())
                .unwrap_or_default();
        } else if line.starts_with("physical id") {
            physical_id = line
                .splitn(2, ':')
                .last()
                .map(|x| x.trim())
                .unwrap_or_default();
        }
        if !core_id.is_empty() && !physical_id.is_empty() {
            core_ids_and_physical_ids.insert(format!("{} {}", core_id, physical_id));
            core_id = "";
            physical_id = "";
        }
    }

    Some(core_ids_and_physical_ids.len())
}

/// Returns the brand/vendor string for the first CPU (which should be the same for all CPUs).
pub fn get_vendor_id_and_brand() -> (String, String) {
    let mut s = String::new();
    if File::open("/proc/cpuinfo")
        .and_then(|mut f| f.read_to_string(&mut s))
        .is_err()
    {
        return (String::new(), String::new());
    }

    fn get_value(s: &str) -> String {
        s.split(':')
            .last()
            .map(|x| x.trim().to_owned())
            .unwrap_or_default()
    }

    let mut vendor_id = None;
    let mut brand = None;

    for it in s.split('\n') {
        if it.starts_with("vendor_id\t") {
            vendor_id = Some(get_value(it));
        } else if it.starts_with("model name\t") {
            brand = Some(get_value(it));
        } else {
            continue;
        }
        if brand.is_some() && vendor_id.is_some() {
            break;
        }
    }
    (vendor_id.unwrap_or_default(), brand.unwrap_or_default())
}
<commit_msg>Prefix unused field in Linux Processor<commit_after>//
// Sysinfo
//
// Copyright (c) 2015 Guillaume Gomez
//

#![allow(clippy::too_many_arguments)]

use std::collections::HashSet;
use std::fs::File;
use std::io::Read;

use crate::ProcessorExt;

/// Struct containing values to compute a CPU usage.
#[derive(Clone, Copy)]
pub struct CpuValues {
    user: u64,
    nice: u64,
    system: u64,
    idle: u64,
    iowait: u64,
    irq: u64,
    softirq: u64,
    steal: u64,
    _guest: u64,
    _guest_nice: u64,
}

impl CpuValues {
    /// Creates a new instance of `CpuValues` with everything set to `0`.
    pub fn new() -> CpuValues {
        CpuValues {
            user: 0,
            nice: 0,
            system: 0,
            idle: 0,
            iowait: 0,
            irq: 0,
            softirq: 0,
            steal: 0,
            _guest: 0,
            _guest_nice: 0,
        }
    }

    /// Creates a new instance of `CpuValues` with everything set to the corresponding argument.
    pub fn new_with_values(
        user: u64,
        nice: u64,
        system: u64,
        idle: u64,
        iowait: u64,
        irq: u64,
        softirq: u64,
        steal: u64,
        guest: u64,
        guest_nice: u64,
    ) -> CpuValues {
        CpuValues {
            user,
            nice,
            system,
            idle,
            iowait,
            irq,
            softirq,
            steal,
            _guest: guest,
            _guest_nice: guest_nice,
        }
    }

    /*pub fn is_zero(&self) -> bool {
        self.user == 0 && self.nice == 0 && self.system == 0 && self.idle == 0 &&
        self.iowait == 0 && self.irq == 0 && self.softirq == 0 && self.steal == 0 &&
        self.guest == 0 && self.guest_nice == 0
    }*/

    /// Sets the given argument to the corresponding fields.
    pub fn set(
        &mut self,
        user: u64,
        nice: u64,
        system: u64,
        idle: u64,
        iowait: u64,
        irq: u64,
        softirq: u64,
        steal: u64,
        guest: u64,
        guest_nice: u64,
    ) {
        self.user = user;
        self.nice = nice;
        self.system = system;
        self.idle = idle;
        self.iowait = iowait;
        self.irq = irq;
        self.softirq = softirq;
        self.steal = steal;
        self._guest = guest;
        self._guest_nice = guest_nice;
    }

    /// Returns work time.
    pub fn work_time(&self) -> u64 {
        self.user + self.nice + self.system + self.irq + self.softirq + self.steal
    }

    /// Returns total time.
    pub fn total_time(&self) -> u64 {
        // `guest` is already included in `user`
        // `guest_nice` is already included in `nice`
        self.work_time() + self.idle + self.iowait
    }
}

/// Struct containing a processor information.
pub struct Processor {
    old_values: CpuValues,
    new_values: CpuValues,
    pub(crate) name: String,
    cpu_usage: f32,
    total_time: u64,
    old_total_time: u64,
    pub(crate) frequency: u64,
    pub(crate) vendor_id: String,
    pub(crate) brand: String,
}

impl Processor {
    pub(crate) fn new_with_values(
        name: &str,
        user: u64,
        nice: u64,
        system: u64,
        idle: u64,
        iowait: u64,
        irq: u64,
        softirq: u64,
        steal: u64,
        guest: u64,
        guest_nice: u64,
        frequency: u64,
        vendor_id: String,
        brand: String,
    ) -> Processor {
        Processor {
            name: name.to_owned(),
            old_values: CpuValues::new(),
            new_values: CpuValues::new_with_values(
                user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice,
            ),
            cpu_usage: 0f32,
            total_time: 0,
            old_total_time: 0,
            frequency,
            vendor_id,
            brand,
        }
    }

    pub(crate) fn set(
        &mut self,
        user: u64,
        nice: u64,
        system: u64,
        idle: u64,
        iowait: u64,
        irq: u64,
        softirq: u64,
        steal: u64,
        guest: u64,
        guest_nice: u64,
    ) {
        macro_rules! min {
            ($a:expr, $b:expr) => {
                if $a > $b {
                    ($a - $b) as f32
                } else {
                    1.
                }
            };
        }
        self.old_values = self.new_values;
        self.new_values.set(
            user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice,
        );
        self.total_time = self.new_values.total_time();
        self.old_total_time = self.old_values.total_time();
        self.cpu_usage = min!(self.new_values.work_time(), self.old_values.work_time())
            / min!(self.total_time, self.old_total_time)
            * 100.;
        if self.cpu_usage > 100. {
            self.cpu_usage = 100.; // to prevent the pourcentage to go above 100%
        }
    }
}

impl ProcessorExt for Processor {
    fn get_cpu_usage(&self) -> f32 {
        self.cpu_usage
    }

    fn get_name(&self) -> &str {
        &self.name
    }

    /// Returns the CPU frequency in MHz.
    fn get_frequency(&self) -> u64 {
        self.frequency
    }

    fn get_vendor_id(&self) -> &str {
        &self.vendor_id
    }

    fn get_brand(&self) -> &str {
        &self.brand
    }
}

pub fn get_raw_times(p: &Processor) -> (u64, u64) {
    (p.total_time, p.old_total_time)
}

pub fn get_cpu_frequency(cpu_core_index: usize) -> u64 {
    let mut s = String::new();
    if File::open(format!(
        "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq",
        cpu_core_index
    ))
    .and_then(|mut f| f.read_to_string(&mut s))
    .is_ok()
    {
        let freq_option = s.trim().split('\n').next();
        if let Some(freq_string) = freq_option {
            if let Ok(freq) = freq_string.parse::<u64>() {
                return freq / 1000;
            }
        }
    }
    s.clear();
    if File::open("/proc/cpuinfo")
        .and_then(|mut f| f.read_to_string(&mut s))
        .is_err()
    {
        return 0;
    }
    let find_cpu_mhz = s.split('\n').find(|line| {
        line.starts_with("cpu MHz\t")
            || line.starts_with("BogoMIPS")
            || line.starts_with("clock\t")
            || line.starts_with("bogomips per cpu")
    });
    find_cpu_mhz
        .and_then(|line| line.split(':').last())
        .and_then(|val| val.replace("MHz", "").trim().parse::<f64>().ok())
        .map(|speed| speed as u64)
        .unwrap_or_default()
}

pub fn get_physical_core_count() -> Option<usize> {
    let mut s = String::new();
    if File::open("/proc/cpuinfo")
        .and_then(|mut f| f.read_to_string(&mut s))
        .is_err()
    {
        return None;
    }

    let mut core_ids_and_physical_ids: HashSet<String> = HashSet::new();
    let mut core_id = "";
    let mut physical_id = "";
    for line in s.lines() {
        if line.starts_with("core id") {
            core_id = line
                .splitn(2, ':')
                .last()
                .map(|x| x.trim())
                .unwrap_or_default();
        } else if line.starts_with("physical id") {
            physical_id = line
                .splitn(2, ':')
                .last()
                .map(|x| x.trim())
                .unwrap_or_default();
        }
        if !core_id.is_empty() && !physical_id.is_empty() {
            core_ids_and_physical_ids.insert(format!("{} {}", core_id, physical_id));
            core_id = "";
            physical_id = "";
        }
    }

    Some(core_ids_and_physical_ids.len())
}

/// Returns the brand/vendor string for the first CPU (which should be the same for all CPUs).
pub fn get_vendor_id_and_brand() -> (String, String) {
    let mut s = String::new();
    if File::open("/proc/cpuinfo")
        .and_then(|mut f| f.read_to_string(&mut s))
        .is_err()
    {
        return (String::new(), String::new());
    }

    fn get_value(s: &str) -> String {
        s.split(':')
            .last()
            .map(|x| x.trim().to_owned())
            .unwrap_or_default()
    }

    let mut vendor_id = None;
    let mut brand = None;

    for it in s.split('\n') {
        if it.starts_with("vendor_id\t") {
            vendor_id = Some(get_value(it));
        } else if it.starts_with("model name\t") {
            brand = Some(get_value(it));
        } else {
            continue;
        }
        if brand.is_some() && vendor_id.is_some() {
            break;
        }
    }
    (vendor_id.unwrap_or_default(), brand.unwrap_or_default())
}
<|endoftext|>


<commit_before>use std::cmp::Eq;
use std::fmt::Display;
use std::iter::FromIterator;

trait Parser<T: Display + Eq, E> {
    fn preview(&self) -> Option<&T>;
    fn consume(&mut self) -> Option<T>;
    fn current_pos(&self) -> (i32, i32);
    fn error<S: Into<String>>(&self, message: S) -> E;

    // for state rewind with attemp! macro
    fn record(&mut self);
    fn rewind(&mut self);

    fn next(&mut self) -> Result<T, E> {
        self.consume().ok_or(self.error("unexpected eof"))
    }

    fn predicate<F>(&mut self, pred: F) -> Result<T, E>
        where F: Fn(&T) -> bool
    {
        let x = try!(self.next());
        if pred(&x) {
            Ok(x)
        } else {
            Err(self.error(format!("unexpected token {}", x)))
        }
    }

    fn atom(&mut self, expected: T) -> Result<T, E> {
        let x = try!(self.next());
        if x == expected {
            Ok(x)
        } else {
            Err(self.error(format!("unexpected token {}, expected {}", x, expected)))
        }
    }

    fn string<S, O>(&mut self, s: S) -> Result<O, E>
        where S: IntoIterator<Item = T>,
              O: FromIterator<T>
    {
        let mut res: Vec<T> = Vec::new();
        for c in s {
            match self.atom(c) {
                Ok(x) => res.push(x),
                Err(err) => return Err(err),
            }
        }
        Ok(O::from_iter(res))
    }

    fn try<O, F>(&mut self, parser: F) -> Result<O, E>
        where F: Fn(&mut Self) -> Result<O, E>
    {
        self.record();
        parser(self).map_err(|x| {
                                 self.rewind();
                                 x
                             })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::vec_deque::VecDeque;

    struct TP {
        input: VecDeque<i32>,
        recorded: Vec<i32>,
        recording: bool,
    }

    impl TP {
        fn new(input: &[i32]) -> TP {
            TP {
                input: VecDeque::from(input.to_vec()),
                recorded: vec![],
                recording: false,
            }
        }
    }

    impl Parser<i32, String> for TP {
        fn consume(&mut self) -> Option<i32> {
            self.input
                .pop_front()
                .map(|c| {
                         if self.recording {
                             self.recorded.push(c);
                         }
                         c
                     })
        }

        fn preview(&self) -> Option<&i32> {
            self.input.front()
        }

        fn current_pos(&self) -> (i32, i32) {
            (0, 0)
        }

        fn error<S: Into<String>>(&self, message: S) -> String {
            message.into()
        }

        fn record(&mut self) {
            self.recording = true;
            self.recorded.clear();
        }

        fn rewind(&mut self) {
            self.recording = false;

            while let Some(c) = self.recorded.pop() {
                self.input.push_front(c)
            }
        }
    }

    type TPR = Result<Vec<i32>, String>;

    fn err<T>(m: &str) -> Result<T, String> {
        Err(String::from(m))
    }

    #[test]
    fn next_success() {
        let mut p = TP::new(&[1, 2, 3]);
        assert_eq!(p.next(), Ok(1));
        assert_eq!(p.next(), Ok(2));
        assert_eq!(p.next(), Ok(3));
    }

    #[test]
    fn next_fail_empty() {
        let mut p = TP::new(&[]);
        assert_eq!(p.next(), err("unexpected eof"));
    }

    #[test]
    fn predicate_success() {
        let mut p = TP::new(&[2, 4, 6]);
        assert_eq!(p.predicate(|x| x % 2 == 0), Ok(2));
        assert_eq!(p.predicate(|x| x % 2 == 0), Ok(4));
        assert_eq!(p.predicate(|x| x % 2 == 0), Ok(6));
    }

    #[test]
    fn predicate_fail_empty() {
        let mut p = TP::new(&[]);
        assert_eq!(p.predicate(|x| x % 2 == 0), err("unexpected eof"));
    }

    #[test]
    fn predicate_fail_not_satisfy() {
        let mut p = TP::new(&[3, 5, 7]);
        assert_eq!(p.predicate(|x| x % 2 == 0), err("unexpected token 3"));
    }

    #[test]
    fn atom_success() {
        let mut p = TP::new(&[2, 4, 6]);
        assert_eq!(p.atom(2), Ok(2));
        assert_eq!(p.atom(4), Ok(4));
        assert_eq!(p.atom(6), Ok(6));
    }

    #[test]
    fn atom_fail_empty() {
        let mut p = TP::new(&[]);
        assert_eq!(p.atom(2), err("unexpected eof"));
    }

    #[test]
    fn atom_fail_not_expected() {
        let mut p = TP::new(&[3, 5, 7]);
        assert_eq!(p.atom(3), Ok(3));
        assert_eq!(p.atom(4), err("unexpected token 5, expected 4"));
    }

    #[test]
    fn string_success() {
        let mut p = TP::new(&[2, 4, 6]);
        assert_eq!(p.string(vec![2, 4, 6]), Ok(vec![2, 4, 6]));
    }

    #[test]
    fn string_fail_empty() {
        let mut p = TP::new(&[]);
        assert_eq!(p.string(vec![2, 4, 6]) as TPR, err("unexpected eof"));
    }

    #[test]
    fn string_fail_not_expected() {
        let mut p = TP::new(&[2, 5, 6]);
        assert_eq!(p.string(vec![2, 4, 6]) as TPR,
                   err("unexpected token 5, expected 4"));
    }

    #[test]
    fn try_success() {
        let mut p = TP::new(&[2, 4, 6]);
        assert_eq!(p.try(|p| p.string(vec![2, 4, 6])), Ok(vec![2, 4, 6]));
    }

    #[test]
    fn try_fail_recover() {
        let mut p = TP::new(&[2, 4, 6]);
        assert_eq!(p.try(|p| p.string(vec![2, 4, 7])) as TPR,
                   err("unexpected token 6, expected 7"));
        assert_eq!(p.try(|p| p.string(vec![2, 4, 6])), Ok(vec![2, 4, 6]));
    }
}
<commit_msg>Use export and import for try logic<commit_after>use std::cmp::Eq;
use std::fmt::Display;
use std::iter::FromIterator;

trait Parser<T: Display + Eq, E>: Sized {
    fn preview(&self) -> Option<&T>;
    fn consume(&mut self) -> Option<T>;
    fn current_pos(&self) -> (i32, i32);
    fn error<S: Into<String>>(&self, message: S) -> E;

    fn export(&self) -> Self;
    fn import(&mut self, backup: Self);

    fn next(&mut self) -> Result<T, E> {
        self.consume().ok_or(self.error("unexpected eof"))
    }

    fn predicate<F>(&mut self, pred: F) -> Result<T, E>
        where F: Fn(&T) -> bool
    {
        let x = try!(self.next());
        if pred(&x) {
            Ok(x)
        } else {
            Err(self.error(format!("unexpected token {}", x)))
        }
    }

    fn atom(&mut self, expected: T) -> Result<T, E> {
        let x = try!(self.next());
        if x == expected {
            Ok(x)
        } else {
            Err(self.error(format!("unexpected token {}, expected {}", x, expected)))
        }
    }

    fn string<S, O>(&mut self, s: S) -> Result<O, E>
        where S: IntoIterator<Item = T>,
              O: FromIterator<T>
    {
        let mut res: Vec<T> = Vec::new();
        for c in s {
            match self.atom(c) {
                Ok(x) => res.push(x),
                Err(err) => return Err(err),
            }
        }
        Ok(O::from_iter(res))
    }

    fn try<O, F>(&mut self, parser: F) -> Result<O, E>
        where F: Fn(&mut Self) -> Result<O, E>
    {
        let backup = self.export();
        parser(self).map_err(|x| {
                                 self.import(backup);
                                 x
                             })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::vec_deque::VecDeque;

    struct TP {
        input: VecDeque<i32>,
    }

    impl TP {
        fn new(input: &[i32]) -> TP {
            TP { input: VecDeque::from(input.to_vec()) }
        }
    }

    impl Parser<i32, String> for TP {
        fn consume(&mut self) -> Option<i32> {
            self.input.pop_front()
        }

        fn preview(&self) -> Option<&i32> {
            self.input.front()
        }

        fn current_pos(&self) -> (i32, i32) {
            (0, 0)
        }

        fn error<S: Into<String>>(&self, message: S) -> String {
            message.into()
        }

        fn export(&self) -> Self {
            TP { input: self.input.clone() }
        }

        fn import(&mut self, backup: Self) {
            self.input = backup.input;
        }
    }

    type TPR = Result<Vec<i32>, String>;

    fn err<T>(m: &str) -> Result<T, String> {
        Err(String::from(m))
    }

    #[test]
    fn next_success() {
        let mut p = TP::new(&[1, 2, 3]);
        assert_eq!(p.next(), Ok(1));
        assert_eq!(p.next(), Ok(2));
        assert_eq!(p.next(), Ok(3));
    }

    #[test]
    fn next_fail_empty() {
        let mut p = TP::new(&[]);
        assert_eq!(p.next(), err("unexpected eof"));
    }

    #[test]
    fn predicate_success() {
        let mut p = TP::new(&[2, 4, 6]);
        assert_eq!(p.predicate(|x| x % 2 == 0), Ok(2));
        assert_eq!(p.predicate(|x| x % 2 == 0), Ok(4));
        assert_eq!(p.predicate(|x| x % 2 == 0), Ok(6));
    }

    #[test]
    fn predicate_fail_empty() {
        let mut p = TP::new(&[]);
        assert_eq!(p.predicate(|x| x % 2 == 0), err("unexpected eof"));
    }

    #[test]
    fn predicate_fail_not_satisfy() {
        let mut p = TP::new(&[3, 5, 7]);
        assert_eq!(p.predicate(|x| x % 2 == 0), err("unexpected token 3"));
    }

    #[test]
    fn atom_success() {
        let mut p = TP::new(&[2, 4, 6]);
        assert_eq!(p.atom(2), Ok(2));
        assert_eq!(p.atom(4), Ok(4));
        assert_eq!(p.atom(6), Ok(6));
    }

    #[test]
    fn atom_fail_empty() {
        let mut p = TP::new(&[]);
        assert_eq!(p.atom(2), err("unexpected eof"));
    }

    #[test]
    fn atom_fail_not_expected() {
        let mut p = TP::new(&[3, 5, 7]);
        assert_eq!(p.atom(3), Ok(3));
        assert_eq!(p.atom(4), err("unexpected token 5, expected 4"));
    }

    #[test]
    fn string_success() {
        let mut p = TP::new(&[2, 4, 6]);
        assert_eq!(p.string(vec![2, 4, 6]), Ok(vec![2, 4, 6]));
    }

    #[test]
    fn string_fail_empty() {
        let mut p = TP::new(&[]);
        assert_eq!(p.string(vec![2, 4, 6]) as TPR, err("unexpected eof"));
    }

    #[test]
    fn string_fail_not_expected() {
        let mut p = TP::new(&[2, 5, 6]);
        assert_eq!(p.string(vec![2, 4, 6]) as TPR,
                   err("unexpected token 5, expected 4"));
    }

    #[test]
    fn try_success() {
        let mut p = TP::new(&[2, 4, 6]);
        assert_eq!(p.try(|p| p.string(vec![2, 4, 6])), Ok(vec![2, 4, 6]));
    }

    #[test]
    fn try_fail_recover() {
        let mut p = TP::new(&[2, 4, 6]);
        assert_eq!(p.try(|p| p.string(vec![2, 4, 7])) as TPR,
                   err("unexpected token 6, expected 7"));
        assert_eq!(p.try(|p| p.string(vec![2, 4, 6])), Ok(vec![2, 4, 6]));
    }
}
<|endoftext|>


<commit_before>//Problem        : Messed up Rugby
//Language       : Rust
//Compiled Using : rustc
//Version        : rustc 1.0.0
//Input for your program will be provided from STDIN
//Print out all output from your program to STDOUT

use std::io;
use std::io::prelude::*;
use std::cmp::Ordering;
use std::fmt::{Debug, Formatter, Error};
use std::collections::BTreeSet;

fn main() {
  let stdin = io::stdin();
  let mut x = stdin.lock().lines();
  let val = x.next().unwrap().unwrap();
  if let Ok(target_score) = val.parse::<u16>() {
    let target_scores = find_score(target_score);
    for score in target_scores {
      println!("{:?} {:?} {:?}", score.conversions, score.tries, score.kicks);
    }
  }
}

#[derive(Hash, Eq, Clone)]
struct HandEggScore {
  conversions: u16,
  tries: u16,
  kicks: u16,
  //scoreType: ScoreType,
}

impl HandEggScore {
  fn new(conversions: u16, tries: u16, kicks: u16,) -> HandEggScore {
    HandEggScore { conversions:conversions, tries:tries, kicks:kicks }
  }
  fn points(&self) -> u16 {
    //println!("{:?}, {:?}, {:?}", self.conversions, self.tries, self.kicks);
    self.conversions * 7 + self.tries * 3 + self.kicks * 2
  }
}

impl PartialEq for HandEggScore {
  fn eq(&self, other: &HandEggScore) -> bool {
    self.conversions == other.conversions
    && self.tries == other.tries
    && self.kicks == other.kicks
  }
}

impl Ord for HandEggScore {
    fn cmp(&self, other: &HandEggScore) -> Ordering {
      match self.conversions.cmp(&other.conversions) {
        Ordering::Equal => match self.tries.cmp(&other.tries) {
          Ordering::Equal => self.tries.cmp(&other.tries),
          a => return a,
        },
        a => return a,
      }
    }
}

impl PartialOrd for HandEggScore {
  fn partial_cmp(&self, other: &HandEggScore) -> Option<Ordering> {
    Some(self.cmp(other))
  }
}

impl Debug for HandEggScore {
  fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
    write!(f, "{} {} {}",
      self.conversions, self.tries, self.kicks)
  }
}

fn find_score(target_score: u16) -> Vec<HandEggScore> {
  let mut target_scores : BTreeSet<HandEggScore> = BTreeSet::new();
  for conversion in 0..(target_score/7 + 1) {
    for trie in 0..((target_score - conversion*7)/3 + 1) {
      for kick in 0..((target_score - conversion*7 - trie*3)/2 + 1) {
        let score = HandEggScore::new(conversion,
          trie, kick);
        match score.points().cmp(&target_score) {
          Ordering::Greater => {break;},
          Ordering::Equal => {target_scores.insert(score);},
          _ => (),
        }
      }
    }
  }
  if target_scores.is_empty() {
    vec!(HandEggScore::new(0, 0, 0))
  } else {
    target_scores.into_iter().collect()
  }
}

#[test]
fn test_sample1() {
  let test_result = vec![
    HandEggScore::new(0, 0, 5),
    HandEggScore::new(0, 2, 2),
    HandEggScore::new(1, 1, 0),
  ];
  let score = find_score(10);
  assert_eq!(test_result, score);
}

#[test]
fn test_sample2() {
  let test_result = vec![
    HandEggScore::new(0, 1, 7),
    HandEggScore::new(0, 3, 4),
    HandEggScore::new(0, 5, 1),
    HandEggScore::new(1, 0, 5),
    HandEggScore::new(1, 2, 2),
    HandEggScore::new(2, 1, 0),
  ];
  let score = find_score(17);
  assert_eq!(test_result, score);
}

#[bench]
fn bench_for_0_222(b: &mut Bencher) {
  b.iter(|| {
    for i in 0..223 {
      find_score(i);
    }
  });
}

<commit_msg>Fix compile issue<commit_after>//Problem        : Messed up Rugby
//Language       : Rust
//Compiled Using : rustc
//Version        : rustc 1.0.0
//Input for your program will be provided from STDIN
//Print out all output from your program to STDOUT

use std::io;
use std::io::prelude::*;
use std::cmp::Ordering;
use std::fmt::{Debug, Formatter, Error};
use std::collections::BTreeSet;

fn main() {
  let stdin = io::stdin();
  let mut x = stdin.lock().lines();
  let val = x.next().unwrap().unwrap();
  if let Ok(target_score) = val.parse::<u16>() {
    let target_scores = find_score(target_score);
    for score in target_scores {
      println!("{:?} {:?} {:?}", score.conversions, score.tries, score.kicks);
    }
  }
}

#[derive(Hash, Eq, Clone)]
struct HandEggScore {
  conversions: u16,
  tries: u16,
  kicks: u16,
  //scoreType: ScoreType,
}

impl HandEggScore {
  fn new(conversions: u16, tries: u16, kicks: u16,) -> HandEggScore {
    HandEggScore { conversions:conversions, tries:tries, kicks:kicks }
  }
  fn points(&self) -> u16 {
    //println!("{:?}, {:?}, {:?}", self.conversions, self.tries, self.kicks);
    self.conversions * 7 + self.tries * 3 + self.kicks * 2
  }
}

impl PartialEq for HandEggScore {
  fn eq(&self, other: &HandEggScore) -> bool {
    self.conversions == other.conversions
    && self.tries == other.tries
    && self.kicks == other.kicks
  }
}

impl Ord for HandEggScore {
    fn cmp(&self, other: &HandEggScore) -> Ordering {
      match self.conversions.cmp(&other.conversions) {
        Ordering::Equal => match self.tries.cmp(&other.tries) {
          Ordering::Equal => self.tries.cmp(&other.tries),
          a => return a,
        },
        a => return a,
      }
    }
}

impl PartialOrd for HandEggScore {
  fn partial_cmp(&self, other: &HandEggScore) -> Option<Ordering> {
    Some(self.cmp(other))
  }
}

impl Debug for HandEggScore {
  fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
    write!(f, "{} {} {}",
      self.conversions, self.tries, self.kicks)
  }
}

fn find_score(target_score: u16) -> Vec<HandEggScore> {
  let mut target_scores : BTreeSet<HandEggScore> = BTreeSet::new();
  for conversion in 0..(target_score/7 + 1) {
    for trie in 0..((target_score - conversion*7)/3 + 1) {
      for kick in 0..((target_score - conversion*7 - trie*3)/2 + 1) {
        let score = HandEggScore::new(conversion,
          trie, kick);
        match score.points().cmp(&target_score) {
          Ordering::Greater => {break;},
          Ordering::Equal => {target_scores.insert(score);},
          _ => (),
        }
      }
    }
  }
  if target_scores.is_empty() {
    vec!(HandEggScore::new(0, 0, 0))
  } else {
    target_scores.into_iter().collect()
  }
}

#[test]
fn test_sample1() {
  let test_result = vec![
    HandEggScore::new(0, 0, 5),
    HandEggScore::new(0, 2, 2),
    HandEggScore::new(1, 1, 0),
  ];
  let score = find_score(10);
  assert_eq!(test_result, score);
}

#[test]
fn test_sample2() {
  let test_result = vec![
    HandEggScore::new(0, 1, 7),
    HandEggScore::new(0, 3, 4),
    HandEggScore::new(0, 5, 1),
    HandEggScore::new(1, 0, 5),
    HandEggScore::new(1, 2, 2),
    HandEggScore::new(2, 1, 0),
  ];
  let score = find_score(17);
  assert_eq!(test_result, score);
}
<|endoftext|>


<commit_before>//! A filter for cross-site request forgery (CSRF).
//!
//! This middleware is stateless and [based on request
//! headers](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Verifying_Same_Origin_with_Standard_Headers).
//!
//! By default requests are allowed only if one of these is true:
//!
//! * The request method is safe (`GET`, `HEAD`, `OPTIONS`). It is the
//!   applications responsibility to ensure these methods cannot be used to
//!   execute unwanted actions. Note that upgrade requests for websockets are
//!   also considered safe.
//! * The `Origin` header (added automatically by the browser) matches one
//!   of the allowed origins.
//! * There is no `Origin` header but the `Referer` header matches one of
//!   the allowed origins.
//!
//! Use [`CsrfFilter::allow_xhr()`](struct.CsrfFilter.html#method.allow_xhr)
//! if you want to allow requests with unsafe methods via
//! [CORS](../cors/struct.Cors.html).
//!
//! # Example
//!
//! ```
//! # extern crate actix_web;
//! use actix_web::{http, App, HttpRequest, HttpResponse};
//! use actix_web::middleware::csrf;
//!
//! fn handle_post(_: HttpRequest) -> &'static str {
//!     "This action should only be triggered with requests from the same site"
//! }
//!
//! fn main() {
//!     let app = App::new()
//!         .middleware(
//!             csrf::CsrfFilter::new()
//!                 .allowed_origin("https://www.example.com"))
//!         .resource("/", |r| {
//!             r.method(http::Method::GET).f(|_| HttpResponse::Ok());
//!             r.method(http::Method::POST).f(handle_post);
//!         })
//!         .finish();
//! }
//! ```
//!
//! In this example the entire application is protected from CSRF.

use std::borrow::Cow;
use std::collections::HashSet;

use bytes::Bytes;
use error::{ResponseError, Result};
use http::{header, HeaderMap, HttpTryFrom, Uri};
use httpmessage::HttpMessage;
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use middleware::{Middleware, Started};

/// Potential cross-site request forgery detected.
#[derive(Debug, Fail)]
pub enum CsrfError {
    /// The HTTP request header `Origin` was required but not provided.
    #[fail(display = "Origin header required")]
    MissingOrigin,
    /// The HTTP request header `Origin` could not be parsed correctly.
    #[fail(display = "Could not parse Origin header")]
    BadOrigin,
    /// The cross-site request was denied.
    #[fail(display = "Cross-site request denied")]
    CsrDenied,
}

impl ResponseError for CsrfError {
    fn error_response(&self) -> HttpResponse {
        HttpResponse::Forbidden().body(self.to_string())
    }
}

fn uri_origin(uri: &Uri) -> Option<String> {
    match (uri.scheme_part(), uri.host(), uri.port()) {
        (Some(scheme), Some(host), Some(port)) => {
            Some(format!("{}://{}:{}", scheme, host, port))
        }
        (Some(scheme), Some(host), None) => Some(format!("{}://{}", scheme, host)),
        _ => None,
    }
}

fn origin(headers: &HeaderMap) -> Option<Result<Cow<str>, CsrfError>> {
    headers
        .get(header::ORIGIN)
        .map(|origin| {
            origin
                .to_str()
                .map_err(|_| CsrfError::BadOrigin)
                .map(|o| o.into())
        })
        .or_else(|| {
            headers.get(header::REFERER).map(|referer| {
                Uri::try_from(Bytes::from(referer.as_bytes()))
                    .ok()
                    .as_ref()
                    .and_then(uri_origin)
                    .ok_or(CsrfError::BadOrigin)
                    .map(|o| o.into())
            })
        })
}

/// A middleware that filters cross-site requests.
///
/// To construct a CSRF filter:
///
/// 1. Call [`CsrfFilter::build`](struct.CsrfFilter.html#method.build) to
///    start building.
/// 2. [Add](struct.CsrfFilterBuilder.html#method.allowed_origin) allowed
///    origins.
/// 3. Call [finish](struct.CsrfFilterBuilder.html#method.finish) to retrieve
///    the constructed filter.
///
/// # Example
///
/// ```
/// use actix_web::App;
/// use actix_web::middleware::csrf;
///
/// # fn main() {
///    let app = App::new().middleware(
///        csrf::CsrfFilter::new()
///            .allowed_origin("https://www.example.com"));
/// # }
/// ```
#[derive(Default)]
pub struct CsrfFilter {
    origins: HashSet<String>,
    allow_xhr: bool,
    allow_missing_origin: bool,
    allow_upgrade: bool,
}

impl CsrfFilter {
    /// Start building a `CsrfFilter`.
    pub fn new() -> CsrfFilter {
        CsrfFilter {
            origins: HashSet::new(),
            allow_xhr: false,
            allow_missing_origin: false,
            allow_upgrade: false,
        }
    }

    /// Add an origin that is allowed to make requests. Will be verified
    /// against the `Origin` request header.
    pub fn allowed_origin(mut self, origin: &str) -> CsrfFilter {
        self.origins.insert(origin.to_owned());
        self
    }

    /// Allow all requests with an `X-Requested-With` header.
    ///
    /// A cross-site attacker should not be able to send requests with custom
    /// headers unless a CORS policy whitelists them. Therefore it should be
    /// safe to allow requests with an `X-Requested-With` header (added
    /// automatically by many JavaScript libraries).
    ///
    /// This is disabled by default, because in Safari it is possible to
    /// circumvent this using redirects and Flash.
    ///
    /// Use this method to enable more lax filtering.
    pub fn allow_xhr(mut self) -> CsrfFilter {
        self.allow_xhr = true;
        self
    }

    /// Allow requests if the expected `Origin` header is missing (and
    /// there is no `Referer` to fall back on).
    ///
    /// The filter is conservative by default, but it should be safe to allow
    /// missing `Origin` headers because a cross-site attacker cannot prevent
    /// the browser from sending `Origin` on unsafe requests.
    pub fn allow_missing_origin(mut self) -> CsrfFilter {
        self.allow_missing_origin = true;
        self
    }

    /// Allow cross-site upgrade requests (for example to open a WebSocket).
    pub fn allow_upgrade(mut self) -> CsrfFilter {
        self.allow_upgrade = true;
        self
    }

    fn validate<S>(&self, req: &mut HttpRequest<S>) -> Result<(), CsrfError> {
        let is_upgrade = req.headers().contains_key(header::UPGRADE);
        let is_safe = req.method().is_safe() && (self.allow_upgrade || !is_upgrade);

        if is_safe || (self.allow_xhr && req.headers().contains_key("x-requested-with"))
        {
            Ok(())
        } else if let Some(header) = origin(req.headers()) {
            match header {
                Ok(ref origin) if self.origins.contains(origin.as_ref()) => Ok(()),
                Ok(_) => Err(CsrfError::CsrDenied),
                Err(err) => Err(err),
            }
        } else if self.allow_missing_origin {
            Ok(())
        } else {
            Err(CsrfError::MissingOrigin)
        }
    }
}

impl<S> Middleware<S> for CsrfFilter {
    fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
        self.validate(req)?;
        Ok(Started::Done)
    }
}

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

    #[test]
    fn test_safe() {
        let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");

        let mut req = TestRequest::with_header("Origin", "https://www.w3.org")
            .method(Method::HEAD)
            .finish();

        assert!(csrf.start(&mut req).is_ok());
    }

    #[test]
    fn test_csrf() {
        let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");

        let mut req = TestRequest::with_header("Origin", "https://www.w3.org")
            .method(Method::POST)
            .finish();

        assert!(csrf.start(&mut req).is_err());
    }

    #[test]
    fn test_referer() {
        let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");

        let mut req = TestRequest::with_header(
            "Referer",
            "https://www.example.com/some/path?query=param",
        ).method(Method::POST)
            .finish();

        assert!(csrf.start(&mut req).is_ok());
    }

    #[test]
    fn test_upgrade() {
        let strict_csrf = CsrfFilter::new().allowed_origin("https://www.example.com");

        let lax_csrf = CsrfFilter::new()
            .allowed_origin("https://www.example.com")
            .allow_upgrade();

        let mut req = TestRequest::with_header("Origin", "https://cswsh.com")
            .header("Connection", "Upgrade")
            .header("Upgrade", "websocket")
            .method(Method::GET)
            .finish();

        assert!(strict_csrf.start(&mut req).is_err());
        assert!(lax_csrf.start(&mut req).is_ok());
    }
}
<commit_msg>Let CSRF's `allowed_origin()` be specified as a type supporting `Into<String>`<commit_after>//! A filter for cross-site request forgery (CSRF).
//!
//! This middleware is stateless and [based on request
//! headers](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Verifying_Same_Origin_with_Standard_Headers).
//!
//! By default requests are allowed only if one of these is true:
//!
//! * The request method is safe (`GET`, `HEAD`, `OPTIONS`). It is the
//!   applications responsibility to ensure these methods cannot be used to
//!   execute unwanted actions. Note that upgrade requests for websockets are
//!   also considered safe.
//! * The `Origin` header (added automatically by the browser) matches one
//!   of the allowed origins.
//! * There is no `Origin` header but the `Referer` header matches one of
//!   the allowed origins.
//!
//! Use [`CsrfFilter::allow_xhr()`](struct.CsrfFilter.html#method.allow_xhr)
//! if you want to allow requests with unsafe methods via
//! [CORS](../cors/struct.Cors.html).
//!
//! # Example
//!
//! ```
//! # extern crate actix_web;
//! use actix_web::{http, App, HttpRequest, HttpResponse};
//! use actix_web::middleware::csrf;
//!
//! fn handle_post(_: HttpRequest) -> &'static str {
//!     "This action should only be triggered with requests from the same site"
//! }
//!
//! fn main() {
//!     let app = App::new()
//!         .middleware(
//!             csrf::CsrfFilter::new()
//!                 .allowed_origin("https://www.example.com"))
//!         .resource("/", |r| {
//!             r.method(http::Method::GET).f(|_| HttpResponse::Ok());
//!             r.method(http::Method::POST).f(handle_post);
//!         })
//!         .finish();
//! }
//! ```
//!
//! In this example the entire application is protected from CSRF.

use std::borrow::Cow;
use std::collections::HashSet;

use bytes::Bytes;
use error::{ResponseError, Result};
use http::{header, HeaderMap, HttpTryFrom, Uri};
use httpmessage::HttpMessage;
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use middleware::{Middleware, Started};

/// Potential cross-site request forgery detected.
#[derive(Debug, Fail)]
pub enum CsrfError {
    /// The HTTP request header `Origin` was required but not provided.
    #[fail(display = "Origin header required")]
    MissingOrigin,
    /// The HTTP request header `Origin` could not be parsed correctly.
    #[fail(display = "Could not parse Origin header")]
    BadOrigin,
    /// The cross-site request was denied.
    #[fail(display = "Cross-site request denied")]
    CsrDenied,
}

impl ResponseError for CsrfError {
    fn error_response(&self) -> HttpResponse {
        HttpResponse::Forbidden().body(self.to_string())
    }
}

fn uri_origin(uri: &Uri) -> Option<String> {
    match (uri.scheme_part(), uri.host(), uri.port()) {
        (Some(scheme), Some(host), Some(port)) => {
            Some(format!("{}://{}:{}", scheme, host, port))
        }
        (Some(scheme), Some(host), None) => Some(format!("{}://{}", scheme, host)),
        _ => None,
    }
}

fn origin(headers: &HeaderMap) -> Option<Result<Cow<str>, CsrfError>> {
    headers
        .get(header::ORIGIN)
        .map(|origin| {
            origin
                .to_str()
                .map_err(|_| CsrfError::BadOrigin)
                .map(|o| o.into())
        })
        .or_else(|| {
            headers.get(header::REFERER).map(|referer| {
                Uri::try_from(Bytes::from(referer.as_bytes()))
                    .ok()
                    .as_ref()
                    .and_then(uri_origin)
                    .ok_or(CsrfError::BadOrigin)
                    .map(|o| o.into())
            })
        })
}

/// A middleware that filters cross-site requests.
///
/// To construct a CSRF filter:
///
/// 1. Call [`CsrfFilter::build`](struct.CsrfFilter.html#method.build) to
///    start building.
/// 2. [Add](struct.CsrfFilterBuilder.html#method.allowed_origin) allowed
///    origins.
/// 3. Call [finish](struct.CsrfFilterBuilder.html#method.finish) to retrieve
///    the constructed filter.
///
/// # Example
///
/// ```
/// use actix_web::App;
/// use actix_web::middleware::csrf;
///
/// # fn main() {
///    let app = App::new().middleware(
///        csrf::CsrfFilter::new()
///            .allowed_origin("https://www.example.com"));
/// # }
/// ```
#[derive(Default)]
pub struct CsrfFilter {
    origins: HashSet<String>,
    allow_xhr: bool,
    allow_missing_origin: bool,
    allow_upgrade: bool,
}

impl CsrfFilter {
    /// Start building a `CsrfFilter`.
    pub fn new() -> CsrfFilter {
        CsrfFilter {
            origins: HashSet::new(),
            allow_xhr: false,
            allow_missing_origin: false,
            allow_upgrade: false,
        }
    }

    /// Add an origin that is allowed to make requests. Will be verified
    /// against the `Origin` request header.
    pub fn allowed_origin<T: Into<String>>(mut self, origin: T) -> CsrfFilter {
        self.origins.insert(origin.into());
        self
    }

    /// Allow all requests with an `X-Requested-With` header.
    ///
    /// A cross-site attacker should not be able to send requests with custom
    /// headers unless a CORS policy whitelists them. Therefore it should be
    /// safe to allow requests with an `X-Requested-With` header (added
    /// automatically by many JavaScript libraries).
    ///
    /// This is disabled by default, because in Safari it is possible to
    /// circumvent this using redirects and Flash.
    ///
    /// Use this method to enable more lax filtering.
    pub fn allow_xhr(mut self) -> CsrfFilter {
        self.allow_xhr = true;
        self
    }

    /// Allow requests if the expected `Origin` header is missing (and
    /// there is no `Referer` to fall back on).
    ///
    /// The filter is conservative by default, but it should be safe to allow
    /// missing `Origin` headers because a cross-site attacker cannot prevent
    /// the browser from sending `Origin` on unsafe requests.
    pub fn allow_missing_origin(mut self) -> CsrfFilter {
        self.allow_missing_origin = true;
        self
    }

    /// Allow cross-site upgrade requests (for example to open a WebSocket).
    pub fn allow_upgrade(mut self) -> CsrfFilter {
        self.allow_upgrade = true;
        self
    }

    fn validate<S>(&self, req: &mut HttpRequest<S>) -> Result<(), CsrfError> {
        let is_upgrade = req.headers().contains_key(header::UPGRADE);
        let is_safe = req.method().is_safe() && (self.allow_upgrade || !is_upgrade);

        if is_safe || (self.allow_xhr && req.headers().contains_key("x-requested-with"))
        {
            Ok(())
        } else if let Some(header) = origin(req.headers()) {
            match header {
                Ok(ref origin) if self.origins.contains(origin.as_ref()) => Ok(()),
                Ok(_) => Err(CsrfError::CsrDenied),
                Err(err) => Err(err),
            }
        } else if self.allow_missing_origin {
            Ok(())
        } else {
            Err(CsrfError::MissingOrigin)
        }
    }
}

impl<S> Middleware<S> for CsrfFilter {
    fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
        self.validate(req)?;
        Ok(Started::Done)
    }
}

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

    #[test]
    fn test_safe() {
        let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");

        let mut req = TestRequest::with_header("Origin", "https://www.w3.org")
            .method(Method::HEAD)
            .finish();

        assert!(csrf.start(&mut req).is_ok());
    }

    #[test]
    fn test_csrf() {
        let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");

        let mut req = TestRequest::with_header("Origin", "https://www.w3.org")
            .method(Method::POST)
            .finish();

        assert!(csrf.start(&mut req).is_err());
    }

    #[test]
    fn test_referer() {
        let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");

        let mut req = TestRequest::with_header(
            "Referer",
            "https://www.example.com/some/path?query=param",
        ).method(Method::POST)
            .finish();

        assert!(csrf.start(&mut req).is_ok());
    }

    #[test]
    fn test_upgrade() {
        let strict_csrf = CsrfFilter::new().allowed_origin("https://www.example.com");

        let lax_csrf = CsrfFilter::new()
            .allowed_origin("https://www.example.com")
            .allow_upgrade();

        let mut req = TestRequest::with_header("Origin", "https://cswsh.com")
            .header("Connection", "Upgrade")
            .header("Upgrade", "websocket")
            .method(Method::GET)
            .finish();

        assert!(strict_csrf.start(&mut req).is_err());
        assert!(lax_csrf.start(&mut req).is_ok());
    }
}
<|endoftext|>


<commit_before>#[test]
fn dogfood_clippy() {
    // run clippy on itself and fail the test if lint warnings are reported
    if option_env!("RUSTC_TEST_SUITE").is_some() || cfg!(windows) {
        return;
    }
    let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let clippy_binary = std::path::Path::new(&root_dir)
        .join("target")
        .join(env!("PROFILE"))
        .join("cargo-clippy");

    let output = std::process::Command::new(clippy_binary)
        .current_dir(root_dir)
        .env("CLIPPY_DOGFOOD", "1")
        .env("CARGO_INCREMENTAL", "0")
        .arg("clippy-preview")
        .arg("--all-targets")
        .arg("--all-features")
        .arg("--")
        .args(&["-D", "clippy::all"])
        .args(&["-D", "clippy::internal"])
        .args(&["-D", "clippy::pedantic"])
        .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
        .output()
        .unwrap();
    println!("status: {}", output.status);
    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

    assert!(output.status.success());
}

#[test]
fn dogfood_subprojects() {
    // run clippy on remaining subprojects and fail the test if lint warnings are reported
    if option_env!("RUSTC_TEST_SUITE").is_some() || cfg!(windows) {
        return;
    }
    let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let clippy_binary = std::path::Path::new(&root_dir)
        .join("target")
        .join(env!("PROFILE"))
        .join("cargo-clippy");

    for d in &[
        "clippy_workspace_tests",
        "clippy_workspace_tests/src",
        "clippy_workspace_tests/subcrate",
        "clippy_workspace_tests/subcrate/src",
        "clippy_dev",
        "rustc_tools_util",
    ] {
        let output = std::process::Command::new(&clippy_binary)
            .current_dir(root_dir.join(d))
            .env("CLIPPY_DOGFOOD", "1")
            .env("CARGO_INCREMENTAL", "0")
            .arg("clippy")
            .arg("--")
            .args(&["-D", "clippy::all"])
            .args(&["-D", "clippy::pedantic"])
            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
            .output()
            .unwrap();
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

        assert!(output.status.success());
    }
}
<commit_msg>Fix dogfood to use cargo mod too<commit_after>use std::path::PathBuf;
use std::process::Command;

#[allow(dead_code)]
mod cargo;

fn clippy_path() -> PathBuf {
    let build_info = cargo::BuildInfo::new();
    build_info.target_lib().join("cargo-clippy")
}

#[test]
fn dogfood_clippy() {
    // run clippy on itself and fail the test if lint warnings are reported
    if option_env!("RUSTC_TEST_SUITE").is_some() || cfg!(windows) {
        return;
    }
    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let clippy_binary = clippy_path();

    let output = Command::new(clippy_binary)
        .current_dir(root_dir)
        .env("CLIPPY_DOGFOOD", "1")
        .env("CARGO_INCREMENTAL", "0")
        .arg("clippy-preview")
        .arg("--all-targets")
        .arg("--all-features")
        .arg("--")
        .args(&["-D", "clippy::all"])
        .args(&["-D", "clippy::internal"])
        .args(&["-D", "clippy::pedantic"])
        .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
        .output()
        .unwrap();
    println!("status: {}", output.status);
    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

    assert!(output.status.success());
}

#[test]
fn dogfood_subprojects() {
    // run clippy on remaining subprojects and fail the test if lint warnings are reported
    if option_env!("RUSTC_TEST_SUITE").is_some() || cfg!(windows) {
        return;
    }
    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let clippy_binary = clippy_path();

    for d in &[
        "clippy_workspace_tests",
        "clippy_workspace_tests/src",
        "clippy_workspace_tests/subcrate",
        "clippy_workspace_tests/subcrate/src",
        "clippy_dev",
        "rustc_tools_util",
    ] {
        let output = Command::new(&clippy_binary)
            .current_dir(root_dir.join(d))
            .env("CLIPPY_DOGFOOD", "1")
            .env("CARGO_INCREMENTAL", "0")
            .arg("clippy")
            .arg("--")
            .args(&["-D", "clippy::all"])
            .args(&["-D", "clippy::pedantic"])
            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
            .output()
            .unwrap();
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

        assert!(output.status.success());
    }
}
<|endoftext|>


<commit_before>use super::{Point, Size};
use std::ops::BitAnd;
use std::slice;

pub type BitBoard = u64;
const BIT_BOARD_BITS: u32 = 64;

#[derive(Copy, Clone, Debug)]
pub struct Offset {
    offs: [u32; 4],
    masks: [BitBoard; 8],
}

impl Offset {
    pub fn from_size(size: Size) -> Self {
        Offset {
            offs: Self::offs(size),
            masks: Self::masks(size),
        }
    }

    fn offs(size: Size) -> [u32; 4] {
        let r = pt2off((1, 0), size);
        let d = pt2off((0, 1), size);
        let r_off = r;
        let d_off = d;
        let dl_off = d - r;
        let dr_off = d + r;

        [r_off, d_off, dl_off, dr_off]
    }

    fn masks(size: Size) -> [BitBoard; 8] {
        let all_mask = all_mask(size);
        let mut r_mask = all_mask;
        let mut l_mask = all_mask;
        for y in 0..size.1 {
            r_mask ^= pt2mask((size.0 - 1, y), size);
            l_mask ^= pt2mask((0, y), size);
        }

        let mut d_mask = all_mask;
        let mut u_mask = all_mask;
        for x in 0..size.0 {
            d_mask ^= pt2mask((x, size.1 - 1), size);
            u_mask ^= pt2mask((x, 0), size);
        }

        let dl_mask = d_mask & l_mask;
        let dr_mask = d_mask & r_mask;
        let ur_mask = u_mask & r_mask;
        let ul_mask = u_mask & l_mask;

        [r_mask, d_mask, dl_mask, dr_mask, l_mask, u_mask, ur_mask, ul_mask]
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Mask {
    masks: [BitBoard; 8],
}

impl Mask {
    pub fn new(mask: BitBoard) -> Self {
        Mask { masks: [mask; 8] }
    }

    pub fn shift(self, offset: Offset) -> Mask {
        Mask {
            masks: [(self.masks[0] & offset.masks[0]) << offset.offs[0],
                    (self.masks[1] & offset.masks[1]) << offset.offs[1],
                    (self.masks[2] & offset.masks[2]) << offset.offs[2],
                    (self.masks[3] & offset.masks[3]) << offset.offs[3],
                    (self.masks[4] & offset.masks[4]) >> offset.offs[0],
                    (self.masks[5] & offset.masks[5]) >> offset.offs[1],
                    (self.masks[6] & offset.masks[6]) >> offset.offs[2],
                    (self.masks[7] & offset.masks[7]) >> offset.offs[3]],
        }
    }

    pub fn or_all(self) -> BitBoard {
        self.masks[0] | self.masks[1] | self.masks[2] | self.masks[3] | self.masks[4] |
        self.masks[5] | self.masks[6] | self.masks[7]
    }

    pub fn iter_mut(&mut self) -> slice::IterMut<BitBoard> {
        self.masks.iter_mut()
    }
}

impl BitAnd for Mask {
    type Output = Mask;

    fn bitand(self, rhs: Mask) -> Mask {
        Mask {
            masks: [self.masks[0] & rhs.masks[0],
                    self.masks[1] & rhs.masks[1],
                    self.masks[2] & rhs.masks[2],
                    self.masks[3] & rhs.masks[3],
                    self.masks[4] & rhs.masks[4],
                    self.masks[5] & rhs.masks[5],
                    self.masks[6] & rhs.masks[6],
                    self.masks[7] & rhs.masks[7]],
        }
    }
}

pub fn all_mask(size: Size) -> BitBoard {
    let num_cell = size.0 * size.1;
    if num_cell == BIT_BOARD_BITS {
        !0
    } else {
        (1 << num_cell) - 1
    }
}

pub fn pt2mask(pt: Point, size: Size) -> BitBoard {
    1 << pt2off(pt, size)
}

fn pt2off((x, y): Point, (_, sy): Size) -> u32 {
    (x + sy * y)
}

fn off2ptr(off: u32, size: Size) -> Point {
    (off % size.1, off / size.1)
}

pub fn points(mask: BitBoard, size: Size) -> Points {
    Points {
        size: size,
        mask: mask,
    }
}

pub struct Points {
    size: Size,
    mask: BitBoard,
}

impl Iterator for Points {
    type Item = Point;

    fn next(&mut self) -> Option<Point> {
        if self.mask == 0 {
            return None;
        }
        let off = self.mask.trailing_zeros();
        self.mask ^= 1 << off;
        Some(off2ptr(off, self.size))
    }
}
<commit_msg>Implement iterator traits for bit_board::Points<commit_after>use super::{Point, Size};
use std::ops::BitAnd;
use std::slice;

pub type BitBoard = u64;
const BIT_BOARD_BITS: u32 = 64;

#[derive(Copy, Clone, Debug)]
pub struct Offset {
    offs: [u32; 4],
    masks: [BitBoard; 8],
}

impl Offset {
    pub fn from_size(size: Size) -> Self {
        Offset {
            offs: Self::offs(size),
            masks: Self::masks(size),
        }
    }

    fn offs(size: Size) -> [u32; 4] {
        let r = pt2off((1, 0), size);
        let d = pt2off((0, 1), size);
        let r_off = r;
        let d_off = d;
        let dl_off = d - r;
        let dr_off = d + r;

        [r_off, d_off, dl_off, dr_off]
    }

    fn masks(size: Size) -> [BitBoard; 8] {
        let all_mask = all_mask(size);
        let mut r_mask = all_mask;
        let mut l_mask = all_mask;
        for y in 0..size.1 {
            r_mask ^= pt2mask((size.0 - 1, y), size);
            l_mask ^= pt2mask((0, y), size);
        }

        let mut d_mask = all_mask;
        let mut u_mask = all_mask;
        for x in 0..size.0 {
            d_mask ^= pt2mask((x, size.1 - 1), size);
            u_mask ^= pt2mask((x, 0), size);
        }

        let dl_mask = d_mask & l_mask;
        let dr_mask = d_mask & r_mask;
        let ur_mask = u_mask & r_mask;
        let ul_mask = u_mask & l_mask;

        [r_mask, d_mask, dl_mask, dr_mask, l_mask, u_mask, ur_mask, ul_mask]
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Mask {
    masks: [BitBoard; 8],
}

impl Mask {
    pub fn new(mask: BitBoard) -> Self {
        Mask { masks: [mask; 8] }
    }

    pub fn shift(self, offset: Offset) -> Mask {
        Mask {
            masks: [(self.masks[0] & offset.masks[0]) << offset.offs[0],
                    (self.masks[1] & offset.masks[1]) << offset.offs[1],
                    (self.masks[2] & offset.masks[2]) << offset.offs[2],
                    (self.masks[3] & offset.masks[3]) << offset.offs[3],
                    (self.masks[4] & offset.masks[4]) >> offset.offs[0],
                    (self.masks[5] & offset.masks[5]) >> offset.offs[1],
                    (self.masks[6] & offset.masks[6]) >> offset.offs[2],
                    (self.masks[7] & offset.masks[7]) >> offset.offs[3]],
        }
    }

    pub fn or_all(self) -> BitBoard {
        self.masks[0] | self.masks[1] | self.masks[2] | self.masks[3] | self.masks[4] |
        self.masks[5] | self.masks[6] | self.masks[7]
    }

    pub fn iter_mut(&mut self) -> slice::IterMut<BitBoard> {
        self.masks.iter_mut()
    }
}

impl BitAnd for Mask {
    type Output = Mask;

    fn bitand(self, rhs: Mask) -> Mask {
        Mask {
            masks: [self.masks[0] & rhs.masks[0],
                    self.masks[1] & rhs.masks[1],
                    self.masks[2] & rhs.masks[2],
                    self.masks[3] & rhs.masks[3],
                    self.masks[4] & rhs.masks[4],
                    self.masks[5] & rhs.masks[5],
                    self.masks[6] & rhs.masks[6],
                    self.masks[7] & rhs.masks[7]],
        }
    }
}

pub fn all_mask(size: Size) -> BitBoard {
    let num_cell = size.0 * size.1;
    if num_cell == BIT_BOARD_BITS {
        !0
    } else {
        (1 << num_cell) - 1
    }
}

pub fn pt2mask(pt: Point, size: Size) -> BitBoard {
    1 << pt2off(pt, size)
}

fn pt2off((x, y): Point, (_, sy): Size) -> u32 {
    (x + sy * y)
}

fn off2ptr(off: u32, size: Size) -> Point {
    (off % size.1, off / size.1)
}

pub fn points(mask: BitBoard, size: Size) -> Points {
    Points {
        size: size,
        mask: mask,
    }
}

pub struct Points {
    size: Size,
    mask: BitBoard,
}

impl Iterator for Points {
    type Item = Point;

    fn next(&mut self) -> Option<Point> {
        if self.mask == 0 {
            return None;
        }
        let off = self.mask.trailing_zeros();
        self.mask ^= 1 << off;
        Some(off2ptr(off, self.size))
    }
}

impl DoubleEndedIterator for Points {
    fn next_back(&mut self) -> Option<Point> {
        if self.mask == 0 {
            return None;
        }
        let off = self.mask.leading_zeros();
        self.mask ^= 1 << off;
        Some(off2ptr(off, self.size))
    }
}

impl ExactSizeIterator for Points {
    fn len(&self) -> usize {
        self.mask.count_ones() as usize
    }
}
<|endoftext|>


<commit_before>use std::iter::Peekable;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathToken {
    Root,
    Field(String),
    Index(usize),
    Star,
    StarIndex
}

fn peek<I>(chars: &mut Peekable<I>) -> Option<(usize, char)> where I: Iterator<Item = (usize, char)> {
    chars.peek().map(|tup| (tup.0.clone(), tup.1.clone()))
}

// identifier -> a-zA-Z0-9+
fn identifier<I>(ch: char, chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    id.push(ch);
    let mut next_char = peek(chars);
    while next_char.is_some() {
        let ch = next_char.unwrap();
        if ch.1.is_alphabetic() || ch.1.is_numeric() || ch.1 == '_' {
            chars.next();
            id.push(ch.1);
        } else if ch.1 == '.' || ch.1 == '\'' || ch.1 == '[' {
            break;
        } else {
            return Err(format!("\"{}\" is not allowed in an identifier in path expression \"{}\" at index {}",
                ch.1, path, ch.0));
        }
        next_char = peek(chars);
    }
    tokens.push(PathToken::Field(id));
    Ok(())
}

// path_identifier -> identifier | *
fn path_identifier<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    match chars.next() {
        Some(ch) => match ch.1 {
            '*' => {
                tokens.push(PathToken::Star);
                Ok(())
            },
            c if c.is_alphabetic() || c.is_numeric() => {
              identifier(c, chars, tokens, path)?;
              Ok(())
            },
            _ => Err(format!("Expected either a \"*\" or path identifier in path expression \"{}\" at index {}",
                path, ch.0))
        },
        None => Err(format!("Expected a path after \".\" in path expression \"{}\" at index {}",
            path, index))
    }
}

// string_path -> [^']+
fn string_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    let mut next_char = peek(chars);
    if next_char.is_some() {
        chars.next();
        let mut ch = next_char.unwrap();
        next_char = peek(chars);
        while ch.1 != '\'' && next_char.is_some() {
            id.push(ch.1);
            chars.next();
            ch = next_char.unwrap();
            next_char = peek(chars);
        }
        if ch.1 == '\'' {
            if id.is_empty() {
                Err(format!("Empty strings are not allowed in path expression \"{}\" at index {}", path, ch.0))
            } else {
                tokens.push(PathToken::Field(id));
                Ok(())
            }
        } else {
            Err(format!("Unterminated string in path expression \"{}\" at index {}", path, ch.0))
        }
    } else {
        Err(format!("Unterminated string in path expression \"{}\" at index {}", path, index))
    }
}

// index_path -> [0-9]+
fn index_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    let mut next_char = chars.next();
    id.push(next_char.unwrap().1);
    next_char = peek(chars);
    while next_char.is_some() {
        let ch = next_char.unwrap();
        if ch.1.is_numeric() {
            id.push(ch.1);
            chars.next();
        } else {
            break;
        }
        next_char = peek(chars);
    }

    if let Some(ch) = next_char {
        if ch.1 != ']' {
            return Err(format!("Indexes can only consist of numbers or a \"*\", found \"{}\" instead in path expression \"{}\" at index {}",
                ch.1, path, ch.0))
        }
    }

    tokens.push(PathToken::Index(id.parse().unwrap()));
    Ok(())
}

// bracket_path -> (string_path | index | *) ]
fn bracket_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut ch = peek(chars);
    match ch {
        Some(c) => {
            if c.1 == '\'' {
              chars.next();
              string_path(chars, tokens, path, c.0)?
            } else if c.1.is_numeric() {
              index_path(chars, tokens, path)?
            } else if c.1 == '*' {
                chars.next();
                tokens.push(PathToken::StarIndex);
            } else if c.1 == ']' {
                return Err(format!("Empty bracket expressions are not allowed in path expression \"{}\" at index {}",
                    path, c.0));
            } else {
                return Err(format!("Indexes can only consist of numbers or a \"*\", found \"{}\" instead in path expression \"{}\" at index {}",
                    c.1, path, c.0));
            };
            ch = peek(chars);
            match ch {
                Some(c) => if c.1 != ']' {
                    Err(format!("Unterminated brackets, found \"{}\" instead of \"]\" in path expression \"{}\" at index {}",
                        c.1, path, c.0))
                } else {
                    chars.next();
                    Ok(())
                },
                None => Err(format!("Unterminated brackets in path expression \"{}\" at index {}",
                    path, path.len() - 1))
            }
        },
        None => Err(format!("Expected a \"'\" (single qoute) or a digit in path expression \"{}\" after index {}",
            path, index))
    }
}

// path_exp -> (dot-path | bracket-path)*
fn path_exp<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String)-> Result<(), String>
    where I: Iterator<Item = (usize, char)> {
    let mut next_char = chars.next();
    while next_char.is_some() {
        let ch = next_char.unwrap();
        match ch.1 {
            '.' => path_identifier(chars, tokens, path, ch.0)?,
            '[' => bracket_path(chars, tokens, path, ch.0)?,
            _ => return Err(format!("Expected a \".\" or \"[\" instead of \"{}\" in path expression \"{}\" at index {}",
                ch.1, path, ch.0))
        }
        next_char = chars.next();
    }
    Ok(())
}

pub fn parse_path_exp(path: String) -> Result<Vec<PathToken>, String> {
    let mut tokens = vec![];

    // parse_path_exp -> $ path_exp | empty
    let mut chars = path.chars().enumerate().peekable();
    match chars.next() {
      Some(ch) => {
        match ch.1 {
          '$' => {
            tokens.push(PathToken::Root);
            path_exp(&mut chars, &mut tokens, &path)?;
            Ok(tokens)
          },
          c if c.is_alphabetic() || c.is_numeric() => {
            tokens.push(PathToken::Root);
            identifier(c, &mut chars, &mut tokens, &path)?;
            path_exp(&mut chars, &mut tokens, &path)?;
            Ok(tokens)
          },
          _ => Err(format ! ("Path expression \"{}\" does not start with a root marker \"$\"", path))
        }
      },
      None => Ok(tokens)
    }
}

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

    #[test]
    fn parse_path_exp_handles_empty_string() {
        expect!(parse_path_exp(s!(""))).to(be_ok().value(vec![]));
    }

    #[test]
    fn parse_path_exp_handles_root() {
        expect!(parse_path_exp(s!("$"))).to(be_ok().value(vec![PathToken::Root]));
    }

    #[test]
    fn parse_path_exp_handles_missing_root() {
        expect!(parse_path_exp(s!("adsjhaskjdh")))
          .to(be_ok().value(vec![PathToken::Root, PathToken::Field(s!("adsjhaskjdh"))]));
    }

    #[test]
    fn parse_path_exp_handles_missing_path() {
        expect!(parse_path_exp(s!("$adsjhaskjdh"))).to(
            be_err().value(s!("Expected a \".\" or \"[\" instead of \"a\" in path expression \"$adsjhaskjdh\" at index 1")));
    }

    #[test]
    fn parse_path_exp_handles_missing_path_name() {
        expect!(parse_path_exp(s!("$."))).to(
            be_err().value(s!("Expected a path after \".\" in path expression \"$.\" at index 1")));
        expect!(parse_path_exp(s!("$.a.b.c."))).to(
            be_err().value(s!("Expected a path after \".\" in path expression \"$.a.b.c.\" at index 7")));
    }

    #[test]
    fn parse_path_exp_handles_invalid_identifiers() {
        expect!(parse_path_exp(s!("$.abc!"))).to(
            be_err().value(s!("\"!\" is not allowed in an identifier in path expression \"$.abc!\" at index 5")));
        expect!(parse_path_exp(s!("$.a.b.c.}"))).to(
            be_err().value(s!("Expected either a \"*\" or path identifier in path expression \"$.a.b.c.}\" at index 8")));
    }

    #[test]
    fn parse_path_exp_with_simple_identifiers() {
      expect!(parse_path_exp(s!("$.a"))).to(
          be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a"))]));
      expect!(parse_path_exp(s!("$.a.b.c"))).to(
          be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("b")),
          PathToken::Field(s!("c"))]));
      expect!(parse_path_exp(s!("a.b.c"))).to(
        be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("b")),
                           PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_handles_underscores() {
        expect!(parse_path_exp(s!("$.user_id"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("user_id"))])
        );
    }

    #[test]
    fn parse_path_exp_with_star_instead_of_identifiers() {
        expect!(parse_path_exp(s!("$.*"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Star]));
        expect!(parse_path_exp(s!("$.a.*.c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Star,
            PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_with_bracket_notation() {
        expect!(parse_path_exp(s!("$['val1']"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("val1"))]));
        expect!(parse_path_exp(s!("$.a['val@1.'].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("val@1.")),
            PathToken::Field(s!("c"))]));
        expect!(parse_path_exp(s!("$.a[1].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Index(1),
            PathToken::Field(s!("c"))]));
        expect!(parse_path_exp(s!("$.a[*].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::StarIndex,
            PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_with_invalid_bracket_notation() {
        expect!(parse_path_exp(s!("$["))).to(
            be_err().value(s!("Expected a \"'\" (single qoute) or a digit in path expression \"$[\" after index 1")));
        expect!(parse_path_exp(s!("$['"))).to(
            be_err().value(s!("Unterminated string in path expression \"$['\" at index 2")));
        expect!(parse_path_exp(s!("$['Unterminated string"))).to(
            be_err().value(s!("Unterminated string in path expression \"$['Unterminated string\" at index 21")));
        expect!(parse_path_exp(s!("$['']"))).to(
            be_err().value(s!("Empty strings are not allowed in path expression \"$['']\" at index 3")));
        expect!(parse_path_exp(s!("$['test'.b.c"))).to(
            be_err().value(s!("Unterminated brackets, found \".\" instead of \"]\" in path expression \"$['test'.b.c\" at index 8")));
        expect!(parse_path_exp(s!("$['test'"))).to(
            be_err().value(s!("Unterminated brackets in path expression \"$['test'\" at index 7")));
        expect!(parse_path_exp(s!("$['test']b.c"))).to(
            be_err().value(s!("Expected a \".\" or \"[\" instead of \"b\" in path expression \"$[\'test\']b.c\" at index 9")));
    }

    #[test]
    fn parse_path_exp_with_invalid_bracket_index_notation() {
        expect!(parse_path_exp(s!("$[dhghh]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"d\" instead in path expression \"$[dhghh]\" at index 2")));
        expect!(parse_path_exp(s!("$[12abc]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"a\" instead in path expression \"$[12abc]\" at index 4")));
        expect!(parse_path_exp(s!("$[]"))).to(
            be_err().value(s!("Empty bracket expressions are not allowed in path expression \"$[]\" at index 2")));
        expect!(parse_path_exp(s!("$[-1]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"-\" instead in path expression \"$[-1]\" at index 2")));
    }
}
<commit_msg>fix: Allow dashes in path expressions for headers like Content-Type<commit_after>use std::iter::Peekable;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathToken {
    Root,
    Field(String),
    Index(usize),
    Star,
    StarIndex
}

fn peek<I>(chars: &mut Peekable<I>) -> Option<(usize, char)> where I: Iterator<Item = (usize, char)> {
    chars.peek().map(|tup| (tup.0.clone(), tup.1.clone()))
}

// identifier -> a-zA-Z0-9+
fn identifier<I>(ch: char, chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    id.push(ch);
    let mut next_char = peek(chars);
    while next_char.is_some() {
        let ch = next_char.unwrap();
        if ch.1.is_alphabetic() || ch.1.is_numeric() || ch.1 == '_' || ch.1 == '-' {
            chars.next();
            id.push(ch.1);
        } else if ch.1 == '.' || ch.1 == '\'' || ch.1 == '[' {
            break;
        } else {
            return Err(format!("\"{}\" is not allowed in an identifier in path expression \"{}\" at index {}",
                ch.1, path, ch.0));
        }
        next_char = peek(chars);
    }
    tokens.push(PathToken::Field(id));
    Ok(())
}

// path_identifier -> identifier | *
fn path_identifier<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    match chars.next() {
        Some(ch) => match ch.1 {
            '*' => {
                tokens.push(PathToken::Star);
                Ok(())
            },
            c if c.is_alphabetic() || c.is_numeric() => {
              identifier(c, chars, tokens, path)?;
              Ok(())
            },
            _ => Err(format!("Expected either a \"*\" or path identifier in path expression \"{}\" at index {}",
                path, ch.0))
        },
        None => Err(format!("Expected a path after \".\" in path expression \"{}\" at index {}",
            path, index))
    }
}

// string_path -> [^']+
fn string_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    let mut next_char = peek(chars);
    if next_char.is_some() {
        chars.next();
        let mut ch = next_char.unwrap();
        next_char = peek(chars);
        while ch.1 != '\'' && next_char.is_some() {
            id.push(ch.1);
            chars.next();
            ch = next_char.unwrap();
            next_char = peek(chars);
        }
        if ch.1 == '\'' {
            if id.is_empty() {
                Err(format!("Empty strings are not allowed in path expression \"{}\" at index {}", path, ch.0))
            } else {
                tokens.push(PathToken::Field(id));
                Ok(())
            }
        } else {
            Err(format!("Unterminated string in path expression \"{}\" at index {}", path, ch.0))
        }
    } else {
        Err(format!("Unterminated string in path expression \"{}\" at index {}", path, index))
    }
}

// index_path -> [0-9]+
fn index_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    let mut next_char = chars.next();
    id.push(next_char.unwrap().1);
    next_char = peek(chars);
    while next_char.is_some() {
        let ch = next_char.unwrap();
        if ch.1.is_numeric() {
            id.push(ch.1);
            chars.next();
        } else {
            break;
        }
        next_char = peek(chars);
    }

    if let Some(ch) = next_char {
        if ch.1 != ']' {
            return Err(format!("Indexes can only consist of numbers or a \"*\", found \"{}\" instead in path expression \"{}\" at index {}",
                ch.1, path, ch.0))
        }
    }

    tokens.push(PathToken::Index(id.parse().unwrap()));
    Ok(())
}

// bracket_path -> (string_path | index | *) ]
fn bracket_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut ch = peek(chars);
    match ch {
        Some(c) => {
            if c.1 == '\'' {
              chars.next();
              string_path(chars, tokens, path, c.0)?
            } else if c.1.is_numeric() {
              index_path(chars, tokens, path)?
            } else if c.1 == '*' {
                chars.next();
                tokens.push(PathToken::StarIndex);
            } else if c.1 == ']' {
                return Err(format!("Empty bracket expressions are not allowed in path expression \"{}\" at index {}",
                    path, c.0));
            } else {
                return Err(format!("Indexes can only consist of numbers or a \"*\", found \"{}\" instead in path expression \"{}\" at index {}",
                    c.1, path, c.0));
            };
            ch = peek(chars);
            match ch {
                Some(c) => if c.1 != ']' {
                    Err(format!("Unterminated brackets, found \"{}\" instead of \"]\" in path expression \"{}\" at index {}",
                        c.1, path, c.0))
                } else {
                    chars.next();
                    Ok(())
                },
                None => Err(format!("Unterminated brackets in path expression \"{}\" at index {}",
                    path, path.len() - 1))
            }
        },
        None => Err(format!("Expected a \"'\" (single qoute) or a digit in path expression \"{}\" after index {}",
            path, index))
    }
}

// path_exp -> (dot-path | bracket-path)*
fn path_exp<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String)-> Result<(), String>
    where I: Iterator<Item = (usize, char)> {
    let mut next_char = chars.next();
    while next_char.is_some() {
        let ch = next_char.unwrap();
        match ch.1 {
            '.' => path_identifier(chars, tokens, path, ch.0)?,
            '[' => bracket_path(chars, tokens, path, ch.0)?,
            _ => return Err(format!("Expected a \".\" or \"[\" instead of \"{}\" in path expression \"{}\" at index {}",
                ch.1, path, ch.0))
        }
        next_char = chars.next();
    }
    Ok(())
}

pub fn parse_path_exp(path: String) -> Result<Vec<PathToken>, String> {
    let mut tokens = vec![];

    // parse_path_exp -> $ path_exp | empty
    let mut chars = path.chars().enumerate().peekable();
    match chars.next() {
      Some(ch) => {
        match ch.1 {
          '$' => {
            tokens.push(PathToken::Root);
            path_exp(&mut chars, &mut tokens, &path)?;
            Ok(tokens)
          },
          c if c.is_alphabetic() || c.is_numeric() => {
            tokens.push(PathToken::Root);
            identifier(c, &mut chars, &mut tokens, &path)?;
            path_exp(&mut chars, &mut tokens, &path)?;
            Ok(tokens)
          },
          _ => Err(format ! ("Path expression \"{}\" does not start with a root marker \"$\"", path))
        }
      },
      None => Ok(tokens)
    }
}

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

    #[test]
    fn parse_path_exp_handles_empty_string() {
        expect!(parse_path_exp(s!(""))).to(be_ok().value(vec![]));
    }

    #[test]
    fn parse_path_exp_handles_root() {
        expect!(parse_path_exp(s!("$"))).to(be_ok().value(vec![PathToken::Root]));
    }

    #[test]
    fn parse_path_exp_handles_missing_root() {
        expect!(parse_path_exp(s!("adsjhaskjdh")))
          .to(be_ok().value(vec![PathToken::Root, PathToken::Field(s!("adsjhaskjdh"))]));
    }

    #[test]
    fn parse_path_exp_handles_missing_path() {
        expect!(parse_path_exp(s!("$adsjhaskjdh"))).to(
            be_err().value(s!("Expected a \".\" or \"[\" instead of \"a\" in path expression \"$adsjhaskjdh\" at index 1")));
    }

    #[test]
    fn parse_path_exp_handles_missing_path_name() {
        expect!(parse_path_exp(s!("$."))).to(
            be_err().value(s!("Expected a path after \".\" in path expression \"$.\" at index 1")));
        expect!(parse_path_exp(s!("$.a.b.c."))).to(
            be_err().value(s!("Expected a path after \".\" in path expression \"$.a.b.c.\" at index 7")));
    }

    #[test]
    fn parse_path_exp_handles_invalid_identifiers() {
        expect!(parse_path_exp(s!("$.abc!"))).to(
            be_err().value(s!("\"!\" is not allowed in an identifier in path expression \"$.abc!\" at index 5")));
        expect!(parse_path_exp(s!("$.a.b.c.}"))).to(
            be_err().value(s!("Expected either a \"*\" or path identifier in path expression \"$.a.b.c.}\" at index 8")));
    }

    #[test]
    fn parse_path_exp_with_simple_identifiers() {
      expect!(parse_path_exp(s!("$.a"))).to(
          be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a"))]));
      expect!(parse_path_exp(s!("$.a.b.c"))).to(
          be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("b")),
          PathToken::Field(s!("c"))]));
      expect!(parse_path_exp(s!("a.b.c"))).to(
        be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("b")),
                           PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_handles_underscores_and_dashes() {
        expect!(parse_path_exp(s!("$.user_id.user-id"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("user_id")),
                               PathToken::Field(s!("user-id"))])
        );
    }

    #[test]
    fn parse_path_exp_with_star_instead_of_identifiers() {
        expect!(parse_path_exp(s!("$.*"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Star]));
        expect!(parse_path_exp(s!("$.a.*.c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Star,
            PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_with_bracket_notation() {
        expect!(parse_path_exp(s!("$['val1']"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("val1"))]));
        expect!(parse_path_exp(s!("$.a['val@1.'].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("val@1.")),
            PathToken::Field(s!("c"))]));
        expect!(parse_path_exp(s!("$.a[1].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Index(1),
            PathToken::Field(s!("c"))]));
        expect!(parse_path_exp(s!("$.a[*].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::StarIndex,
            PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_with_invalid_bracket_notation() {
        expect!(parse_path_exp(s!("$["))).to(
            be_err().value(s!("Expected a \"'\" (single qoute) or a digit in path expression \"$[\" after index 1")));
        expect!(parse_path_exp(s!("$['"))).to(
            be_err().value(s!("Unterminated string in path expression \"$['\" at index 2")));
        expect!(parse_path_exp(s!("$['Unterminated string"))).to(
            be_err().value(s!("Unterminated string in path expression \"$['Unterminated string\" at index 21")));
        expect!(parse_path_exp(s!("$['']"))).to(
            be_err().value(s!("Empty strings are not allowed in path expression \"$['']\" at index 3")));
        expect!(parse_path_exp(s!("$['test'.b.c"))).to(
            be_err().value(s!("Unterminated brackets, found \".\" instead of \"]\" in path expression \"$['test'.b.c\" at index 8")));
        expect!(parse_path_exp(s!("$['test'"))).to(
            be_err().value(s!("Unterminated brackets in path expression \"$['test'\" at index 7")));
        expect!(parse_path_exp(s!("$['test']b.c"))).to(
            be_err().value(s!("Expected a \".\" or \"[\" instead of \"b\" in path expression \"$[\'test\']b.c\" at index 9")));
    }

    #[test]
    fn parse_path_exp_with_invalid_bracket_index_notation() {
        expect!(parse_path_exp(s!("$[dhghh]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"d\" instead in path expression \"$[dhghh]\" at index 2")));
        expect!(parse_path_exp(s!("$[12abc]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"a\" instead in path expression \"$[12abc]\" at index 4")));
        expect!(parse_path_exp(s!("$[]"))).to(
            be_err().value(s!("Empty bracket expressions are not allowed in path expression \"$[]\" at index 2")));
        expect!(parse_path_exp(s!("$[-1]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"-\" instead in path expression \"$[-1]\" at index 2")));
    }
}
<|endoftext|>


<commit_before>use glm;

use resource_manager::Texture;

#[derive(Clone)]
pub struct TileSheet {
    tiles: glm::UVec2,
    texture: Texture,
}

pub struct Tile {
    pub id: usize,
    pub src: glm::IVec4,
}

impl TileSheet {
    pub fn new(tiles: glm::UVec2, texture: Texture) -> TileSheet {
        TileSheet {
            tiles: tiles,
            texture: texture,
        }
    }

    pub fn uv(&self, index: u32) -> glm::DVec4 {
        let x_tile = index % self.tiles.x;
        let y_tile = index / self.tiles.x;

        let tiles = glm::to_dvec2(self.tiles);
        let x_pos = x_tile as f64 / tiles.x;
        let y_pos = y_tile as f64 / tiles.y;
        let x_dims = 1. / tiles.x;
        let y_dims = 1. / tiles.y;

        glm::dvec4(x_pos, y_pos, x_dims, y_dims)
    }
}

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

    #[test]
    fn single_frame() {
        let texture = Texture {
            id: 1,
            dims: glm::uvec2(10, 10),
        };
        let sheet = TileSheet::new(glm::uvec2(1, 1), texture);
        let expected = glm::dvec4(0., 0., 1., 1.);
        let actual = sheet.uv(0);
        assert_eq!(actual, expected);
    }

    #[test]
    fn single_row() {
        let texture = Texture {
            id: 1,
            dims: glm::uvec2(10, 10),
        };
        let sheet = TileSheet::new(glm::uvec2(10, 1), texture);
        let expected = glm::dvec4(0.4, 0., 0.1, 1.);
        let actual = sheet.uv(4);
        assert_eq!(actual, expected);
    }

    #[test]
    fn single_column() {
        let texture = Texture {
            id: 1,
            dims: glm::uvec2(10, 10),
        };
        let sheet = TileSheet::new(glm::uvec2(1, 5), texture);
        let expected = glm::dvec4(0., 0.8, 1., 0.2);
        let actual = sheet.uv(4);
        assert_eq!(actual, expected);
    }

    #[test]
    fn mult_frames() {
        let texture = Texture {
            id: 1,
            dims: glm::uvec2(10, 10),
        };
        let sheet = TileSheet::new(glm::uvec2(4, 2), texture);
        let expected = glm::dvec4(0.25, 0.5, 0.25, 0.5);
        let actual = sheet.uv(5);
        assert_eq!(actual, expected);
    }
}
<commit_msg>adding a method to get a given tile on a tilesheet<commit_after>use glm;

use resource_manager::Texture;

#[derive(Clone)]
pub struct TileSheet {
    id: usize,
    dimensions: glm::UVec2,
    tiles: glm::UVec2,
}

pub struct Tile {
    pub id: usize,
    pub src: glm::UVec4,
}

impl TileSheet {
    pub fn new(tiles: glm::UVec2, texture: Texture) -> TileSheet {
        let dimensions = texture.dims / tiles;
        TileSheet {
            id: texture.id,
            dimensions: dimensions,
            tiles: tiles,
        }
    }

    pub fn tile(&self, index: u32) -> Tile {
        let tile_pos = glm::uvec2(index % self.tiles.x, index / self.tiles.x);
        let position = tile_pos * self.dimensions;
        let src = glm::uvec4(position.x, position.y, self.dimensions.x, self.dimensions.y);

        Tile {
            id: self.id,
            src: src,
        }
    }

    pub fn uv(&self, index: u32) -> glm::DVec4 {
        let x_tile = index % self.tiles.x;
        let y_tile = index / self.tiles.x;

        let tiles = glm::to_dvec2(self.tiles);
        let x_pos = x_tile as f64 / tiles.x;
        let y_pos = y_tile as f64 / tiles.y;
        let x_dims = 1. / tiles.x;
        let y_dims = 1. / tiles.y;

        glm::dvec4(x_pos, y_pos, x_dims, y_dims)
    }
}

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

    #[test]
    fn single_frame() {
        let texture = Texture {
            id: 1,
            dims: glm::uvec2(10, 10),
        };
        let sheet = TileSheet::new(glm::uvec2(1, 1), texture);
        let expected = glm::dvec4(0., 0., 1., 1.);
        let actual = sheet.uv(0);
        assert_eq!(actual, expected);

        let tile = sheet.tile(0);
        assert_eq!(tile.id, 1);
        assert_eq!(tile.src, glm::uvec4(0, 0, 10, 10));
    }

    #[test]
    fn single_row() {
        let texture = Texture {
            id: 2,
            dims: glm::uvec2(10, 10),
        };
        let sheet = TileSheet::new(glm::uvec2(10, 1), texture);
        let expected = glm::dvec4(0.4, 0., 0.1, 1.);
        let actual = sheet.uv(4);
        assert_eq!(actual, expected);

        let tile = sheet.tile(4);
        assert_eq!(tile.id, 2);
        assert_eq!(tile.src, glm::uvec4(4, 0, 1, 10));
    }

    #[test]
    fn single_column() {
        let texture = Texture {
            id: 1,
            dims: glm::uvec2(10, 10),
        };
        let sheet = TileSheet::new(glm::uvec2(1, 5), texture);
        let expected = glm::dvec4(0., 0.8, 1., 0.2);
        let actual = sheet.uv(4);
        assert_eq!(actual, expected);

        let tile = sheet.tile(4);
        assert_eq!(tile.id, 1);
        assert_eq!(tile.src, glm::uvec4(0, 8, 10, 2));
    }

    #[test]
    fn mult_frames() {
        let texture = Texture {
            id: 10,
            dims: glm::uvec2(20, 10),
        };
        let sheet = TileSheet::new(glm::uvec2(4, 2), texture);
        let expected = glm::dvec4(0.25, 0.5, 0.25, 0.5);
        let actual = sheet.uv(5);
        assert_eq!(actual, expected);

        let tile = sheet.tile(5);
        assert_eq!(tile.id, 10);
        assert_eq!(tile.src, glm::uvec4(5, 5, 5, 5));
    }
}
<|endoftext|>