<commit_before>use chrono::UTC;
use chrono::datetime::DateTime;

use params::*;

use rs_es::Client;
use rs_es::query::{Filter, Query};
use rs_es::units::{JsonVal, DurationUnit};
use rs_es::units::Duration as ESDuration;
use rs_es::operations::search::{Sort, SortField, Order};
use rs_es::operations::index::IndexResult;
use rs_es::operations::bulk::Action;
use rs_es::error::EsError;

extern crate searchspot;
use searchspot::terms::VectorOfTerms;

#[derive(RustcEncodable, RustcDecodable, Debug)]
pub struct Talent {
  pub id:                 u32,
  pub accepted:           bool,
  pub work_roles:         Vec<String>,
  pub work_languages:     Vec<String>,
  pub work_experience:    String,
  pub work_locations:     Vec<String>,
  pub work_authorization: String,
  pub company_ids:        Vec<u32>,
  pub batch_starts_at:    i64,
  pub batch_ends_at:      i64,
  pub added_to_batch_at:  i64,
  pub weight:             i32,
  pub blocked_companies:  Vec<u32>
}

impl Talent {
  /// Populate the ElasticSearch index with `self`.
  pub fn index(&self, mut es: &mut Client, index: &str) -> Result<IndexResult, EsError> {
    es.index(index, "talent")
      .with_doc(&self)
      .with_id(&*format!("{}", self.id))
      .send()
  }

  #[allow(dead_code)]
  pub fn delete_all(mut es: &mut Client, index: &str) {
    let mut scan = match es.search_query()
                           .with_indexes(&[&index])
                           .with_query(&Query::build_match_all().build())
                           .scan(ESDuration::new(1, DurationUnit::Minute)) {
                              Ok(scan) => scan,
                              Err(_)   => return
                           };

    loop {
      let     page = scan.scroll(&mut es).unwrap();
      let mut hits = page.hits.hits;

      if hits.len() == 0 {
        break;
      }

      let actions = hits.drain(..)
                        .map(|hit| {
                           Action::delete(hit.id).with_index(&*index)
                                                 .with_doc_type(hit.doc_type)
                        })
                        .collect::<Vec<Action>>();
      es.bulk(&actions)
        .send()
        .unwrap();
    }

    scan.close(&mut es)
        .unwrap();
  }

  /// Query ElasticSearch on given `indexes` and `params` and return the IDs of
  /// the found talents.
  pub fn search(mut es: &mut Client, default_index: &str, params: &Map) -> Vec<u32> {
    let epoch = match params.find(&["epoch"]) {
      Some(&Value::I64(epoch)) => epoch,
      _ => DateTime::timestamp(&UTC::now())
    };

    let index: Vec<&str> = match params.find(&["index"]) {
      Some(&Value::String(ref index)) => vec![&index[..]],
      _ => vec![default_index]
    };

    let result = es.search_query()
                   .with_indexes(&*index)
                   .with_query(&Talent::search_filters(params, epoch))
                   .with_sort(&Talent::sorting_criteria())
                   .send();

    match result {
      Ok(result) => {
        let mut results = result.hits.hits.into_iter()
                                          .map(|hit| hit.source.unwrap()["id"]
                                                               .as_u64()
                                                               .unwrap() as u32)
                                          .collect::<Vec<u32>>();
        results.dedup();
        results
      },
      Err(err) => {
        println!("{:?}", err);
        vec![]
      }
    }
  }

  /// Return a `Vec<Filter>` with visibility criteria for the talents.
  /// The `epoch` must be given as `I64` (UNIX time in seconds) and is
  /// the range in which batches are searched.
  /// If `presented_talents` is provided, talents who match the IDs
  /// contained there skip the standard visibility criteria.
  ///
  /// Basically, the talents must be accepted into the platform and must be
  /// inside a living batch to match the visibility criteria.
  fn visibility_filters(epoch: i64, presented_talents: Vec<i32>) -> Vec<Filter> {
    let visibility_rules = Filter::build_bool()
                                  .with_must(
                                    vec![
                                      Filter::build_term("accepted", true)
                                             .build(),
                                      Filter::build_range("batch_starts_at")
                                             .with_lte(JsonVal::from(epoch))
                                             .with_format("epoch_second")
                                             .build(),
                                      Filter::build_range("batch_ends_at")
                                             .with_gte(JsonVal::from(epoch))
                                             .with_format("epoch_second")
                                             .build()
                                    ])
                                  .build();

    if presented_talents.len() > 0 { // preferred over !_.is_empty()
      let presented_talents_filters = Filter::build_bool()
                                             .with_must(
                                               vec![
                                                 <Filter as VectorOfTerms<i32>>::build_terms(
                                                   "ids", &presented_talents)
                                               ].into_iter()
                                                .flat_map(|x| x)
                                                .collect::<Vec<Filter>>())
                                             .build();
      vec![
        Filter::build_bool()
               .with_should(vec![visibility_rules, presented_talents_filters])
               .build()
      ]
    }
    else {
      vec![visibility_rules]
    }
  }

  /// Given parameters inside the query string mapped inside a `Map`,
  /// and the `epoch` (defined as UNIX time in seconds) for batches,
  /// return a `Query` for ElasticSearch.
  ///
  /// `VectorOfTerms` are ORred, while `Filter`s are ANDed.
  /// I.e.: given ["Fullstack", "DevOps"] as `work_roles`, found talents
  /// will present at least one of these roles), but both `work_roles`
  /// and `work_languages`, if provided, must not be empty.
  fn search_filters(params: &Map, epoch: i64) -> Query {
    let company_id = i32_vec_from_params!(params, "company_id");

    Query::build_filtered(Filter::build_bool()
                                 .with_must(
                                   vec![
                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_roles", &vec_from_params!(params, "work_roles")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_languages", &vec_from_params!(params, "work_languages")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_experience", &string_vec_from_params!(params, "work_experience")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                      "work_authorization", &string_vec_from_params!(params, "work_authorization")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_locations", &vec_from_params!(params, "work_locations")),

                                     Talent::visibility_filters(epoch,
                                       i32_vec_from_params!(params, "presented_talents"))
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .with_must_not(
                                   vec![
                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "company_ids", &company_id),

                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "blocked_companies", &company_id)
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .build())
          .build()
  }

  /// Return a `Sort` that makes values be sorted for given fields, descendently.
  fn sorting_criteria() -> Sort {
    Sort::new(
      vec![
        SortField::new("batch_starts_at",   Some(Order::Desc)).build(),
        SortField::new("weight",            Some(Order::Desc)).build(),
        SortField::new("added_to_batch_at", Some(Order::Desc)).build()
      ])
  }
}

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

  use rs_es::Client;

  use params::*;

  extern crate searchspot;
  use searchspot::config::*;

  use resources::user::Talent;

  const CONFIG_FILE: &'static str = "examples/tests.toml";

  lazy_static! {
    static ref config: Config = Config::from_file(CONFIG_FILE.to_owned());
  }

  pub fn make_client() -> Client {
    Client::new(&*config.es.host, config.es.port)
  }

  macro_rules! epoch_from_year {
    ($year:expr) => {
      UTC.datetime_from_str(&format!("{}-01-01 12:00:00", $year),
        "%Y-%m-%d %H:%M:%S").unwrap().timestamp()
    }
  }

  pub fn populate_es(mut client: &mut Client) {
    vec![
      Talent {
        id:                 1,
        accepted:           true,
        work_roles:         vec![],
        work_languages:     vec![],
        work_experience:    "1..2".to_owned(),
        work_locations:     vec!["Berlin".to_owned()],
        work_authorization: "yes".to_owned(),
        company_ids:        vec![],
        batch_starts_at:    epoch_from_year!("2006"),
        batch_ends_at:      epoch_from_year!("2020"),
        added_to_batch_at:  epoch_from_year!("2006"),
        weight:             -5,
        blocked_companies:  vec![]
      },

      Talent {
        id:                 2,
        accepted:           true,
        work_roles:         vec![],
        work_languages:     vec![],
        work_experience:    "1..2".to_owned(),
        work_locations:     vec!["Berlin".to_owned()],
        work_authorization: "yes".to_owned(),
        company_ids:        vec![],
        batch_starts_at:    epoch_from_year!("2006"),
        batch_ends_at:      epoch_from_year!("2020"),
        added_to_batch_at:  epoch_from_year!("2006"),
        weight:             6,
        blocked_companies:  vec![]
      },

      Talent {
        id:                 3,
        accepted:           false,
        work_roles:         vec![],
        work_languages:     vec![],
        work_experience:    "1..2".to_owned(),
        work_locations:     vec!["Berlin".to_owned()],
        work_authorization: "yes".to_owned(),
        company_ids:        vec![],
        batch_starts_at:    epoch_from_year!("2007"),
        batch_ends_at:      epoch_from_year!("2020"),
        added_to_batch_at:  epoch_from_year!("2011"),
        weight:             6,
        blocked_companies:  vec![]
      },

      Talent {
        id:                 4,
        accepted:           true,
        work_roles:         vec!["Fullstack".to_owned(), "DevOps".to_owned()],
        work_languages:     vec![],
        work_experience:    "1..2".to_owned(),
        work_locations:     vec!["Berlin".to_owned()],
        work_authorization:  "yes".to_owned(),
        company_ids:        vec![6],
        batch_starts_at:    epoch_from_year!("2008"),
        batch_ends_at:      epoch_from_year!("2020"),
        added_to_batch_at:  epoch_from_year!("2011"),
        weight:             0,
        blocked_companies:  vec![]
      }
    ].iter()
     .all(|user| user.index(&mut client, &config.es.index)
                     .is_ok());
  }

