<commit_before>use crate::data::PINYIN_DATA;
use crate::{get_block_and_index, PinyinData};
use std::convert::TryFrom;
use std::str::Chars;

/// 单个字符的拼音信息
#[derive(Copy, Clone)]
pub struct Pinyin(pub(crate) &'static PinyinData);

impl Pinyin {
    /// 普通风格，不带声调
    ///
    /// *仅在启用 `plain` 特性时可用*
    /// ```
    /// # use pinyin::*;
    /// assert_eq!(to_pinyin_vec("拼音", Pinyin::plain), vec!["pin", "yin"]);
    /// ```
    #[cfg(feature = "plain")]
    pub fn plain(self) -> &'static str {
        self.0.plain
    }

    /// 带声调的风格
    ///
    /// *仅在启用 `with_tone` 特性时可用*
    /// ```
    /// # use pinyin::*;
    /// assert_eq!(to_pinyin_vec("拼音", Pinyin::with_tone), vec!["pīn", "yīn"]);
    /// ```
    #[cfg(feature = "with_tone")]
    pub fn with_tone(self) -> &'static str {
        self.0.with_tone
    }

    /// 声调在各个拼音之后，使用数字0-4表示的风格
    ///
    /// *仅在启用 `with_tone_num` 特性时可用*
    /// ```
    /// # use pinyin::*;
    /// assert_eq!(to_pinyin_vec("拼音", Pinyin::with_tone_num), vec!["pi1n", "yi1n"]);
    /// ```
    #[cfg(feature = "with_tone_num")]
    pub fn with_tone_num(self) -> &'static str {
        self.0.with_tone_num
    }

    /// 首字母风格
    ///
    /// *仅在启用 `plain` 特性时可用*
    /// ```
    /// # use pinyin::*;
    /// assert_eq!(to_pinyin_vec("拼音", Pinyin::first_letter), vec!["p", "y"]);
    /// assert_eq!(to_pinyin_vec("中国", Pinyin::first_letter), vec!["z", "g"]);
    /// assert_eq!(to_pinyin_vec("安心", Pinyin::first_letter), vec!["a", "x"]);
    /// ```
    #[cfg(feature = "plain")]
    pub fn first_letter(self) -> &'static str {
        let ch = self.0.plain.chars().next().unwrap();
        &self.0.plain[..ch.len_utf8()]
    }

    #[cfg(feature = "compat")]
    pub(crate) fn initials(self) -> &'static str {
        &self.0.plain[..self.0.split]
    }

    #[cfg(feature = "compat")]
    pub(crate) fn finals_plain(self) -> &'static str {
        &self.0.plain[self.0.split..]
    }

    #[cfg(feature = "compat")]
    pub(crate) fn finals_with_tone(self) -> &'static str {
        &self.0.with_tone[self.0.split..]
    }

    #[cfg(feature = "compat")]
    pub(crate) fn finals_with_tone_num(self) -> &'static str {
        &self.0.with_tone_num[self.0.split..]
    }
}

/// 用于获取拼音信息的trait
pub trait ToPinyin {
    type Output;
    fn to_pinyin(&self) -> Self::Output;
}

/// ```
/// # #[cfg(feature = "plain")] {
/// use pinyin::ToPinyin;
/// assert_eq!('拼'.to_pinyin().unwrap().plain(), "pin");
/// # }
/// ```
impl ToPinyin for char {
    type Output = Option<Pinyin>;

    fn to_pinyin(&self) -> Option<Pinyin> {
        get_block_and_index(*self).and_then(|(block, index)| {
            match usize::try_from(block.data[index]).unwrap() {
                0 => None,
                idx => Some(Pinyin(&PINYIN_DATA[idx])),
            }
        })
    }
}

/// ```
/// # #[cfg(feature = "plain")] {
/// use pinyin::{ToPinyin, Pinyin};
/// let mut iter = "拼音".to_pinyin();
/// let mut next_plain = || iter.next().and_then(|p| p).map(Pinyin::plain);
/// assert_eq!(next_plain(), Some("pin"));
/// assert_eq!(next_plain(), Some("yin"));
/// assert_eq!(next_plain(), None);
/// # }
/// ```
impl<'a> ToPinyin for &'a str {
    type Output = PinyinStrIter<'a>;

    #[inline]
    fn to_pinyin(&self) -> Self::Output {
        PinyinStrIter(self.chars())
    }
}

/// *辅助迭代器*，用于获取字符串的拼音信息
pub struct PinyinStrIter<'a>(Chars<'a>);

impl<'a> Iterator for PinyinStrIter<'a> {
    type Item = Option<Pinyin>;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|c| c.to_pinyin())
    }
}
<commit_msg>添加一个最大Unicode码位的测试<commit_after>use crate::data::PINYIN_DATA;
use crate::{get_block_and_index, PinyinData};
use std::convert::TryFrom;
use std::str::Chars;

/// 单个字符的拼音信息
#[derive(Copy, Clone)]
pub struct Pinyin(pub(crate) &'static PinyinData);

impl Pinyin {
    /// 普通风格，不带声调
    ///
    /// *仅在启用 `plain` 特性时可用*
    /// ```
    /// # use pinyin::*;
    /// assert_eq!(to_pinyin_vec("拼音", Pinyin::plain), vec!["pin", "yin"]);
    /// ```
    #[cfg(feature = "plain")]
    pub fn plain(self) -> &'static str {
        self.0.plain
    }

    /// 带声调的风格
    ///
    /// *仅在启用 `with_tone` 特性时可用*
    /// ```
    /// # use pinyin::*;
    /// assert_eq!(to_pinyin_vec("拼音", Pinyin::with_tone), vec!["pīn", "yīn"]);
    /// ```
    #[cfg(feature = "with_tone")]
    pub fn with_tone(self) -> &'static str {
        self.0.with_tone
    }

    /// 声调在各个拼音之后，使用数字0-4表示的风格
    ///
    /// *仅在启用 `with_tone_num` 特性时可用*
    /// ```
    /// # use pinyin::*;
    /// assert_eq!(to_pinyin_vec("拼音", Pinyin::with_tone_num), vec!["pi1n", "yi1n"]);
    /// ```
    #[cfg(feature = "with_tone_num")]
    pub fn with_tone_num(self) -> &'static str {
        self.0.with_tone_num
    }

    /// 首字母风格
    ///
    /// *仅在启用 `plain` 特性时可用*
    /// ```
    /// # use pinyin::*;
    /// assert_eq!(to_pinyin_vec("拼音", Pinyin::first_letter), vec!["p", "y"]);
    /// assert_eq!(to_pinyin_vec("中国", Pinyin::first_letter), vec!["z", "g"]);
    /// assert_eq!(to_pinyin_vec("安心", Pinyin::first_letter), vec!["a", "x"]);
    /// ```
    #[cfg(feature = "plain")]
    pub fn first_letter(self) -> &'static str {
        let ch = self.0.plain.chars().next().unwrap();
        &self.0.plain[..ch.len_utf8()]
    }

    #[cfg(feature = "compat")]
    pub(crate) fn initials(self) -> &'static str {
        &self.0.plain[..self.0.split]
    }

    #[cfg(feature = "compat")]
    pub(crate) fn finals_plain(self) -> &'static str {
        &self.0.plain[self.0.split..]
    }

    #[cfg(feature = "compat")]
    pub(crate) fn finals_with_tone(self) -> &'static str {
        &self.0.with_tone[self.0.split..]
    }

    #[cfg(feature = "compat")]
    pub(crate) fn finals_with_tone_num(self) -> &'static str {
        &self.0.with_tone_num[self.0.split..]
    }
}

/// 用于获取拼音信息的trait
pub trait ToPinyin {
    type Output;
    fn to_pinyin(&self) -> Self::Output;
}

/// ```
/// # #[cfg(feature = "plain")] {
/// use pinyin::ToPinyin;
/// assert_eq!('拼'.to_pinyin().unwrap().plain(), "pin");
/// # }
/// ```
impl ToPinyin for char {
    type Output = Option<Pinyin>;

    fn to_pinyin(&self) -> Option<Pinyin> {
        get_block_and_index(*self).and_then(|(block, index)| {
            match usize::try_from(block.data[index]).unwrap() {
                0 => None,
                idx => Some(Pinyin(&PINYIN_DATA[idx])),
            }
        })
    }
}

/// ```
/// # #[cfg(feature = "plain")] {
/// use pinyin::{ToPinyin, Pinyin};
/// let mut iter = "拼音".to_pinyin();
/// let mut next_plain = || iter.next().and_then(|p| p).map(Pinyin::plain);
/// assert_eq!(next_plain(), Some("pin"));
/// assert_eq!(next_plain(), Some("yin"));
/// assert_eq!(next_plain(), None);
/// # }
/// ```
impl<'a> ToPinyin for &'a str {
    type Output = PinyinStrIter<'a>;

    #[inline]
    fn to_pinyin(&self) -> Self::Output {
        PinyinStrIter(self.chars())
    }
}

/// *辅助迭代器*，用于获取字符串的拼音信息
pub struct PinyinStrIter<'a>(Chars<'a>);

impl<'a> Iterator for PinyinStrIter<'a> {
    type Item = Option<Pinyin>;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|c| c.to_pinyin())
    }
}

#[cfg(test)]
mod tests {
    use crate::ToPinyin;

    #[test]
    fn special_code_point() {
        assert!('\u{10FFFF}'.to_pinyin().is_none());
    }
}
<|endoftext|>


<commit_before>use linear_map::LinearMap;
use regex::Regex;
use error::Error;
use query::Query;
use value::{Key, Value};

lazy_static! {
  static ref INTEGER_RE: Regex = Regex::new(r"^\d+$").unwrap();
}

/// A schema detailing what the data received from the driver (or inserted
/// into the driver) should be. Inspired after [JSON Schema][1]. A reference
/// on JSON Schema type-specific validations used in this enum may be found
/// [here][2].
///
/// [1]: http://json-schema.org
/// [2]: http://spacetelescope.github.io/understanding-json-schema/reference/type.html
pub enum Schema {
  /// There is no schema. No validations should occur. Does not represent the
  /// abscense of any value, only represents that a schema does not define the
  /// data structure at this point.
  None,
  /// Represents the absence of any value.
  Null,
  /// Represents a binary true/false value.
  Boolean,
  /// Represents a numeric type.
  Number {
    /// Forces the number to be a multiple of another. This helps in specifying
    /// integers if this value is `Some(1)` for example.
    multiple_of: Option<f32>,
    /// The minimum value the number can be.
    minimum: Option<f64>,
    /// Whether or not the minimum value should be included when validating.
    /// Default is `false`.
    exclusive_minimum: bool,
    /// The maximum value the number can be.
    maximum: Option<f64>,
    /// Whether or not the maximum value should be included when validating.
    /// Default is `false`.
    exclusive_maximum: bool
  },
  String {
    /// The mimimum length of characters in the string.
    min_length: Option<u64>,
    /// The maximum length of characters in the string.
    max_length: Option<u64>,
    /// A regular expression pattern to validate the string against.
    // TODO: Use a regex crate.
    pattern: Option<String>
  },
  /// Represents a set of any type.
  Array {
    /// A schema which all items in the array must match.
    items: Box<Schema>
  },
  /// Represents any tuple of values.
  Tuple {
    /// Schemas which each tuple value (in the same place) must comply with.
    items: Vec<Schema>,
    /// Whether or not there can be more items in the tuple.
    additional_items: bool
  },
  /// Represents a set of key/value pairs.
  Object {
    /// Schemas associated to the object properties.
    properties: LinearMap<Key, Schema>,
    /// Properties that are required to be in the object.
    required: Vec<Key>,
    /// Whether or not there may be extra properties outside of the ones
    /// defined by the properties map.
    additional_properties: bool
  },
  /// Represents a value which *must* be one of the defined values. An enum is
  /// considered a primitive type as if it is a single value is a higher order
  /// type, no variation is allowed.
  Enum(Vec<Value>)
}

