<commit_before>use std::path::PathBuf;

use serde::{Serialize, Deserialize};

use crate::{Color, Point, Speed, Event, Distance, Size};
use crate::state::TurtleId;
use crate::radians::Radians;

/// The different kinds of requests that can be sent from a client
///
/// Not all requests warrant a response. The response for each request is listed in its
/// documentation.
///
/// If a request initiates an animation on a resource, further requests for that resource will be
/// held until the animation is completed. For example, if a request animates a turtle and then
/// a property on that turtle is requested, this will cause the second request to wait for the
/// animation to be completed. Other requests may still continue in the meantime. Only requests
/// pertaining to that resource (the turtle) will be held. This is important to ensure consistency
/// and avoid race conditions while an animation is running.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ClientRequest {
    /// Export the drawing in its current state to the given path using the given format
    ///
    /// Response: `ServerResponse::ExportComplete`
    Export(PathBuf, ExportFormat),

    /// Get the next event
    ///
    /// Response: `ServerResponse::Event`
    NextEvent,

    /// Get the given property of the drawing
    ///
    /// Response: `ServerResponse::DrawingProp`
    DrawingProp(TurtleProp),
    /// Set the given property of the drawing
    ///
    /// Response: N/A
    SetDrawingProp(TurtlePropValue),

    /// Get the given property of a turtle
    ///
    /// Response: `ServerResponse::TurtleProp`
    TurtleProp(TurtleId, TurtleProp),
    /// Set the given property of a turtle
    ///
    /// Response: N/A
    SetTurtleProp(TurtleId, TurtlePropValue),

    /// Move a turtle forward by the given amount
    ///
    /// The turtle moves in the direction of its heading and is animated at its current speed. This
    /// may draw a line if the turtle's pen is down. It may also result in a change to the current
    /// fill if the turtle is currently filling a shape.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    MoveForward(TurtleId, Distance),
    /// Rotate a turtle in place by the given angle in the given direction
    ///
    /// The turtle rotates with an animation at its current speed. It will not draw any line while
    /// rotating since it has not moved.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    RotateInPlace(TurtleId, Radians, RotationDirection),

    /// Creates a fill polygon from a turtle's current position
    ///
    /// The color of the fill will always be consistent with the fill color property of the turtle.
    /// That is, if the color changes in the middle of filling, the entire filled area will change
    /// color. It does not matter what the fill color was when this request was first sent.
    ///
    /// Response: N/A
    BeginFill(TurtleId),
    /// Completes a fill polygon using a turtle's current position
    ///
    /// No further points will be added to the polygon and it will remain the color that it was at
    /// the time that this request was sent.
    ///
    /// Response: N/A
    EndFill(TurtleId),

    /// Clears all drawings created by a turtle
    ///
    /// If no ID is provided, all drawings from all turtles will be cleared.
    ///
    /// The turtle will not move. None of its position, heading, pen, or other properties will
    /// change. No properties on the drawing will change, including the background image/title/etc.
    ///
    /// Response: N/A
    Clear(Option<TurtleId>),
}

#[derive(Debug, Serialize, Deserialize)]
pub enum ServerResponse {
    /// The result of the export, possibly an error if something went wrong
    ExportComplete(Result<(), String>),

    /// The next event
    Event(Event),

    /// The value of the given property of the drawing
    DrawingProp(DrawingPropValue),

    /// The value of the given property of a turtle
    TurtleProp(TurtleId, TurtlePropValue),