  #[test]
  fn test_search() {
    let mut client = make_client();
    Talent::delete_all(&mut client, &config.es.index);
    populate_es(&mut client);

    // no parameters are given
    {
      let results = Talent::search(&mut client, &*config.es.index, &Map::new());
      assert_eq!(vec![4, 2, 1], results);
    }

    // a non existing index is given
    {
      let mut map = Map::new();
      map.assign("index", Value::String("lololol".to_owned())).unwrap();

      let results = Talent::search(&mut client, &*config.es.index, &map);
      assert!(results.is_empty());
    }

    // a date that doesn't match given indexes is given
    {
      let mut map = Map::new();
      map.assign("epoch", Value::I64(epoch_from_year!("2040"))).unwrap();

      let results = Talent::search(&mut client, &*config.es.index, &map);
      assert!(results.is_empty());
    }

    // TODO: map field type properly (see benashford/rs-es#11)
    // // filtering for valid work roles
    // // {
    // //   let mut map = Map::new();
    // //   map.assign("work_roles[]", Value::String("Fullstack".to_owned())).unwrap();
    // //
    // //   let results = Talent::search(&mut client, &*config.es.index, &map);
    // //   assert_eq!(vec![4], results);
    // // }

    // filtering for given company_id
    {
      let mut map = Map::new();
      map.assign("company_id", Value::String("6".into())).unwrap();

      let results = Talent::search(&mut client, &*config.es.index, &map);
      assert_eq!(vec![2, 1], results);
    }
  }
}
<commit_msg>resources/user: fix tests on circleci<commit_after>use chrono::UTC;
use chrono::datetime::DateTime;

use params::*;

use rs_es::Client;
use rs_es::query::{Filter, Query};
use rs_es::units::{JsonVal, DurationUnit};
use rs_es::units::Duration as ESDuration;
use rs_es::operations::search::{Sort, SortField, Order};
use rs_es::operations::index::IndexResult;
use rs_es::operations::bulk::Action;
use rs_es::error::EsError;

extern crate searchspot;
use searchspot::terms::VectorOfTerms;

#[derive(RustcEncodable, RustcDecodable, Debug)]
pub struct Talent {
  pub id:                 u32,
  pub accepted:           bool,
  pub work_roles:         Vec<String>,
  pub work_languages:     Vec<String>,
  pub work_experience:    String,
  pub work_locations:     Vec<String>,
  pub work_authorization: String,
  pub company_ids:        Vec<u32>,
  pub batch_starts_at:    i64,
  pub batch_ends_at:      i64,
  pub added_to_batch_at:  i64,
  pub weight:             i32,
  pub blocked_companies:  Vec<u32>
}

impl Talent {
  /// Populate the ElasticSearch index with `self`.
  pub fn index(&self, mut es: &mut Client, index: &str) -> Result<IndexResult, EsError> {
    es.index(index, "talent")
      .with_doc(&self)
      .with_id(&*format!("{}", self.id))
      .send()
  }

  #[allow(dead_code)]
  pub fn delete_all(mut es: &mut Client, index: &str) {
    let mut scan = match es.search_query()
                           .with_indexes(&[&index])
                           .with_query(&Query::build_match_all().build())
                           .scan(ESDuration::new(1, DurationUnit::Minute)) {
                              Ok(scan) => scan,
                              Err(_)   => return
                           };

    loop {
      let     page = scan.scroll(&mut es).unwrap();
      let mut hits = page.hits.hits;

      if hits.len() == 0 {
        break;
      }

      let actions = hits.drain(..)
                        .map(|hit| {
                           Action::delete(hit.id).with_index(&*index)
                                                 .with_doc_type(hit.doc_type)
                        })
                        .collect::<Vec<Action>>();
      es.bulk(&actions)
        .send()
        .unwrap();
    }

    scan.close(&mut es)
        .unwrap();
  }

  /// Query ElasticSearch on given `indexes` and `params` and return the IDs of
  /// the found talents.
  pub fn search(mut es: &mut Client, default_index: &str, params: &Map) -> Vec<u32> {
    let epoch = match params.find(&["epoch"]) {
      Some(&Value::I64(epoch)) => epoch,
      _ => DateTime::timestamp(&UTC::now())
    };

    let index: Vec<&str> = match params.find(&["index"]) {
      Some(&Value::String(ref index)) => vec![&index[..]],
      _ => vec![default_index]
    };

    let result = es.search_query()
                   .with_indexes(&*index)
                   .with_query(&Talent::search_filters(params, epoch))
                   .with_sort(&Talent::sorting_criteria())
                   .send();

    match result {
      Ok(result) => {
        let mut results = result.hits.hits.into_iter()
                                          .map(|hit| hit.source.unwrap()["id"]
                                                               .as_u64()
                                                               .unwrap() as u32)
                                          .collect::<Vec<u32>>();
        results.dedup();
        results
      },
      Err(err) => {
        println!("{:?}", err);
        vec![]
      }
    }
  }

  /// Return a `Vec<Filter>` with visibility criteria for the talents.
  /// The `epoch` must be given as `I64` (UNIX time in seconds) and is
  /// the range in which batches are searched.
  /// If `presented_talents` is provided, talents who match the IDs
  /// contained there skip the standard visibility criteria.
  ///
  /// Basically, the talents must be accepted into the platform and must be
  /// inside a living batch to match the visibility criteria.
  fn visibility_filters(epoch: i64, presented_talents: Vec<i32>) -> Vec<Filter> {
    let visibility_rules = Filter::build_bool()
                                  .with_must(
                                    vec![
                                      Filter::build_term("accepted", true)
                                             .build(),
                                      Filter::build_range("batch_starts_at")
                                             .with_lte(JsonVal::from(epoch))
                                             .with_format("epoch_second")
                                             .build(),
                                      Filter::build_range("batch_ends_at")
                                             .with_gte(JsonVal::from(epoch))
                                             .with_format("epoch_second")
                                             .build()
                                    ])
                                  .build();

    if presented_talents.len() > 0 { // preferred over !_.is_empty()
      let presented_talents_filters = Filter::build_bool()
                                             .with_must(
                                               vec![
                                                 <Filter as VectorOfTerms<i32>>::build_terms(
                                                   "ids", &presented_talents)
                                               ].into_iter()
                                                .flat_map(|x| x)
                                                .collect::<Vec<Filter>>())
                                             .build();
      vec![
        Filter::build_bool()
               .with_should(vec![visibility_rules, presented_talents_filters])
               .build()
      ]
    }
    else {
      vec![visibility_rules]
    }
  }

  /// Given parameters inside the query string mapped inside a `Map`,
  /// and the `epoch` (defined as UNIX time in seconds) for batches,
  /// return a `Query` for ElasticSearch.
  ///
  /// `VectorOfTerms` are ORred, while `Filter`s are ANDed.
  /// I.e.: given ["Fullstack", "DevOps"] as `work_roles`, found talents
  /// will present at least one of these roles), but both `work_roles`
  /// and `work_languages`, if provided, must not be empty.
  fn search_filters(params: &Map, epoch: i64) -> Query {
    let company_id = i32_vec_from_params!(params, "company_id");

    Query::build_filtered(Filter::build_bool()
                                 .with_must(
                                   vec![
                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_roles", &vec_from_params!(params, "work_roles")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_languages", &vec_from_params!(params, "work_languages")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_experience", &string_vec_from_params!(params, "work_experience")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                      "work_authorization", &string_vec_from_params!(params, "work_authorization")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_locations", &vec_from_params!(params, "work_locations")),

                                     Talent::visibility_filters(epoch,
                                       i32_vec_from_params!(params, "presented_talents"))
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .with_must_not(
                                   vec![
                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "company_ids", &company_id),

                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "blocked_companies", &company_id)
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .build())
          .build()
  }

  /// Return a `Sort` that makes values be sorted for given fields, descendently.
  fn sorting_criteria() -> Sort {
    Sort::new(
      vec![
        SortField::new("batch_starts_at",   Some(Order::Desc)).build(),
        SortField::new("weight",            Some(Order::Desc)).build(),
        SortField::new("added_to_batch_at", Some(Order::Desc)).build()
      ])
  }
}

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

  use rs_es::Client;

  use params::*;

  extern crate searchspot;
  use searchspot::config::*;

  use resources::user::Talent;

  use std::time::Duration as TimeDuration;
  use std::thread::sleep;

  const CONFIG_FILE: &'static str = "examples/tests.toml";

  lazy_static! {
    static ref config: Config = Config::from_file(CONFIG_FILE.to_owned());
  }

  pub fn make_client() -> Client {
    Client::new(&*config.es.host, config.es.port)
  }

  macro_rules! epoch_from_year {
    ($year:expr) => {
      UTC.datetime_from_str(&format!("{}-01-01 12:00:00", $year),
        "%Y-%m-%d %H:%M:%S").unwrap().timestamp()
    }
  }

  pub fn populate_es(mut client: &mut Client) {
    vec![
      Talent {
        id:                 1,
        accepted:           true,
        work_roles:         vec![],
        work_languages:     vec![],
        work_experience:    "1..2".to_owned(),
        work_locations:     vec!["Berlin".to_owned()],
        work_authorization: "yes".to_owned(),
        company_ids:        vec![],
        batch_starts_at:    epoch_from_year!("2006"),
        batch_ends_at:      epoch_from_year!("2020"),
        added_to_batch_at:  epoch_from_year!("2006"),
        weight:             -5,
        blocked_companies:  vec![]
      },

      Talent {
        id:                 2,
        accepted:           true,
        work_roles:         vec![],
        work_languages:     vec![],
        work_experience:    "1..2".to_owned(),
        work_locations:     vec!["Berlin".to_owned()],
        work_authorization: "yes".to_owned(),
        company_ids:        vec![],
        batch_starts_at:    epoch_from_year!("2006"),
        batch_ends_at:      epoch_from_year!("2020"),
        added_to_batch_at:  epoch_from_year!("2006"),
        weight:             6,
        blocked_companies:  vec![]
      },

      Talent {
        id:                 3,
        accepted:           false,
        work_roles:         vec![],
        work_languages:     vec![],
        work_experience:    "1..2".to_owned(),
        work_locations:     vec!["Berlin".to_owned()],
        work_authorization: "yes".to_owned(),
        company_ids:        vec![],
        batch_starts_at:    epoch_from_year!("2007"),
        batch_ends_at:      epoch_from_year!("2020"),
        added_to_batch_at:  epoch_from_year!("2011"),
        weight:             6,
        blocked_companies:  vec![]
      },

      Talent {
        id:                 4,
        accepted:           true,
        work_roles:         vec!["Fullstack".to_owned(), "DevOps".to_owned()],
        work_languages:     vec![],
        work_experience:    "1..2".to_owned(),
        work_locations:     vec!["Berlin".to_owned()],
        work_authorization:  "yes".to_owned(),
        company_ids:        vec![6],
        batch_starts_at:    epoch_from_year!("2008"),
        batch_ends_at:      epoch_from_year!("2020"),
        added_to_batch_at:  epoch_from_year!("2011"),
        weight:             0,
        blocked_companies:  vec![]
      }
    ].iter()
     .all(|user| user.index(&mut client, &config.es.index)
                     .is_ok());

    sleep(TimeDuration::from_millis(1000));
  }