impl Schema {
  /// Validates a query that a user would like to make on the database by
  /// comparing it to the schema. Mostly checks that all properties described
  /// in the query are accessible according to the schema.
  pub fn validate_query(&self, query: &Query) -> Result<(), Error> {
    static NO_PRIMITIVE_HINT: &'static str = "Try not querying specific properties of a primitive like `null` or `boolean`.";
    match (self, query) {
      // No schema describes these values, its the wild west. Go crazy query.
      // `Schema::None` does not represent the absence of value, just the
      // absence of validation.
      (&Schema::None, _) => Ok(()),
      (&Schema::Null, &Query::Value) => Ok(()),
      (&Schema::Null, &Query::Object(_)) => Err(Error::validation("Cannot deeply query null.", NO_PRIMITIVE_HINT)),
      (&Schema::Boolean, &Query::Value) => Ok(()),
      (&Schema::Boolean, &Query::Object(_)) => Err(Error::validation("Cannot deeply query a boolean.", NO_PRIMITIVE_HINT)),
      (&Schema::Number{..}, &Query::Value) => Ok(()),
      (&Schema::Number{..}, &Query::Object(_)) => Err(Error::validation("Cannot deeply query a number.", NO_PRIMITIVE_HINT)),
      (&Schema::String{..}, &Query::Value) => Ok(()),
      (&Schema::String{..}, &Query::Object(_)) => Err(Error::validation("Cannot deeply query a string.", NO_PRIMITIVE_HINT)),
      (&Schema::Array{..}, &Query::Value) => Ok(()),
      (&Schema::Array{ref items}, &Query::Object(ref query_properties)) => {
        match query_properties.keys().map(|key| {
          if !INTEGER_RE.is_match(key) {
            Err(Error::validation(format!("Cannot query non-integer \"{}\" array property.", key), "Only query integer array keys like 1, 2, and 3."))
          } else {
            items.validate_query(&query_properties[key])
          }
        }).find(|r| r.is_err()) {
          None => Ok(()),
          Some(error) => error
        }
      },
      (&Schema::Tuple{..}, &Query::Value) => Ok(()),
      (&Schema::Tuple{ref items, ref additional_items}, &Query::Object(ref query_properties)) => {
        match query_properties.keys().map(|key| {
          if !INTEGER_RE.is_match(key) {
            Err(Error::validation(format!("Cannot query non-integer \"{}\" array property.", key), "Only query integer array keys like 1, 2, and 3."))
          } else if let Some(item_schema) = items.get(key.parse::<usize>().unwrap()) {
            item_schema.validate_query(&query_properties[key])
          } else if !additional_items {
            Err(Error::validation(format!("Tuple has only {} values. Can’t query the index {}.", items.len(), key), format!("Query a key less than or equal to {}.", items.len() - 1)))
          } else {
            Ok(())
          }
        }).find(|r| r.is_err()) {
          None => Ok(()),
          Some(error) => error
        }
      },
      (&Schema::Object{..}, &Query::Value) => Ok(()),
      (&Schema::Object{ref properties, ref additional_properties,..}, &Query::Object(ref query_properties)) => {
        match query_properties.keys().map(|key| {
          if let Some(property_schema) = properties.get(key) {
            property_schema.validate_query(&query_properties[key])
          } else if !additional_properties {
            Err(Error::validation(format!("Cannot query object property \"{}\".", key), "Query an object property that is defined in the schema."))
          } else {
            Ok(())
          }
        }).find(|r| r.is_err()) {
          None => Ok(()),
          Some(error) => error
        }
      },
      (&Schema::Enum(_), &Query::Value) => Ok(()),
      (&Schema::Enum(_), &Query::Object(_)) => Err(Error::validation("Cannot deeply query an enum.", NO_PRIMITIVE_HINT))
    }
  }
}

#[cfg(test)]
mod tests {
  use linear_map::LinearMap;
  use schema::Schema;

  #[test]
  fn test_query_none() {
    assert_eq!(Schema::None.validate_query(&qvalue!()).is_ok(), true);
    assert_eq!(Schema::None.validate_query(&qobject!{
      "s@#f&/Ij)82h(;pa0]" => qvalue!(),
      "123" => qvalue!(),
      "hello" => qvalue!(),
      "nested" => qobject!{
        "yo" => qvalue!()
      }
    }).is_ok(), true);
  }

  #[test]
  fn test_query_primitive() {
    assert!(Schema::Null.validate_query(&qvalue!()).is_ok());
    let obj_query = qobject!{};
    Schema::Null.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::Boolean.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::Number{
      multiple_of: None,
      minimum: None,
      exclusive_minimum: false,
      maximum: None,
      exclusive_maximum: false
    }.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::String{
      min_length: None,
      max_length: None,
      pattern: None
    }.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::Enum(vec![
      vbool!(true),
      vbool!(false)
    ]).validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
  }