    /// An animation was completed for a given turtle
    AnimationComplete(TurtleId),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RotationDirection {
    Clockwise,
    Counterclockwise,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ExportFormat {
    Svg,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DrawingProp {
    Title,
    BackgroundColor,
    Center,
    Size,
    Width,
    Height,
    Maximized,
    Fullscreen,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DrawingPropValue {
    Title(String),
    BackgroundColor(Color),
    Center(Point),
    Size(Size),
    Width(u32),
    Height(u32),
    Maximized(bool),
    Fullscreen(bool),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TurtleProp {
    Pen(PenProp),
    FillColor,
    IsFilling,
    Position,
    PositionX,
    PositionY,
    Heading,
    Speed,
    IsVisible,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TurtlePropValue {
    Pen(PenPropValue),
    FillColor(Color),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `BeginFill` and `EndFill` instead.
    IsFilling(bool),
    Position(Point),
    PositionX(f64),
    PositionY(f64),
    Heading(Radians),
    Speed(Speed),
    IsVisible(bool),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PenProp {
    IsEnabled,
    Thickness,
    Color,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PenPropValue {
    IsEnabled(bool),
    Thickness(f64),
    Color(Color),
}
<commit_msg>Added request and response for getting a TurtleId<commit_after>use std::path::PathBuf;

use serde::{Serialize, Deserialize};

use crate::{Color, Point, Speed, Event, Distance, Size};
use crate::state::TurtleId;
use crate::radians::Radians;

/// The different kinds of requests that can be sent from a client
///
/// Not all requests warrant a response. The response for each request is listed in its
/// documentation.
///
/// If a request initiates an animation on a resource, further requests for that resource will be
/// held until the animation is completed. For example, if a request animates a turtle and then
/// a property on that turtle is requested, this will cause the second request to wait for the
/// animation to be completed. Other requests may still continue in the meantime. Only requests
/// pertaining to that resource (the turtle) will be held. This is important to ensure consistency
/// and avoid race conditions while an animation is running.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ClientRequest {
    /// Sent initially and on creating any turtle in order to get a `TurtleId`
    ///
    /// Response: `ServerResponse::NewTurtle`
    CreateTurtle,

    /// Export the drawing in its current state to the given path using the given format
    ///
    /// Response: `ServerResponse::ExportComplete`
    Export(PathBuf, ExportFormat),

    /// Get the next event
    ///
    /// Response: `ServerResponse::Event`
    NextEvent,

    /// Get the given property of the drawing
    ///
    /// Response: `ServerResponse::DrawingProp`
    DrawingProp(TurtleProp),
    /// Set the given property of the drawing
    ///
    /// Response: N/A
    SetDrawingProp(TurtlePropValue),

    /// Get the given property of a turtle
    ///
    /// Response: `ServerResponse::TurtleProp`
    TurtleProp(TurtleId, TurtleProp),
    /// Set the given property of a turtle
    ///
    /// Response: N/A
    SetTurtleProp(TurtleId, TurtlePropValue),

    /// Move a turtle forward by the given amount
    ///
    /// The turtle moves in the direction of its heading and is animated at its current speed. This
    /// may draw a line if the turtle's pen is down. It may also result in a change to the current
    /// fill if the turtle is currently filling a shape.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    MoveForward(TurtleId, Distance),
    /// Rotate a turtle in place by the given angle in the given direction
    ///
    /// The turtle rotates with an animation at its current speed. It will not draw any line while
    /// rotating since it has not moved.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    RotateInPlace(TurtleId, Radians, RotationDirection),

    /// Creates a fill polygon from a turtle's current position
    ///
    /// The color of the fill will always be consistent with the fill color property of the turtle.
    /// That is, if the color changes in the middle of filling, the entire filled area will change
    /// color. It does not matter what the fill color was when this request was first sent.
    ///
    /// Response: N/A
    BeginFill(TurtleId),
    /// Completes a fill polygon using a turtle's current position
    ///
    /// No further points will be added to the polygon and it will remain the color that it was at
    /// the time that this request was sent.
    ///
    /// Response: N/A
    EndFill(TurtleId),

    /// Clears all drawings created by a turtle
    ///
    /// If no ID is provided, all drawings from all turtles will be cleared.
    ///
    /// The turtle will not move. None of its position, heading, pen, or other properties will
    /// change. No properties on the drawing will change, including the background image/title/etc.
    ///
    /// Response: N/A
    Clear(Option<TurtleId>),
}

#[derive(Debug, Serialize, Deserialize)]
pub enum ServerResponse {
    /// The ID of a newly created turtle, guaranteed to be unique
    NewTurtle(TurtleId),

    /// The result of the export, possibly an error if something went wrong
    ExportComplete(Result<(), String>),

    /// The next event
    Event(Event),

    /// The value of the given property of the drawing
    DrawingProp(DrawingPropValue),

    /// The value of the given property of a turtle
    TurtleProp(TurtleId, TurtlePropValue),

    /// An animation was completed for a given turtle
    AnimationComplete(TurtleId),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RotationDirection {
    Clockwise,
    Counterclockwise,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ExportFormat {
    Svg,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DrawingProp {
    Title,
    BackgroundColor,
    Center,
    Size,
    Width,
    Height,
    Maximized,
    Fullscreen,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DrawingPropValue {
    Title(String),
    BackgroundColor(Color),
    Center(Point),
    Size(Size),
    Width(u32),
    Height(u32),
    Maximized(bool),
    Fullscreen(bool),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TurtleProp {
    Pen(PenProp),
    FillColor,
    IsFilling,
    Position,
    PositionX,
    PositionY,
    Heading,
    Speed,
    IsVisible,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TurtlePropValue {
    Pen(PenPropValue),
    FillColor(Color),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `BeginFill` and `EndFill` instead.
    IsFilling(bool),
    Position(Point),
    PositionX(f64),
    PositionY(f64),
    Heading(Radians),
    Speed(Speed),
    IsVisible(bool),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PenProp {
    IsEnabled,
    Thickness,
    Color,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PenPropValue {
    IsEnabled(bool),
    Thickness(f64),
    Color(Color),
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::LinkedList;

use math::rich_number::Unit;

use parser::ast::Function;

pub struct ConversionTable {
    // (from unit, to unit) : Function definition
    converters: HashMap<(Unit, Unit), Function>,
    // from unit : all other associated units
    associations: HashMap<Unit, Vec<Unit>>,
}

impl ConversionTable {
    pub fn new() -> ConversionTable {
        ConversionTable {
            converters: HashMap::new(),
            associations: HashMap::new(),
        }
    }

    pub fn get_converter(&self, from: Unit, to: Unit) -> Option<Function> {
        self.converters.get(&(from, to)).map(|v| v.clone())
    }

    pub fn define_conversion(&mut self, from: Unit, to: Unit, converter: Function) {
        assert!(from != to, "Cannot convert from the same unit to the same unit");

        self.converters.insert((from, to), converter);

        let assoc = self.associations.entry(from).or_insert(Vec::new());
        if !assoc.contains(&to) {
            assoc.push(to);
        }
    }

    pub fn conversion_steps(&mut self, start: Unit, target: Unit) -> Option<Vec<Unit>> {
        // Technically this is a valid conversion, there are just no steps to take
        if start == target {
            return Some(vec![]);
        }

        let mut shortest: Option<Vec<Unit>> = None;

        let mut open: LinkedList<(Unit, Vec<Unit>)> = LinkedList::new();
        open.push_back((start, Vec::new()));

        let mut seen = HashSet::new();

        while !open.is_empty() {
            let (unit, path) = open.pop_front().unwrap();
            if seen.contains(&unit) {
                continue;
            }
            seen.insert(unit);

            // Dead end
            if !self.associations.contains_key(&unit) {
                continue;
            }

            for next_unit in self.associations.get(&unit).unwrap() {
                if seen.contains(&next_unit) {
                    continue;
                }
                seen.insert(*next_unit);

                let mut next_path = path.clone();
                next_path.push(*next_unit);

                // no path is always shorter than the current path
                let shorter = shortest.as_ref().map_or(true,
                    |ref p| next_path.len() < p.len());

                if *next_unit == target {
                    if shorter {
                        shortest = Some(next_path);
                    }
                }
                // Haven't found target, only continue if this
                // will result in a shorter path than the current
                // shortest path
                else if shorter {
                    // continue searching at the next unit
                    open.push_back((*next_unit, next_path));
                }
            }
        }

        shortest
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::ast::{Function, Expr};
    use math::rich_number::Unit;

    #[test]
    #[should_panic(expected = "same unit")]
    fn reject_defining_conversion_to_same_unit() {
        let unit: Unit = Default::default();

        let fake_func: Function = Function {
            params: Vec::new(),
            body: Expr::new(),
        };

        ConversionTable::new().define_conversion(unit, unit, fake_func);
    }

    #[test]
    fn conversion_to_same_unit() {
        let unit: Unit = Default::default();

        let mut table = ConversionTable::new();
        assert_eq!(table.conversion_steps(unit, unit).unwrap(), vec![]);
    }

    #[test]
    fn varying_conversion_steps() {
        // These numbers are arbitrary
        const mm: Unit = 3;
        const cm: Unit = 1;
        const m: Unit = 2;
        const km: Unit = 5;

        let fake_func: Function = Function {
            params: Vec::new(),
            body: Expr::new(),
        };

        let mut table = ConversionTable::new();

        table.define_conversion(mm, cm, fake_func.clone());
        table.define_conversion(cm, m, fake_func.clone());
        table.define_conversion(m, km, fake_func.clone());

        // Basic forward conversions
        assert_eq!(table.conversion_steps(mm, cm).unwrap(), vec![cm]);
        assert_eq!(table.conversion_steps(mm, m).unwrap(), vec![cm, m]);
        assert_eq!(table.conversion_steps(mm, km).unwrap(), vec![cm, m, km]);

        assert_eq!(table.conversion_steps(cm, mm), None);
        assert_eq!(table.conversion_steps(cm, m).unwrap(), vec![m]);
        assert_eq!(table.conversion_steps(cm, km).unwrap(), vec![m, km]);

        assert_eq!(table.conversion_steps(m, mm), None);
        assert_eq!(table.conversion_steps(m, cm), None);
        assert_eq!(table.conversion_steps(m, km).unwrap(), vec![km]);

        assert_eq!(table.conversion_steps(km, mm), None);
        assert_eq!(table.conversion_steps(km, cm), None);
        assert_eq!(table.conversion_steps(km, m), None);

        // Did not define the backwards conversions yet
        assert_eq!(table.conversion_steps(km, mm), None);

        // These are being added in addition to the previous forward conversions
        table.define_conversion(km, m, fake_func.clone());
        table.define_conversion(m, cm, fake_func.clone());
        table.define_conversion(cm, mm, fake_func.clone());

        // Test forward once again to make sure this doesn't mess that up
        assert_eq!(table.conversion_steps(mm, cm).unwrap(), vec![cm]);
        assert_eq!(table.conversion_steps(mm, m).unwrap(), vec![cm, m]);
        assert_eq!(table.conversion_steps(mm, km).unwrap(), vec![cm, m, km]);

        assert_eq!(table.conversion_steps(cm, mm), None);
        assert_eq!(table.conversion_steps(cm, m).unwrap(), vec![m]);
        assert_eq!(table.conversion_steps(cm, km).unwrap(), vec![m, km]);

        assert_eq!(table.conversion_steps(m, mm), None);
        assert_eq!(table.conversion_steps(m, cm), None);
        assert_eq!(table.conversion_steps(m, km).unwrap(), vec![km]);

        assert_eq!(table.conversion_steps(km, mm), None);
        assert_eq!(table.conversion_steps(km, cm), None);
        assert_eq!(table.conversion_steps(km, m), None);

        // Test backward conversions
        assert_eq!(table.conversion_steps(km, m).unwrap(), vec![m]);
        assert_eq!(table.conversion_steps(km, cm).unwrap(), vec![m, cm]);
        assert_eq!(table.conversion_steps(km, mm).unwrap(), vec![m, cm, mm]);

        assert_eq!(table.conversion_steps(m, cm).unwrap(), vec![cm]);
        assert_eq!(table.conversion_steps(m, mm).unwrap(), vec![cm, mm]);

        assert_eq!(table.conversion_steps(cm, mm).unwrap(), vec![mm]);

        // Short circuit the conversions
        table.define_conversion(m, mm, fake_func.clone());

        // Ensure the shortest path is always taken
        assert_eq!(table.conversion_steps(m, mm).unwrap(), vec![mm]);
        assert_eq!(table.conversion_steps(km, mm).unwrap(), vec![m, mm]);
    }
}
<commit_msg>Fixing bugs in conversion table, tests now passing<commit_after>use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::LinkedList;

use math::rich_number::Unit;

use parser::ast::Function;

pub struct ConversionTable {
    // (from unit, to unit) : Function definition
    converters: HashMap<(Unit, Unit), Function>,
    // from unit : all other associated units
    associations: HashMap<Unit, Vec<Unit>>,
}

impl ConversionTable {
    pub fn new() -> ConversionTable {
        ConversionTable {
            converters: HashMap::new(),
            associations: HashMap::new(),
        }
    }

    pub fn get_converter(&self, from: Unit, to: Unit) -> Option<Function> {
        self.converters.get(&(from, to)).map(|v| v.clone())
    }

    pub fn define_conversion(&mut self, from: Unit, to: Unit, converter: Function) {
        assert!(from != to, "Cannot convert from the same unit to the same unit");

        self.converters.insert((from, to), converter);

        let assoc = self.associations.entry(from).or_insert(Vec::new());
        if !assoc.contains(&to) {
            assoc.push(to);
        }
    }

    pub fn conversion_steps(&mut self, start: Unit, target: Unit) -> Option<Vec<Unit>> {
        // Technically this is a valid conversion, there are just no steps to take
        if start == target {
            return Some(vec![]);
        }

        let mut shortest: Option<Vec<Unit>> = None;

        let mut open: LinkedList<(Unit, Vec<Unit>)> = LinkedList::new();
        open.push_back((start, Vec::new()));

        let mut seen = HashSet::new();

        while !open.is_empty() {
            let (unit, path) = open.pop_front().unwrap();
            if seen.contains(&unit) {
                continue;
            }
            seen.insert(unit);

            // Dead end
            if !self.associations.contains_key(&unit) {
                continue;
            }

            for next_unit in self.associations.get(&unit).unwrap() {
                let mut next_path = path.clone();
                next_path.push(*next_unit);

                // no path is always shorter than the current path
                let shorter = shortest.as_ref().map_or(true,
                    |ref p| next_path.len() < p.len());

                if *next_unit == target {
                    if shorter {
                        shortest = Some(next_path);
                    }
                }
                // Haven't found target, only continue if this
                // will result in a shorter path than the current
                // shortest path
                else if shorter {
                    // continue searching at the next unit
                    open.push_back((*next_unit, next_path));
                }
            }
        }

        shortest
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::ast::{Function, Expr};
    use math::rich_number::Unit;

    #[test]
    #[should_panic(expected = "same unit")]
    fn reject_defining_conversion_to_same_unit() {
        let unit: Unit = Default::default();

        let fake_func: Function = Function {
            params: Vec::new(),
            body: Expr::new(),
        };

        ConversionTable::new().define_conversion(unit, unit, fake_func);
    }

    #[test]
    fn conversion_to_same_unit() {
        let unit: Unit = Default::default();

        let mut table = ConversionTable::new();
        assert_eq!(table.conversion_steps(unit, unit).unwrap(), vec![]);
    }

    #[test]
    fn varying_conversion_steps() {
        // These numbers are arbitrary
        const mm: Unit = 3;
        const cm: Unit = 1;
        const m: Unit = 2;
        const km: Unit = 5;

        let fake_func: Function = Function {
            params: Vec::new(),
            body: Expr::new(),
        };

        let mut table = ConversionTable::new();

        table.define_conversion(mm, cm, fake_func.clone());
        table.define_conversion(cm, m, fake_func.clone());
        table.define_conversion(m, km, fake_func.clone());

        // Basic forward conversions
        assert_eq!(table.conversion_steps(mm, cm).unwrap(), vec![cm]);
        assert_eq!(table.conversion_steps(mm, m).unwrap(), vec![cm, m]);
        assert_eq!(table.conversion_steps(mm, km).unwrap(), vec![cm, m, km]);

        assert_eq!(table.conversion_steps(cm, mm), None);
        assert_eq!(table.conversion_steps(cm, m).unwrap(), vec![m]);
        assert_eq!(table.conversion_steps(cm, km).unwrap(), vec![m, km]);

        assert_eq!(table.conversion_steps(m, mm), None);
        assert_eq!(table.conversion_steps(m, cm), None);
        assert_eq!(table.conversion_steps(m, km).unwrap(), vec![km]);

        assert_eq!(table.conversion_steps(km, mm), None);
        assert_eq!(table.conversion_steps(km, cm), None);
        assert_eq!(table.conversion_steps(km, m), None);

        // Did not define the backwards conversions yet
        assert_eq!(table.conversion_steps(km, mm), None);

        // These are being added in addition to the previous forward conversions
        table.define_conversion(km, m, fake_func.clone());
        table.define_conversion(m, cm, fake_func.clone());
        table.define_conversion(cm, mm, fake_func.clone());

        // Test forward once again to make sure this doesn't mess that up
        assert_eq!(table.conversion_steps(mm, cm).unwrap(), vec![cm]);
        assert_eq!(table.conversion_steps(mm, m).unwrap(), vec![cm, m]);
        assert_eq!(table.conversion_steps(mm, km).unwrap(), vec![cm, m, km]);

        assert_eq!(table.conversion_steps(cm, m).unwrap(), vec![m]);
        assert_eq!(table.conversion_steps(cm, km).unwrap(), vec![m, km]);

        assert_eq!(table.conversion_steps(m, km).unwrap(), vec![km]);

        // Test backward conversions
        assert_eq!(table.conversion_steps(km, m).unwrap(), vec![m]);
        assert_eq!(table.conversion_steps(km, cm).unwrap(), vec![m, cm]);
        assert_eq!(table.conversion_steps(km, mm).unwrap(), vec![m, cm, mm]);

        assert_eq!(table.conversion_steps(m, cm).unwrap(), vec![cm]);
        assert_eq!(table.conversion_steps(m, mm).unwrap(), vec![cm, mm]);

        assert_eq!(table.conversion_steps(cm, mm).unwrap(), vec![mm]);

        // Short circuit the conversions
        table.define_conversion(m, mm, fake_func.clone());

        // Ensure the shortest path is always taken
        assert_eq!(table.conversion_steps(m, mm).unwrap(), vec![mm]);
        assert_eq!(table.conversion_steps(km, mm).unwrap(), vec![m, mm]);
    }
}
<|endoftext|>


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

// This test uses only GDB Python API features which should be available in
// older versions of GDB too. A more extensive test can be found in
// gdb-pretty-struct-and-enums.rs

// ignore-bitrig
// ignore-windows failing on win32 bot
// ignore-freebsd: gdb package too new
// ignore-tidy-linelength
// ignore-lldb
// ignore-android: FIXME(#10381)
// compile-flags:-g

// gdb-command: run

// gdb-command: print regular_struct
// gdb-check:$1 = RegularStruct = {the_first_field = 101, the_second_field = 102.5, the_third_field = false}

// gdb-command: print empty_struct
// gdb-check:$2 = EmptyStruct

// gdb-command: print c_style_enum1
// gdb-check:$3 = CStyleEnumVar1

// gdb-command: print c_style_enum2
// gdb-check:$4 = CStyleEnumVar2

// gdb-command: print c_style_enum3
// gdb-check:$5 = CStyleEnumVar3

#![allow(dead_code, unused_variables)]

struct RegularStruct {
    the_first_field: isize,
    the_second_field: f64,
    the_third_field: bool,
}

struct EmptyStruct;

enum CStyleEnum {
    CStyleEnumVar1,
    CStyleEnumVar2,
    CStyleEnumVar3,
}

fn main() {

    let regular_struct = RegularStruct {
        the_first_field: 101,
        the_second_field: 102.5,
        the_third_field: false
    };

    let empty_struct = EmptyStruct;

    let c_style_enum1 = CStyleEnum::CStyleEnumVar1;
    let c_style_enum2 = CStyleEnum::CStyleEnumVar2;
    let c_style_enum3 = CStyleEnum::CStyleEnumVar3;

    zzz(); // #break
}

fn zzz() { () }
<commit_msg>Remove reference to gdb-pretty-struct-and-enums.rs<commit_after>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-bitrig
// ignore-windows failing on win32 bot
// ignore-freebsd: gdb package too new
// ignore-tidy-linelength
// ignore-lldb
// ignore-android: FIXME(#10381)
// compile-flags:-g

// gdb-command: run

// gdb-command: print regular_struct
// gdb-check:$1 = RegularStruct = {the_first_field = 101, the_second_field = 102.5, the_third_field = false}

// gdb-command: print empty_struct
// gdb-check:$2 = EmptyStruct

// gdb-command: print c_style_enum1
// gdb-check:$3 = CStyleEnumVar1

// gdb-command: print c_style_enum2
// gdb-check:$4 = CStyleEnumVar2

// gdb-command: print c_style_enum3
// gdb-check:$5 = CStyleEnumVar3

#![allow(dead_code, unused_variables)]

struct RegularStruct {
    the_first_field: isize,
    the_second_field: f64,
    the_third_field: bool,
}

struct EmptyStruct;

enum CStyleEnum {
    CStyleEnumVar1,
    CStyleEnumVar2,
    CStyleEnumVar3,
}

fn main() {

    let regular_struct = RegularStruct {
        the_first_field: 101,
        the_second_field: 102.5,
        the_third_field: false
    };

    let empty_struct = EmptyStruct;

    let c_style_enum1 = CStyleEnum::CStyleEnumVar1;
    let c_style_enum2 = CStyleEnum::CStyleEnumVar2;
    let c_style_enum3 = CStyleEnum::CStyleEnumVar3;

    zzz(); // #break
}

fn zzz() { () }
<|endoftext|>


<commit_before>
use syntax::ast;
use syntax::ast::{MetaItemKind,NestedMetaItemKind};
use rustc::hir::def_id::DefId;
use rustc::hir::def;
use syntax::codemap::Span;
use rustc::lint::{self, LintContext, LintPass, LateLintPass, LintArray};
use rustc::ty::{self, TyCtxt};
use rustc::hir;

declare_lint!(NOT_TAGGED_SAFE, Warn, "Warn about use of non-tagged methods within tagged function");

#[derive(Default)]
pub struct Pass
{
    visit_stack: Vec<ast::NodeId>,
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(NOT_TAGGED_SAFE)
    }
}

impl<'a,'b> LateLintPass<'a,'b> for Pass {
    fn check_fn(&mut self, cx: &lint::LateContext, _kind: hir::intravisit::FnKind, _decl: &hir::FnDecl, body: &hir::Body, _: Span, id: ast::NodeId) {
        let attrs = cx.tcx.map.attrs(id);

        // If this function is tagged with a particular safety, store
        {
            let mut lh = ::database::CACHE.write().unwrap();
            for tag_name in get_tags(attrs, "is_safe")
            {
                let tag = lh.get_tag_or_add(&tag_name.as_str());
                lh.mark(id, tag,  true);
            }
            for tag_name in get_tags(attrs, "not_safe")
            {
                let tag = lh.get_tag_or_add(&tag_name.as_str());
                lh.mark(id, tag,  false);
            }
        }

        // For each required safety, check
        for tag_name in get_tags(attrs, "req_safe")
        {
            let ty_tag = {
                let mut lh = ::database::CACHE.write().unwrap();
                let tag = lh.get_tag_or_add(&tag_name.as_str());
                //let tag = if let Some(v) = lh.get_tag_opt(&tag_name.as_str()) { v } else { error!("Tag {} unknown", ty_name);  continue; };
                lh.mark(id, tag,  true);
                tag
                };

            // Search body for calls to non safe methods
            let mut v = Visitor {
                    pass: self, tcx: &cx.tcx, tag: ty_tag,
                    cb: |span| {
                            cx.span_lint(NOT_TAGGED_SAFE, *span,
                                &format!("Calling {0}-unsafe method from a #[req_safe({0})] method", tag_name)[..]
                                );
                        },
                    };
            debug!("Method {:?} is marked safe '{}'", id, tag_name);
            hir::intravisit::walk_body(&mut v, body);
        }

        // TODO: For all known safeties (that aren't already set) populate.
        // - Requires knowing all safeties (which we don't, ... yet)
    }
}

impl Pass
{
    fn fill_cache_for(&mut self, tcx: &TyCtxt, node_id: ast::NodeId)
    {
        debug!("Filling cache for node {:?}", node_id);
        let attrs = tcx.map.attrs(node_id);
        let mut lh = ::database::CACHE.write().unwrap();
        for tag_name in Iterator::chain( get_tags(attrs, "is_safe"), get_tags(attrs, "req_safe") )
        {
            debug!("#[is_safe/req_safe] {} - {}", tag_name, node_id);
            let tag = lh.get_tag_or_add(&tag_name.as_str());
            lh.mark(node_id, tag,  true);
        }
        for tag_name in get_tags(attrs, "not_safe")
        {
            debug!("#[not_safe] {} - {}", tag_name, node_id);
            let tag = lh.get_tag_or_add(&tag_name.as_str());
            lh.mark(node_id, tag,  false);
        }
    }

    /// Recursively check that the provided function is either safe or unsafe.
    // Used to avoid excessive annotating
    fn recurse_fcn_body(&mut self, tcx: &TyCtxt, node_id: ast::NodeId, tag: ::database::Tag) -> bool
    {
        // and apply a visitor to all
        match tcx.map.get(node_id)
        {
        hir::map::NodeItem(i) =>
            match i.node {
            hir::ItemFn(_, _, _, _, _, ref body) => {
                // Enumerate this function's code, recursively checking for a call to an unsafe method
                let mut is_safe = true;
                {
                    let mut v = Visitor {
                        pass: self, tcx: tcx, tag: tag,
                        cb: |_| { is_safe = false; }
                        };
                    hir::intravisit::walk_body(&mut v, tcx.map.body(*body));
                }
                is_safe
                },
            ref v @ _ => {
                error!("Node ID {} points to a non-function item {:?}", node_id, v);
                true
                },
            },
        hir::map::NodeImplItem(i) =>
            match i.node {
            hir::ImplItemKind::Method(_, ref body) => {

                let mut is_safe = true;
                {
                    let mut v = Visitor {
                        pass: self, tcx: tcx, tag: tag,
                        cb: |_| { is_safe = false; }
                        };
                    hir::intravisit::walk_body(&mut v, tcx.map.body(*body));
                }
                is_safe
                },
            _ => true,
            },
        //hir::map::NodeForeignItem(i) =>
        //    if Self::check_for_marker(tcx, i.id, "tag_safe", name) {
        //        true
        //    }
        //    else if Self::check_for_marker(tcx, i.id, "tag_unsafe", name) {
        //        false
        //    }
        //    else {
        //        unknown_assume
        //    },
        ref v @ _ => {
            error!("Node ID {} points to non-item {:?}", node_id, v);
            true
            }
        }
    }

    /// Locate a #[tag_safe(<name>)] attribute on the passed item
    pub fn method_is_safe(&mut self, tcx: &TyCtxt, id: DefId, tag: ::database::Tag) -> bool
    {
        if ! id.is_local()
        {
            // TODO: Get the entry from the crate cache
            if let Some(v) = ::database::CACHE.read().unwrap().get_extern(tcx,id.krate, id.index, tag) {
                debug!("{:?} - {} (extern cached)", id, v);
                v
            }
            else {
                debug!("{:?} - {} (extern assumed)", id, true);
                true
            }
        }
        else
        {
            let node_id = tcx.map.as_local_node_id(id).unwrap();
            let mut local_opt = ::database::CACHE.read().unwrap().get_local(node_id, tag);
            // NOTE: This only fires once (ideally)
            if local_opt.is_none() {
                self.fill_cache_for(tcx, node_id);
                local_opt = ::database::CACHE.read().unwrap().get_local(node_id, tag);
            }
            if let Some(v) = local_opt {
                debug!("{} - {} (cached)", node_id, v);
                v
            }
            else {
                // If this node is currently being checked, assume it's valid.
                // TODO: This can lead to a false positive being stored.
                if self.visit_stack.iter().position(|x| *x == node_id).is_some() {
                    warn!("Recursion, assuming true");
                    true
                }
                else {
                    self.visit_stack.push(node_id);
                    let rv = self.recurse_fcn_body(tcx, node_id, tag);
                    self.visit_stack.pop();

                    debug!("{} - {} (recursed)", node_id, rv);
                    ::database::CACHE.write().unwrap().mark(node_id, tag,  rv);
                    rv
                }
            }
        }
    }
}

struct Visitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a, F: FnMut(&Span) + 'a>
{
    pass: &'a mut Pass,
    tcx: &'a TyCtxt<'a, 'gcx, 'tcx>,
    tag: ::database::Tag,
    cb: F,
}

impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a, F: FnMut(&Span)> hir::intravisit::Visitor<'a> for Visitor<'a,'gcx, 'tcx, F>
{
	fn nested_visit_map<'this>(&'this mut self) -> hir::intravisit::NestedVisitorMap<'this, 'a> {
		hir::intravisit::NestedVisitorMap::None
	}

    // Locate function/method calls in a code block
    fn visit_expr(&mut self, ex: &'a hir::Expr) {
        debug!("visit node - {:?}", ex);
        match ex.node
        {
        // Call expressions - check that it's a path call
        hir::ExprCall(ref fcn, _) =>
            match fcn.node
            {
            hir::ExprPath(ref qp, ..) => {
				match self.tcx.tables.borrow().qpath_def(qp, fcn.id)
				{
				def::Def::Fn(did) | def::Def::Method(did) => {
					// Check for a safety tag
					if !self.pass.method_is_safe(self.tcx, did, self.tag)
					{
						(self.cb)(&ex.span);
					}
					else {
						debug!("Safe call {:?}", ex);
					}
					},
				_ => {
					info!("Call ExprPath with an unknown Def type");
					}
				}
                },
            _ => {
                info!("Call without ExprPath");
                },
            },

        // Method call expressions - get the relevant method
        hir::ExprMethodCall(ref _id, ref _tys, ref _exprs) =>
            {
                let tables = self.tcx.tables.borrow();
                let mm = &tables.method_map;

                let callee = mm.get( &ty::MethodCall::expr(ex.id) ).unwrap();
                let id = callee.def_id;

				// Check for a safety tag
				if !self.pass.method_is_safe(self.tcx, id, self.tag) {
					(self.cb)(&ex.span);
				}
            },

        // Ignore any other type of node
        _ => {},
        }
        hir::intravisit::walk_expr(self, ex);
    }
}

fn get_tags<'a>(meta_items: &'a [ast::Attribute], attr_name: &'a str) -> impl Iterator<Item=::syntax::symbol::Symbol>+'a {
    meta_items.iter()
        .filter(move |attr| attr.value.name == attr_name)
        .flat_map(|attr|
            if let MetaItemKind::List(ref v) = attr.value.node {
                v.iter()
            }
            else {
                panic!("");
            }
            )
        .filter_map(|tag_meta|
            if let NestedMetaItemKind::MetaItem(ref ptr) = tag_meta.node {
                if let MetaItemKind::Word = ptr.node {
                    Some(ptr.name)
                }
                else {
                    warn!("");
                    None
                }
            }
            else {
                warn!("");
                None
            }
            )
}
<commit_msg>Fix nightly breakage<commit_after>
use syntax::ast;
use syntax::ast::{MetaItemKind,NestedMetaItemKind};
use rustc::hir::def_id::DefId;
use rustc::hir::def;
use syntax::codemap::Span;
use rustc::lint::{self, LintContext, LintPass, LateLintPass, LintArray};
use rustc::ty::{self, TyCtxt};
use rustc::hir;

declare_lint!(NOT_TAGGED_SAFE, Warn, "Warn about use of non-tagged methods within tagged function");

#[derive(Default)]
pub struct Pass
{
    visit_stack: Vec<ast::NodeId>,
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(NOT_TAGGED_SAFE)
    }
}

impl<'a,'b> LateLintPass<'a,'b> for Pass {
    fn check_fn(&mut self, cx: &lint::LateContext, _kind: hir::intravisit::FnKind, _decl: &hir::FnDecl, body: &hir::Body, _: Span, id: ast::NodeId) {
        let attrs = cx.tcx.map.attrs(id);

        // If this function is tagged with a particular safety, store
        {
            let mut lh = ::database::CACHE.write().unwrap();
            for tag_name in get_tags(attrs, "is_safe")
            {
                let tag = lh.get_tag_or_add(&tag_name.as_str());
                lh.mark(id, tag,  true);
            }
            for tag_name in get_tags(attrs, "not_safe")
            {
                let tag = lh.get_tag_or_add(&tag_name.as_str());
                lh.mark(id, tag,  false);
            }
        }

        // For each required safety, check
        for tag_name in get_tags(attrs, "req_safe")
        {
            let ty_tag = {
                let mut lh = ::database::CACHE.write().unwrap();
                let tag = lh.get_tag_or_add(&tag_name.as_str());
                //let tag = if let Some(v) = lh.get_tag_opt(&tag_name.as_str()) { v } else { error!("Tag {} unknown", ty_name);  continue; };
                lh.mark(id, tag,  true);
                tag
                };

            // Search body for calls to non safe methods
            let mut v = Visitor {
                    pass: self, cx: cx, tag: ty_tag,
                    cb: |span| {
                            cx.span_lint(NOT_TAGGED_SAFE, *span,
                                &format!("Calling {0}-unsafe method from a #[req_safe({0})] method", tag_name)[..]
                                );
                        },
                    };
            debug!("Method {:?} is marked safe '{}'", id, tag_name);
            hir::intravisit::walk_body(&mut v, body);
        }

        // TODO: For all known safeties (that aren't already set) populate.
        // - Requires knowing all safeties (which we don't, ... yet)
    }
}

impl Pass
{
    fn fill_cache_for(&mut self, tcx: &TyCtxt, node_id: ast::NodeId)
    {
        debug!("Filling cache for node {:?}", node_id);
        let attrs = tcx.map.attrs(node_id);
        let mut lh = ::database::CACHE.write().unwrap();
        for tag_name in Iterator::chain( get_tags(attrs, "is_safe"), get_tags(attrs, "req_safe") )
        {
            debug!("#[is_safe/req_safe] {} - {}", tag_name, node_id);
            let tag = lh.get_tag_or_add(&tag_name.as_str());
            lh.mark(node_id, tag,  true);
        }
        for tag_name in get_tags(attrs, "not_safe")
        {
            debug!("#[not_safe] {} - {}", tag_name, node_id);
            let tag = lh.get_tag_or_add(&tag_name.as_str());
            lh.mark(node_id, tag,  false);
        }
    }

    /// Recursively check that the provided function is either safe or unsafe.
    // Used to avoid excessive annotating
    fn recurse_fcn_body(&mut self, cx: &lint::LateContext, node_id: ast::NodeId, tag: ::database::Tag) -> bool
    {
        // and apply a visitor to all
        match cx.tcx.map.get(node_id)
        {
        hir::map::NodeItem(i) =>
            match i.node {
            hir::ItemFn(_, _, _, _, _, ref body) => {
                // Enumerate this function's code, recursively checking for a call to an unsafe method
                let mut is_safe = true;
                {
                    let mut v = Visitor {
                        pass: self, cx: cx, tag: tag,
                        cb: |_| { is_safe = false; }
                        };
                    hir::intravisit::walk_body(&mut v, cx.tcx.map.body(*body));
                }
                is_safe
                },
            ref v @ _ => {
                error!("Node ID {} points to a non-function item {:?}", node_id, v);
                true
                },
            },
        hir::map::NodeImplItem(i) =>
            match i.node {
            hir::ImplItemKind::Method(_, ref body) => {

                let mut is_safe = true;
                {
                    let mut v = Visitor {
                        pass: self, cx: cx, tag: tag,
                        cb: |_| { is_safe = false; }
                        };
                    hir::intravisit::walk_body(&mut v, cx.tcx.map.body(*body));
                }
                is_safe
                },
            _ => true,
            },
        //hir::map::NodeForeignItem(i) =>
        //    if Self::check_for_marker(tcx, i.id, "tag_safe", name) {
        //        true
        //    }
        //    else if Self::check_for_marker(tcx, i.id, "tag_unsafe", name) {
        //        false
        //    }
        //    else {
        //        unknown_assume
        //    },
        ref v @ _ => {
            error!("Node ID {} points to non-item {:?}", node_id, v);
            true
            }
        }
    }

    /// Locate a #[tag_safe(<name>)] attribute on the passed item
    pub fn method_is_safe(&mut self, cx: &lint::LateContext, id: DefId, tag: ::database::Tag) -> bool
    {
        if ! id.is_local()
        {
            // TODO: Get the entry from the crate cache
            if let Some(v) = ::database::CACHE.read().unwrap().get_extern(&cx.tcx,id.krate, id.index, tag) {
                debug!("{:?} - {} (extern cached)", id, v);
                v
            }
            else {
                debug!("{:?} - {} (extern assumed)", id, true);
                true
            }
        }
        else
        {
            let node_id = cx.tcx.map.as_local_node_id(id).unwrap();
            let mut local_opt = ::database::CACHE.read().unwrap().get_local(node_id, tag);
            // NOTE: This only fires once (ideally)
            if local_opt.is_none() {
                self.fill_cache_for(&cx.tcx, node_id);
                local_opt = ::database::CACHE.read().unwrap().get_local(node_id, tag);
            }
            if let Some(v) = local_opt {
                debug!("{} - {} (cached)", node_id, v);
                v
            }
            else {
                // If this node is currently being checked, assume it's valid.
                // TODO: This can lead to a false positive being stored.
                if self.visit_stack.iter().position(|x| *x == node_id).is_some() {
                    warn!("Recursion, assuming true");
                    true
                }
                else {
                    self.visit_stack.push(node_id);
                    let rv = self.recurse_fcn_body(cx, node_id, tag);
                    self.visit_stack.pop();

                    debug!("{} - {} (recursed)", node_id, rv);
                    ::database::CACHE.write().unwrap().mark(node_id, tag,  rv);
                    rv
                }
            }
        }
    }
}

struct Visitor<'a, 'tcx: 'a, F: FnMut(&Span) + 'a>
{
    pass: &'a mut Pass,
	cx: &'a lint::LateContext<'a,'tcx>,
    tag: ::database::Tag,
    cb: F,
}

impl<'a, 'tcx: 'a, F: FnMut(&Span)> hir::intravisit::Visitor<'a> for Visitor<'a, 'tcx, F>
{
	fn nested_visit_map<'this>(&'this mut self) -> hir::intravisit::NestedVisitorMap<'this, 'a> {
		hir::intravisit::NestedVisitorMap::None
	}

    // Locate function/method calls in a code block
    fn visit_expr(&mut self, ex: &'a hir::Expr) {
        debug!("visit node - {:?}", ex);
        match ex.node
        {
        // Call expressions - check that it's a path call
        hir::ExprCall(ref fcn, ..) =>
			match fcn.node
			{
			hir::ExprPath(ref qp, ..) =>
				match self.cx.tables.qpath_def(qp, fcn.id)
				{
				def::Def::Fn(did) | def::Def::Method(did) =>
					// Check for a safety tag
					if !self.pass.method_is_safe(self.cx, did, self.tag)
					{
						(self.cb)(&ex.span);
					}
					else {
						debug!("Safe call {:?}", ex);
					},
				_ => {
					info!("Call ExprPath with an unknown Def type");
					},
				},
			_ => {
				info!("Call without ExprPath");
				},
			},

        // Method call expressions - get the relevant method
        hir::ExprMethodCall(ref _id, ref _tys, ref _exprs) =>
            {
                let mm = &self.cx.tables.method_map;

                let callee = mm.get( &ty::MethodCall::expr(ex.id) ).unwrap();
                let id = callee.def_id;

				// Check for a safety tag
				if !self.pass.method_is_safe(self.cx, id, self.tag) {
					(self.cb)(&ex.span);
				}
            },

        // Ignore any other type of node
        _ => {},
        }
        hir::intravisit::walk_expr(self, ex);
    }
}

fn get_tags<'a>(meta_items: &'a [ast::Attribute], attr_name: &'a str) -> impl Iterator<Item=::syntax::symbol::Symbol>+'a {
    meta_items.iter()
        .filter(move |attr| attr.value.name == attr_name)
        .flat_map(|attr|
            if let MetaItemKind::List(ref v) = attr.value.node {
                v.iter()
            }
            else {
                panic!("");
            }
            )
        .filter_map(|tag_meta|
            if let NestedMetaItemKind::MetaItem(ref ptr) = tag_meta.node {
                if let MetaItemKind::Word = ptr.node {
                    Some(ptr.name)
                }
                else {
                    warn!("");
                    None
                }
            }
            else {
                warn!("");
                None
            }
            )
}
<|endoftext|>


<commit_before>use std::sync::Mutex;
use std::thread;
use stopwatch;
use time;

use common::block_position::BlockPosition;
use common::communicate::{ClientToServer, ServerToClient, TerrainBlockSend};
use common::interval_timer::IntervalTimer;
use common::serialize::Copyable;
use common::surroundings_loader::LODChange;

use client::Client;
use load_terrain::{load_terrain_block, lod_index};
use server_update::apply_server_update;
use view_update::ClientToView;

pub fn update_thread<RecvServer, RecvBlock, UpdateView, UpdateServer, QueueBlock>(
  quit: &Mutex<bool>,
  client: &Client,
  recv_server: &mut RecvServer,
  recv_block: &mut RecvBlock,
  update_view: &mut UpdateView,
  update_server: &mut UpdateServer,
  queue_block: &mut QueueBlock,
) where
  RecvServer: FnMut() -> Option<ServerToClient>,
  RecvBlock: FnMut() -> Option<TerrainBlockSend>,
  UpdateView: FnMut(ClientToView),
  UpdateServer: FnMut(ClientToServer),
  QueueBlock: FnMut(TerrainBlockSend),
{
  let mut surroundings_timer = IntervalTimer::new(1_000_000, time::precise_time_ns());

  'update_loop: loop {
    if *quit.lock().unwrap() == true {
      break 'update_loop;
    } else if let Some(up) = recv_server() {
      apply_server_update(
        client,
        update_view,
        update_server,
        queue_block,
        up,
      );
    } else {
      if surroundings_timer.update(time::precise_time_ns()) > 0 {
        stopwatch::time("update_surroundings", || {
          let position = *client.player_position.lock().unwrap();
          let position = BlockPosition::from_world_position(&position);
          let mut loaded_blocks = client.loaded_blocks.lock().unwrap();
          let mut surroundings_loader = client.surroundings_loader.lock().unwrap();
          let updates =
            surroundings_loader
            .updates(position)
            .take(1 << 7)
          ;
          for lod_change in updates {
            match lod_change {
              LODChange::Load(block_position, distance) => {
                stopwatch::time("update_thread.load_block", || {
                  let lod = lod_index(distance);
                  let loaded_lod =
                    loaded_blocks
                    .get(&block_position)
                    .map(|&(_, lod)| lod);
                  if loaded_lod != Some(lod) {
                    update_server(
                      ClientToServer::RequestBlock(
                        Copyable(client.id),
                        Copyable(block_position),
                        Copyable(lod),
                      )
                    );
                  } else {
                    trace!("Not re-loading {:?} at {:?}", block_position, lod);
                  }
                })
              },
              LODChange::Unload(block_position) => {
                stopwatch::time("update_thread.unload", || {
                  // The block removal code is duplicated elsewhere.

                  loaded_blocks
                    .remove(&block_position)
                    // If it wasn't loaded, don't unload anything.
                    .map(|(block, prev_lod)| {
                      for id in &block.ids {
                        update_view(ClientToView::RemoveTerrain(*id));
                      }
                      update_view(ClientToView::RemoveBlockData(block_position, prev_lod));
                    });
                })
              },
            };
          }
        });
      } else if let Some(block) = recv_block() {
        trace!("Got block: {:?} at {:?}", block.position, block.lod);
        load_terrain_block(
          client,
          update_view,
          block,
        );
      } else {
        thread::yield_now();
      }
    }
  }
}
<commit_msg>change priority-switching<commit_after>use std::sync::Mutex;
use stopwatch;
use time;

use common::block_position::BlockPosition;
use common::communicate::{ClientToServer, ServerToClient, TerrainBlockSend};
use common::serialize::Copyable;
use common::surroundings_loader::LODChange;

use client::Client;
use load_terrain::{load_terrain_block, lod_index};
use server_update::apply_server_update;
use view_update::ClientToView;

pub fn update_thread<RecvServer, RecvBlock, UpdateView, UpdateServer, QueueBlock>(
  quit: &Mutex<bool>,
  client: &Client,
  recv_server: &mut RecvServer,
  recv_block: &mut RecvBlock,
  update_view: &mut UpdateView,
  update_server: &mut UpdateServer,
  queue_block: &mut QueueBlock,
) where
  RecvServer: FnMut() -> Option<ServerToClient>,
  RecvBlock: FnMut() -> Option<TerrainBlockSend>,
  UpdateView: FnMut(ClientToView),
  UpdateServer: FnMut(ClientToServer),
  QueueBlock: FnMut(TerrainBlockSend),
{
  'update_loop: loop {
    if *quit.lock().unwrap() == true {
      break 'update_loop
    } else {
      stopwatch::time("update_iteration", || {
        let start = time::precise_time_ns();
        while let Some(up) = recv_server() {
          apply_server_update(
            client,
            update_view,
            update_server,
            queue_block,
            up,
          );

          if time::precise_time_ns() - start >= 1_000_000 {
            break
          }
        }

        stopwatch::time("update_surroundings", || {
          let start = time::precise_time_ns();
          let position = *client.player_position.lock().unwrap();
          let position = BlockPosition::from_world_position(&position);
          let mut loaded_blocks = client.loaded_blocks.lock().unwrap();
          let mut surroundings_loader = client.surroundings_loader.lock().unwrap();
          let updates =
            surroundings_loader
            .updates(position)
          ;
          for lod_change in updates {
            match lod_change {
              LODChange::Load(block_position, distance) => {
                stopwatch::time("update_thread.load_block", || {
                  let lod = lod_index(distance);
                  let loaded_lod =
                    loaded_blocks
                    .get(&block_position)
                    .map(|&(_, lod)| lod);
                  if loaded_lod != Some(lod) {
                    update_server(
                      ClientToServer::RequestBlock(
                        Copyable(client.id),
                        Copyable(block_position),
                        Copyable(lod),
                      )
                    );
                  } else {
                    trace!("Not re-loading {:?} at {:?}", block_position, lod);
                  }
                })
              },
              LODChange::Unload(block_position) => {
                stopwatch::time("update_thread.unload", || {
                  // The block removal code is duplicated elsewhere.

                  loaded_blocks
                    .remove(&block_position)
                    // If it wasn't loaded, don't unload anything.
                    .map(|(block, prev_lod)| {
                      for id in &block.ids {
                        update_view(ClientToView::RemoveTerrain(*id));
                      }
                      update_view(ClientToView::RemoveBlockData(block_position, prev_lod));
                    });
                })
              },
            };

            if time::precise_time_ns() - start >= 1_000_000 {
              break
            }
          }
        });

        let start = time::precise_time_ns();
        while let Some(block) = recv_block() {
          trace!("Got block: {:?} at {:?}", block.position, block.lod);
          load_terrain_block(
            client,
            update_view,
            block,
          );

          if time::precise_time_ns() - start >= 1_000_000 {
            break
          }
        }
      })
    }
  }
}
<|endoftext|>


<commit_before>//! Utilities and parsers for dealing with ASCII data in `u8` format.

use conv::{NoError, ValueFrom};
use conv::errors::UnwrapOk;

use std::ops::{Add, Mul};

use {Input, U8Result};
use parsers::take_while1;

/// Lowercase ASCII predicate.
#[inline]
pub fn is_lowercase(c: u8) -> bool {
    b'a' <= c && c <= b'z'
}

/// Uppercase ASCII character predicate.
#[inline]
pub fn is_uppercase(c: u8) -> bool {
    b'A' <= c && c <= b'Z'
}

/// ASCII whitespace predicate.
///
/// Includes:
///
/// * Horizontal tab (TAB)
/// * Line feed (LF)
/// * Vertical tab (VT)
/// * Form feed (FF)
/// * Carriage return (CR)
/// * Space
#[inline]
pub fn is_whitespace(c: u8) -> bool {
    9 <= c && c <= 13 || c == b' '
}

/// ASCII digit predicate.
#[inline]
pub fn is_digit(c: u8) -> bool {
    b'0' <= c && c <= b'9'
}

/// ASCII alphabetic predicate.
#[inline]
pub fn is_alpha(c: u8) -> bool {
    is_lowercase(c) || is_uppercase(c)
}

/// ASCII alphanumeric predicate.
#[inline]
pub fn is_alphanumeric(c: u8) -> bool {
    is_alpha(c) || is_digit(c)
}

/// Parses a series of digits and converts them to an integer.
///
/// # Note
///
/// The `T` type must be larger than `u8` if it is signed.
///
/// # Example
///
/// ```
/// use chomp::Input;
/// use chomp::ascii::decimal;
///
/// let i = Input::new(b"123");
///
/// let r = decimal::<u8>(i);
///
/// assert_eq!(r.unwrap(), 123u8);
/// ```
#[inline]
pub fn decimal<T: Copy + ValueFrom<u8, Err=NoError> + Add<Output=T> + Mul<Output=T>>(i: Input<u8>) -> U8Result<T> {
    take_while1(i, is_digit).bind(|i, buf| i.ret(to_decimal(buf)))
}

/// Internal function converting a `[u8]` to the given integer type `T`.
///
/// # Notes
///
/// * The slice must not contain any other characters besides 0 to 9.
/// * The `T` type must be larger than `u8` if it is signed.
#[inline]
fn to_decimal<T: Copy + ValueFrom<u8, Err=NoError> + Add<Output=T> + Mul<Output=T>>(buf: &[u8]) -> T {
    buf.iter().fold(T::value_from(0).unwrap_ok(), |a, n| a * T::value_from(10).unwrap_ok() + T::value_from(n - b'0').unwrap_ok())
}

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

    macro_rules! test_to_decimal {
        ( $($n:ty),+ ) => { $(
            assert_eq!(to_decimal::<$n>(b""), 0);
            assert_eq!(to_decimal::<$n>(b"0"), 0);
            assert_eq!(to_decimal::<$n>(b"1"), 1);
            assert_eq!(to_decimal::<$n>(b"2"), 2);
            assert_eq!(to_decimal::<$n>(b"10"), 10);
            assert_eq!(to_decimal::<$n>(b"20"), 20);
            assert_eq!(to_decimal::<$n>(b"25"), 25);
        )+ }
    }

    #[test]
    fn test_to_decimal_u8() {
        test_to_decimal!(u8, u16, u32, u64, i16, i32, i64);
    }
}
<commit_msg>Ascii: digit<commit_after>//! Utilities and parsers for dealing with ASCII data in `u8` format.

use conv::{NoError, ValueFrom};
use conv::errors::UnwrapOk;

use std::ops::{Add, Mul};

use {Input, U8Result};
use parsers::take_while1;
use parsers::satisfy;

/// Lowercase ASCII predicate.
#[inline]
pub fn is_lowercase(c: u8) -> bool {
    b'a' <= c && c <= b'z'
}

/// Uppercase ASCII character predicate.
#[inline]
pub fn is_uppercase(c: u8) -> bool {
    b'A' <= c && c <= b'Z'
}

/// ASCII whitespace predicate.
///
/// Includes:
///
/// * Horizontal tab (TAB)
/// * Line feed (LF)
/// * Vertical tab (VT)
/// * Form feed (FF)
/// * Carriage return (CR)
/// * Space
#[inline]
pub fn is_whitespace(c: u8) -> bool {
    9 <= c && c <= 13 || c == b' '
}

/// ASCII digit predicate.
#[inline]
pub fn is_digit(c: u8) -> bool {
    b'0' <= c && c <= b'9'
}

/// ASCII alphabetic predicate.
#[inline]
pub fn is_alpha(c: u8) -> bool {
    is_lowercase(c) || is_uppercase(c)
}

/// ASCII alphanumeric predicate.
#[inline]
pub fn is_alphanumeric(c: u8) -> bool {
    is_alpha(c) || is_digit(c)
}

/// Parses a single digit.
///
/// # Note
///
/// The result is the code of the digit, no conversion takes place.
///
/// # Example
///
/// ```
/// use chomp::Input;
/// use chomp::ascii::digit;
///
/// let i = Input::new(b"1");
///
/// let r = digit(i);
///
/// assert_eq!(r.unwrap(), b'1');
/// ```
#[inline]
pub fn digit(i: Input<u8>) -> U8Result<u8> {
    satisfy(i, is_digit)
}

/// Parses a series of digits and converts them to an integer.
///
/// # Note
///
/// The `T` type must be larger than `u8` if it is signed.
///
/// # Example
///
/// ```
/// use chomp::Input;
/// use chomp::ascii::decimal;
///
/// let i = Input::new(b"123");
///
/// let r = decimal::<u8>(i);
///
/// assert_eq!(r.unwrap(), 123u8);
/// ```
#[inline]
pub fn decimal<T: Copy + ValueFrom<u8, Err=NoError> + Add<Output=T> + Mul<Output=T>>(i: Input<u8>) -> U8Result<T> {
    take_while1(i, is_digit).bind(|i, buf| i.ret(to_decimal(buf)))
}

/// Internal function converting a `[u8]` to the given integer type `T`.
///
/// # Notes
///
/// * The slice must not contain any other characters besides 0 to 9.
/// * The `T` type must be larger than `u8` if it is signed.
#[inline]
fn to_decimal<T: Copy + ValueFrom<u8, Err=NoError> + Add<Output=T> + Mul<Output=T>>(buf: &[u8]) -> T {
    buf.iter().fold(T::value_from(0).unwrap_ok(), |a, n| a * T::value_from(10).unwrap_ok() + T::value_from(n - b'0').unwrap_ok())
}

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

    macro_rules! test_to_decimal {
        ( $($n:ty),+ ) => { $(
            assert_eq!(to_decimal::<$n>(b""), 0);
            assert_eq!(to_decimal::<$n>(b"0"), 0);
            assert_eq!(to_decimal::<$n>(b"1"), 1);
            assert_eq!(to_decimal::<$n>(b"2"), 2);
            assert_eq!(to_decimal::<$n>(b"10"), 10);
            assert_eq!(to_decimal::<$n>(b"20"), 20);
            assert_eq!(to_decimal::<$n>(b"25"), 25);
        )+ }
    }

    #[test]
    fn test_to_decimal_u8() {
        test_to_decimal!(u8, u16, u32, u64, i16, i32, i64);
    }
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate itertools;

use std::collections::HashMap;

use self::itertools::Itertools;
use self::rustc_serialize::json;

use ngrams::ngrams;

pub struct Model {
    pub ngram_ranks: HashMap<String, usize>,
}

impl Model {
    pub fn build_from_text(text: &str) -> Model {
        let mut ngram_counts = HashMap::new();
        for n in 1..6 {
            for ngram in ngrams(text, n) {
                // If you don't want to unecessarily allocate strings, this is
                // the only way to do it. This RFC should fix this if it ever
                // gets accepted: // https://github.com/rust-lang/rfcs/pull/1533
                if let Some(count) = ngram_counts.get_mut(ngram) {
                    *count += 1;
                    continue;
                }
                ngram_counts.insert(ngram.to_owned(), 1);
            }
        }

        let ngrams = ngram_counts
            .into_iter()
            .sorted_by(|a, b| Ord::cmp(&b.1, &a.1))
            .into_iter()
            .take(300)
            .map(|(ngram, _count)| ngram);

        // Nicer way to build a hash map.
        Model { ngram_ranks: ngrams.enumerate().map(|(a, b)| (b, a)).collect() }
    }

    pub fn deserialize(bytes: Vec<u8>) -> Model {
        let string = String::from_utf8(bytes).unwrap();
        let ngram_ranks = json::decode(string.as_str()).unwrap();
        Model { ngram_ranks: ngram_ranks }
    }

    pub fn serialize(&self) -> Vec<u8> {
        json::encode(&self.ngram_ranks).unwrap().into_bytes()
    }

    pub fn compare(&self, other: &Model) -> usize {
        let max_difference = other.ngram_ranks.len();
        let mut difference = 0;
        for (ngram, rank) in &self.ngram_ranks {
            difference += match other.ngram_ranks.get(ngram) {
                Some(other_rank) => get_difference(*rank, *other_rank),
                None => max_difference,
            }
        }
        difference
    }
}

fn get_difference(a: usize, b: usize) -> usize {
    if a > b { a - b } else { b - a }
}
<commit_msg>Add a comment explaining why take(300) is needed<commit_after>extern crate rustc_serialize;
extern crate itertools;

use std::collections::HashMap;

use self::itertools::Itertools;
use self::rustc_serialize::json;

use ngrams::ngrams;

pub struct Model {
    pub ngram_ranks: HashMap<String, usize>,
}

impl Model {
    pub fn build_from_text(text: &str) -> Model {
        let mut ngram_counts = HashMap::new();
        for n in 1..6 {
            for ngram in ngrams(text, n) {
                // If you don't want to unecessarily allocate strings, this is
                // the only way to do it. This RFC should fix this if it ever
                // gets accepted: // https://github.com/rust-lang/rfcs/pull/1533
                if let Some(count) = ngram_counts.get_mut(ngram) {
                    *count += 1;
                    continue;
                }
                ngram_counts.insert(ngram.to_owned(), 1);
            }
        }

        let ngrams = ngram_counts
            .into_iter()
            .sorted_by(|a, b| Ord::cmp(&b.1, &a.1))
            .into_iter()
            .take(300)  // Models need to have the same size, or have normalized "differences"
            .map(|(ngram, _count)| ngram);

        // Nicer way to build a hash map.
        Model { ngram_ranks: ngrams.enumerate().map(|(a, b)| (b, a)).collect() }
    }

    pub fn deserialize(bytes: Vec<u8>) -> Model {
        let string = String::from_utf8(bytes).unwrap();
        let ngram_ranks = json::decode(string.as_str()).unwrap();
        Model { ngram_ranks: ngram_ranks }
    }

    pub fn serialize(&self) -> Vec<u8> {
        json::encode(&self.ngram_ranks).unwrap().into_bytes()
    }

    pub fn compare(&self, other: &Model) -> usize {
        let max_difference = other.ngram_ranks.len();
        let mut difference = 0;
        for (ngram, rank) in &self.ngram_ranks {
            difference += match other.ngram_ranks.get(ngram) {
                Some(other_rank) => get_difference(*rank, *other_rank),
                None => max_difference,
            }
        }
        difference
    }
}

fn get_difference(a: usize, b: usize) -> usize {
    if a > b { a - b } else { b - a }
}
<|endoftext|>


<commit_before>use regex::Regex;
use types::MistType;
use types::MistType::*;
//use std::iter::Peekable;

#[derive(Debug, PartialEq,Copy,Clone)]
enum TokenType {
    Number,
    ListStart,
    ListEnd
}

struct Tokenizer {
    input: String,
    pos: usize
}

impl Tokenizer {
    fn new(input: &str) -> Tokenizer {
        Tokenizer { input: input.to_string(), pos: 0 }
    }
}

impl Iterator for Tokenizer {
    type Item = (TokenType, String);

    fn next(&mut self) -> Option<(TokenType, String)> {
        let input = &self.input[self.pos..];
        let input_trimmed = input.trim_left();
        self.pos += input.len() - input_trimmed.len();
        let input = input_trimmed;

        if input.starts_with('(') {
            self.pos += 1;
            return Some((TokenType::ListStart, '('.to_string()))
        }

        if input.starts_with(')') {
            self.pos += 1;
            return Some((TokenType::ListEnd, ')'.to_string()))
        }

        let number_regexp = Regex::new(r"^-?\d+").unwrap();
        if let Some(cap) = number_regexp.captures(input) {
            self.pos += cap[0].len();
            return Some((TokenType::Number, cap[0].to_string()))
        }
        None
    }
}

fn parse_internal(tokenizer: &mut Iterator<Item=(TokenType, String)>) -> Result<MistType, String> {
    let mut tokenizer = tokenizer.peekable();
    if let Some(token) = tokenizer.next() {
        return match token {
            (TokenType::Number, token_string) => {
                Ok(Int(token_string.parse().unwrap()))
            }

            (TokenType::ListStart, _token_string) => {
                let mut list = vec![];
                loop {
                    let token_option = tokenizer.peek().cloned();
                    if let Some(element_token) = token_option {
                        match element_token {
                            (TokenType::ListEnd, _) => {
                                break;
                            }
                            _ => {
                                let parsed_element = parse_internal(&mut tokenizer)?;
                                list.push(parsed_element);
                            }
                        }
                    } else {
                        return Err("Unexpected end of input".to_string());
                    }
                }
                Ok(List(list))
            }

            (TokenType::ListEnd, token_string) => {
                Err("Unexpected end of list".to_string() + &token_string[..])
            }
        }
    }

    Err("Error".to_string())
}

pub fn parse(input: &str) -> Result<MistType, String> {
    let mut tokenizer = Tokenizer::new(input);
    return parse_internal(&mut tokenizer);
}

#[test]
fn test_parse_number() {
    assert_eq!(parse("0"), Ok(Int(0)));
    assert_eq!(parse("1"), Ok(Int(1)));
    assert_eq!(parse("42"), Ok(Int(42)));
    assert_eq!(parse("-42"), Ok(Int(-42)));
}

fn tokenize(input: &str) -> Vec<(TokenType, String)> {
    Tokenizer::new(input).collect()
}

#[test]
fn test_tokenizer() {
    assert_eq!(tokenize("42"), vec![(TokenType::Number, "42".to_string())]);
    assert_eq!(tokenize("("), vec![(TokenType::ListStart, "(".to_string())]);
    assert_eq!(tokenize(")"), vec![(TokenType::ListEnd, ")".to_string())]);
    assert_eq!(tokenize("( )"), vec![
        (TokenType::ListStart, "(".to_string()),
        (TokenType::ListEnd, ")".to_string())
    ]);
    assert_eq!(tokenize("(\n)"), vec![
        (TokenType::ListStart, "(".to_string()),
        (TokenType::ListEnd, ")".to_string())
    ]);
    assert_eq!(tokenize("(42)"), vec![
        (TokenType::ListStart, "(".to_string()),
        (TokenType::Number, "42".to_string()),
        (TokenType::ListEnd, ")".to_string())
    ]);
}

#[test]
fn test_list() {
    assert_eq!(parse("()"), Ok(List(vec![])));
    assert_eq!(parse("(42)"), Ok(List(vec![Int(42)])));
    assert_eq!(parse("(42 23)"), Ok(List(vec![Int(42), Int(23)])));
}
<commit_msg>Test for parsing nested lists<commit_after>use regex::Regex;
use types::MistType;
use types::MistType::*;
//use std::iter::Peekable;

#[derive(Debug, PartialEq,Copy,Clone)]
enum TokenType {
    Number,
    ListStart,
    ListEnd
}

struct Tokenizer {
    input: String,
    pos: usize
}

impl Tokenizer {
    fn new(input: &str) -> Tokenizer {
        Tokenizer { input: input.to_string(), pos: 0 }
    }
}

impl Iterator for Tokenizer {
    type Item = (TokenType, String);

    fn next(&mut self) -> Option<(TokenType, String)> {
        let input = &self.input[self.pos..];
        let input_trimmed = input.trim_left();
        self.pos += input.len() - input_trimmed.len();
        let input = input_trimmed;

        if input.starts_with('(') {
            self.pos += 1;
            return Some((TokenType::ListStart, '('.to_string()))
        }

        if input.starts_with(')') {
            self.pos += 1;
            return Some((TokenType::ListEnd, ')'.to_string()))
        }

        let number_regexp = Regex::new(r"^-?\d+").unwrap();
        if let Some(cap) = number_regexp.captures(input) {
            self.pos += cap[0].len();
            return Some((TokenType::Number, cap[0].to_string()))
        }
        None
    }
}

fn parse_internal(tokenizer: &mut Iterator<Item=(TokenType, String)>) -> Result<MistType, String> {
    let mut tokenizer = tokenizer.peekable();
    if let Some(token) = tokenizer.next() {
        return match token {
            (TokenType::Number, token_string) => {
                Ok(Int(token_string.parse().unwrap()))
            }

            (TokenType::ListStart, _token_string) => {
                let mut list = vec![];
                loop {
                    let token_option = tokenizer.peek().cloned();
                    if let Some(element_token) = token_option {
                        match element_token {
                            (TokenType::ListEnd, _) => {
                                break;
                            }
                            _ => {
                                let parsed_element = parse_internal(&mut tokenizer)?;
                                list.push(parsed_element);
                            }
                        }
                    } else {
                        return Err("Unexpected end of input".to_string());
                    }
                }
                Ok(List(list))
            }

            (TokenType::ListEnd, token_string) => {
                Err("Unexpected end of list".to_string() + &token_string[..])
            }
        }
    }

    Err("Error".to_string())
}

pub fn parse(input: &str) -> Result<MistType, String> {
    let mut tokenizer = Tokenizer::new(input);
    return parse_internal(&mut tokenizer);
}

#[test]
fn test_parse_number() {
    assert_eq!(parse("0"), Ok(Int(0)));
    assert_eq!(parse("1"), Ok(Int(1)));
    assert_eq!(parse("42"), Ok(Int(42)));
    assert_eq!(parse("-42"), Ok(Int(-42)));
}

fn tokenize(input: &str) -> Vec<(TokenType, String)> {
    Tokenizer::new(input).collect()
}

#[test]
fn test_tokenizer() {
    assert_eq!(tokenize("42"), vec![(TokenType::Number, "42".to_string())]);
    assert_eq!(tokenize("("), vec![(TokenType::ListStart, "(".to_string())]);
    assert_eq!(tokenize(")"), vec![(TokenType::ListEnd, ")".to_string())]);
    assert_eq!(tokenize("( )"), vec![
        (TokenType::ListStart, "(".to_string()),
        (TokenType::ListEnd, ")".to_string())
    ]);
    assert_eq!(tokenize("(\n)"), vec![
        (TokenType::ListStart, "(".to_string()),
        (TokenType::ListEnd, ")".to_string())
    ]);
    assert_eq!(tokenize("(42)"), vec![
        (TokenType::ListStart, "(".to_string()),
        (TokenType::Number, "42".to_string()),
        (TokenType::ListEnd, ")".to_string())
    ]);
}

#[test]
fn test_list() {
    assert_eq!(parse("()"), Ok(List(vec![])));
    assert_eq!(parse("(42)"), Ok(List(vec![Int(42)])));
    assert_eq!(parse("(42 23)"), Ok(List(vec![Int(42), Int(23)])));
    assert_eq!(parse("(42 (23))"), Ok(List(vec![Int(42), List(vec![Int(23)])])));
}
<|endoftext|>


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

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

extern crate getopts;
extern crate remove_dir_all;
extern crate walkdir;

#[macro_use]
extern crate uucore;

use std::collections::VecDeque;
use std::fs;
use std::io::{stderr, stdin, BufRead, Write};
use std::ops::BitOr;
use std::path::Path;
use remove_dir_all::remove_dir_all;
use walkdir::{DirEntry, WalkDir};

#[derive(Eq, PartialEq, Clone, Copy)]
enum InteractiveMode {
    InteractiveNone,
    InteractiveOnce,
    InteractiveAlways,
}

struct Options {
    force: bool,
    interactive: InteractiveMode,
    #[allow(dead_code)]
    one_fs: bool,
    preserve_root: bool,
    recursive: bool,
    dir: bool,
    verbose: bool,
}

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

pub fn uumain(args: Vec<String>) -> i32 {
    // TODO: make getopts support -R in addition to -r
    let mut opts = getopts::Options::new();

    opts.optflag(
        "f",
        "force",
        "ignore nonexistent files and arguments, never prompt",
    );
    opts.optflag("i", "", "prompt before every removal");
    opts.optflag("I", "", "prompt once before removing more than three files, or when removing recursively.  Less intrusive than -i, while still giving some protection against most mistakes");
    opts.optflagopt(
        "",
        "interactive",
        "prompt according to WHEN: never, once (-I), or always (-i).  Without WHEN, prompts always",
        "WHEN",
    );
    opts.optflag("", "one-file-system", "when removing a hierarchy recursively, skip any directory that is on a file system different from that of the corresponding command line argument (NOT IMPLEMENTED)");
    opts.optflag("", "no-preserve-root", "do not treat '/' specially");
    opts.optflag("", "preserve-root", "do not remove '/' (default)");
    opts.optflag(
        "r",
        "recursive",
        "remove directories and their contents recursively",
    );
    opts.optflag("d", "dir", "remove empty directories");
    opts.optflag("v", "verbose", "explain what is being done");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f),
    };

    let force = matches.opt_present("force");

    if matches.opt_present("help") {
        println!("{} {}", NAME, VERSION);
        println!();
        println!("Usage:");
        println!("  {0} [OPTION]... [FILE]...", NAME);
        println!();
        println!("{}", opts.usage("Remove (unlink) the FILE(s)."));
        println!("By default, rm does not remove directories.  Use the --recursive (-r)");
        println!("option to remove each listed directory, too, along with all of its contents");
        println!();
        println!("To remove a file whose name starts with a '-', for example '-foo',");
        println!("use one of these commands:");
        println!("rm -- -foo");
        println!();
        println!("rm ./-foo");
        println!();
        println!("Note that if you use rm to remove a file, it might be possible to recover");
        println!("some of its contents, given sufficient expertise and/or time.  For greater");
        println!("assurance that the contents are truly unrecoverable, consider using shred.");
    } else if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
    } else if matches.free.is_empty() && !force {
        show_error!("missing an argument");
        show_error!("for help, try '{0} --help'", NAME);
        return 1;
    } else {
        let options = Options {
            force,
            interactive: {
                if matches.opt_present("i") {
                    InteractiveMode::InteractiveAlways
                } else if matches.opt_present("I") {
                    InteractiveMode::InteractiveOnce
                } else if matches.opt_present("interactive") {
                    match &matches.opt_str("interactive").unwrap()[..] {
                        "none" => InteractiveMode::InteractiveNone,
                        "once" => InteractiveMode::InteractiveOnce,
                        "always" => InteractiveMode::InteractiveAlways,
                        val => crash!(1, "Invalid argument to interactive ({})", val),
                    }
                } else {
                    InteractiveMode::InteractiveNone
                }
            },
            one_fs: matches.opt_present("one-file-system"),
            preserve_root: !matches.opt_present("no-preserve-root"),
            recursive: matches.opt_present("recursive"),
            dir: matches.opt_present("dir"),
            verbose: matches.opt_present("verbose"),
        };
        if options.interactive == InteractiveMode::InteractiveOnce
            && (options.recursive || matches.free.len() > 3)
        {
            let msg = if options.recursive {
                "Remove all arguments recursively? "
            } else {
                "Remove all arguments? "
            };
            if !prompt(msg) {
                return 0;
            }
        }

        if remove(matches.free, options) {
            return 1;
        }
    }

    0
}

// TODO: implement one-file-system (this may get partially implemented in walkdir)
fn remove(files: Vec<String>, options: Options) -> bool {
    let mut had_err = false;

    for filename in &files {
        let file = Path::new(filename);
        had_err = match file.symlink_metadata() {
            Ok(metadata) => {
                if metadata.is_dir() {
                    handle_dir(file, &options)
                } else if is_symlink_dir(&metadata) {
                    remove_dir(file, &options)
                } else {
                    remove_file(file, &options)
                }
            }
            Err(_e) => {
                // TODO: actually print out the specific error
                // TODO: When the error is not about missing files
                // (e.g., permission), even rm -f should fail with
                // outputting the error, but there's no easy eay.
                if !options.force {
                    show_error!("no such file or directory '{}'", filename);
                    true
                } else {
                    false
                }
            }
        }.bitor(had_err);
    }

    had_err
}

fn handle_dir(path: &Path, options: &Options) -> bool {
    let mut had_err = false;

    let is_root = path.has_root() && path.parent().is_none();
    if options.recursive && (!is_root || !options.preserve_root) {
        if options.interactive != InteractiveMode::InteractiveAlways {
            // we need the extra crate because apparently fs::remove_dir_all() does not function
            // correctly on Windows
            if let Err(e) = remove_dir_all(path) {
                had_err = true;
                show_error!("could not remove '{}': {}", path.display(), e);
            }
        } else {
            let mut dirs: VecDeque<DirEntry> = VecDeque::new();

            for entry in WalkDir::new(path) {
                match entry {
                    Ok(entry) => {
                        let file_type = entry.file_type();
                        if file_type.is_dir() {
                            dirs.push_back(entry);
                        } else {
                            had_err = remove_file(entry.path(), options).bitor(had_err);
                        }
                    }
                    Err(e) => {
                        had_err = true;
                        show_error!("recursing in '{}': {}", path.display(), e);
                    }
                }
            }

            for dir in dirs.iter().rev() {
                had_err = remove_dir(dir.path(), options).bitor(had_err);
            }
        }
    } else if options.dir && (!is_root || !options.preserve_root) {
        had_err = remove_dir(path, options).bitor(had_err);
    } else if options.recursive {
        show_error!("could not remove directory '{}'", path.display());
        had_err = true;
    } else {
        show_error!(
            "could not remove directory '{}' (did you mean to pass '-r'?)",
            path.display()
        );
        had_err = true;
    }

    had_err
}

fn remove_dir(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::InteractiveAlways {
        prompt_file(path, true)
    } else {
        true
    };
    if response {
        match fs::remove_dir(path) {
            Ok(_) => if options.verbose {
                println!("removed '{}'", path.display());
            },
            Err(e) => {
                show_error!("removing '{}': {}", path.display(), e);
                return true;
            }
        }
    }

    false
}

fn remove_file(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::InteractiveAlways {
        prompt_file(path, false)
    } else {
        true
    };
    if response {
        match fs::remove_file(path) {
            Ok(_) => if options.verbose {
                println!("removed '{}'", path.display());
            },
            Err(e) => {
                show_error!("removing '{}': {}", path.display(), e);
                return true;
            }
        }
    }

    false
}

fn prompt_file(path: &Path, is_dir: bool) -> bool {
    if is_dir {
        prompt(&(format!("rm: remove directory '{}'? ", path.display())))
    } else {
        prompt(&(format!("rm: remove file '{}'? ", path.display())))
    }
}

fn prompt(msg: &str) -> bool {
    let _ = stderr().write_all(msg.as_bytes());
    let _ = stderr().flush();

    let mut buf = Vec::new();
    let stdin = stdin();
    let mut stdin = stdin.lock();

    match stdin.read_until('\n' as u8, &mut buf) {
        Ok(x) if x > 0 => match buf[0] {
            b'y' | b'Y' => true,
            _ => false,
        },
        _ => false,
    }
}

#[cfg(not(windows))]
fn is_symlink_dir(_metadata: &fs::Metadata) -> bool {
    false
}

#[cfg(windows)]
use std::os::windows::prelude::MetadataExt;

#[cfg(windows)]
fn is_symlink_dir(metadata: &fs::Metadata) -> bool {
    use std::os::raw::c_ulong;
    pub type DWORD = c_ulong;
    pub const FILE_ATTRIBUTE_DIRECTORY: DWORD = 0x10;

    metadata.file_type().is_symlink() && ((metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY) != 0)
}
<commit_msg>refactor/polish ~ fix `cargo clippy` complaints (simplify enum element names/InteractiveMode)<commit_after>#![crate_name = "uu_rm"]

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

extern crate getopts;
extern crate remove_dir_all;
extern crate walkdir;

#[macro_use]
extern crate uucore;

use std::collections::VecDeque;
use std::fs;
use std::io::{stderr, stdin, BufRead, Write};
use std::ops::BitOr;
use std::path::Path;
use remove_dir_all::remove_dir_all;
use walkdir::{DirEntry, WalkDir};

#[derive(Eq, PartialEq, Clone, Copy)]
enum InteractiveMode {
    None,
    Once,
    Always,
}

struct Options {
    force: bool,
    interactive: InteractiveMode,
    #[allow(dead_code)]
    one_fs: bool,
    preserve_root: bool,
    recursive: bool,
    dir: bool,
    verbose: bool,
}

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

pub fn uumain(args: Vec<String>) -> i32 {
    // TODO: make getopts support -R in addition to -r
    let mut opts = getopts::Options::new();

    opts.optflag(
        "f",
        "force",
        "ignore nonexistent files and arguments, never prompt",
    );
    opts.optflag("i", "", "prompt before every removal");
    opts.optflag("I", "", "prompt once before removing more than three files, or when removing recursively.  Less intrusive than -i, while still giving some protection against most mistakes");
    opts.optflagopt(
        "",
        "interactive",
        "prompt according to WHEN: never, once (-I), or always (-i).  Without WHEN, prompts always",
        "WHEN",
    );
    opts.optflag("", "one-file-system", "when removing a hierarchy recursively, skip any directory that is on a file system different from that of the corresponding command line argument (NOT IMPLEMENTED)");
    opts.optflag("", "no-preserve-root", "do not treat '/' specially");
    opts.optflag("", "preserve-root", "do not remove '/' (default)");
    opts.optflag(
        "r",
        "recursive",
        "remove directories and their contents recursively",
    );
    opts.optflag("d", "dir", "remove empty directories");
    opts.optflag("v", "verbose", "explain what is being done");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f),
    };

    let force = matches.opt_present("force");

    if matches.opt_present("help") {
        println!("{} {}", NAME, VERSION);
        println!();
        println!("Usage:");
        println!("  {0} [OPTION]... [FILE]...", NAME);
        println!();
        println!("{}", opts.usage("Remove (unlink) the FILE(s)."));
        println!("By default, rm does not remove directories.  Use the --recursive (-r)");
        println!("option to remove each listed directory, too, along with all of its contents");
        println!();
        println!("To remove a file whose name starts with a '-', for example '-foo',");
        println!("use one of these commands:");
        println!("rm -- -foo");
        println!();
        println!("rm ./-foo");
        println!();
        println!("Note that if you use rm to remove a file, it might be possible to recover");
        println!("some of its contents, given sufficient expertise and/or time.  For greater");
        println!("assurance that the contents are truly unrecoverable, consider using shred.");
    } else if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
    } else if matches.free.is_empty() && !force {
        show_error!("missing an argument");
        show_error!("for help, try '{0} --help'", NAME);
        return 1;
    } else {
        let options = Options {
            force,
            interactive: {
                if matches.opt_present("i") {
                    InteractiveMode::Always
                } else if matches.opt_present("I") {
                    InteractiveMode::Once
                } else if matches.opt_present("interactive") {
                    match &matches.opt_str("interactive").unwrap()[..] {
                        "none" => InteractiveMode::None,
                        "once" => InteractiveMode::Once,
                        "always" => InteractiveMode::Always,
                        val => crash!(1, "Invalid argument to interactive ({})", val),
                    }
                } else {
                    InteractiveMode::None
                }
            },
            one_fs: matches.opt_present("one-file-system"),
            preserve_root: !matches.opt_present("no-preserve-root"),
            recursive: matches.opt_present("recursive"),
            dir: matches.opt_present("dir"),
            verbose: matches.opt_present("verbose"),
        };
        if options.interactive == InteractiveMode::Once
            && (options.recursive || matches.free.len() > 3)
        {
            let msg = if options.recursive {
                "Remove all arguments recursively? "
            } else {
                "Remove all arguments? "
            };
            if !prompt(msg) {
                return 0;
            }
        }

        if remove(matches.free, options) {
            return 1;
        }
    }

    0
}

// TODO: implement one-file-system (this may get partially implemented in walkdir)
fn remove(files: Vec<String>, options: Options) -> bool {
    let mut had_err = false;

    for filename in &files {
        let file = Path::new(filename);
        had_err = match file.symlink_metadata() {
            Ok(metadata) => {
                if metadata.is_dir() {
                    handle_dir(file, &options)
                } else if is_symlink_dir(&metadata) {
                    remove_dir(file, &options)
                } else {
                    remove_file(file, &options)
                }
            }
            Err(_e) => {
                // TODO: actually print out the specific error
                // TODO: When the error is not about missing files
                // (e.g., permission), even rm -f should fail with
                // outputting the error, but there's no easy eay.
                if !options.force {
                    show_error!("no such file or directory '{}'", filename);
                    true
                } else {
                    false
                }
            }
        }.bitor(had_err);
    }

    had_err
}

fn handle_dir(path: &Path, options: &Options) -> bool {
    let mut had_err = false;

    let is_root = path.has_root() && path.parent().is_none();
    if options.recursive && (!is_root || !options.preserve_root) {
        if options.interactive != InteractiveMode::Always {
            // we need the extra crate because apparently fs::remove_dir_all() does not function
            // correctly on Windows
            if let Err(e) = remove_dir_all(path) {
                had_err = true;
                show_error!("could not remove '{}': {}", path.display(), e);
            }
        } else {
            let mut dirs: VecDeque<DirEntry> = VecDeque::new();

            for entry in WalkDir::new(path) {
                match entry {
                    Ok(entry) => {
                        let file_type = entry.file_type();
                        if file_type.is_dir() {
                            dirs.push_back(entry);
                        } else {
                            had_err = remove_file(entry.path(), options).bitor(had_err);
                        }
                    }
                    Err(e) => {
                        had_err = true;
                        show_error!("recursing in '{}': {}", path.display(), e);
                    }
                }
            }

            for dir in dirs.iter().rev() {
                had_err = remove_dir(dir.path(), options).bitor(had_err);
            }
        }
    } else if options.dir && (!is_root || !options.preserve_root) {
        had_err = remove_dir(path, options).bitor(had_err);
    } else if options.recursive {
        show_error!("could not remove directory '{}'", path.display());
        had_err = true;
    } else {
        show_error!(
            "could not remove directory '{}' (did you mean to pass '-r'?)",
            path.display()
        );
        had_err = true;
    }

    had_err
}

fn remove_dir(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, true)
    } else {
        true
    };
    if response {
        match fs::remove_dir(path) {
            Ok(_) => if options.verbose {
                println!("removed '{}'", path.display());
            },
            Err(e) => {
                show_error!("removing '{}': {}", path.display(), e);
                return true;
            }
        }
    }

    false
}

fn remove_file(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, false)
    } else {
        true
    };
    if response {
        match fs::remove_file(path) {
            Ok(_) => if options.verbose {
                println!("removed '{}'", path.display());
            },
            Err(e) => {
                show_error!("removing '{}': {}", path.display(), e);
                return true;
            }
        }
    }

    false
}

fn prompt_file(path: &Path, is_dir: bool) -> bool {
    if is_dir {
        prompt(&(format!("rm: remove directory '{}'? ", path.display())))
    } else {
        prompt(&(format!("rm: remove file '{}'? ", path.display())))
    }
}

fn prompt(msg: &str) -> bool {
    let _ = stderr().write_all(msg.as_bytes());
    let _ = stderr().flush();

    let mut buf = Vec::new();
    let stdin = stdin();
    let mut stdin = stdin.lock();

    match stdin.read_until('\n' as u8, &mut buf) {
        Ok(x) if x > 0 => match buf[0] {
            b'y' | b'Y' => true,
            _ => false,
        },
        _ => false,
    }
}

#[cfg(not(windows))]
fn is_symlink_dir(_metadata: &fs::Metadata) -> bool {
    false
}

#[cfg(windows)]
use std::os::windows::prelude::MetadataExt;

#[cfg(windows)]
fn is_symlink_dir(metadata: &fs::Metadata) -> bool {
    use std::os::raw::c_ulong;
    pub type DWORD = c_ulong;
    pub const FILE_ATTRIBUTE_DIRECTORY: DWORD = 0x10;

    metadata.file_type().is_symlink() && ((metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY) != 0)
}
<|endoftext|>