  #[test]
  fn test_search() {
    let mut client = make_client();
    Talent::delete_all(&mut client, &config.es.index);
    populate_es(&mut client);

    // no parameters are given
    {
      let results = Talent::search(&mut client, &*config.es.index, &Map::new());
      assert_eq!(vec![4, 2, 1], results);
    }

    // a non existing index is given
    {
      let mut map = Map::new();
      map.assign("index", Value::String("lololol".to_owned())).unwrap();

      let results = Talent::search(&mut client, &*config.es.index, &map);
      assert!(results.is_empty());
    }

    // a date that doesn't match given indexes is given
    {
      let mut map = Map::new();
      map.assign("epoch", Value::I64(epoch_from_year!("2040"))).unwrap();

      let results = Talent::search(&mut client, &*config.es.index, &map);
      assert!(results.is_empty());
    }

    // TODO: map field type properly (see benashford/rs-es#11)
    // // filtering for valid work roles
    // // {
    // //   let mut map = Map::new();
    // //   map.assign("work_roles[]", Value::String("Fullstack".to_owned())).unwrap();
    // //
    // //   let results = Talent::search(&mut client, &*config.es.index, &map);
    // //   assert_eq!(vec![4], results);
    // // }

    // filtering for given company_id
    {
      let mut map = Map::new();
      map.assign("company_id", Value::String("6".into())).unwrap();

      let results = Talent::search(&mut client, &*config.es.index, &map);
      assert_eq!(vec![2, 1], results);
    }
  }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::ops::Deref;
use regex::Regex;

pub struct Matcher {
    pub path: Cow<'static, str>,
    pub regex: Regex
}

impl Matcher {
    pub fn new<P: Into<Cow<'static, str>>>(path: P, regex: Regex) -> Matcher {
        Matcher {
            path: path.into(),
            regex: regex
        }
    }

    pub fn path(&self) -> &str {
        &self.path
    }
}

impl Deref for Matcher {
    type Target = Regex;

    fn deref(&self) -> &Regex {
        &self.regex
    }
}
<commit_msg>refactor(matcher): keep internals private for now<commit_after>use std::borrow::Cow;
use std::ops::Deref;
use regex::Regex;

pub struct Matcher {
    path: Cow<'static, str>,
    regex: Regex
}

impl Matcher {
    pub fn new<P: Into<Cow<'static, str>>>(path: P, regex: Regex) -> Matcher {
        Matcher {
            path: path.into(),
            regex: regex
        }
    }

    pub fn path(&self) -> &str {
        &self.path
    }
}

impl Deref for Matcher {
    type Target = Regex;

    fn deref(&self) -> &Regex {
        &self.regex
    }
}
<|endoftext|>


<commit_before>use std::ops::{AddAssign, MulAssign};

// For implementing base10_digits() accessor on LitInt.
pub struct BigInt {
    digits: Vec<u8>,
}

impl BigInt {
    pub fn new() -> Self {
        BigInt {
            digits: Vec::new(),
        }
    }

    pub fn to_string(&self) -> String {
        unimplemented!()
    }

    fn reserve_two_digits(&mut self) {
        let len = self.digits.len();
        let desired = len
            + !self.digits.ends_with(&[0, 0]) as usize
            + !self.digits.ends_with(&[0]) as usize;
        self.digits.resize(desired, 0);
    }
}

impl AddAssign<u8> for BigInt {
    // Assumes increment <16.
    fn add_assign(&mut self, mut increment: u8) {
        self.reserve_two_digits();

        let mut i = 0;
        while increment > 0 {
            let sum = self.digits[i] + increment;
            self.digits[i] = sum % 10;
            increment = sum / 10;
            i += 1;
        }
    }
}

impl MulAssign<u8> for BigInt {
    fn mul_assign(&mut self, base: u8) {
        let _ = base;
        unimplemented!()
    }
}
<commit_msg>Implement bigint MulAssign<commit_after>use std::ops::{AddAssign, MulAssign};

// For implementing base10_digits() accessor on LitInt.
pub struct BigInt {
    digits: Vec<u8>,
}

impl BigInt {
    pub fn new() -> Self {
        BigInt {
            digits: Vec::new(),
        }
    }

    pub fn to_string(&self) -> String {
        unimplemented!()
    }

    fn reserve_two_digits(&mut self) {
        let len = self.digits.len();
        let desired = len
            + !self.digits.ends_with(&[0, 0]) as usize
            + !self.digits.ends_with(&[0]) as usize;
        self.digits.resize(desired, 0);
    }
}

impl AddAssign<u8> for BigInt {
    // Assumes increment <16.
    fn add_assign(&mut self, mut increment: u8) {
        self.reserve_two_digits();

        let mut i = 0;
        while increment > 0 {
            let sum = self.digits[i] + increment;
            self.digits[i] = sum % 10;
            increment = sum / 10;
            i += 1;
        }
    }
}

impl MulAssign<u8> for BigInt {
    // Assumes base <=16.
    fn mul_assign(&mut self, base: u8) {
        self.reserve_two_digits();

        let mut carry = 0;
        for digit in &mut self.digits {
            let prod = *digit * base + carry;
            *digit = prod % 10;
            carry = prod / 10;
        }
    }
}
<|endoftext|>


<commit_before>use std::net::TcpStream;
use std::sync::mpsc;

use metrics::MetricsRecorder;
use metrics::Timer;
use options::MemcacheOptions;
use protocol::cmd::Resp;
use tcp_transport::TcpTransport;

use super::CmdSender;
use super::MetricsSender;
use super::RespReceiver;
use super::RespSender;
use super::TransportId;

pub struct TransportTask {
    id: TransportId,
    cmd_tx: CmdSender,
    met_tx: MetricsSender,
    options: MemcacheOptions,
}

impl TransportTask {
    pub fn new(id: TransportId,
               cmd_tx: CmdSender,
               met_tx: MetricsSender,
               options: MemcacheOptions)
               -> TransportTask {
        TransportTask {
            id: id,
            cmd_tx: cmd_tx,
            met_tx: met_tx,
            options: options,
        }
    }

    pub fn run(&self, stream: TcpStream) {
        let mut rec = MetricsRecorder::new(self.met_tx.clone(),
                                           self.options.get_metrics_enabled());

        let mut transport = TcpTransport::new(stream);
        let (resp_tx, resp_rx): (RespSender, RespReceiver) = mpsc::channel();

        loop {
            // Time the whole loop
            rec.start_timer("TransportTask:loop");

            // println!("Ready to read command...");
            let rv = {
                let _t = Timer::new(&mut rec, "TransportTask:read_cmd");
                transport.read_cmd()
            };

            // If we couldn't parse the command return an error
            if !rv.is_ok() {
                println!("Failed to read command, returning error (client disconncted?)");
                let _ = transport.write_resp(&Resp::Error);
                return; // Here we just drop the connection
            }

            // Send the command to the driver
            let cmd = rv.unwrap();
            let resp_tx_clone = resp_tx.clone();
            let stats = transport.get_stats_clone();
            {
                let _t = Timer::new(&mut rec, "TransportTask:send_cmd");
                self.cmd_tx
                    .send((self.id, resp_tx_clone, cmd, stats))
                    .unwrap();
            }

            // Obtain a response
            let resp = {
                let _t = Timer::new(&mut rec, "TransportTask:recv_resp");
                resp_rx.recv().unwrap()
            };

            // Return a response
            // println!("Returning response: {:?}", &resp);
            let rv = {
                let _t = Timer::new(&mut rec, "TransportTask:write_resp");
                transport.write_resp(&resp)
            };
            if !rv.is_ok() {
                println!("Failed to write response :(");
            }

            // Stop timing the loop
            rec.stop_timer("TransportTask:loop");

            // Now flush metrics outside the request path
            rec.flush_metrics();
        }
    }
}
<commit_msg>fix typo<commit_after>use std::net::TcpStream;
use std::sync::mpsc;

use metrics::MetricsRecorder;
use metrics::Timer;
use options::MemcacheOptions;
use protocol::cmd::Resp;
use tcp_transport::TcpTransport;

use super::CmdSender;
use super::MetricsSender;
use super::RespReceiver;
use super::RespSender;
use super::TransportId;

pub struct TransportTask {
    id: TransportId,
    cmd_tx: CmdSender,
    met_tx: MetricsSender,
    options: MemcacheOptions,
}

impl TransportTask {
    pub fn new(id: TransportId,
               cmd_tx: CmdSender,
               met_tx: MetricsSender,
               options: MemcacheOptions)
               -> TransportTask {
        TransportTask {
            id: id,
            cmd_tx: cmd_tx,
            met_tx: met_tx,
            options: options,
        }
    }

    pub fn run(&self, stream: TcpStream) {
        let mut rec = MetricsRecorder::new(self.met_tx.clone(),
                                           self.options.get_metrics_enabled());

        let mut transport = TcpTransport::new(stream);
        let (resp_tx, resp_rx): (RespSender, RespReceiver) = mpsc::channel();

        loop {
            // Time the whole loop
            rec.start_timer("TransportTask:loop");

            // println!("Ready to read command...");
            let rv = {
                let _t = Timer::new(&mut rec, "TransportTask:read_cmd");
                transport.read_cmd()
            };

            // If we couldn't parse the command return an error
            if !rv.is_ok() {
                println!("Failed to read command, returning error (client disconnected?)");
                let _ = transport.write_resp(&Resp::Error);
                return; // Here we just drop the connection
            }

            // Send the command to the driver
            let cmd = rv.unwrap();
            let resp_tx_clone = resp_tx.clone();
            let stats = transport.get_stats_clone();
            {
                let _t = Timer::new(&mut rec, "TransportTask:send_cmd");
                self.cmd_tx
                    .send((self.id, resp_tx_clone, cmd, stats))
                    .unwrap();
            }

            // Obtain a response
            let resp = {
                let _t = Timer::new(&mut rec, "TransportTask:recv_resp");
                resp_rx.recv().unwrap()
            };

            // Return a response
            // println!("Returning response: {:?}", &resp);
            let rv = {
                let _t = Timer::new(&mut rec, "TransportTask:write_resp");
                transport.write_resp(&resp)
            };
            if !rv.is_ok() {
                println!("Failed to write response :(");
            }

            // Stop timing the loop
            rec.stop_timer("TransportTask:loop");

            // Now flush metrics outside the request path
            rec.flush_metrics();
        }
    }
}
<|endoftext|>