  #[test]
  fn test_query_array() {
    let array_none = Schema::Array {
      items: Box::new(Schema::None)
    };
    let array_bool = Schema::Array {
      items: Box::new(Schema::Boolean)
    };
    assert!(array_none.validate_query(&qvalue!()).is_ok());
    assert!(array_none.validate_query(&qobject!{
      "1" => qvalue!()
    }).is_ok());
    assert!(array_none.validate_query(&qobject!{
      "1" => qobject!{}
    }).is_ok());
    assert!(array_bool.validate_query(&qobject!{
      "1" => qvalue!(),
      "2" => qvalue!(),
      "3" => qvalue!(),
      "50" => qvalue!(),
      "999999999999999" => qvalue!()
    }).is_ok());
    array_none.validate_query(&qobject!{
      "hello" => qvalue!()
    }).unwrap_err().assert_message("non-integer \"hello\"");
    array_bool.validate_query(&qobject!{
      "1" => qobject!{}
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
  }

  #[test]
  fn test_query_tuple() {
    let nums = Schema::Tuple{
      items: vec![Schema::Boolean, Schema::Boolean, Schema::Boolean],
      additional_items: false
    };
    let nums_additional = Schema::Tuple{
      items: vec![Schema::Boolean, Schema::Boolean, Schema::Boolean],
      additional_items: true
    };
    let nums_and_object = Schema::Tuple{
      items: vec![
        Schema::Boolean,
        Schema::Object{
          properties: {
            let mut map = LinearMap::new();
            map.insert(String::from("hello"), Schema::Boolean);
            map
          },
          required: vec![],
          additional_properties: false
        },
        Schema::Boolean
      ],
      additional_items: false
    };
    assert!(nums.validate_query(&qobject!{
      "0" => qvalue!(),
      "2" => qvalue!()
    }).is_ok());
    nums.validate_query(&qobject!{
      "0" => qvalue!(),
      "1" => qvalue!(),
      "2" => qvalue!(),
      "3" => qvalue!()
    }).unwrap_err().assert_message(r"Tuple has only 3 values\. Can’t query the index 3\.");
    nums.validate_query(&qobject!{
      "asd" => qvalue!(),
      "1" => qvalue!(),
      "2" => qvalue!(),
      "3" => qvalue!()
    }).unwrap_err().assert_message("non-integer \"asd\"");
    nums.validate_query(&qobject!{
      "1" => qvalue!(),
      "2" => qobject!{},
      "3" => qvalue!()
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
    assert!(nums_additional.validate_query(&qobject!{
      "0" => qvalue!(),
      "1" => qvalue!(),
      "3" => qvalue!(),
      "99999999" => qvalue!()
    }).is_ok());
    assert!(nums_and_object.validate_query(&qobject!{
      "0" => qvalue!(),
      "1" => qobject!{"hello" => qvalue!()},
      "2" => qvalue!()
    }).is_ok());
  }

  #[test]
  fn test_query_object() {
    let object = Schema::Object{
      properties: {
        let mut map1 = LinearMap::new();
        map1.insert(String::from("hello"), Schema::Boolean);
        map1.insert(String::from("world"), Schema::Boolean);
        map1.insert(String::from("5"), Schema::Boolean);
        map1.insert(String::from("goodbye"), Schema::Object{
          properties: {
            let mut map2 = LinearMap::new();
            map2.insert(String::from("hello"), Schema::Boolean);
            map2.insert(String::from("world"), Schema::Boolean);
            map2
          },
          required: vec![],
          additional_properties: false
        });
        map1
      },
      required: vec![],
      additional_properties: false
    };
    let object_additional = Schema::Object{
      properties: {
        let mut map = LinearMap::new();
        map.insert(String::from("hello"), Schema::Boolean);
        map.insert(String::from("world"), Schema::Boolean);
        map
      },
      required: vec![],
      additional_properties: true
    };
    assert!(object.validate_query(&qobject!{
      "world" => qvalue!(),
      "5" => qvalue!(),
      "goodbye" => qvalue!()
    }).is_ok());
    object.validate_query(&qobject!{
      "hello" => qvalue!(),
      "moon" => qvalue!()
    }).unwrap_err().assert_message("Cannot query object property \"moon\".");
    object.validate_query(&qobject!{
      "hello" => qobject!{}
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
    assert!(object.validate_query(&qobject!{
      "goodbye" => qobject!{
        "hello" => qvalue!()
      }
    }).is_ok());
    object.validate_query(&qobject!{
      "goodbye" => qobject!{
        "hello" => qobject!{}
      }
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
    assert!(object_additional.validate_query(&qobject!{
      "world" => qvalue!(),
      "5" => qvalue!(),
      "goodbye" => qvalue!(),
      "moon" => qvalue!()
    }).is_ok());
  }
}
<commit_msg>refactor: remove tuple schema type<commit_after>use linear_map::LinearMap;
use regex::Regex;
use error::Error;
use query::Query;
use value::{Key, Value};

lazy_static! {
  static ref INTEGER_RE: Regex = Regex::new(r"^\d+$").unwrap();
}

/// A schema detailing what the data received from the driver (or inserted
/// into the driver) should be. Inspired after [JSON Schema][1]. A reference
/// on JSON Schema type-specific validations used in this enum may be found
/// [here][2].
///
/// [1]: http://json-schema.org
/// [2]: http://spacetelescope.github.io/understanding-json-schema/reference/type.html
pub enum Schema {
  /// There is no schema. No validations should occur. Does not represent the
  /// abscense of any value, only represents that a schema does not define the
  /// data structure at this point.
  None,
  /// Represents the absence of any value.
  Null,
  /// Represents a binary true/false value.
  Boolean,
  /// Represents a numeric type.
  Number {
    /// Forces the number to be a multiple of another. This helps in specifying
    /// integers if this value is `Some(1)` for example.
    multiple_of: Option<f32>,
    /// The minimum value the number can be.
    minimum: Option<f64>,
    /// Whether or not the minimum value should be included when validating.
    /// Default is `false`.
    exclusive_minimum: bool,
    /// The maximum value the number can be.
    maximum: Option<f64>,
    /// Whether or not the maximum value should be included when validating.
    /// Default is `false`.
    exclusive_maximum: bool
  },
  String {
    /// The mimimum length of characters in the string.
    min_length: Option<u64>,
    /// The maximum length of characters in the string.
    max_length: Option<u64>,
    /// A regular expression pattern to validate the string against.
    // TODO: Use a regex crate.
    pattern: Option<String>
  },
  /// Represents a set of any type.
  Array {
    /// A schema which all items in the array must match.
    items: Box<Schema>
  },
  /// Represents a set of key/value pairs.
  Object {
    /// Schemas associated to the object properties.
    properties: LinearMap<Key, Schema>,
    /// Properties that are required to be in the object.
    required: Vec<Key>,
    /// Whether or not there may be extra properties outside of the ones
    /// defined by the properties map.
    additional_properties: bool
  },
  /// Represents a value which *must* be one of the defined values. An enum is
  /// considered a primitive type as if it is a single value is a higher order
  /// type, no variation is allowed.
  Enum(Vec<Value>)
}

impl Schema {
  /// Validates a query that a user would like to make on the database by
  /// comparing it to the schema. Mostly checks that all properties described
  /// in the query are accessible according to the schema.
  pub fn validate_query(&self, query: &Query) -> Result<(), Error> {
    static NO_PRIMITIVE_HINT: &'static str = "Try not querying specific properties of a primitive like `null` or `boolean`.";
    match (self, query) {
      // No schema describes these values, its the wild west. Go crazy query.
      // `Schema::None` does not represent the absence of value, just the
      // absence of validation.
      (&Schema::None, _) => Ok(()),
      (&Schema::Null, &Query::Value) => Ok(()),
      (&Schema::Null, &Query::Object(_)) => Err(Error::validation("Cannot deeply query null.", NO_PRIMITIVE_HINT)),
      (&Schema::Boolean, &Query::Value) => Ok(()),
      (&Schema::Boolean, &Query::Object(_)) => Err(Error::validation("Cannot deeply query a boolean.", NO_PRIMITIVE_HINT)),
      (&Schema::Number{..}, &Query::Value) => Ok(()),
      (&Schema::Number{..}, &Query::Object(_)) => Err(Error::validation("Cannot deeply query a number.", NO_PRIMITIVE_HINT)),
      (&Schema::String{..}, &Query::Value) => Ok(()),
      (&Schema::String{..}, &Query::Object(_)) => Err(Error::validation("Cannot deeply query a string.", NO_PRIMITIVE_HINT)),
      (&Schema::Array{..}, &Query::Value) => Ok(()),
      (&Schema::Array{ref items}, &Query::Object(ref query_properties)) => {
        match query_properties.keys().map(|key| {
          if !INTEGER_RE.is_match(key) {
            Err(Error::validation(format!("Cannot query non-integer \"{}\" array property.", key), "Only query integer array keys like 1, 2, and 3."))
          } else {
            items.validate_query(&query_properties[key])
          }
        }).find(|r| r.is_err()) {
          None => Ok(()),
          Some(error) => error
        }
      },
      (&Schema::Object{..}, &Query::Value) => Ok(()),
      (&Schema::Object{ref properties, ref additional_properties,..}, &Query::Object(ref query_properties)) => {
        match query_properties.keys().map(|key| {
          if let Some(property_schema) = properties.get(key) {
            property_schema.validate_query(&query_properties[key])
          } else if !additional_properties {
            Err(Error::validation(format!("Cannot query object property \"{}\".", key), "Query an object property that is defined in the schema."))
          } else {
            Ok(())
          }
        }).find(|r| r.is_err()) {
          None => Ok(()),
          Some(error) => error
        }
      },
      (&Schema::Enum(_), &Query::Value) => Ok(()),
      (&Schema::Enum(_), &Query::Object(_)) => Err(Error::validation("Cannot deeply query an enum.", NO_PRIMITIVE_HINT))
    }
  }
}

#[cfg(test)]
mod tests {
  use linear_map::LinearMap;
  use schema::Schema;

  #[test]
  fn test_query_none() {
    assert_eq!(Schema::None.validate_query(&qvalue!()).is_ok(), true);
    assert_eq!(Schema::None.validate_query(&qobject!{
      "s@#f&/Ij)82h(;pa0]" => qvalue!(),
      "123" => qvalue!(),
      "hello" => qvalue!(),
      "nested" => qobject!{
        "yo" => qvalue!()
      }
    }).is_ok(), true);
  }

  #[test]
  fn test_query_primitive() {
    assert!(Schema::Null.validate_query(&qvalue!()).is_ok());
    let obj_query = qobject!{};
    Schema::Null.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::Boolean.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::Number{
      multiple_of: None,
      minimum: None,
      exclusive_minimum: false,
      maximum: None,
      exclusive_maximum: false
    }.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::String{
      min_length: None,
      max_length: None,
      pattern: None
    }.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::Enum(vec![
      vbool!(true),
      vbool!(false)
    ]).validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
  }

  #[test]
  fn test_query_array() {
    let array_none = Schema::Array {
      items: Box::new(Schema::None)
    };
    let array_bool = Schema::Array {
      items: Box::new(Schema::Boolean)
    };
    assert!(array_none.validate_query(&qvalue!()).is_ok());
    assert!(array_none.validate_query(&qobject!{
      "1" => qvalue!()
    }).is_ok());
    assert!(array_none.validate_query(&qobject!{
      "1" => qobject!{}
    }).is_ok());
    assert!(array_bool.validate_query(&qobject!{
      "1" => qvalue!(),
      "2" => qvalue!(),
      "3" => qvalue!(),
      "50" => qvalue!(),
      "999999999999999" => qvalue!()
    }).is_ok());
    array_none.validate_query(&qobject!{
      "hello" => qvalue!()
    }).unwrap_err().assert_message("non-integer \"hello\"");
    array_bool.validate_query(&qobject!{
      "1" => qobject!{}
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
  }

  #[test]
  fn test_query_object() {
    let object = Schema::Object{
      properties: {
        let mut map1 = LinearMap::new();
        map1.insert(String::from("hello"), Schema::Boolean);
        map1.insert(String::from("world"), Schema::Boolean);
        map1.insert(String::from("5"), Schema::Boolean);
        map1.insert(String::from("goodbye"), Schema::Object{
          properties: {
            let mut map2 = LinearMap::new();
            map2.insert(String::from("hello"), Schema::Boolean);
            map2.insert(String::from("world"), Schema::Boolean);
            map2
          },
          required: vec![],
          additional_properties: false
        });
        map1
      },
      required: vec![],
      additional_properties: false
    };
    let object_additional = Schema::Object{
      properties: {
        let mut map = LinearMap::new();
        map.insert(String::from("hello"), Schema::Boolean);
        map.insert(String::from("world"), Schema::Boolean);
        map
      },
      required: vec![],
      additional_properties: true
    };
    assert!(object.validate_query(&qobject!{
      "world" => qvalue!(),
      "5" => qvalue!(),
      "goodbye" => qvalue!()
    }).is_ok());
    object.validate_query(&qobject!{
      "hello" => qvalue!(),
      "moon" => qvalue!()
    }).unwrap_err().assert_message("Cannot query object property \"moon\".");
    object.validate_query(&qobject!{
      "hello" => qobject!{}
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
    assert!(object.validate_query(&qobject!{
      "goodbye" => qobject!{
        "hello" => qvalue!()
      }
    }).is_ok());
    object.validate_query(&qobject!{
      "goodbye" => qobject!{
        "hello" => qobject!{}
      }
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
    assert!(object_additional.validate_query(&qobject!{
      "world" => qvalue!(),
      "5" => qvalue!(),
      "goodbye" => qvalue!(),
      "moon" => qvalue!()
    }).is_ok());
  }
}
<|endoftext|>


<commit_before>extern crate tiny_http;
extern crate ascii;
extern crate rand;

use std::io::Write;
use std::fs;
use std::fs::File;
use std::borrow::Borrow;
use std::str::FromStr;

use std::sync::Mutex;

use tiny_http::{ServerBuilder, Response, Request, Header, StatusCode};

use util::read_whole;

pub use image_url::*;

macro_rules! try_print(
    ($e:expr) => {{
        match $e {
            Ok(v) => v,
            Err(e) => { println!("Error: {}", e); return; }
        }
    }}
);

pub struct Server<'a> {
    pub image_dir : &'a str,
    pub content_type: ContentType<'a>,
    //pub server_url: &'a str,
    pub used_urls: Mutex<UsedUrlSet>,
}

pub type ContentType<'a> = &'a str;

impl <'a> Server<'a> {

    pub fn new(image_dir: &'a str, content_type: ContentType<'a>) -> Server<'a> {
        let used_urls = match fs::read_dir(image_dir) {
            Ok(paths) => {
                let mut urls = UsedUrlSet::new();
                for path in paths {
                    match path.unwrap().file_name().into_string() {
                        Ok(s) => {
                            match ImageUrl::from_str(s.split(".").nth(0).unwrap()) {
                                Ok(num) => { urls.insert(num); }
                                Err(_) => { continue; }
                            }
                        },
                        Err(_) => { continue; }
                    }
                }

                urls
            }
            Err(_) => UsedUrlSet::new()
        };

        for i in used_urls.iter() {
            println!("url exists: {}", i);
        }

        Server { image_dir: image_dir,
                 content_type: content_type,
                 server_url: server_url,
                 used_urls: Mutex::new(used_urls),
                 }
    }

    pub fn run(&self, port: u16, threads: u32) {
        let server = ServerBuilder::new().with_port(port).build().unwrap();

        // TODO: multiple worker threads
        loop {
            match server.recv() {
                Ok(req) => {
                    self.on_req(req);
                },
                Err(e) => { println!("Error: {}", e); continue; }
            }
        }

        /*
        let guards = Vec::with_capacity(threads);

        for i in 0..threads {
            let server = tiny_http_server.clone();

            let guard = thread::spawn(move || {
                loop {
                    match server.recv() {
                        Ok(req) => {
                            self.on_req(req);
                        },
                        Err(e) => { println!("Error: {}", e); continue; }
                    }
                }
            });

            guards.push(guard);
        }
        */
    }

    // retrieves an image path
    // name.png => <image_dir>/name
    // name => <image_dir>/name
    fn get_image_path(&self, path: &str) -> String {
        // remove extension
        let base = path.split(".").nth(0).unwrap();
        self.image_dir.to_string() + base
    }

    fn handle_get_image_req(&self, req: Request) {
        let full_path = self.get_image_path(req.url().borrow());
        match fs::metadata(&full_path) {
            Ok(metadata) => {
                if !metadata.is_file() {
                    let res = Response::from_string("404").with_status_code(StatusCode(404));
                    req.respond(res);
                    return;
                }

                match File::open(&full_path) {
                    Ok(file) => {
                        let res = Response::from_file(file)
                                  .with_header(Header::from_bytes(&b"Content-Type"[..], self.content_type.as_bytes()).unwrap())
                                  .with_status_code(StatusCode(200));
                        req.respond(res);
                    },
                    Err(_) => {
                        let res = Response::from_string("500").with_status_code(StatusCode(500));
                        req.respond(res);
                    }
                }
            },
            Err(_) => {
                let res = Response::from_string("404").with_status_code(StatusCode(404));
                req.respond(res);
            }
        }
    }

    fn upload_image(&self, data: &[u8]) -> Option<String> {

        let mut used_urls = self.used_urls.lock().unwrap();

        let mut num = gen_image_url();
        while used_urls.contains(&num) {
            num = gen_image_url();
        }

        let file_name = self.get_image_path(num.to_string().borrow());
        used_urls.insert(ImageUrl(num.0));

        return match File::create(&file_name) {
            Ok(mut file) => {
                match file.write_all(data) {
                    Ok(_) => Some(num.to_string().borrow()),
                    Err(_) => None
                }
            }
            Err(_) => None
        }
    }

    fn on_req(&self, mut req: Request) {
        // determine what request they are making:
        // if it is a GET method, then retrieve the image
        // if it is a POST method:
        //    - then ensure it is an image
        //    - save it locally with a random 6 character long 62 bit number (a-zA-Z0-9)
        //    - give them the URL

        print!("{:?} ", req);
        let method = req.method().to_string();
        match method.borrow() {
            "GET" => {
                self.handle_get_image_req(req);
            }
            "POST" => {

                {
                    let content_type = req.headers().iter().find(|&header| header.field.equiv("Content-Type"));
                    match content_type {
                        Some(x) => {
                            if x.value != self.content_type {
                                println!("Unsupported content type {}", x.value);
                                return;
                            }

                            // correct format
                            // fall-through
                        },
                        None => {
                            // TODO
                            return;
                        }
                    }
                }

                let data = try_print!(read_whole(req.as_reader()));
                let url = self.upload_image(data.borrow()).unwrap();
                print!("generated url: {}", url);
                let res = Response::from_string(url).with_status_code(StatusCode(200)).with_header(Header::from_bytes(&b"Content-Type"[..], &b"text/html; charset=UTF-8"[..]).unwrap());
                req.respond(res);
            },
            _ => ()
        }
        println!("");
    }
}
<commit_msg>Fix compilation errors<commit_after>extern crate tiny_http;
extern crate ascii;
extern crate rand;

use std::io::Write;
use std::fs;
use std::fs::File;
use std::borrow::Borrow;
use std::str::FromStr;

use std::sync::Mutex;

use tiny_http::{ServerBuilder, Response, Request, Header, StatusCode};

use util::read_whole;

pub use image_url::*;

macro_rules! try_print(
    ($e:expr) => {{
        match $e {
            Ok(v) => v,
            Err(e) => { println!("Error: {}", e); return; }
        }
    }}
);

pub struct Server<'a> {
    pub image_dir : &'a str,
    pub content_type: ContentType<'a>,
    pub used_urls: Mutex<UsedUrlSet>,
}

pub type ContentType<'a> = &'a str;

impl <'a> Server<'a> {

    pub fn new(image_dir: &'a str, content_type: ContentType<'a>) -> Server<'a> {
        let used_urls = match fs::read_dir(image_dir) {
            Ok(paths) => {
                let mut urls = UsedUrlSet::new();
                for path in paths {
                    match path.unwrap().file_name().into_string() {
                        Ok(s) => {
                            match ImageUrl::from_str(s.split(".").nth(0).unwrap()) {
                                Ok(num) => { urls.insert(num); }
                                Err(_) => { continue; }
                            }
                        },
                        Err(_) => { continue; }
                    }
                }

                urls
            }
            Err(_) => UsedUrlSet::new()
        };

        for i in used_urls.iter() {
            println!("url exists: {}", i);
        }

        Server { image_dir: image_dir,
                 content_type: content_type,
                 used_urls: Mutex::new(used_urls),
                 }
    }

    pub fn run(&self, port: u16, threads: u32) {
        let server = ServerBuilder::new().with_port(port).build().unwrap();

        // TODO: multiple worker threads
        loop {
            match server.recv() {
                Ok(req) => {
                    self.on_req(req);
                },
                Err(e) => { println!("Error: {}", e); continue; }
            }
        }

        /*
        let guards = Vec::with_capacity(threads);

        for i in 0..threads {
            let server = tiny_http_server.clone();

            let guard = thread::spawn(move || {
                loop {
                    match server.recv() {
                        Ok(req) => {
                            self.on_req(req);
                        },
                        Err(e) => { println!("Error: {}", e); continue; }
                    }
                }
            });

            guards.push(guard);
        }
        */
    }

    // retrieves an image path
    // name.png => <image_dir>/name
    // name => <image_dir>/name
    fn get_image_path(&self, path: &str) -> String {
        // remove extension
        let base = path.split(".").nth(0).unwrap();
        self.image_dir.to_string() + base
    }

    fn handle_get_image_req(&self, req: Request) {
        let full_path = self.get_image_path(req.url().borrow());
        match fs::metadata(&full_path) {
            Ok(metadata) => {
                if !metadata.is_file() {
                    let res = Response::from_string("404").with_status_code(StatusCode(404));
                    req.respond(res);
                    return;
                }

                match File::open(&full_path) {
                    Ok(file) => {
                        let res = Response::from_file(file)
                                  .with_header(Header::from_bytes(&b"Content-Type"[..], self.content_type.as_bytes()).unwrap())
                                  .with_status_code(StatusCode(200));
                        req.respond(res);
                    },
                    Err(_) => {
                        let res = Response::from_string("500").with_status_code(StatusCode(500));
                        req.respond(res);
                    }
                }
            },
            Err(_) => {
                let res = Response::from_string("404").with_status_code(StatusCode(404));
                req.respond(res);
            }
        }
    }

    fn upload_image(&self, data: &[u8]) -> Option<String> {

        let mut used_urls = self.used_urls.lock().unwrap();

        let mut num = gen_image_url();
        while used_urls.contains(&num) {
            num = gen_image_url();
        }

        let file_name = self.get_image_path(num.to_string().borrow());
        used_urls.insert(ImageUrl(num.0));

        return match File::create(&file_name) {
            Ok(mut file) => {
                match file.write_all(data) {
                    Ok(_) => Some(num.to_string()),
                    Err(_) => None
                }
            }
            Err(_) => None
        }
    }

    fn on_req(&self, mut req: Request) {
        // determine what request they are making:
        // if it is a GET method, then retrieve the image
        // if it is a POST method:
        //    - then ensure it is an image
        //    - save it locally with a random 6 character long 62 bit number (a-zA-Z0-9)
        //    - give them the URL

        print!("{:?} ", req);
        let method = req.method().to_string();
        match method.borrow() {
            "GET" => {
                self.handle_get_image_req(req);
            }
            "POST" => {

                {
                    let content_type = req.headers().iter().find(|&header| header.field.equiv("Content-Type"));
                    match content_type {
                        Some(x) => {
                            if x.value != self.content_type {
                                println!("Unsupported content type {}", x.value);
                                return;
                            }

                            // correct format
                            // fall-through
                        },
                        None => {
                            // TODO
                            return;
                        }
                    }
                }

                let data = try_print!(read_whole(req.as_reader()));
                let url = self.upload_image(data.borrow()).unwrap();
                print!("generated url: {}", url);
                let res = Response::from_string(url).with_status_code(StatusCode(200)).with_header(Header::from_bytes(&b"Content-Type"[..], &b"text/html; charset=UTF-8"[..]).unwrap());
                req.respond(res);
            },
            _ => ()
        }
        println!("");
    }
}
<|endoftext|>


<commit_before>use std::ops::Deref;
use syntax::ast::*;
use syntax::codemap::Span;
use syntax::visit::FnKind;

use rustc::lint::{Context, LintArray, LintPass};
use rustc::middle::def::Def::{DefVariant, DefStruct};

use utils::{in_external_macro, snippet, span_lint};

declare_lint!(pub SHADOW_SAME, Allow,
    "rebinding a name to itself, e.g. `let mut x = &mut x`");
declare_lint!(pub SHADOW_REUSE, Allow,
    "rebinding a name to an expression that re-uses the original value, e.g. \
    `let x = x + 1`");
declare_lint!(pub SHADOW_UNRELATED, Warn,
    "The name is re-bound without even using the original value");

#[derive(Copy, Clone)]
pub struct ShadowPass;

impl LintPass for ShadowPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED)
    }

    fn check_fn(&mut self, cx: &Context, _: FnKind, decl: &FnDecl,
            block: &Block, _: Span, _: NodeId) {
        if in_external_macro(cx, block.span) { return; }
        check_fn(cx, decl, block);
    }
}

fn check_fn(cx: &Context, decl: &FnDecl, block: &Block) {
    let mut bindings = Vec::new();
    for arg in &decl.inputs {
        if let PatIdent(_, ident, _) = arg.pat.node {
            bindings.push(ident.node.name)
        }
    }
    check_block(cx, block, &mut bindings);
}

fn check_block(cx: &Context, block: &Block, bindings: &mut Vec<Name>) {
    let len = bindings.len();
    for stmt in &block.stmts {
        match stmt.node {
            StmtDecl(ref decl, _) => check_decl(cx, decl, bindings),
            StmtExpr(ref e, _) | StmtSemi(ref e, _) =>
                check_expr(cx, e, bindings),
            _ => ()
        }
    }
    if let Some(ref o) = block.expr { check_expr(cx, o, bindings); }
    bindings.truncate(len);
}

fn check_decl(cx: &Context, decl: &Decl, bindings: &mut Vec<Name>) {
    if in_external_macro(cx, decl.span) { return; }
    if let DeclLocal(ref local) = decl.node {
        let Local{ ref pat, ref ty, ref init, id: _, span } = **local;
        if let &Some(ref t) = ty { check_ty(cx, t, bindings) }
        if let &Some(ref o) = init {
            check_expr(cx, o, bindings);
            check_pat(cx, pat, &Some(o), span, bindings);
        } else {
            check_pat(cx, pat, &None, span, bindings);
        }
    }
}

fn is_binding(cx: &Context, pat: &Pat) -> bool {
    match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) {
        Some(DefVariant(..)) | Some(DefStruct(..)) => false,
        _ => true
    }
}

fn check_pat(cx: &Context, pat: &Pat, init: &Option<&Expr>, span: Span,
        bindings: &mut Vec<Name>) {
    //TODO: match more stuff / destructuring
    match pat.node {
        PatIdent(_, ref ident, ref inner) => {
            let name = ident.node.name;
            if is_binding(cx, pat) {
                if bindings.contains(&name) {
                    lint_shadow(cx, name, span, pat.span, init);
                } else {
                    bindings.push(name);
                }
            }
            if let Some(ref p) = *inner { check_pat(cx, p, init, span, bindings); }
        },
        //PatEnum(Path, Option<Vec<P<Pat>>>),
        PatStruct(_, ref pfields, _) =>
            if let Some(ref init_struct) = *init { // TODO follow
                if let ExprStruct(_, ref efields, ref _base) = init_struct.node {
                    // TODO: follow base
                    for field in pfields {
                        let ident = field.node.ident;
                        let efield = efields.iter()
                            .find(|ref f| f.ident.node == ident)
                            .map(|f| &*f.expr);
                        check_pat(cx, &field.node.pat, &efield, span, bindings);
                    }
                } else {
                    for field in pfields {
                        check_pat(cx, &field.node.pat, &None, span, bindings);
                    }
                }
            } else {
                for field in pfields {
                    check_pat(cx, &field.node.pat, &None, span, bindings);
                }
            },
        PatTup(ref inner) =>
            if let Some(ref init_tup) = *init { //TODO: follow
                if let ExprTup(ref tup) = init_tup.node {
                    for (i, p) in inner.iter().enumerate() {
                        check_pat(cx, p, &Some(&tup[i]), p.span, bindings);
                    }
                } else {
                    for p in inner {
                        check_pat(cx, p, &None, span, bindings);
                    }
                }
            } else {
                for p in inner {
                    check_pat(cx, p, &None, span, bindings);
                }
            },
        PatBox(ref inner) => {
            if let Some(ref initp) = *init {
                match initp.node {
                    ExprBox(_, ref inner_init) =>
                        check_pat(cx, inner, &Some(&**inner_init), span, bindings),
                    //TODO: ExprCall on Box::new
                    _ => check_pat(cx, inner, init, span, bindings),
                }
            } else {
                check_pat(cx, inner, init, span, bindings);
            }
        },
        //PatRegion(P<Pat>, Mutability),
        //PatRange(P<Expr>, P<Expr>),
        //PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
        _ => (),
    }
}

fn lint_shadow<T>(cx: &Context, name: Name, span: Span, lspan: Span, init:
        &Option<T>) where T: Deref<Target=Expr> {
    if let &Some(ref expr) = init {
        if is_self_shadow(name, expr) {
            span_lint(cx, SHADOW_SAME, span, &format!(
                "{} is shadowed by itself in {}",
                snippet(cx, lspan, "_"),
                snippet(cx, expr.span, "..")));
        } else {
            if contains_self(name, expr) {
                span_lint(cx, SHADOW_REUSE, span, &format!(
                    "{} is shadowed by {} which reuses the original value",
                    snippet(cx, lspan, "_"),
                    snippet(cx, expr.span, "..")));
            } else {
                span_lint(cx, SHADOW_UNRELATED, span, &format!(
                    "{} is shadowed by {} in this declaration",
                    snippet(cx, lspan, "_"),
                    snippet(cx, expr.span, "..")));
            }
        }
    } else {
        span_lint(cx, SHADOW_UNRELATED, span, &format!(
            "{} is shadowed in this declaration", snippet(cx, lspan, "_")));
    }
}

fn check_expr(cx: &Context, expr: &Expr, bindings: &mut Vec<Name>) {
    if in_external_macro(cx, expr.span) { return; }
    match expr.node {
        ExprUnary(_, ref e) | ExprParen(ref e) | ExprField(ref e, _) |
        ExprTupField(ref e, _) | ExprAddrOf(_, ref e) | ExprBox(None, ref e)
            => { check_expr(cx, e, bindings) },
        ExprBox(Some(ref place), ref e) => {
            check_expr(cx, place, bindings); check_expr(cx, e, bindings) }
        ExprBlock(ref block) | ExprLoop(ref block, _) =>
            { check_block(cx, block, bindings) },
        //ExprCall
        //ExprMethodCall
        ExprVec(ref v) | ExprTup(ref v) =>
            for ref e in v { check_expr(cx, e, bindings) },
        ExprIf(ref cond, ref then, ref otherwise) => {
            check_expr(cx, cond, bindings);
            check_block(cx, then, bindings);
            if let &Some(ref o) = otherwise { check_expr(cx, o, bindings); }
        },
        ExprWhile(ref cond, ref block, _) => {
            check_expr(cx, cond, bindings);
            check_block(cx, block, bindings);
        },
        ExprMatch(ref init, ref arms, _) => {
            check_expr(cx, init, bindings);
            let len = bindings.len();
            for ref arm in arms {
                for ref pat in &arm.pats {
                    check_pat(cx, &pat, &Some(&**init), pat.span, bindings);
                    //TODO: This is ugly, but needed to get the right type
                }
                if let Some(ref guard) = arm.guard {
                    check_expr(cx, guard, bindings);
                }
                check_expr(cx, &arm.body, bindings);
                bindings.truncate(len);
            }
        },
        _ => ()
    }
}

fn check_ty(cx: &Context, ty: &Ty, bindings: &mut Vec<Name>) {
    match ty.node {
        TyParen(ref sty) | TyObjectSum(ref sty, _) |
        TyVec(ref sty) => check_ty(cx, sty, bindings),
        TyFixedLengthVec(ref fty, ref expr) => {
            check_ty(cx, fty, bindings);
            check_expr(cx, expr, bindings);
        },
        TyPtr(MutTy{ ty: ref mty, .. }) |
        TyRptr(_, MutTy{ ty: ref mty, .. }) => check_ty(cx, mty, bindings),
        TyTup(ref tup) => { for ref t in tup { check_ty(cx, t, bindings) } },
        TyTypeof(ref expr) => check_expr(cx, expr, bindings),
        _ => (),
    }
}

fn is_self_shadow(name: Name, expr: &Expr) -> bool {
    match expr.node {
        ExprBox(_, ref inner) |
        ExprParen(ref inner) |
        ExprAddrOf(_, ref inner) => is_self_shadow(name, inner),
        ExprBlock(ref block) => block.stmts.is_empty() && block.expr.as_ref().
            map_or(false, |ref e| is_self_shadow(name, e)),
        ExprUnary(op, ref inner) => (UnUniq == op || UnDeref == op) &&
            is_self_shadow(name, inner),
        ExprPath(_, ref path) => path_eq_name(name, path),
        _ => false,
    }
}

fn path_eq_name(name: Name, path: &Path) -> bool {
    !path.global && path.segments.len() == 1 &&
        path.segments[0].identifier.name == name
}

fn contains_self(name: Name, expr: &Expr) -> bool {
    match expr.node {
        ExprUnary(_, ref e) | ExprParen(ref e) | ExprField(ref e, _) |
        ExprTupField(ref e, _) | ExprAddrOf(_, ref e) | ExprBox(_, ref e)
            => contains_self(name, e),
        ExprBinary(_, ref l, ref r) =>
            contains_self(name, l) || contains_self(name, r),
        ExprBlock(ref block) | ExprLoop(ref block, _) =>
            contains_block_self(name, block),
        ExprCall(ref fun, ref args) => contains_self(name, fun) ||
            args.iter().any(|ref a| contains_self(name, a)),
        ExprMethodCall(_, _, ref args) =>
            args.iter().any(|ref a| contains_self(name, a)),
        ExprVec(ref v) | ExprTup(ref v) =>
            v.iter().any(|ref e| contains_self(name, e)),
        ExprIf(ref cond, ref then, ref otherwise) =>
            contains_self(name, cond) || contains_block_self(name, then) ||
            otherwise.as_ref().map_or(false, |ref e| contains_self(name, e)),
        ExprWhile(ref e, ref block, _)  =>
            contains_self(name, e) || contains_block_self(name, block),
        ExprMatch(ref e, ref arms, _) =>
            arms.iter().any(|ref arm| arm.pats.iter().any(|ref pat|
                contains_pat_self(name, pat))) || contains_self(name, e),
        ExprPath(_, ref path) => path_eq_name(name, path),
        _ => false
    }
}

fn contains_block_self(name: Name, block: &Block) -> bool {
    for stmt in &block.stmts {
        match stmt.node {
            StmtDecl(ref decl, _) =>
            if let DeclLocal(ref local) = decl.node {
                //TODO: We don't currently handle the case where the name
                //is shadowed wiithin the block; this means code including this
                //degenerate pattern will get the wrong warning.
                if let Some(ref init) = local.init {
                    if contains_self(name, init) { return true; }
                }
            },
            StmtExpr(ref e, _) | StmtSemi(ref e, _) =>
                if contains_self(name, e) { return true },
            _ => ()
        }
    }
    if let Some(ref e) = block.expr { contains_self(name, e) } else { false }
}

fn contains_pat_self(name: Name, pat: &Pat) -> bool {
    match pat.node {
        PatIdent(_, ref ident, ref inner) => name == ident.node.name ||
            inner.as_ref().map_or(false, |ref p| contains_pat_self(name, p)),
        PatEnum(_, ref opats) => opats.as_ref().map_or(false,
            |pats| pats.iter().any(|p| contains_pat_self(name, p))),
        PatQPath(_, ref path) => path_eq_name(name, path),
        PatStruct(_, ref fieldpats, _) => fieldpats.iter().any(
            |ref fp| contains_pat_self(name, &fp.node.pat)),
        PatTup(ref ps) => ps.iter().any(|ref p| contains_pat_self(name, p)),
        PatBox(ref p) |
        PatRegion(ref p, _) => contains_pat_self(name, p),
        PatRange(ref from, ref until) =>
            contains_self(name, from) || contains_self(name, until),
        PatVec(ref pre, ref opt, ref post) =>
            pre.iter().any(|ref p| contains_pat_self(name, p)) ||
                opt.as_ref().map_or(false, |ref p| contains_pat_self(name, p)) ||
                post.iter().any(|ref p| contains_pat_self(name, p)),
        _ => false,
    }
}
<commit_msg>extended pattern matching<commit_after>use std::ops::Deref;
use syntax::ast::*;
use syntax::codemap::Span;
use syntax::visit::FnKind;

use rustc::lint::{Context, LintArray, LintPass};
use rustc::middle::def::Def::{DefVariant, DefStruct};

use utils::{in_external_macro, snippet, span_lint};

declare_lint!(pub SHADOW_SAME, Allow,
    "rebinding a name to itself, e.g. `let mut x = &mut x`");
declare_lint!(pub SHADOW_REUSE, Allow,
    "rebinding a name to an expression that re-uses the original value, e.g. \
    `let x = x + 1`");
declare_lint!(pub SHADOW_UNRELATED, Warn,
    "The name is re-bound without even using the original value");

#[derive(Copy, Clone)]
pub struct ShadowPass;

impl LintPass for ShadowPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED)
    }

    fn check_fn(&mut self, cx: &Context, _: FnKind, decl: &FnDecl,
            block: &Block, _: Span, _: NodeId) {
        if in_external_macro(cx, block.span) { return; }
        check_fn(cx, decl, block);
    }
}

fn check_fn(cx: &Context, decl: &FnDecl, block: &Block) {
    let mut bindings = Vec::new();
    for arg in &decl.inputs {
        if let PatIdent(_, ident, _) = arg.pat.node {
            bindings.push(ident.node.name)
        }
    }
    check_block(cx, block, &mut bindings);
}

fn check_block(cx: &Context, block: &Block, bindings: &mut Vec<Name>) {
    let len = bindings.len();
    for stmt in &block.stmts {
        match stmt.node {
            StmtDecl(ref decl, _) => check_decl(cx, decl, bindings),
            StmtExpr(ref e, _) | StmtSemi(ref e, _) =>
                check_expr(cx, e, bindings),
            _ => ()
        }
    }
    if let Some(ref o) = block.expr { check_expr(cx, o, bindings); }
    bindings.truncate(len);
}

fn check_decl(cx: &Context, decl: &Decl, bindings: &mut Vec<Name>) {
    if in_external_macro(cx, decl.span) { return; }
    if let DeclLocal(ref local) = decl.node {
        let Local{ ref pat, ref ty, ref init, id: _, span } = **local;
        if let &Some(ref t) = ty { check_ty(cx, t, bindings) }
        if let &Some(ref o) = init {
            check_expr(cx, o, bindings);
            check_pat(cx, pat, &Some(o), span, bindings);
        } else {
            check_pat(cx, pat, &None, span, bindings);
        }
    }
}

fn is_binding(cx: &Context, pat: &Pat) -> bool {
    match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) {
        Some(DefVariant(..)) | Some(DefStruct(..)) => false,
        _ => true
    }
}

fn check_pat(cx: &Context, pat: &Pat, init: &Option<&Expr>, span: Span,
        bindings: &mut Vec<Name>) {
    //TODO: match more stuff / destructuring
    match pat.node {
        PatIdent(_, ref ident, ref inner) => {
            let name = ident.node.name;
            if is_binding(cx, pat) {
                if bindings.contains(&name) {
                    lint_shadow(cx, name, span, pat.span, init);
                } else {
                    bindings.push(name);
                }
            }
            if let Some(ref p) = *inner { check_pat(cx, p, init, span, bindings); }
        },
        //PatEnum(Path, Option<Vec<P<Pat>>>),
        PatStruct(_, ref pfields, _) =>
            if let Some(ref init_struct) = *init {
                if let ExprStruct(_, ref efields, _) = init_struct.node {
                    for field in pfields {
                        let ident = field.node.ident;
                        let efield = efields.iter()
                            .find(|ref f| f.ident.node == ident)
                            .map(|f| &*f.expr);
                        check_pat(cx, &field.node.pat, &efield, span, bindings);
                    }
                } else {
                    for field in pfields {
                        check_pat(cx, &field.node.pat, init, span, bindings);
                    }
                }
            } else {
                for field in pfields {
                    check_pat(cx, &field.node.pat, &None, span, bindings);
                }
            },
        PatTup(ref inner) =>
            if let Some(ref init_tup) = *init {
                if let ExprTup(ref tup) = init_tup.node {
                    for (i, p) in inner.iter().enumerate() {
                        check_pat(cx, p, &Some(&tup[i]), p.span, bindings);
                    }
                } else {
                    for p in inner {
                        check_pat(cx, p, init, span, bindings);
                    }
                }
            } else {
                for p in inner {
                    check_pat(cx, p, &None, span, bindings);
                }
            },
        PatBox(ref inner) => {
            if let Some(ref initp) = *init {
                match initp.node {
                    ExprBox(_, ref inner_init) =>
                        check_pat(cx, inner, &Some(&**inner_init), span, bindings),
                    //TODO: ExprCall on Box::new
                    _ => check_pat(cx, inner, init, span, bindings),
                }
            } else {
                check_pat(cx, inner, init, span, bindings);
            }
        },
        //PatRegion(P<Pat>, Mutability),
        //PatRange(P<Expr>, P<Expr>),
        //PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
        _ => (),
    }
}

fn lint_shadow<T>(cx: &Context, name: Name, span: Span, lspan: Span, init:
        &Option<T>) where T: Deref<Target=Expr> {
    if let &Some(ref expr) = init {
        if is_self_shadow(name, expr) {
            span_lint(cx, SHADOW_SAME, span, &format!(
                "{} is shadowed by itself in {}",
                snippet(cx, lspan, "_"),
                snippet(cx, expr.span, "..")));
        } else {
            if contains_self(name, expr) {
                span_lint(cx, SHADOW_REUSE, span, &format!(
                    "{} is shadowed by {} which reuses the original value",
                    snippet(cx, lspan, "_"),
                    snippet(cx, expr.span, "..")));
            } else {
                span_lint(cx, SHADOW_UNRELATED, span, &format!(
                    "{} is shadowed by {} in this declaration",
                    snippet(cx, lspan, "_"),
                    snippet(cx, expr.span, "..")));
            }
        }
    } else {
        span_lint(cx, SHADOW_UNRELATED, span, &format!(
            "{} is shadowed in this declaration", snippet(cx, lspan, "_")));
    }
}

fn check_expr(cx: &Context, expr: &Expr, bindings: &mut Vec<Name>) {
    if in_external_macro(cx, expr.span) { return; }
    match expr.node {
        ExprUnary(_, ref e) | ExprParen(ref e) | ExprField(ref e, _) |
        ExprTupField(ref e, _) | ExprAddrOf(_, ref e) | ExprBox(None, ref e)
            => { check_expr(cx, e, bindings) },
        ExprBox(Some(ref place), ref e) => {
            check_expr(cx, place, bindings); check_expr(cx, e, bindings) }
        ExprBlock(ref block) | ExprLoop(ref block, _) =>
            { check_block(cx, block, bindings) },
        //ExprCall
        //ExprMethodCall
        ExprVec(ref v) | ExprTup(ref v) =>
            for ref e in v { check_expr(cx, e, bindings) },
        ExprIf(ref cond, ref then, ref otherwise) => {
            check_expr(cx, cond, bindings);
            check_block(cx, then, bindings);
            if let &Some(ref o) = otherwise { check_expr(cx, o, bindings); }
        },
        ExprWhile(ref cond, ref block, _) => {
            check_expr(cx, cond, bindings);
            check_block(cx, block, bindings);
        },
        ExprMatch(ref init, ref arms, _) => {
            check_expr(cx, init, bindings);
            let len = bindings.len();
            for ref arm in arms {
                for ref pat in &arm.pats {
                    check_pat(cx, &pat, &Some(&**init), pat.span, bindings);
                    //TODO: This is ugly, but needed to get the right type
                }
                if let Some(ref guard) = arm.guard {
                    check_expr(cx, guard, bindings);
                }
                check_expr(cx, &arm.body, bindings);
                bindings.truncate(len);
            }
        },
        _ => ()
    }
}

fn check_ty(cx: &Context, ty: &Ty, bindings: &mut Vec<Name>) {
    match ty.node {
        TyParen(ref sty) | TyObjectSum(ref sty, _) |
        TyVec(ref sty) => check_ty(cx, sty, bindings),
        TyFixedLengthVec(ref fty, ref expr) => {
            check_ty(cx, fty, bindings);
            check_expr(cx, expr, bindings);
        },
        TyPtr(MutTy{ ty: ref mty, .. }) |
        TyRptr(_, MutTy{ ty: ref mty, .. }) => check_ty(cx, mty, bindings),
        TyTup(ref tup) => { for ref t in tup { check_ty(cx, t, bindings) } },
        TyTypeof(ref expr) => check_expr(cx, expr, bindings),
        _ => (),
    }
}

fn is_self_shadow(name: Name, expr: &Expr) -> bool {
    match expr.node {
        ExprBox(_, ref inner) |
        ExprParen(ref inner) |
        ExprAddrOf(_, ref inner) => is_self_shadow(name, inner),
        ExprBlock(ref block) => block.stmts.is_empty() && block.expr.as_ref().
            map_or(false, |ref e| is_self_shadow(name, e)),
        ExprUnary(op, ref inner) => (UnUniq == op || UnDeref == op) &&
            is_self_shadow(name, inner),
        ExprPath(_, ref path) => path_eq_name(name, path),
        _ => false,
    }
}

fn path_eq_name(name: Name, path: &Path) -> bool {
    !path.global && path.segments.len() == 1 &&
        path.segments[0].identifier.name == name
}

fn contains_self(name: Name, expr: &Expr) -> bool {
    match expr.node {
        ExprUnary(_, ref e) | ExprParen(ref e) | ExprField(ref e, _) |
        ExprTupField(ref e, _) | ExprAddrOf(_, ref e) | ExprBox(_, ref e)
            => contains_self(name, e),
        ExprBinary(_, ref l, ref r) =>
            contains_self(name, l) || contains_self(name, r),
        ExprBlock(ref block) | ExprLoop(ref block, _) =>
            contains_block_self(name, block),
        ExprCall(ref fun, ref args) => contains_self(name, fun) ||
            args.iter().any(|ref a| contains_self(name, a)),
        ExprMethodCall(_, _, ref args) =>
            args.iter().any(|ref a| contains_self(name, a)),
        ExprVec(ref v) | ExprTup(ref v) =>
            v.iter().any(|ref e| contains_self(name, e)),
        ExprIf(ref cond, ref then, ref otherwise) =>
            contains_self(name, cond) || contains_block_self(name, then) ||
            otherwise.as_ref().map_or(false, |ref e| contains_self(name, e)),
        ExprWhile(ref e, ref block, _)  =>
            contains_self(name, e) || contains_block_self(name, block),
        ExprMatch(ref e, ref arms, _) =>
            arms.iter().any(|ref arm| arm.pats.iter().any(|ref pat|
                contains_pat_self(name, pat))) || contains_self(name, e),
        ExprPath(_, ref path) => path_eq_name(name, path),
        _ => false
    }
}

fn contains_block_self(name: Name, block: &Block) -> bool {
    for stmt in &block.stmts {
        match stmt.node {
            StmtDecl(ref decl, _) =>
            if let DeclLocal(ref local) = decl.node {
                //TODO: We don't currently handle the case where the name
                //is shadowed wiithin the block; this means code including this
                //degenerate pattern will get the wrong warning.
                if let Some(ref init) = local.init {
                    if contains_self(name, init) { return true; }
                }
            },
            StmtExpr(ref e, _) | StmtSemi(ref e, _) =>
                if contains_self(name, e) { return true },
            _ => ()
        }
    }
    if let Some(ref e) = block.expr { contains_self(name, e) } else { false }
}

fn contains_pat_self(name: Name, pat: &Pat) -> bool {
    match pat.node {
        PatIdent(_, ref ident, ref inner) => name == ident.node.name ||
            inner.as_ref().map_or(false, |ref p| contains_pat_self(name, p)),
        PatEnum(_, ref opats) => opats.as_ref().map_or(false,
            |pats| pats.iter().any(|p| contains_pat_self(name, p))),
        PatQPath(_, ref path) => path_eq_name(name, path),
        PatStruct(_, ref fieldpats, _) => fieldpats.iter().any(
            |ref fp| contains_pat_self(name, &fp.node.pat)),
        PatTup(ref ps) => ps.iter().any(|ref p| contains_pat_self(name, p)),
        PatBox(ref p) |
        PatRegion(ref p, _) => contains_pat_self(name, p),
        PatRange(ref from, ref until) =>
            contains_self(name, from) || contains_self(name, until),
        PatVec(ref pre, ref opt, ref post) =>
            pre.iter().any(|ref p| contains_pat_self(name, p)) ||
                opt.as_ref().map_or(false, |ref p| contains_pat_self(name, p)) ||
                post.iter().any(|ref p| contains_pat_self(name, p)),
        _ => false,
    }
}
<|endoftext|>


<commit_before>extern crate c_ares_sys;

#[cfg(target_os = "linux")]
use std::os::unix::io::{
    AsRawFd,
    RawFd,
};

/// The platform-specific socket / file descriptor.
pub struct Socket(pub c_ares_sys::ares_socket_t);

#[cfg(target_os = "linux")]
impl AsRawFd for Socket {
    fn as_raw_fd(&self) -> RawFd {
        self.0
    }
}

/// An invalid socket / file descriptor.  Use this to represent 'no action'
/// when calling `process_fd()` on a channel.
pub const SOCKET_BAD: Socket = Socket(c_ares_sys::ARES_SOCKET_BAD);
<commit_msg>Implement AsRawSocket for Socket<commit_after>extern crate c_ares_sys;

#[cfg(target_os = "linux")]
use std::os::unix::io::{
    AsRawFd,
    RawFd,
};

#[cfg(target_os = "windows")]
use std::os::windows::io::{
    AsRawSocket,
    RawSocket,
};

/// The platform-specific socket / file descriptor.
pub struct Socket(pub c_ares_sys::ares_socket_t);

#[cfg(target_os = "linux")]
impl AsRawFd for Socket {
    fn as_raw_fd(&self) -> RawFd {
        self.0
    }
}

#[cfg(target_os = "windows")]
impl AsRawSocket for Socket {
    fn as_raw_socket(&self) -> RawSocket {
        self.0
    }
}

/// An invalid socket / file descriptor.  Use this to represent 'no action'
/// when calling `process_fd()` on a channel.
pub const SOCKET_BAD: Socket = Socket(c_ares_sys::ARES_SOCKET_BAD);
<|endoftext|>


<commit_before>//! Types for square grids.

use graph::{Graph, Node};
use visible_graph::{IndexedSegment, Point, VisibleGraph};

/// A grid of 1✕1 squares, of a given number of rows and columns. A cell's
/// neighbors are those above, below, and to the left and right of it; diagonal
/// connections are not neigbors.
///
/// In graph space, the grid constructed by the call `SquareGrid::new(r, c)`
/// extends from `(0,0)` to `(c, r)`. Node are numbered in row-major order,
/// bottom to top, left to right.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SquareGrid {
    rows: usize,
    cols: usize
}

impl SquareGrid {
    /// Construct a `SquareGrid` with the given number of rows and columns.
    pub fn new(rows: usize, cols: usize) -> SquareGrid {
        SquareGrid { rows, cols }
    }