<commit_before>#![cfg(windows)]

extern crate winapi;
extern crate kernel32;

use self::kernel32::*;
use self::winapi::*;
use std::ptr;
use std::iter::repeat;
use std::cmp::min;
use screen::Key;
use screen::Key::*;

use std::thread;
use std::sync::mpsc::Receiver;
use std::sync::mpsc;
use ::NEWLINE;

macro_rules! win32 {
    ($funcall:expr) => (
        if unsafe { $funcall } == 0 {
            panic!("Win32 call failed");
        }
    );
}

pub struct Screen {
    visible_choices: u16,
    start_line: u16,
    original_console_mode: DWORD,
    original_colors: WORD,
    input: Receiver<INPUT_RECORD>,
    conout: HANDLE,
    default_cursor_info: CONSOLE_CURSOR_INFO,
    shifted: bool,
}

impl Screen {
    pub fn open_screen(desired_rows: u16) -> Screen {
        let mut orig_mode;
        let conin: HANDLE;
        let conout: HANDLE;
        unsafe {
            const OPEN_EXISTING: DWORD = 3;
            let rw_access = GENERIC_READ | GENERIC_WRITE;
            conin = CreateFileA("CONIN$\0".as_ptr() as *const i8, rw_access, FILE_SHARE_READ, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut());
            conout = CreateFileA("CONOUT$\0".as_ptr() as *const i8, rw_access, FILE_SHARE_READ, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut());
            orig_mode = ::std::mem::uninitialized();
        }

        if conin == INVALID_HANDLE_VALUE || conout == INVALID_HANDLE_VALUE {
            panic!("Unable to open console");
        }

        let (_, rows) = Screen::winsize(conout).unwrap();

        win32!(GetConsoleMode(conin, &mut orig_mode));
        let new_mode = orig_mode & !(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
        win32!(SetConsoleMode(conin, new_mode));
        let mut default_cursor_info = CONSOLE_CURSOR_INFO { dwSize: 100, bVisible: TRUE };
        win32!(GetConsoleCursorInfo(conout, &mut default_cursor_info as PCONSOLE_CURSOR_INFO));

        let rx = Screen::spawn_input_thread(conin as usize);
        let initial_pos = Screen::get_cursor_pos(conout);
        let visible_choices = min(desired_rows, rows - 1);
        let start_line = get_start_line(rows, visible_choices, initial_pos);
        let original_colors = Screen::get_original_colors(conout);
        let (column, _) = initial_pos;
        if column > 0 {
            Self::write_to(conout, NEWLINE);
        }
        for _ in 0..visible_choices {
            Self::write_to(conout, NEWLINE);
        }
        Screen {
            visible_choices: visible_choices,
            start_line: start_line + Self::get_buffer_offset(conout),
            original_console_mode: orig_mode,
            original_colors: original_colors,
            input: rx,
            conout: conout,
            default_cursor_info: default_cursor_info,
            shifted: false,
        }
    }

    pub fn visible_choices(&self) -> u16 {
        self.visible_choices
    }

    pub fn width(&self) -> u16 {
        let (cols, _) = Screen::winsize(self.conout).unwrap();
        cols
    }

    // We have to take the conin handle as a usize instead of a *mut c_void in order to avoid a
    // lecture from the compiler about how the latter type cannot be safely sent between threads.
    // I'm not sure if a better solution exists at this time.
    fn spawn_input_thread(conin: usize) -> Receiver<INPUT_RECORD> {
        let (tx, rx) = mpsc::channel();

        thread::spawn(move || {
            loop {
                let conin = conin as *mut c_void;
                let mut input_record = INPUT_RECORD{EventType: 0 as WORD, Event: [0, 0, 0, 0]};
                let mut events_read: DWORD = 0;
                win32!(ReadConsoleInputW(conin, &mut input_record as PINPUT_RECORD, 1, &mut events_read as LPDWORD));
                if events_read > 0 {
                    tx.send(input_record).unwrap();
                }
            }
        });

        rx
    }

    fn move_cursor(&mut self, line: u16, column: u16) {
        win32!(SetConsoleCursorPosition(self.conout, COORD { X: column as i16, Y: line as i16}));
    }

    pub fn move_cursor_to_prompt_line(&mut self, col: u16) {
        let start_line = self.start_line;
        self.move_cursor(start_line, col);
    }

    pub fn blank_screen(&mut self) {
        let blank_line = repeat(' ').take((self.width() - 1) as usize).collect::<String>();
        let start_line = self.start_line;
        self.move_cursor(start_line, 0);
        for _ in 0..self.visible_choices {
            self.write(&blank_line);
            self.write(NEWLINE);
        }
        self.write(&blank_line);
        self.move_cursor(start_line, 0);
    }

    pub fn show_cursor(&mut self) {
        win32!(SetConsoleCursorInfo(self.conout, &self.default_cursor_info));
    }

    pub fn hide_cursor(&mut self) {
        let cursor_info = CONSOLE_CURSOR_INFO { dwSize: 100, bVisible: FALSE };
        win32!(SetConsoleCursorInfo(self.conout, &cursor_info));
    }

    pub fn write(&mut self, s: &str) {
        Self::write_to(self.conout, s);
    }

    fn write_to(conout: HANDLE, s: &str) {
        let mut chars_written: DWORD = 0;
        let utf16 = s.encode_utf16().collect::<Vec<u16>>();
        let chars_to_write = utf16.len() as DWORD;
        win32!(WriteConsoleW(conout, utf16.as_ptr() as PVOID, chars_to_write, &mut chars_written as LPDWORD, ptr::null_mut()));
    }

    pub fn write_red_inverted(&mut self, s: &str) {
        let orig = self.original_colors;
        const WHITE_ON_RED: WORD = BACKGROUND_RED as WORD;
        self.set_colors(WHITE_ON_RED);
        self.write(s);
        self.set_colors(orig);
    }

    pub fn write_red(&mut self, s: &str) {
        let orig = self.original_colors;
        const RED_ON_BLACK: WORD = FOREGROUND_RED as WORD;
        self.set_colors(RED_ON_BLACK);
        self.write(s);
        self.set_colors(orig);
    }

    pub fn write_inverted(&mut self, s: &str) {
        let orig = self.original_colors;
        const BLACK_ON_WHITE: WORD = (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) as WORD;
        self.set_colors(BLACK_ON_WHITE);
        self.write(s);
        self.set_colors(orig);
    }

    fn set_colors(&mut self, colors: WORD) {
        win32!(SetConsoleTextAttribute(self.conout, colors));
    }

    pub fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut ret = Vec::new();
        while let Ok(event) = self.input.try_recv() {
            ret.push(Screen::translate_event(event, &mut self.shifted));
        }
        if ret.is_empty() {
            let event = self.input.recv().unwrap();
            ret.push(Screen::translate_event(event, &mut self.shifted));
        }
        ret
    }

    fn translate_event(event: INPUT_RECORD, shifted: &mut bool) -> Key {
        if event.EventType != KEY_EVENT {
            return Nothing;
        }

        let key_event = unsafe { event.KeyEvent() };
        let vk_code = key_event.wVirtualKeyCode as i32;

        if vk_code == VK_SHIFT {
            *shifted = key_event.bKeyDown == TRUE;

            return Nothing;
        }

        if key_event.bKeyDown == FALSE {
            return Nothing;
        }

        if vk_code == VK_UP {
            Up
        } else if vk_code == VK_DOWN {
            Down
        } else if vk_code == VK_TAB {
            if *shifted { ShiftTab } else { Tab }
        } else if vk_code == VK_BACK {
            Backspace
        } else if vk_code == VK_RETURN {
            Enter
        } else {
            let byte = key_event.UnicodeChar;
            if byte & 96 == 0 {
                Control(((byte + 96u16) as u8) as char)
            } else {
                Char((byte as u8) as char)
            }
        }
    }

    fn get_cursor_pos(handle: HANDLE) -> (u16, u16) {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        win32!(GetConsoleScreenBufferInfo(handle, &mut buffer_info));
        let cursor_pos = buffer_info.dwCursorPosition;
        (cursor_pos.X as u16, cursor_pos.Y as u16)
    }

    fn get_original_colors(handle: HANDLE) -> WORD {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        win32!(GetConsoleScreenBufferInfo(handle, &mut buffer_info));
        buffer_info.wAttributes
    }

    fn winsize(conout: HANDLE) -> Option<(u16, u16)> {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        let result = unsafe { GetConsoleScreenBufferInfo(conout, &mut buffer_info) };
        if result != 0 {
            // This code specifically computes the size of the window,
            // *not* the size of the buffer (which is easily available
            // from dwSize). I got the algorithm from:
            //
            // http://stackoverflow.com/a/12642749
            let left = buffer_info.srWindow.Left;
            let top = buffer_info.srWindow.Top;
            let right = buffer_info.srWindow.Right;
            let bottom = buffer_info.srWindow.Bottom;
            let cols = right - left + 1;
            let rows = bottom - top + 1;
            Some((cols as u16, rows as u16))
        } else {
            None
        }
    }

    fn get_buffer_offset(conout: HANDLE) -> u16 {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        win32!(GetConsoleScreenBufferInfo(conout, &mut buffer_info));
        buffer_info.srWindow.Top as u16
    }
}

fn get_start_line(rows: u16, visible_choices: u16, initial_pos: (u16, u16)) -> u16 {
    let bottom_most_line = rows - visible_choices - 1;
    let (initial_x, initial_y) = initial_pos;
    let line_under_cursor = if initial_x == 0 { initial_y } else { initial_y + 1 };
    if line_under_cursor + 1 + visible_choices > rows {
        bottom_most_line
    } else {
        line_under_cursor
    }
}

#[cfg(test)]
mod tests {
    use super::kernel32;
    use super::winapi::STD_OUTPUT_HANDLE;
    use super::{Screen, get_start_line};

    #[test]
    fn winsize_test() {
        // AppVeyor builds run without a console, making this test impossible.
        if option_env!("APPVEYOR").is_some() {
            // TODO: It should be made obvious from the output that this test was skipped
            return;
        }
        let conout = unsafe { kernel32::GetStdHandle(STD_OUTPUT_HANDLE) };
        let (cols, rows) = Screen::winsize(conout).expect("Failed to get window size!");
        // We don't know the window size a priori, but we can at least
        // assert that it is within some kind of sensible range.
        assert!(cols > 40);
        assert!(rows > 40);
        assert!(cols < 1000);
        assert!(rows < 1000);
    }