    /// Return the row and column of `node`.
    fn node_rc(&self, node: Node) -> (usize, usize) {
        assert!(node < self.nodes());
        (node / self.cols, node % self.cols)
    }

    /// Return the `Node` index for the node at the given row and column.
    fn rc_node(&self, row: usize, col: usize) -> Node {
        assert!(row < self.rows);
        assert!(col < self.cols);
        row * self.cols + col
    }
}

impl Graph for SquareGrid {
    fn nodes(&self) -> Node { self.rows * self.cols }

    fn neighbors(&self, node: Node) -> Vec<usize> {
        let mut neighbors = Vec::new();

        let (row, col) = self.node_rc(node);
        if row + 1 < self.rows {
            neighbors.push(self.rc_node(row + 1, col));
        }
        if col + 1 < self.cols {
            neighbors.push(self.rc_node(row, col + 1));
        }
        if row >= 1 {
            neighbors.push(self.rc_node(row - 1, col));
        }
        if col >= 1 {
            neighbors.push(self.rc_node(row, col - 1));
        }

        neighbors
    }
}

#[cfg(test)]
mod square_grid_as_graph {
    use graph::Graph;
    use super::SquareGrid;

    #[test]
    fn nodes() {
        assert_eq!(SquareGrid::new(4, 7).nodes(), 28);
        assert_eq!(SquareGrid::new(0, 100).nodes(), 0);
    }

    #[test]
    fn neighbors() {
        let grid = SquareGrid::new(4,7);

        // Corners.
        assert_same_elements!(grid.neighbors(0), vec![1, 7]);
        assert_same_elements!(grid.neighbors(6), vec![5, 13]);
        assert_same_elements!(grid.neighbors(21), vec![14, 22]);
        assert_same_elements!(grid.neighbors(27), vec![26, 20]);

        // Edges.
        assert_same_elements!(grid.neighbors(4), vec![3, 5, 11]);
        assert_same_elements!(grid.neighbors(7), vec![0, 8, 14]);
        assert_same_elements!(grid.neighbors(20), vec![13, 19, 27]);
        assert_same_elements!(grid.neighbors(23), vec![22, 16, 24]);

        // Interior points.
        assert_same_elements!(grid.neighbors(8), vec![7, 9, 1, 15]);
        assert_same_elements!(grid.neighbors(17), vec![16, 18, 10, 24]);

        let grid = SquareGrid::new(1, 3);
        assert_same_elements!(grid.neighbors(0), vec![1]);
        assert_same_elements!(grid.neighbors(1), vec![0,2]);
        assert_same_elements!(grid.neighbors(2), vec![1]);

        let grid = SquareGrid::new(3, 1);
        assert_same_elements!(grid.neighbors(0), vec![1]);
        assert_same_elements!(grid.neighbors(1), vec![0,2]);
        assert_same_elements!(grid.neighbors(2), vec![1]);

        let grid = SquareGrid::new(1, 1);
        assert_same_elements!(grid.neighbors(0), vec![]);
    }
}

impl VisibleGraph for SquareGrid {
    fn bounds(&self) -> (f32, f32) {
        (self.cols as f32, self.rows as f32)
    }