    #[test]
    fn start_line_test() {
        assert_eq!(5, get_start_line(100, 20, (0, 5)));
        assert_eq!(6, get_start_line(100, 20, (1, 5)));
        assert_eq!(79, get_start_line(100, 20, (0, 100)));
        assert_eq!(0, get_start_line(15, 14, ((0, 5))));
        assert_eq!(79, get_start_line(100, 20, (50, 100)));
    }
}
<commit_msg>[Win] Include OS error message in panic<commit_after>#![cfg(windows)]

extern crate winapi;
extern crate kernel32;

use self::kernel32::*;
use self::winapi::*;
use std::io;
use std::ptr;
use std::iter::repeat;
use std::cmp::min;
use screen::Key;
use screen::Key::*;

use std::thread;
use std::sync::mpsc::Receiver;
use std::sync::mpsc;
use ::NEWLINE;

macro_rules! win32 {
    ($funcall:expr) => (
        if unsafe { $funcall } == 0 {
            panic!("Win32 call failed: {}", io::Error::last_os_error());
        }
    );
}

pub struct Screen {
    visible_choices: u16,
    start_line: u16,
    original_console_mode: DWORD,
    original_colors: WORD,
    input: Receiver<INPUT_RECORD>,
    conout: HANDLE,
    default_cursor_info: CONSOLE_CURSOR_INFO,
    shifted: bool,
}

impl Screen {
    pub fn open_screen(desired_rows: u16) -> Screen {
        let mut orig_mode;
        let conin: HANDLE;
        let conout: HANDLE;
        unsafe {
            const OPEN_EXISTING: DWORD = 3;
            let rw_access = GENERIC_READ | GENERIC_WRITE;
            conin = CreateFileA("CONIN$\0".as_ptr() as *const i8, rw_access, FILE_SHARE_READ, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut());
            conout = CreateFileA("CONOUT$\0".as_ptr() as *const i8, rw_access, FILE_SHARE_READ, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut());
            orig_mode = ::std::mem::uninitialized();
        }

        if conin == INVALID_HANDLE_VALUE || conout == INVALID_HANDLE_VALUE {
            panic!("Unable to open console");
        }

        let (_, rows) = Screen::winsize(conout).unwrap();

        win32!(GetConsoleMode(conin, &mut orig_mode));
        let new_mode = orig_mode & !(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
        win32!(SetConsoleMode(conin, new_mode));
        let mut default_cursor_info = CONSOLE_CURSOR_INFO { dwSize: 100, bVisible: TRUE };
        win32!(GetConsoleCursorInfo(conout, &mut default_cursor_info as PCONSOLE_CURSOR_INFO));

        let rx = Screen::spawn_input_thread(conin as usize);
        let initial_pos = Screen::get_cursor_pos(conout);
        let visible_choices = min(desired_rows, rows - 1);
        let start_line = get_start_line(rows, visible_choices, initial_pos);
        let original_colors = Screen::get_original_colors(conout);
        let (column, _) = initial_pos;
        if column > 0 {
            Self::write_to(conout, NEWLINE);
        }
        for _ in 0..visible_choices {
            Self::write_to(conout, NEWLINE);
        }
        Screen {
            visible_choices: visible_choices,
            start_line: start_line + Self::get_buffer_offset(conout),
            original_console_mode: orig_mode,
            original_colors: original_colors,
            input: rx,
            conout: conout,
            default_cursor_info: default_cursor_info,
            shifted: false,
        }
    }

    pub fn visible_choices(&self) -> u16 {
        self.visible_choices
    }

    pub fn width(&self) -> u16 {
        let (cols, _) = Screen::winsize(self.conout).unwrap();
        cols
    }

    // We have to take the conin handle as a usize instead of a *mut c_void in order to avoid a
    // lecture from the compiler about how the latter type cannot be safely sent between threads.
    // I'm not sure if a better solution exists at this time.
    fn spawn_input_thread(conin: usize) -> Receiver<INPUT_RECORD> {
        let (tx, rx) = mpsc::channel();

        thread::spawn(move || {
            loop {
                let conin = conin as *mut c_void;
                let mut input_record = INPUT_RECORD{EventType: 0 as WORD, Event: [0, 0, 0, 0]};
                let mut events_read: DWORD = 0;
                win32!(ReadConsoleInputW(conin, &mut input_record as PINPUT_RECORD, 1, &mut events_read as LPDWORD));
                if events_read > 0 {
                    tx.send(input_record).unwrap();
                }
            }
        });

        rx
    }

    fn move_cursor(&mut self, line: u16, column: u16) {
        win32!(SetConsoleCursorPosition(self.conout, COORD { X: column as i16, Y: line as i16}));
    }

    pub fn move_cursor_to_prompt_line(&mut self, col: u16) {
        let start_line = self.start_line;
        self.move_cursor(start_line, col);
    }

    pub fn blank_screen(&mut self) {
        let blank_line = repeat(' ').take((self.width() - 1) as usize).collect::<String>();
        let start_line = self.start_line;
        self.move_cursor(start_line, 0);
        for _ in 0..self.visible_choices {
            self.write(&blank_line);
            self.write(NEWLINE);
        }
        self.write(&blank_line);
        self.move_cursor(start_line, 0);
    }

    pub fn show_cursor(&mut self) {
        win32!(SetConsoleCursorInfo(self.conout, &self.default_cursor_info));
    }

    pub fn hide_cursor(&mut self) {
        let cursor_info = CONSOLE_CURSOR_INFO { dwSize: 100, bVisible: FALSE };
        win32!(SetConsoleCursorInfo(self.conout, &cursor_info));
    }

    pub fn write(&mut self, s: &str) {
        Self::write_to(self.conout, s);
    }

    fn write_to(conout: HANDLE, s: &str) {
        let mut chars_written: DWORD = 0;
        let utf16 = s.encode_utf16().collect::<Vec<u16>>();
        let chars_to_write = utf16.len() as DWORD;
        win32!(WriteConsoleW(conout, utf16.as_ptr() as PVOID, chars_to_write, &mut chars_written as LPDWORD, ptr::null_mut()));
    }

    pub fn write_red_inverted(&mut self, s: &str) {
        let orig = self.original_colors;
        const WHITE_ON_RED: WORD = BACKGROUND_RED as WORD;
        self.set_colors(WHITE_ON_RED);
        self.write(s);
        self.set_colors(orig);
    }

    pub fn write_red(&mut self, s: &str) {
        let orig = self.original_colors;
        const RED_ON_BLACK: WORD = FOREGROUND_RED as WORD;
        self.set_colors(RED_ON_BLACK);
        self.write(s);
        self.set_colors(orig);
    }

    pub fn write_inverted(&mut self, s: &str) {
        let orig = self.original_colors;
        const BLACK_ON_WHITE: WORD = (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) as WORD;
        self.set_colors(BLACK_ON_WHITE);
        self.write(s);
        self.set_colors(orig);
    }

    fn set_colors(&mut self, colors: WORD) {
        win32!(SetConsoleTextAttribute(self.conout, colors));
    }

    pub fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut ret = Vec::new();
        while let Ok(event) = self.input.try_recv() {
            ret.push(Screen::translate_event(event, &mut self.shifted));
        }
        if ret.is_empty() {
            let event = self.input.recv().unwrap();
            ret.push(Screen::translate_event(event, &mut self.shifted));
        }
        ret
    }

    fn translate_event(event: INPUT_RECORD, shifted: &mut bool) -> Key {
        if event.EventType != KEY_EVENT {
            return Nothing;
        }

        let key_event = unsafe { event.KeyEvent() };
        let vk_code = key_event.wVirtualKeyCode as i32;

        if vk_code == VK_SHIFT {
            *shifted = key_event.bKeyDown == TRUE;

            return Nothing;
        }

        if key_event.bKeyDown == FALSE {
            return Nothing;
        }

        if vk_code == VK_UP {
            Up
        } else if vk_code == VK_DOWN {
            Down
        } else if vk_code == VK_TAB {
            if *shifted { ShiftTab } else { Tab }
        } else if vk_code == VK_BACK {
            Backspace
        } else if vk_code == VK_RETURN {
            Enter
        } else {
            let byte = key_event.UnicodeChar;
            if byte & 96 == 0 {
                Control(((byte + 96u16) as u8) as char)
            } else {
                Char((byte as u8) as char)
            }
        }
    }

    fn get_cursor_pos(handle: HANDLE) -> (u16, u16) {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        win32!(GetConsoleScreenBufferInfo(handle, &mut buffer_info));
        let cursor_pos = buffer_info.dwCursorPosition;
        (cursor_pos.X as u16, cursor_pos.Y as u16)
    }

    fn get_original_colors(handle: HANDLE) -> WORD {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        win32!(GetConsoleScreenBufferInfo(handle, &mut buffer_info));
        buffer_info.wAttributes
    }

    fn winsize(conout: HANDLE) -> Option<(u16, u16)> {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        let result = unsafe { GetConsoleScreenBufferInfo(conout, &mut buffer_info) };
        if result != 0 {
            // This code specifically computes the size of the window,
            // *not* the size of the buffer (which is easily available
            // from dwSize). I got the algorithm from:
            //
            // http://stackoverflow.com/a/12642749
            let left = buffer_info.srWindow.Left;
            let top = buffer_info.srWindow.Top;
            let right = buffer_info.srWindow.Right;
            let bottom = buffer_info.srWindow.Bottom;
            let cols = right - left + 1;
            let rows = bottom - top + 1;
            Some((cols as u16, rows as u16))
        } else {
            None
        }
    }

    fn get_buffer_offset(conout: HANDLE) -> u16 {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        win32!(GetConsoleScreenBufferInfo(conout, &mut buffer_info));
        buffer_info.srWindow.Top as u16
    }
}

fn get_start_line(rows: u16, visible_choices: u16, initial_pos: (u16, u16)) -> u16 {
    let bottom_most_line = rows - visible_choices - 1;
    let (initial_x, initial_y) = initial_pos;
    let line_under_cursor = if initial_x == 0 { initial_y } else { initial_y + 1 };
    if line_under_cursor + 1 + visible_choices > rows {
        bottom_most_line
    } else {
        line_under_cursor
    }
}

#[cfg(test)]
mod tests {
    use super::kernel32;
    use super::winapi::STD_OUTPUT_HANDLE;
    use super::{Screen, get_start_line};