    fn center(&self, node: Node) -> Point {
        let (row, col) = self.node_rc(node);
        (col as f32 + 0.5, row as f32 + 0.5)
    }

    fn radius(&self, _node: Node) -> f32 { 0.5 }

    fn boundary(&self, node: Node) -> Vec<IndexedSegment> {
        let (rows, cols) = (self.rows, self.cols);

        // The number of endpoints in a single row of endpoints. This is
        // self.cols + 1, since we have to include the outermost boundary.
        let pt_cols = self.cols + 1;

        let (row, col) = self.node_rc(node);

        let mut segments = Vec::new();

        // Index of southwestern corner.
        let sw = row * pt_cols + col;

        // north
        segments.push(IndexedSegment {
            line: sw + pt_cols .. sw + pt_cols + 1,
            neighbor: if row + 1 < rows { Some(node + cols) } else { None }
        });

        // east
        segments.push(IndexedSegment {
            line: sw + pt_cols + 1 .. sw + 1,
            neighbor: if col + 1 < cols { Some(node + 1) } else { None }
        });

        // south
        segments.push(IndexedSegment {
            line: sw + 1 .. sw,
            neighbor: if 0 < row { Some(node - cols) } else { None }
        });

        // west
        segments.push(IndexedSegment {
            line: sw .. sw + pt_cols,
            neighbor: if 0 < col { Some(node - 1) } else { None }
        });

        segments
    }

    fn endpoints(&self) -> Vec<Point> {
        let mut points = Vec::new();
        for r in 0 .. self.rows + 1 {
            for c in 0 .. self.cols + 1 {
                points.push((c as f32, r as f32))
            }
        }
        points
    }

    /// A `SquareGrid` recognizes boundary hits by excluding hits near segment
    /// endpoints and hits near node centers as ambiguous. Those exclusion areas
    /// are squares, centered on the point in question. We let the squares
    /// overlap a bit, leaving rectangles around each boundary line segment to
    /// treat as hits.
    fn boundary_hit(&self, point: &Point) -> Option<(Node, Node)> {
        // Exclude points further than this from the side of a node, or nearer
        // than this to a square corner.
        const TOLERANCE: f32 = 0.2;

        // Return `true` if `val` is no further than `distance` from the nearest integer.
        fn near(val: f32, distance: f32) -> bool {
            (val - val.round()).abs() <= distance
        }

        // Exclude points near corners.
        if near(point.0, TOLERANCE) && near(point.1, TOLERANCE) {
            return None;
        }

        // Recognize points near vertical edges. This test suffices because
        // we've already excluded corners.
        if near(point.0, TOLERANCE) {
            let col = point.0.round() as usize;
            let row = point.1.floor() as usize;

            // Boundary lines on the left and right edges are not boundaries
            // between two nodes.
            if col == 0 || col == self.cols {
                return None;
            }

            return Some((self.rc_node(col, row),
                         self.rc_node(col - 1, row)));
        }

        // Recognize points near horizontal edges. Similarly.
        if near(point.1, TOLERANCE) {
            let col = point.0.floor() as usize;
            let row = point.0.round() as usize;

            // Boundary lines on the bottom and top edges are not boundaries
            // between two nodes.
            if row == 0 || row == self.rows {
                return None;
            }

            return Some((self.rc_node(col, row),
                         self.rc_node(col, row - 1)));
        }

        None
    }
}

#[cfg(test)]
mod square_grid_as_visible_graph {
    use visible_graph::VisibleGraph;
    use super::SquareGrid;

    #[test]
    fn bounds() {
        assert_eq!(SquareGrid::new(4, 7).bounds(), (7.0, 4.0));
    }

    #[test]
    fn center() {
        let grid = SquareGrid::new(4, 7);
        assert_eq!(grid.center(0), (0.5, 0.5));
        assert_eq!(grid.center(1), (1.5, 0.5));
        assert_eq!(grid.center(6), (6.5, 0.5));

        assert_eq!(grid.center(7), (0.5, 1.5));
        assert_eq!(grid.center(9), (2.5, 1.5));

        assert_eq!(grid.center(21), (0.5, 3.5));
        assert_eq!(grid.center(22), (1.5, 3.5));
        assert_eq!(grid.center(27), (6.5, 3.5));
    }

    #[test]
    fn radius() {
        assert_eq!(SquareGrid::new(1, 1).radius(0), 0.5);
    }

    #[test]
    fn endpoints() {
        use test_utils::into_eq_points;

        assert_same_elements!(
            into_eq_points(SquareGrid::new(1, 1).endpoints()),
            into_eq_points(vec![(0.0, 0.0), (0.0, 1.0),
                                (1.0, 0.0), (1.0, 1.0)]));

        assert_same_elements!(
            into_eq_points(SquareGrid::new(3, 2).endpoints()),
            into_eq_points(vec![(0.0, 0.0), (1.0, 0.0), (2.0, 0.0),
                                (0.0, 1.0), (1.0, 1.0), (2.0, 1.0),
                                (0.0, 2.0), (1.0, 2.0), (2.0, 2.0),
                                (0.0, 3.0), (1.0, 3.0), (2.0, 3.0)]));

    }

    #[test]
    fn boundary() {
        use graph::Node;
        use std::ops::Range;
        use test_utils::{into_points, SegmentWithPoints};
        use visible_graph::Point;

        fn swp(start: Point, end: Point, neighbor: Option<Node>) -> SegmentWithPoints
        {
            SegmentWithPoints::new(&Range { start, end }, neighbor)
        }

        let grid = SquareGrid::new(1, 1);
        let endpoints = grid.endpoints();
        assert_same_elements!(
            into_points(&grid.boundary(0), &endpoints),
            vec![swp((0.0, 0.0), (1.0, 0.0), None),
                 swp((1.0, 0.0), (1.0, 1.0), None),
                 swp((1.0, 1.0), (0.0, 1.0), None),
                 swp((0.0, 1.0), (0.0, 0.0), None)]);

        let grid = SquareGrid::new(3, 2);
        let endpoints = grid.endpoints();
        assert_same_elements!(
            into_points(&grid.boundary(0), &endpoints),
            vec![swp((0.0, 0.0), (1.0, 0.0), None),
                 swp((1.0, 0.0), (1.0, 1.0), Some(1)),
                 swp((1.0, 1.0), (0.0, 1.0), Some(2)),
                 swp((0.0, 1.0), (0.0, 0.0), None)]);
        assert_same_elements!(
            into_points(&grid.boundary(3), &endpoints),
            vec![swp((1.0, 1.0), (2.0, 1.0), Some(1)),
                 swp((2.0, 1.0), (2.0, 2.0), None),
                 swp((2.0, 2.0), (1.0, 2.0), Some(5)),
                 swp((1.0, 2.0), (1.0, 1.0), Some(2))]);
    }
}
<commit_msg>SquareGrid::boundary_hit passes tests.<commit_after>//! Types for square grids.

use graph::{Graph, Node};
use visible_graph::{IndexedSegment, Point, VisibleGraph};

/// A grid of 1✕1 squares, of a given number of rows and columns. A cell's
/// neighbors are those above, below, and to the left and right of it; diagonal
/// connections are not neigbors.
///
/// In graph space, the grid constructed by the call `SquareGrid::new(r, c)`
/// extends from `(0,0)` to `(c, r)`. Node are numbered in row-major order,
/// bottom to top, left to right.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SquareGrid {
    rows: usize,
    cols: usize
}

impl SquareGrid {
    /// Construct a `SquareGrid` with the given number of rows and columns.
    pub fn new(rows: usize, cols: usize) -> SquareGrid {
        SquareGrid { rows, cols }
    }

    /// Return the row and column of `node`.
    fn node_rc(&self, node: Node) -> (usize, usize) {
        assert!(node < self.nodes());
        (node / self.cols, node % self.cols)
    }

    /// Return the `Node` index for the node at the given row and column.
    fn rc_node(&self, row: usize, col: usize) -> Node {
        assert!(row < self.rows);
        assert!(col < self.cols);
        row * self.cols + col
    }
}

impl Graph for SquareGrid {
    fn nodes(&self) -> Node { self.rows * self.cols }

    fn neighbors(&self, node: Node) -> Vec<usize> {
        let mut neighbors = Vec::new();

        let (row, col) = self.node_rc(node);
        if row + 1 < self.rows {
            neighbors.push(self.rc_node(row + 1, col));
        }
        if col + 1 < self.cols {
            neighbors.push(self.rc_node(row, col + 1));
        }
        if row >= 1 {
            neighbors.push(self.rc_node(row - 1, col));
        }
        if col >= 1 {
            neighbors.push(self.rc_node(row, col - 1));
        }

        neighbors
    }
}

#[cfg(test)]
mod square_grid_as_graph {
    use graph::Graph;
    use super::SquareGrid;

    #[test]
    fn nodes() {
        assert_eq!(SquareGrid::new(4, 7).nodes(), 28);
        assert_eq!(SquareGrid::new(0, 100).nodes(), 0);
    }