    #[test]
    fn winsize_test() {
        // AppVeyor builds run without a console, making this test impossible.
        if option_env!("APPVEYOR").is_some() {
            // TODO: It should be made obvious from the output that this test was skipped
            return;
        }
        let conout = unsafe { kernel32::GetStdHandle(STD_OUTPUT_HANDLE) };
        let (cols, rows) = Screen::winsize(conout).expect("Failed to get window size!");
        // We don't know the window size a priori, but we can at least
        // assert that it is within some kind of sensible range.
        assert!(cols > 40);
        assert!(rows > 40);
        assert!(cols < 1000);
        assert!(rows < 1000);
    }

    #[test]
    fn start_line_test() {
        assert_eq!(5, get_start_line(100, 20, (0, 5)));
        assert_eq!(6, get_start_line(100, 20, (1, 5)));
        assert_eq!(79, get_start_line(100, 20, (0, 100)));
        assert_eq!(0, get_start_line(15, 14, ((0, 5))));
        assert_eq!(79, get_start_line(100, 20, (50, 100)));
    }
}
<|endoftext|>


<commit_before>//! Provides drilldown related procedures.
//! This module will be used with `SelectCommand` type by `+` operation
//! to concatinate `select` and `drilldown` queries.
//!
//! Also this module support `pseudo_table` and `staged_columns` manupilation.

pub mod drilldown_type;
pub mod drilldown;
pub mod labeled_drilldown;
pub mod drilldownable;
pub mod labeled_drilldownable;
pub mod labeled_drilldown_sequencable;
pub mod drilldown_builder;
pub mod labeled_drilldown_builder;
pub mod labeled_drilldown_sequence_builder;
/// `fragmentable` module provides making query fragment operation.
/// It is useful to make composable query elements.
/// It makes `select` query to be composable against drilldown related query
/// and `staged_columns` query.
pub mod fragmentable;
/// `pseudo_table` is a trait to be used for tablable state which is appeared in
/// "drilldown in drilldown".
pub mod pseudo_table;
pub mod staged_columns;
pub mod staged_columns_builder;
pub mod staged_columnable;
<commit_msg>Add rustdoc for staged columns<commit_after>//! Provides drilldown related procedures.
//! This module will be used with `SelectCommand` type by `+` operation
//! to concatinate `select` and `drilldown` queries.
//!
//! Also this module support `pseudo_table` and `staged_columns` manupilation.

pub mod drilldown_type;
pub mod drilldown;
pub mod labeled_drilldown;
pub mod drilldownable;
pub mod labeled_drilldownable;
pub mod labeled_drilldown_sequencable;
pub mod drilldown_builder;
pub mod labeled_drilldown_builder;
pub mod labeled_drilldown_sequence_builder;
/// `fragmentable` module provides making query fragment operation.
/// It is useful to make composable query elements.
/// It makes `select` query to be composable against drilldown related query
/// and `staged_columns` query.
pub mod fragmentable;
/// `pseudo_table` is a trait to be used for tablable state which is appeared in
/// "drilldown in drilldown".
pub mod pseudo_table;
/// `staged_columns` provides making query which supports window function syntax.
pub mod staged_columns;
pub mod staged_columns_builder;
pub mod staged_columnable;
<|endoftext|>


<commit_before>pub use self::DecodeError::*;
use std::fmt;

const BASE: u128 = 62;
const MAX_DECODE_LEN: usize = 111;
const ALPHABET: [u8; BASE as usize] = [
    b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', b'F',
    b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V',
    b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l',
    b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z',
];

pub enum DecodeError {
    InvalidBase62Byte(char, usize),
    ArithmeticOverflow,
}

impl fmt::Debug for DecodeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InvalidBase62Byte(ch, idx) => {
                write!(f, "Invalid character '{}' at position {}", ch, idx)
            }
            ArithmeticOverflow => write!(f, "Decode result is too large"),
        }
    }
}

impl fmt::Display for DecodeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self, f)
    }
}

#[must_use]
pub fn encode<T: Into<u128>>(num: T) -> String {
    let mut num = num.into();
    if num == 0 {
        return "0".to_owned();
    }
    let mut bytes: [u8; MAX_DECODE_LEN] = [0; MAX_DECODE_LEN];

    let mut i: usize = 0;
    loop {
        if num == 0 {
            break;
        }
        i += 1;

        bytes[MAX_DECODE_LEN - i] = ALPHABET[(num % BASE) as usize];
        num /= BASE;
    }

    String::from_utf8(bytes[MAX_DECODE_LEN - i..MAX_DECODE_LEN].to_vec()).unwrap()
}

pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<u128, DecodeError> {
    let mut result = 0;
    let input = input.as_ref();

    for (i, c) in input.iter().rev().enumerate() {
        let num = BASE.checked_pow(i as u32).ok_or(ArithmeticOverflow)?;
        match ALPHABET.binary_search(&c) {
            Ok(v) => match (v as u128).checked_mul(num) {
                Some(z) => result += z,
                None => return Err(ArithmeticOverflow),
            },
            Err(_e) => {
                return Err(InvalidBase62Byte(c.to_owned() as char, input.len() - i));
            }
        }
    }

    Ok(result)
}

#[cfg(test)]
mod tests {
    use crate::base62;
    use quickcheck::TestResult;

    quickcheck! {
        fn encode_decode(xs: u128) -> bool {
            let encoded = base62::encode(xs);
            xs == base62::decode(&encoded).unwrap()
        }
    }

    quickcheck! {
        fn decode_good(xs: String) -> TestResult {
            if xs.chars().all(|c| c.is_ascii() && base62::ALPHABET.binary_search(&(c as u8)).is_ok()) && !xs.is_empty() {
                return TestResult::from_bool(base62::decode(&xs).is_ok());
            } else {
                TestResult::discard()
            }
        }
    }

    quickcheck! {
        fn decode_bad(xs: String) -> TestResult {
            if xs.chars().all(|c| c.is_ascii() && base62::ALPHABET.binary_search(&(c as u8)).is_ok()) {
                TestResult::discard()
            } else {
                return TestResult::from_bool(base62::decode(&xs).is_err());
            }
        }
    }

    #[test]
    fn test_encode() {
        assert_eq!(base62::encode(u128::MAX), "7n42DGM5Tflk9n8mt7Fhc7");
        assert_eq!(base62::encode(0u8), "0");
    }

    #[test]
    fn test_decode() {
        assert_eq!(base62::decode("7n42DGM5Tflk9n8mt7Fhc7").unwrap(), u128::MAX);
        assert_eq!(base62::decode("").unwrap(), 0);
    }

    #[test]
    fn test_decode_invalid_char() {
        assert!(base62::decode("ds{Z455f").is_err());
    }

    #[test]
    fn test_decode_long_string() {
        assert!(base62::decode("7n42DGM5Tflk9n8mt7Fhc7B").is_err());
    }
}
<commit_msg>Minimal documentation<commit_after>pub use self::DecodeError::*;
use std::fmt;

const BASE: u128 = 62;
const MAX_DECODE_LEN: usize = 111;
const ALPHABET: [u8; BASE as usize] = [
    b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', b'F',
    b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V',
    b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l',
    b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z',
];

pub enum DecodeError {
    InvalidBase62Byte(char, usize),
    ArithmeticOverflow,
}

impl fmt::Debug for DecodeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InvalidBase62Byte(ch, idx) => {
                write!(f, "Invalid character '{}' at position {}", ch, idx)
            }
            ArithmeticOverflow => write!(f, "Decode result is too large"),
        }
    }
}

impl fmt::Display for DecodeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self, f)
    }
}

///Encode any uint as base64.
///Returns a String.
///
///# Example
///
///```rust
///extern crate base62;
///
///fn main() {
///    let b62 = base62::encode(1337u32);
///    println!("{}", b62);
///}
///```
#[must_use]
pub fn encode<T: Into<u128>>(num: T) -> String {
    let mut num = num.into();
    if num == 0 {
        return "0".to_owned();
    }
    let mut bytes: [u8; MAX_DECODE_LEN] = [0; MAX_DECODE_LEN];

    let mut i: usize = 0;
    loop {
        if num == 0 {
            break;
        }
        i += 1;

        bytes[MAX_DECODE_LEN - i] = ALPHABET[(num % BASE) as usize];
        num /= BASE;
    }

    String::from_utf8(bytes[MAX_DECODE_LEN - i..MAX_DECODE_LEN].to_vec()).unwrap()
}

///Decode from string reference as octets.
///Returns a Result containing a u128 which can be downcasted to any other uint.
///
///# Example
///
///```rust
///extern crate base62;
///
///fn main() {
///    let bytes = base62::decode("rustlang").unwrap();
///    println!("{:?}", bytes);
///}
///```
pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<u128, DecodeError> {
    let mut result = 0;
    let input = input.as_ref();

    for (i, c) in input.iter().rev().enumerate() {
        let num = BASE.checked_pow(i as u32).ok_or(ArithmeticOverflow)?;
        match ALPHABET.binary_search(&c) {
            Ok(v) => match (v as u128).checked_mul(num) {
                Some(z) => result += z,
                None => return Err(ArithmeticOverflow),
            },
            Err(_e) => {
                return Err(InvalidBase62Byte(c.to_owned() as char, input.len() - i));
            }
        }
    }

    Ok(result)
}

#[cfg(test)]
mod tests {
    use crate::base62;
    use quickcheck::TestResult;

    quickcheck! {
        fn encode_decode(xs: u128) -> bool {
            let encoded = base62::encode(xs);
            xs == base62::decode(&encoded).unwrap()
        }
    }

    quickcheck! {
        fn decode_good(xs: String) -> TestResult {
            if xs.chars().all(|c| c.is_ascii() && base62::ALPHABET.binary_search(&(c as u8)).is_ok()) && !xs.is_empty() {
                return TestResult::from_bool(base62::decode(&xs).is_ok());
            } else {
                TestResult::discard()
            }
        }
    }

    quickcheck! {
        fn decode_bad(xs: String) -> TestResult {
            if xs.chars().all(|c| c.is_ascii() && base62::ALPHABET.binary_search(&(c as u8)).is_ok()) {
                TestResult::discard()
            } else {
                return TestResult::from_bool(base62::decode(&xs).is_err());
            }
        }
    }

    #[test]
    fn test_encode() {
        assert_eq!(base62::encode(u128::MAX), "7n42DGM5Tflk9n8mt7Fhc7");
        assert_eq!(base62::encode(0u8), "0");
    }