    #[test]
    fn neighbors() {
        let grid = SquareGrid::new(4,7);

        // Corners.
        assert_same_elements!(grid.neighbors(0), vec![1, 7]);
        assert_same_elements!(grid.neighbors(6), vec![5, 13]);
        assert_same_elements!(grid.neighbors(21), vec![14, 22]);
        assert_same_elements!(grid.neighbors(27), vec![26, 20]);

        // Edges.
        assert_same_elements!(grid.neighbors(4), vec![3, 5, 11]);
        assert_same_elements!(grid.neighbors(7), vec![0, 8, 14]);
        assert_same_elements!(grid.neighbors(20), vec![13, 19, 27]);
        assert_same_elements!(grid.neighbors(23), vec![22, 16, 24]);

        // Interior points.
        assert_same_elements!(grid.neighbors(8), vec![7, 9, 1, 15]);
        assert_same_elements!(grid.neighbors(17), vec![16, 18, 10, 24]);

        let grid = SquareGrid::new(1, 3);
        assert_same_elements!(grid.neighbors(0), vec![1]);
        assert_same_elements!(grid.neighbors(1), vec![0,2]);
        assert_same_elements!(grid.neighbors(2), vec![1]);

        let grid = SquareGrid::new(3, 1);
        assert_same_elements!(grid.neighbors(0), vec![1]);
        assert_same_elements!(grid.neighbors(1), vec![0,2]);
        assert_same_elements!(grid.neighbors(2), vec![1]);

        let grid = SquareGrid::new(1, 1);
        assert_same_elements!(grid.neighbors(0), vec![]);
    }
}

impl VisibleGraph for SquareGrid {
    fn bounds(&self) -> (f32, f32) {
        (self.cols as f32, self.rows as f32)
    }

    fn center(&self, node: Node) -> Point {
        let (row, col) = self.node_rc(node);
        (col as f32 + 0.5, row as f32 + 0.5)
    }

    fn radius(&self, _node: Node) -> f32 { 0.5 }

    fn boundary(&self, node: Node) -> Vec<IndexedSegment> {
        let (rows, cols) = (self.rows, self.cols);

        // The number of endpoints in a single row of endpoints. This is
        // self.cols + 1, since we have to include the outermost boundary.
        let pt_cols = self.cols + 1;

        let (row, col) = self.node_rc(node);

        let mut segments = Vec::new();

        // Index of southwestern corner.
        let sw = row * pt_cols + col;

        // north
        segments.push(IndexedSegment {
            line: sw + pt_cols .. sw + pt_cols + 1,
            neighbor: if row + 1 < rows { Some(node + cols) } else { None }
        });

        // east
        segments.push(IndexedSegment {
            line: sw + pt_cols + 1 .. sw + 1,
            neighbor: if col + 1 < cols { Some(node + 1) } else { None }
        });

        // south
        segments.push(IndexedSegment {
            line: sw + 1 .. sw,
            neighbor: if 0 < row { Some(node - cols) } else { None }
        });

        // west
        segments.push(IndexedSegment {
            line: sw .. sw + pt_cols,
            neighbor: if 0 < col { Some(node - 1) } else { None }
        });

        segments
    }

    fn endpoints(&self) -> Vec<Point> {
        let mut points = Vec::new();
        for r in 0 .. self.rows + 1 {
            for c in 0 .. self.cols + 1 {
                points.push((c as f32, r as f32))
            }
        }
        points
    }

    /// A `SquareGrid` recognizes boundary hits by excluding hits near segment
    /// endpoints and hits near node centers as ambiguous. Those exclusion areas
    /// are squares, centered on the point in question. We let the squares
    /// overlap a bit, leaving rectangles around each boundary line segment to
    /// treat as hits.
    fn boundary_hit(&self, point: &Point) -> Option<(Node, Node)> {
        // Exclude points further than this from the side of a node, or nearer
        // than this to a square corner. Clearly, this must be less than 0.5, to
        // avoid ambiguity.
        const TOLERANCE: f32 = 0.2;

        // Exclude points outside the grid altogether, or on the outer edges.
        // This lets us assume that every hit we find is an interior boundary,
        // with another node on the other side.
        let (max_x, max_y) = self.bounds();
        if point.0 < TOLERANCE || point.0 > max_x - TOLERANCE ||
            point.1 < TOLERANCE || point.1 > max_y - TOLERANCE
        {
            return None;
        }

        // Return `true` if `val` is no further than `distance` from the nearest integer.
        fn near(val: f32, distance: f32) -> bool {
            (val - val.round()).abs() <= distance
        }

        // Exclude points near corners.
        if near(point.0, TOLERANCE) && near(point.1, TOLERANCE) {
            return None;
        }

        // Recognize points near vertical edges. We know these points cannot
        // also be near horizontal edges, since we've already excluded corners.
        if near(point.0, TOLERANCE) {
            // Both the round and floor here produce positive numbers, given the
            // exclusions above.
            let col = point.0.round() as usize;
            let row = point.1.floor() as usize;

            return Some((self.rc_node(row, col),
                         self.rc_node(row, col - 1)));
        }

        // Recognize points near horizontal edges. As above, just transposed.
        if near(point.1, TOLERANCE) {
            let col = point.0.floor() as usize;
            let row = point.1.round() as usize;

            return Some((self.rc_node(row, col),
                         self.rc_node(row - 1, col)));
        }

        None
    }
}

#[cfg(test)]
mod square_grid_as_visible_graph {
    use visible_graph::VisibleGraph;
    use super::SquareGrid;

    #[test]
    fn bounds() {
        assert_eq!(SquareGrid::new(4, 7).bounds(), (7.0, 4.0));
    }

    #[test]
    fn center() {
        let grid = SquareGrid::new(4, 7);
        assert_eq!(grid.center(0), (0.5, 0.5));
        assert_eq!(grid.center(1), (1.5, 0.5));
        assert_eq!(grid.center(6), (6.5, 0.5));

        assert_eq!(grid.center(7), (0.5, 1.5));
        assert_eq!(grid.center(9), (2.5, 1.5));

        assert_eq!(grid.center(21), (0.5, 3.5));
        assert_eq!(grid.center(22), (1.5, 3.5));
        assert_eq!(grid.center(27), (6.5, 3.5));
    }

    #[test]
    fn radius() {
        assert_eq!(SquareGrid::new(1, 1).radius(0), 0.5);
    }

    #[test]
    fn endpoints() {
        use test_utils::into_eq_points;

        assert_same_elements!(
            into_eq_points(SquareGrid::new(1, 1).endpoints()),
            into_eq_points(vec![(0.0, 0.0), (0.0, 1.0),
                                (1.0, 0.0), (1.0, 1.0)]));

        assert_same_elements!(
            into_eq_points(SquareGrid::new(3, 2).endpoints()),
            into_eq_points(vec![(0.0, 0.0), (1.0, 0.0), (2.0, 0.0),
                                (0.0, 1.0), (1.0, 1.0), (2.0, 1.0),
                                (0.0, 2.0), (1.0, 2.0), (2.0, 2.0),
                                (0.0, 3.0), (1.0, 3.0), (2.0, 3.0)]));

    }

    #[test]
    fn boundary() {
        use graph::Node;
        use std::ops::Range;
        use test_utils::{into_points, SegmentWithPoints};
        use visible_graph::Point;

        fn swp(start: Point, end: Point, neighbor: Option<Node>) -> SegmentWithPoints
        {
            SegmentWithPoints::new(&Range { start, end }, neighbor)
        }

        let grid = SquareGrid::new(1, 1);
        let endpoints = grid.endpoints();
        assert_same_elements!(
            into_points(&grid.boundary(0), &endpoints),
            vec![swp((0.0, 0.0), (1.0, 0.0), None),
                 swp((1.0, 0.0), (1.0, 1.0), None),
                 swp((1.0, 1.0), (0.0, 1.0), None),
                 swp((0.0, 1.0), (0.0, 0.0), None)]);

        let grid = SquareGrid::new(3, 2);
        let endpoints = grid.endpoints();
        assert_same_elements!(
            into_points(&grid.boundary(0), &endpoints),
            vec![swp((0.0, 0.0), (1.0, 0.0), None),
                 swp((1.0, 0.0), (1.0, 1.0), Some(1)),
                 swp((1.0, 1.0), (0.0, 1.0), Some(2)),
                 swp((0.0, 1.0), (0.0, 0.0), None)]);
        assert_same_elements!(
            into_points(&grid.boundary(3), &endpoints),
            vec![swp((1.0, 1.0), (2.0, 1.0), Some(1)),
                 swp((2.0, 1.0), (2.0, 2.0), None),
                 swp((2.0, 2.0), (1.0, 2.0), Some(5)),
                 swp((1.0, 2.0), (1.0, 1.0), Some(2))]);
    }

    #[test]
    fn boundary_hit() {
        // These tests are not black-box: they know the general algorithm
        // `boundary_hit` implements, and the value of TOLERANCE. But they
        // should mostly be okay with any reasonable hit definition.

        use graph::Node;
        use super::SquareGrid;

        // Make a result from `boundary_hit` easier to compare by putting the
        // nodes in increasing order, if there are any. Terse name because
        // it's local, and we're using it in lots of tests.
        fn s(opt: Option<(Node, Node)>) -> Option<(Node, Node)> {
            opt.map(|(a, b)| if a > b { (b, a) } else { (a, b) })
        }

        let grid = SquareGrid::new(3, 4);

        // Wild.
        assert_eq!(grid.boundary_hit(&(-100.0, -100.0)), None);
        assert_eq!(grid.boundary_hit(&(-100.0, 1.5)),    None);
        assert_eq!(grid.boundary_hit(&(-100.0, 2000.0)), None);

        assert_eq!(grid.boundary_hit(&(2.0, -100.0)),    None);
        assert_eq!(grid.boundary_hit(&(2.0, 2000.0)),    None);

        assert_eq!(grid.boundary_hit(&(2000.0, -100.0)), None);
        assert_eq!(grid.boundary_hit(&(2000.0, 1.5)),    None);
        assert_eq!(grid.boundary_hit(&(2000.0, 2000.0)), None);

        // Nearby outside.
        assert_eq!(grid.boundary_hit(&(2.0, -0.5)), None);
        assert_eq!(grid.boundary_hit(&(4.5,  1.5)), None);
        assert_eq!(grid.boundary_hit(&(2.0,  3.5)), None);
        assert_eq!(grid.boundary_hit(&(-0.5, 1.5)), None);

        // On corners.
        assert_eq!(grid.boundary_hit(&(0.0, 0.0)), None);
        assert_eq!(grid.boundary_hit(&(4.0, 0.0)), None);
        assert_eq!(grid.boundary_hit(&(4.0, 3.0)), None);
        assert_eq!(grid.boundary_hit(&(0.0, 3.0)), None);

        // On sides.
        assert_eq!(grid.boundary_hit(&(3.5, 0.0)), None);
        assert_eq!(grid.boundary_hit(&(4.0, 2.3)), None);
        assert_eq!(grid.boundary_hit(&(1.7, 3.0)), None);
        assert_eq!(grid.boundary_hit(&(0.0, 1.2)), None);

        // Interior horizontal.
        assert_eq!(s(grid.boundary_hit(&(0.5, 1.1))), Some((0, 4)));
        assert_eq!(s(grid.boundary_hit(&(3.6, 1.9))), Some((7, 11)));

        // Interior vertical.
        assert_eq!(s(grid.boundary_hit(&(2.1, 1.3))), Some((5, 6)));
        assert_eq!(s(grid.boundary_hit(&(3.0, 2.7))), Some((10, 11)));
    }
}
<|endoftext|>