    #[test]
    fn test_decode() {
        assert_eq!(base62::decode("7n42DGM5Tflk9n8mt7Fhc7").unwrap(), u128::MAX);
        assert_eq!(base62::decode("").unwrap(), 0);
    }

    #[test]
    fn test_decode_invalid_char() {
        assert!(base62::decode("ds{Z455f").is_err());
    }

    #[test]
    fn test_decode_long_string() {
        assert!(base62::decode("7n42DGM5Tflk9n8mt7Fhc7B").is_err());
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::default::Default;
use std::old_io::IoResult;
use std::old_io::stdio::{self, StdWriter};
use std::num::Int;
use board::{Board, Edge, Side};
use geom::{Geom, Point, UP, LEFT};
use term::{self, Terminal, StdTerminal};
use term::color::{self, Color};

#[derive(Copy, Debug, RustcDecodable)]
pub enum Mode {
    Ascii, Unicode
}
impl Default for Mode {
    fn default() -> Mode { Mode::Unicode }
}

pub struct Config {
    pub mode: Mode,
    pub is_cjk: bool,
    pub cell_width: usize,
    pub cell_height: usize
}

enum Output<T> {
    Pretty(Box<StdTerminal>),
    Raw(T)
}

fn side_to_color(ty: Option<Side>) -> (Color, Color) {
    match ty {
        Some(Side::In)  => (color::BRIGHT_YELLOW, color::BLACK),
        Some(Side::Out) => (color::BLACK,         color::BRIGHT_WHITE),
        None            => (color::BRIGHT_WHITE,  color::BLACK),
    }
}

struct Printer<'a> {
    output: Output<StdWriter>
}

impl<'a> Printer<'a> {
    fn new() -> Printer<'a> {
        let output = if stdio::stdout_raw().isatty() {
            match term::stdout() {
                Some(t) => Output::Pretty(t),
                None    => Output::Raw(stdio::stdout_raw())
            }
        } else {
            Output::Raw(stdio::stdout_raw())
        };
        Printer { output: output }
    }

    fn write_pretty(&mut self, ty: Option<Side>, s: &str) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => {
                let (bg, fg) = side_to_color(ty);
                try!(term.fg(fg));
                try!(term.bg(bg));
                try!(term.write(s.as_bytes()));
                try!(term.reset());
                Ok(())
            }
            Output::Raw(ref mut stdout) => stdout.write(s.as_bytes())
        }
    }
    fn write_pretty_fmt(&mut self, ty: Option<Side>, fmt: fmt::Arguments) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => {
                let (bg, fg) = side_to_color(ty);
                try!(term.fg(fg));
                try!(term.bg(bg));
                try!(term.write_fmt(fmt));
                try!(term.reset());
                Ok(())
            }
            Output::Raw(ref mut stdout) => stdout.write_fmt(fmt)
        }
    }
    fn write_plain(&mut self, s: &str) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => term.write(s.as_bytes()),
            Output::Raw(ref mut stdout) => stdout.write(s.as_bytes())
        }
    }

    fn write_plain_fmt(&mut self, fmt: fmt::Arguments) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => term.write_fmt(fmt),
            Output::Raw(ref mut stdout) => stdout.write_fmt(fmt)
        }
    }
}

struct Table;
impl Table {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board)
              -> IoResult<()>
    {
        let row = board.row();
        try!(LabelRow::pprint(printer, conf, board));
        for y in (0 .. row) {
            try!(EdgeRow::pprint(printer, conf, board, y));
            try!(CellRow::pprint(printer, conf, board, y));
        }
        try!(EdgeRow::pprint(printer, conf, board, row));
        try!(LabelRow::pprint(printer, conf, board));
        Ok(())
    }
}

struct LabelRow;
impl LabelRow {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board)
              -> IoResult<()>
    {
        try!(printer.write_plain_fmt(
            format_args!("{:1$}", "", conf.cell_width)));
        for x in 0 .. board.column() {
            try!(printer.write_plain_fmt(
                format_args!("{:1$}", "", "─".width(conf.is_cjk))));
            try!(Label::pprint(printer, conf, x, true));
        }
        try!(printer.write_plain("\n"));
        Ok(())
   }
}

struct EdgeRow;
impl EdgeRow {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board, y: i32)
              -> IoResult<()>
    {
        let col = board.column();
        try!(printer.write_plain_fmt(
            format_args!("{:1$}", "", conf.cell_width)));
        for x in (0 .. col) {
            try!(Corner::pprint(printer, conf, board, Point(y, x)));
            try!(EdgeH::pprint(printer, conf, board, Point(y, x)));
        }
        try!(Corner::pprint(printer, conf, board, Point(y, col)));
        try!(printer.write_plain("\n"));
        Ok(())
    }
}

struct CellRow;
impl CellRow {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board, y: i32) -> IoResult<()> {
        let col = board.column();
        for i in (0 .. conf.cell_height) {
            let num_line = (conf.cell_height - 1) / 2 == i;
            try!(Label::pprint(printer, conf, y, num_line));
            for x in (0 .. col) {
                try!(EdgeV::pprint(printer, conf, board, Point(y, x)));
                try!(Cell::pprint(printer, conf, board, Point(y, x), num_line));
            }
            try!(EdgeV::pprint(printer, conf, board, Point(y, col)));
            try!(Label::pprint(printer, conf, y, num_line));
            try!(printer.write_plain("\n"));
        }
        Ok(())
    }
}

struct Corner;
impl Corner {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board, p: Point)
              -> IoResult<()>
    {
        let l = p + LEFT;
        let u = p + UP;
        let eh_p = board.edge_h()[p];
        let eh_l = board.edge_h()[l];
        let ev_p = board.edge_v()[p];
        let ev_u = board.edge_v()[u];

        let is_same_all =
            (eh_p == Some(Edge::Cross)) &&
            (eh_l == Some(Edge::Cross)) &&
            (ev_p == Some((Edge::Cross))) &&
            (ev_u == Some((Edge::Cross)));

        let ty = if is_same_all {
            board.side()[p]
        } else {
            None
        };
        let is_h = eh_p == Some(Edge::Line) &&
            eh_l == Some(Edge::Line);
        let is_v = ev_p == Some(Edge::Line) &&
            ev_u == Some(Edge::Line);

        match conf.mode {
            Mode::Ascii => {
                if is_same_all {
                    try!(printer.write_pretty(ty, "."));
                } else if is_h {
                    try!(printer.write_pretty(ty, "-"));
                } else if is_v {
                    try!(printer.write_pretty(ty, "|"));
                } else {
                    try!(printer.write_pretty(ty, "+"));
                }
            }
            Mode::Unicode => {
                let width = '┼'.width(conf.is_cjk).unwrap();
                if is_same_all {
                    for _ in (0 .. width) {
                        try!(printer.write_pretty(ty, " "));
                    }
                } else if is_h {
                    try!(printer.write_pretty(ty, "─"));
                } else if is_v {
                    try!(printer.write_pretty(ty, "│"));
                } else if eh_p == Some(Edge::Line) && ev_p == Some(Edge::Line) {
                    try!(printer.write_pretty(ty, "┌"));
                } else if eh_l == Some(Edge::Line) && ev_p == Some(Edge::Line) {
                    try!(printer.write_pretty(ty, "┐"));
                } else if eh_l == Some(Edge::Line) && ev_u == Some(Edge::Line) {
                    try!(printer.write_pretty(ty, "┘"));
                } else if eh_p == Some(Edge::Line) && ev_u == Some(Edge::Line) {
                    try!(printer.write_pretty(ty, "└"));
                } else {
                    try!(printer.write_pretty(ty, "┼"));
                }
            }
        }
        Ok(())
    }
}

struct EdgeH;
impl EdgeH {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board, p: Point)
              -> IoResult<()>
    {
        match conf.mode {
            Mode::Ascii => {
                let (s, ty) = match board.edge_h()[p] {
                    Some(Edge::Cross) => (" ", board.side()[p]),
                    Some(Edge::Line)  => ("-", None),
                    None => ("~", None)
                };
                for _ in (0 .. conf.cell_width) {
                    try!(printer.write_pretty(ty, s));
                }
            }
            Mode::Unicode => {
                let (s, ty) = match board.edge_h()[p] {
                    Some(Edge::Cross) => (" ", board.side()[p]),
                    Some(Edge::Line)  => ("─", None),
                    None => ("~", None)
                };
                let w = s.width(conf.is_cjk);
                for _ in (0 .. (conf.cell_width + w - 1) / w) {
                    try!(printer.write_pretty(ty, s));
                }
            }
        }
        Ok(())
    }
}

struct Label;
impl Label {
    fn pprint(printer: &mut Printer, conf: &Config, n: i32, num_line: bool)
              -> IoResult<()>
    {
        if num_line {
            let order = 10.pow(conf.cell_width);
            try!(printer.write_plain_fmt(
                format_args!("{:^1$}", n % order, conf.cell_width)));
        } else {
            try!(printer.write_plain_fmt(
                format_args!("{:1$}", "", conf.cell_width)));
        }
        Ok(())
    }
}

struct EdgeV;
impl EdgeV {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board, p: Point)
              -> IoResult<()>
    {
        match conf.mode {
            Mode::Ascii => {
                let (s, ty) = match board.edge_v()[p] {
                    Some(Edge::Cross) => (" ", board.side()[p]),
                    Some(Edge::Line)  => ("|", None),
                    None => ("?", None)
                };
                try!(printer.write_pretty(ty, s));
            }
            Mode::Unicode => {
                let width = "│".width(conf.is_cjk);
                let (s, ty) = match board.edge_v()[p] {
                    Some(Edge::Cross) => (" ", board.side()[p]),
                    Some(Edge::Line)  => ("│", None),
                    None => ("/", None)
                };
                for _ in (0 .. width / s.width(conf.is_cjk)) {
                    try!(printer.write_pretty(ty, s));
                }
            }
        }
        Ok(())
    }
}

struct Cell;
impl Cell {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board, p: Point,
              num_line: bool)
              -> IoResult<()>
    {
        let ty = board.side()[p];
        match board[p] {
            Some(x) if num_line => {
                try!(printer.write_pretty_fmt(
                    ty, format_args!("{:^1$}", x, conf.cell_width)));
            },
            _ => {
                try!(printer.write_pretty_fmt(
                    ty, format_args!("{:^1$}", "", conf.cell_width)));
            }
        };
        Ok(())
    }
}

pub fn print(conf: &Config, board: &Board) -> IoResult<()> {
    Table::pprint(&mut Printer::new(), conf, board)
}
<commit_msg>fix deprecated warnings<commit_after>use std::fmt;
use std::default::Default;
use std::old_io::IoResult;
use std::old_io::stdio::{self, StdWriter};
use std::num::Int;
use board::{Board, Edge, Side};
use geom::{Geom, Point, UP, LEFT};
use term::{self, Terminal, StdTerminal};
use term::color::{self, Color};

#[derive(Copy, Debug, RustcDecodable)]
pub enum Mode {
    Ascii, Unicode
}
impl Default for Mode {
    fn default() -> Mode { Mode::Unicode }
}

pub struct Config {
    pub mode: Mode,
    pub is_cjk: bool,
    pub cell_width: usize,
    pub cell_height: usize
}

enum Output<T> {
    Pretty(Box<StdTerminal>),
    Raw(T)
}

fn side_to_color(ty: Option<Side>) -> (Color, Color) {
    match ty {
        Some(Side::In)  => (color::BRIGHT_YELLOW, color::BLACK),
        Some(Side::Out) => (color::BLACK,         color::BRIGHT_WHITE),
        None            => (color::BRIGHT_WHITE,  color::BLACK),
    }
}

struct Printer<'a> {
    output: Output<StdWriter>
}

impl<'a> Printer<'a> {
    fn new() -> Printer<'a> {
        let output = if stdio::stdout_raw().isatty() {
            match term::stdout() {
                Some(t) => Output::Pretty(t),
                None    => Output::Raw(stdio::stdout_raw())
            }
        } else {
            Output::Raw(stdio::stdout_raw())
        };
        Printer { output: output }
    }

    fn write_pretty(&mut self, ty: Option<Side>, s: &str) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => {
                let (bg, fg) = side_to_color(ty);
                try!(term.fg(fg));
                try!(term.bg(bg));
                try!(term.write_all(s.as_bytes()));
                try!(term.reset());
                Ok(())
            }
            Output::Raw(ref mut stdout) => stdout.write_all(s.as_bytes())
        }
    }
    fn write_pretty_fmt(&mut self, ty: Option<Side>, fmt: fmt::Arguments) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => {
                let (bg, fg) = side_to_color(ty);
                try!(term.fg(fg));
                try!(term.bg(bg));
                try!(term.write_fmt(fmt));
                try!(term.reset());
                Ok(())
            }
            Output::Raw(ref mut stdout) => stdout.write_fmt(fmt)
        }
    }
    fn write_plain(&mut self, s: &str) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => term.write_all(s.as_bytes()),
            Output::Raw(ref mut stdout) => stdout.write_all(s.as_bytes())
        }
    }

    fn write_plain_fmt(&mut self, fmt: fmt::Arguments) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => term.write_fmt(fmt),
            Output::Raw(ref mut stdout) => stdout.write_fmt(fmt)
        }
    }
}

struct Table;
impl Table {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board)
              -> IoResult<()>
    {
        let row = board.row();
        try!(LabelRow::pprint(printer, conf, board));
        for y in (0 .. row) {
            try!(EdgeRow::pprint(printer, conf, board, y));
            try!(CellRow::pprint(printer, conf, board, y));
        }
        try!(EdgeRow::pprint(printer, conf, board, row));
        try!(LabelRow::pprint(printer, conf, board));
        Ok(())
    }
}

struct LabelRow;
impl LabelRow {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board)
              -> IoResult<()>
    {
        try!(printer.write_plain_fmt(
            format_args!("{:1$}", "", conf.cell_width)));
        for x in 0 .. board.column() {
            try!(printer.write_plain_fmt(
                format_args!("{:1$}", "", "─".width(conf.is_cjk))));
            try!(Label::pprint(printer, conf, x, true));
        }
        try!(printer.write_plain("\n"));
        Ok(())
   }
}

struct EdgeRow;
impl EdgeRow {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board, y: i32)
              -> IoResult<()>
    {
        let col = board.column();
        try!(printer.write_plain_fmt(
            format_args!("{:1$}", "", conf.cell_width)));
        for x in (0 .. col) {
            try!(Corner::pprint(printer, conf, board, Point(y, x)));
            try!(EdgeH::pprint(printer, conf, board, Point(y, x)));
        }
        try!(Corner::pprint(printer, conf, board, Point(y, col)));
        try!(printer.write_plain("\n"));
        Ok(())
    }
}

struct CellRow;
impl CellRow {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board, y: i32) -> IoResult<()> {
        let col = board.column();
        for i in (0 .. conf.cell_height) {
            let num_line = (conf.cell_height - 1) / 2 == i;
            try!(Label::pprint(printer, conf, y, num_line));
            for x in (0 .. col) {
                try!(EdgeV::pprint(printer, conf, board, Point(y, x)));
                try!(Cell::pprint(printer, conf, board, Point(y, x), num_line));
            }
            try!(EdgeV::pprint(printer, conf, board, Point(y, col)));
            try!(Label::pprint(printer, conf, y, num_line));
            try!(printer.write_plain("\n"));
        }
        Ok(())
    }
}

struct Corner;
impl Corner {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board, p: Point)
              -> IoResult<()>
    {
        let l = p + LEFT;
        let u = p + UP;
        let eh_p = board.edge_h()[p];
        let eh_l = board.edge_h()[l];
        let ev_p = board.edge_v()[p];
        let ev_u = board.edge_v()[u];

        let is_same_all =
            (eh_p == Some(Edge::Cross)) &&
            (eh_l == Some(Edge::Cross)) &&
            (ev_p == Some((Edge::Cross))) &&
            (ev_u == Some((Edge::Cross)));

        let ty = if is_same_all {
            board.side()[p]
        } else {
            None
        };
        let is_h = eh_p == Some(Edge::Line) &&
            eh_l == Some(Edge::Line);
        let is_v = ev_p == Some(Edge::Line) &&
            ev_u == Some(Edge::Line);

        match conf.mode {
            Mode::Ascii => {
                if is_same_all {
                    try!(printer.write_pretty(ty, "."));
                } else if is_h {
                    try!(printer.write_pretty(ty, "-"));
                } else if is_v {
                    try!(printer.write_pretty(ty, "|"));
                } else {
                    try!(printer.write_pretty(ty, "+"));
                }
            }
            Mode::Unicode => {
                let width = '┼'.width(conf.is_cjk).unwrap();
                if is_same_all {
                    for _ in (0 .. width) {
                        try!(printer.write_pretty(ty, " "));
                    }
                } else if is_h {
                    try!(printer.write_pretty(ty, "─"));
                } else if is_v {
                    try!(printer.write_pretty(ty, "│"));
                } else if eh_p == Some(Edge::Line) && ev_p == Some(Edge::Line) {
                    try!(printer.write_pretty(ty, "┌"));
                } else if eh_l == Some(Edge::Line) && ev_p == Some(Edge::Line) {
                    try!(printer.write_pretty(ty, "┐"));
                } else if eh_l == Some(Edge::Line) && ev_u == Some(Edge::Line) {
                    try!(printer.write_pretty(ty, "┘"));
                } else if eh_p == Some(Edge::Line) && ev_u == Some(Edge::Line) {
                    try!(printer.write_pretty(ty, "└"));
                } else {
                    try!(printer.write_pretty(ty, "┼"));
                }
            }
        }
        Ok(())
    }
}

struct EdgeH;
impl EdgeH {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board, p: Point)
              -> IoResult<()>
    {
        match conf.mode {
            Mode::Ascii => {
                let (s, ty) = match board.edge_h()[p] {
                    Some(Edge::Cross) => (" ", board.side()[p]),
                    Some(Edge::Line)  => ("-", None),
                    None => ("~", None)
                };
                for _ in (0 .. conf.cell_width) {
                    try!(printer.write_pretty(ty, s));
                }
            }
            Mode::Unicode => {
                let (s, ty) = match board.edge_h()[p] {
                    Some(Edge::Cross) => (" ", board.side()[p]),
                    Some(Edge::Line)  => ("─", None),
                    None => ("~", None)
                };
                let w = s.width(conf.is_cjk);
                for _ in (0 .. (conf.cell_width + w - 1) / w) {
                    try!(printer.write_pretty(ty, s));
                }
            }
        }
        Ok(())
    }
}

struct Label;
impl Label {
    fn pprint(printer: &mut Printer, conf: &Config, n: i32, num_line: bool)
              -> IoResult<()>
    {
        if num_line {
            let order = 10.pow(conf.cell_width);
            try!(printer.write_plain_fmt(
                format_args!("{:^1$}", n % order, conf.cell_width)));
        } else {
            try!(printer.write_plain_fmt(
                format_args!("{:1$}", "", conf.cell_width)));
        }
        Ok(())
    }
}

struct EdgeV;
impl EdgeV {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board, p: Point)
              -> IoResult<()>
    {
        match conf.mode {
            Mode::Ascii => {
                let (s, ty) = match board.edge_v()[p] {
                    Some(Edge::Cross) => (" ", board.side()[p]),
                    Some(Edge::Line)  => ("|", None),
                    None => ("?", None)
                };
                try!(printer.write_pretty(ty, s));
            }
            Mode::Unicode => {
                let width = "│".width(conf.is_cjk);
                let (s, ty) = match board.edge_v()[p] {
                    Some(Edge::Cross) => (" ", board.side()[p]),
                    Some(Edge::Line)  => ("│", None),
                    None => ("/", None)
                };
                for _ in (0 .. width / s.width(conf.is_cjk)) {
                    try!(printer.write_pretty(ty, s));
                }
            }
        }
        Ok(())
    }
}

struct Cell;
impl Cell {
    fn pprint(printer: &mut Printer, conf: &Config, board: &Board, p: Point,
              num_line: bool)
              -> IoResult<()>
    {
        let ty = board.side()[p];
        match board[p] {
            Some(x) if num_line => {
                try!(printer.write_pretty_fmt(
                    ty, format_args!("{:^1$}", x, conf.cell_width)));
            },
            _ => {
                try!(printer.write_pretty_fmt(
                    ty, format_args!("{:^1$}", "", conf.cell_width)));
            }
        };
        Ok(())
    }
}

pub fn print(conf: &Config, board: &Board) -> IoResult<()> {
    Table::pprint(&mut Printer::new(), conf, board)
}
<|endoftext|>