<commit_before>//! Shared types (e.g. for a domain name)

use rocket::http::RawStr;
use rocket::request::FromFormValue;
use std::convert::TryInto;
use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use trust_dns::rr::domain::Name;
use trust_dns_proto::error::ProtoError;

/// A domain name
///
/// A `Domain` represents a domain name as used in the Domain Name System (DNS).
///
/// Warning: At the moment, a `Domain` is merely a tuple struct wrapping a `String`. The API even
/// provides public access to the inner field. However, this is not considered to be stable and
/// may change in the future without being considered to be a breaking change.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Domain(pub String);

impl Display for Domain {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl FromStr for Domain {
    type Err = &'static str; // TODO Use a proper error type when improving Domain!
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Domain(s.into()))
    }
}

impl<'v> FromFormValue<'v> for Domain {
    type Error = &'v RawStr;

    fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
        let inner = String::from_form_value(v)?;
        Ok(Domain(inner))
    }
}

impl TryInto<Name> for Domain {
    type Error = ProtoError;
    fn try_into(self) -> Result<Name, Self::Error> {
        Name::parse(&self.0, None)
    }
}

/// A salted and cryptographically hashed string
///
/// A `Hash` represents a string that was salted and cryptographically hashed using the bcrypt
/// algorithm. The salt is stored alongside the hash. A `Hash` is well suited to store encrypted
/// passwords.
///
/// Since `Hash` implements `From<&str>`, the preferred method to obtain a `Hash` is to convert a
/// string slice using `Into<Hash>` as shown in the example below.
///
/// A `Hash` can be compared to a given string slice (== verified) with the method [`is`].
///
/// Because of different, randomly chosen salts, two hashes are (almost) never equal, even if
/// obtained from the exact same plain text.
///
/// [`is`]: #method.is
///
/// # Example
///
/// ```
/// use dynonym::types::Hash;
///
/// // Create
/// let h: Hash = "foo".into();
///
/// // Verify
/// assert!( h.is("foo"));
/// assert!(!h.is("bar"));
/// ```
#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Hash(String);

impl Hash {
    /// Verifies whether `self` is a hashed version of the given string slice.
    pub fn is(&self, plain: &str) -> bool {
        use bcrypt::verify;
        verify(plain, &self.0).unwrap()
    }
}

impl<'a> From<&'a str> for Hash {
    fn from(plain: &'a str) -> Self {
        use bcrypt::{hash, DEFAULT_COST};
        let hash = hash(plain, DEFAULT_COST).unwrap();
        Hash(hash)
    }
}

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

    #[test]
    fn hash_eq() {
        let h: Hash = "foo".into();
        assert!(h.is("foo"));
    }

    #[test]
    fn hash_ne() {
        let h: Hash = "foo".into();
        assert!(!h.is("bar"));
    }

    #[test]
    fn hash_salt() {
        let h1: Hash = "foo".into();
        let h2: Hash = "foo".into();
        assert!(h1 != h2);  // different salts!
    }
}
<commit_msg>Add documentation for module "types"<commit_after>//! Shared types (e.g. for a domain name)
//!
//! This module defines basic types, that are necessary throughout `dynonym` but unavailable in the
//! [standard library][std]. Sometimes, equivalent types are present in third-party crates but we
//! try to avoid them in order to provide a stable API. In case, the equivalent type is necessary
//! to use the API of a dependency, this module provides a conversion.
//!
//! [std]: https://doc.rust-lang.org/std/

use rocket::http::RawStr;
use rocket::request::FromFormValue;
use std::convert::TryInto;
use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use trust_dns::rr::domain::Name;
use trust_dns_proto::error::ProtoError;

/// A domain name
///
/// A `Domain` represents a domain name as used in the Domain Name System (DNS).
///
/// Warning: At the moment, a `Domain` is merely a tuple struct wrapping a `String`. The API even
/// provides public access to the inner field. However, this is not considered to be stable and
/// may change in the future without being considered to be a breaking change.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Domain(pub String);

impl Display for Domain {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl FromStr for Domain {
    type Err = &'static str; // TODO Use a proper error type when improving Domain!
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Domain(s.into()))
    }
}

impl<'v> FromFormValue<'v> for Domain {
    type Error = &'v RawStr;

    fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
        let inner = String::from_form_value(v)?;
        Ok(Domain(inner))
    }
}

impl TryInto<Name> for Domain {
    type Error = ProtoError;
    fn try_into(self) -> Result<Name, Self::Error> {
        Name::parse(&self.0, None)
    }
}

/// A salted and cryptographically hashed string
///
/// A `Hash` represents a string that was salted and cryptographically hashed using the bcrypt
/// algorithm. The salt is stored alongside the hash. A `Hash` is well suited to store encrypted
/// passwords.
///
/// Since `Hash` implements `From<&str>`, the preferred method to obtain a `Hash` is to convert a
/// string slice using `Into<Hash>` as shown in the example below.
///
/// A `Hash` can be compared to a given string slice (== verified) with the method [`is`].
///
/// Because of different, randomly chosen salts, two hashes are (almost) never equal, even if
/// obtained from the exact same plain text.
///
/// [`is`]: #method.is
///
/// # Example
///
/// ```
/// use dynonym::types::Hash;
///
/// // Create
/// let h: Hash = "foo".into();
///
/// // Verify
/// assert!( h.is("foo"));
/// assert!(!h.is("bar"));
/// ```
#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Hash(String);

impl Hash {
    /// Verifies whether `self` is a hashed version of the given string slice.
    pub fn is(&self, plain: &str) -> bool {
        use bcrypt::verify;
        verify(plain, &self.0).unwrap()
    }
}

impl<'a> From<&'a str> for Hash {
    fn from(plain: &'a str) -> Self {
        use bcrypt::{hash, DEFAULT_COST};
        let hash = hash(plain, DEFAULT_COST).unwrap();
        Hash(hash)
    }
}

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

    #[test]
    fn hash_eq() {
        let h: Hash = "foo".into();
        assert!(h.is("foo"));
    }

    #[test]
    fn hash_ne() {
        let h: Hash = "foo".into();
        assert!(!h.is("bar"));
    }

    #[test]
    fn hash_salt() {
        let h1: Hash = "foo".into();
        let h2: Hash = "foo".into();
        assert!(h1 != h2);  // different salts!
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::time::{Duration, Instant};

use regex::{Captures, Regex};

use console::{measure_text_width, Style};

pub fn duration_to_secs(d: Duration) -> f64 {
    d.as_secs() as f64 + d.subsec_nanos() as f64 / 1_000_000_000f64
}

pub fn secs_to_duration(s: f64) -> Duration {
    let secs = s.trunc() as u64;
    let nanos = (s.fract() * 1_000_000_000f64) as u32;
    Duration::new(secs, nanos)
}

pub struct Estimate {
    buf: Vec<f64>,
    buf_cap: usize,
    last_idx: usize,
    started: Option<(Instant, u64)>,
}

impl Estimate {
    pub fn new() -> Estimate {
        Estimate {
            buf: vec![],
            buf_cap: 10,
            last_idx: 0,
            started: None,
        }
    }

    pub fn reset(&mut self) {
        self.buf.clear();
        self.last_idx = 0;
        self.started = None;
    }

    pub fn record_step(&mut self, value: u64) {
        // record initial position
        let (started_time, started_value) = match self.started {
            None => {
                let rv = (Instant::now(), value);
                self.started = Some(rv);
                rv
            }
            Some(value) => value,
        };

        let item = if value == 0 {
            0.0
        } else {
            duration_to_secs(started_time.elapsed()) / (value.saturating_sub(started_value)) as f64
        };
        if self.buf.len() >= self.buf_cap {
            let idx = self.last_idx % self.buf.len();
            self.buf[idx] = item;
        } else {
            self.buf.push(item);
        }
        self.last_idx += 1;
    }

    pub fn time_per_step(&self) -> Duration {
        if self.buf.is_empty() {
            Duration::new(0, 0)
        } else {
            secs_to_duration(self.buf.iter().sum::<f64>() / self.buf.len() as f64)
        }
    }
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Alignment {
    Left,
    Center,
    Right,
}

#[derive(Debug)]
pub struct TemplateVar<'a> {
    pub key: &'a str,
    pub align: Alignment,
    pub truncate: bool,
    pub width: Option<usize>,
    pub style: Option<Style>,
    pub alt_style: Option<Style>,
    pub last_element: bool,
}

impl<'a> TemplateVar<'a> {
    pub fn duplicate_for_key<'b>(&self, key: &'b str) -> TemplateVar<'b> {
        TemplateVar {
            key: key,
            align: self.align,
            truncate: self.truncate,
            width: self.width,
            style: self.style.clone(),
            alt_style: self.alt_style.clone(),
            last_element: self.last_element,
        }
    }
}

pub fn expand_template<'a, F: Fn(&TemplateVar) -> String>(s: &'a str, f: F) -> Cow<'a, str> {
    lazy_static! {
        static ref VAR_RE: Regex = Regex::new(r"(\}\})|\{(\{|[^}]+\})").unwrap();
        static ref KEY_RE: Regex = Regex::new(
            r"(?x)
                ([^:]+)
                (?:
                    :
                    ([<^>])?
                    (\d+)?
                    (!)?
                    (?:\.([a-z_]+(?:\.[a-z_]+)*))?
                    (?:/([a-z_]+(?:\.[a-z_]+)*))?
                )?
            "
        ).unwrap();
    }
    VAR_RE.replace_all(s, |caps: &Captures| {
        if caps.get(1).is_some() {
            return "}".into();
        }
        let key = &caps[2];
        if key == "{" {
            return "{".into();
        }
        let mut var = TemplateVar {
            key: key,
            align: Alignment::Left,
            truncate: false,
            width: None,
            style: None,
            alt_style: None,
            last_element: caps.get(0).unwrap().end() >= s.len(),
        };
        if let Some(opt_caps) = KEY_RE.captures(&key[..key.len() - 1]) {
            if let Some(short_key) = opt_caps.get(1) {
                var.key = short_key.as_str();
            }
            var.align = match opt_caps.get(2).map(|x| x.as_str()) {
                Some("<") => Alignment::Left,
                Some("^") => Alignment::Center,
                Some(">") => Alignment::Right,
                _ => Alignment::Left,
            };
            if let Some(width) = opt_caps.get(3) {
                var.width = Some(width.as_str().parse().unwrap());
            }
            if opt_caps.get(4).is_some() {
                var.truncate = true;
            }
            if let Some(style) = opt_caps.get(5) {
                var.style = Some(Style::from_dotted_str(style.as_str()));
            }
            if let Some(alt_style) = opt_caps.get(6) {
                var.alt_style = Some(Style::from_dotted_str(alt_style.as_str()));
            }
        }
        let mut rv = f(&var);
        if let Some(width) = var.width {
            rv = pad_str(&rv, width, var.align, var.truncate).to_string()
        }
        if let Some(s) = var.style {
            rv = s.apply_to(rv).to_string();
        }
        rv
    })
}

pub fn pad_str<'a>(s: &'a str, width: usize, align: Alignment, truncate: bool) -> Cow<'a, str> {
    let cols = measure_text_width(s);

    if cols >= width {
        return if truncate {
            Cow::Borrowed(s.get(..width).unwrap_or(s))
        } else {
            Cow::Borrowed(s)
        };
    }

    let diff = width.saturating_sub(cols);

    let (left_pad, right_pad) = match align {
        Alignment::Left => (0, diff),
        Alignment::Right => (diff, 0),
        Alignment::Center => (diff / 2, diff.saturating_sub(diff / 2)),
    };

    let mut rv = String::new();
    for _ in 0..left_pad {
        rv.push(' ');
    }
    rv.push_str(s);
    for _ in 0..right_pad {
        rv.push(' ');
    }
    Cow::Owned(rv)
}

#[test]
fn test_expand_template() {
    let rv = expand_template("{{ {foo} {bar} }}", |var| var.key.to_uppercase());
    assert_eq!(&rv, "{ FOO BAR }");
}

#[test]
fn test_expand_template_flags() {
    use console::set_colors_enabled;
    set_colors_enabled(true);

    let rv = expand_template("{foo:5}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        "XXX".into()
    });
    assert_eq!(&rv, "XXX  ");

    let rv = expand_template("{foo:.red.on_blue}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, None);
        assert_eq!(var.align, Alignment::Left);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44mXXX\u{1b}[0m");

    let rv = expand_template("{foo:^5.red.on_blue}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        assert_eq!(var.align, Alignment::Center);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");

    let rv = expand_template("{foo:^5.red.on_blue/green.on_cyan}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        assert_eq!(var.align, Alignment::Center);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        assert_eq!(var.alt_style, Some(Style::new().green().on_cyan()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");
}

#[test]
fn test_duration_stuff() {
    let duration = Duration::new(42, 100_000_000);
    let secs = duration_to_secs(duration);
    assert_eq!(secs_to_duration(secs), duration);
}
<commit_msg>Fixed a division by zero in eta calculation.  Fixes #81<commit_after>use std::borrow::Cow;
use std::time::{Duration, Instant};

use regex::{Captures, Regex};

use console::{measure_text_width, Style};

pub fn duration_to_secs(d: Duration) -> f64 {
    d.as_secs() as f64 + d.subsec_nanos() as f64 / 1_000_000_000f64
}

pub fn secs_to_duration(s: f64) -> Duration {
    let secs = s.trunc() as u64;
    let nanos = (s.fract() * 1_000_000_000f64) as u32;
    Duration::new(secs, nanos)
}

pub struct Estimate {
    buf: Vec<f64>,
    buf_cap: usize,
    last_idx: usize,
    started: Option<(Instant, u64)>,
}

impl Estimate {
    pub fn new() -> Estimate {
        Estimate {
            buf: vec![],
            buf_cap: 10,
            last_idx: 0,
            started: None,
        }
    }

    pub fn reset(&mut self) {
        self.buf.clear();
        self.last_idx = 0;
        self.started = None;
    }

    pub fn record_step(&mut self, value: u64) {
        // record initial position
        let (started_time, started_value) = match self.started {
            None => {
                let rv = (Instant::now(), value);
                self.started = Some(rv);
                rv
            }
            Some(value) => value,
        };

        let item = if value == 0 {
            0.0
        } else {
            let divisor = value.saturating_sub(started_value) as f64;
            if divisor == 0.0 {
                0.0
            } else {
                duration_to_secs(started_time.elapsed()) / divisor
            }
        };
        if self.buf.len() >= self.buf_cap {
            let idx = self.last_idx % self.buf.len();
            self.buf[idx] = item;
        } else {
            self.buf.push(item);
        }
        self.last_idx += 1;
    }

    pub fn time_per_step(&self) -> Duration {
        if self.buf.is_empty() {
            Duration::new(0, 0)
        } else {
            secs_to_duration(self.buf.iter().sum::<f64>() / self.buf.len() as f64)
        }
    }
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Alignment {
    Left,
    Center,
    Right,
}

#[derive(Debug)]
pub struct TemplateVar<'a> {
    pub key: &'a str,
    pub align: Alignment,
    pub truncate: bool,
    pub width: Option<usize>,
    pub style: Option<Style>,
    pub alt_style: Option<Style>,
    pub last_element: bool,
}

impl<'a> TemplateVar<'a> {
    pub fn duplicate_for_key<'b>(&self, key: &'b str) -> TemplateVar<'b> {
        TemplateVar {
            key: key,
            align: self.align,
            truncate: self.truncate,
            width: self.width,
            style: self.style.clone(),
            alt_style: self.alt_style.clone(),
            last_element: self.last_element,
        }
    }
}

pub fn expand_template<'a, F: Fn(&TemplateVar) -> String>(s: &'a str, f: F) -> Cow<'a, str> {
    lazy_static! {
        static ref VAR_RE: Regex = Regex::new(r"(\}\})|\{(\{|[^}]+\})").unwrap();
        static ref KEY_RE: Regex = Regex::new(
            r"(?x)
                ([^:]+)
                (?:
                    :
                    ([<^>])?
                    (\d+)?
                    (!)?
                    (?:\.([a-z_]+(?:\.[a-z_]+)*))?
                    (?:/([a-z_]+(?:\.[a-z_]+)*))?
                )?
            "
        ).unwrap();
    }
    VAR_RE.replace_all(s, |caps: &Captures| {
        if caps.get(1).is_some() {
            return "}".into();
        }
        let key = &caps[2];
        if key == "{" {
            return "{".into();
        }
        let mut var = TemplateVar {
            key: key,
            align: Alignment::Left,
            truncate: false,
            width: None,
            style: None,
            alt_style: None,
            last_element: caps.get(0).unwrap().end() >= s.len(),
        };
        if let Some(opt_caps) = KEY_RE.captures(&key[..key.len() - 1]) {
            if let Some(short_key) = opt_caps.get(1) {
                var.key = short_key.as_str();
            }
            var.align = match opt_caps.get(2).map(|x| x.as_str()) {
                Some("<") => Alignment::Left,
                Some("^") => Alignment::Center,
                Some(">") => Alignment::Right,
                _ => Alignment::Left,
            };
            if let Some(width) = opt_caps.get(3) {
                var.width = Some(width.as_str().parse().unwrap());
            }
            if opt_caps.get(4).is_some() {
                var.truncate = true;
            }
            if let Some(style) = opt_caps.get(5) {
                var.style = Some(Style::from_dotted_str(style.as_str()));
            }
            if let Some(alt_style) = opt_caps.get(6) {
                var.alt_style = Some(Style::from_dotted_str(alt_style.as_str()));
            }
        }
        let mut rv = f(&var);
        if let Some(width) = var.width {
            rv = pad_str(&rv, width, var.align, var.truncate).to_string()
        }
        if let Some(s) = var.style {
            rv = s.apply_to(rv).to_string();
        }
        rv
    })
}

pub fn pad_str<'a>(s: &'a str, width: usize, align: Alignment, truncate: bool) -> Cow<'a, str> {
    let cols = measure_text_width(s);

    if cols >= width {
        return if truncate {
            Cow::Borrowed(s.get(..width).unwrap_or(s))
        } else {
            Cow::Borrowed(s)
        };
    }

    let diff = width.saturating_sub(cols);

    let (left_pad, right_pad) = match align {
        Alignment::Left => (0, diff),
        Alignment::Right => (diff, 0),
        Alignment::Center => (diff / 2, diff.saturating_sub(diff / 2)),
    };

    let mut rv = String::new();
    for _ in 0..left_pad {
        rv.push(' ');
    }
    rv.push_str(s);
    for _ in 0..right_pad {
        rv.push(' ');
    }
    Cow::Owned(rv)
}

#[test]
fn test_expand_template() {
    let rv = expand_template("{{ {foo} {bar} }}", |var| var.key.to_uppercase());
    assert_eq!(&rv, "{ FOO BAR }");
}

#[test]
fn test_expand_template_flags() {
    use console::set_colors_enabled;
    set_colors_enabled(true);

    let rv = expand_template("{foo:5}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        "XXX".into()
    });
    assert_eq!(&rv, "XXX  ");

    let rv = expand_template("{foo:.red.on_blue}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, None);
        assert_eq!(var.align, Alignment::Left);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44mXXX\u{1b}[0m");

    let rv = expand_template("{foo:^5.red.on_blue}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        assert_eq!(var.align, Alignment::Center);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");

    let rv = expand_template("{foo:^5.red.on_blue/green.on_cyan}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        assert_eq!(var.align, Alignment::Center);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        assert_eq!(var.alt_style, Some(Style::new().green().on_cyan()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");
}

#[test]
fn test_duration_stuff() {
    let duration = Duration::new(42, 100_000_000);
    let secs = duration_to_secs(duration);
    assert_eq!(secs_to_duration(secs), duration);
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};
use std::io;
use std::fs;
use std::fs::File;
use std::io::Read;
use std::collections::HashMap;
use std::hash::Hash;
use std;
use log;

#[cfg(all(windows, target_env = "msvc"))]
pub fn is_msvc() -> bool {
  true
}

#[cfg(not(all(windows, target_env = "msvc")))]
pub fn is_msvc() -> bool {
  false
}

pub trait JoinWithString<S> {
  fn join(self, separator: S) -> String;
}

impl<S, S2, X> JoinWithString<S2> for X
  where S: AsRef<str>,
        S2: AsRef<str>,
        X: Iterator<Item = S>
{
  fn join(self, separator: S2) -> String {
    self.fold("".to_string(), |a, b| {
      let m = if a.len() > 0 {
        a + separator.as_ref()
      } else {
        a
      };
      m + b.as_ref()
    })
  }
}

pub trait PathBufPushTweak {
  fn with_added<P: AsRef<Path>>(&self, path: P) -> PathBuf;
}

impl PathBufPushTweak for PathBuf {
  fn with_added<P: AsRef<Path>>(&self, path: P) -> PathBuf {
    let mut p = self.clone();
    p.push(path);
    p
  }
}

impl PathBufPushTweak for Path {
  fn with_added<P: AsRef<Path>>(&self, path: P) -> PathBuf {
    let mut p = self.to_path_buf();
    p.push(path);
    p
  }
}

pub fn move_files(src: &PathBuf, dst: &PathBuf) -> io::Result<()> {
  if src.as_path().is_dir() {
    if !dst.as_path().is_dir() {
      log::info(format!("New dir created: {}", dst.to_str().unwrap()));
      try!(fs::create_dir(dst));
    }

    for item in try!(fs::read_dir(dst)) {
      let item = try!(item);
      if !src.with_added(item.file_name()).as_path().exists() {
        let path = item.path();
        if path.as_path().is_dir() {
          log::info(format!("Old dir removed: {}", path.to_str().unwrap()));
          try!(fs::remove_dir_all(path));
        } else {
          log::info(format!("Old file removed: {}", path.to_str().unwrap()));
          try!(fs::remove_file(path));
        }
      }
    }

    for item in try!(fs::read_dir(src)) {
      let item = try!(item);
      try!(move_files(&item.path().to_path_buf(),
                      &dst.with_added(item.file_name())));
    }
    try!(fs::remove_dir_all(src));
  } else {
    try!(move_one_file(src, dst));
  }
  Ok(())
}

pub fn copy_recursively(src: &PathBuf, dst: &PathBuf) -> io::Result<()> {
  if src.as_path().is_dir() {
    try!(fs::create_dir(dst));
    for item in try!(fs::read_dir(src)) {
      let item = try!(item);
      try!(copy_recursively(&item.path().to_path_buf(),
                            &dst.with_added(item.file_name())));
    }
  } else {
    try!(fs::copy(src, dst));
  }
  Ok(())
}

pub fn move_one_file(old_path: &PathBuf, new_path: &PathBuf) -> io::Result<()> {
  let is_changed = if new_path.as_path().is_file() {
    let mut string1 = String::new();
    let mut string2 = String::new();
    try!(try!(File::open(old_path)).read_to_string(&mut string1));
    try!(try!(File::open(new_path)).read_to_string(&mut string2));
    string1 != string2
  } else {
    true
  };

  if is_changed {
    if new_path.as_path().exists() {
      try!(fs::remove_file(new_path));
    }
    try!(fs::rename(old_path, new_path));
    log::noisy(format!("File changed: {}", new_path.to_str().unwrap()));
  } else {
    try!(fs::remove_file(old_path));
    log::noisy(format!("File not changed: {}", new_path.to_str().unwrap()));
  }
  Ok(())
}

#[derive(PartialEq, Debug)]
enum WordCase {
  Upper,
  Lower,
  Capitalized,
}

pub struct WordIterator<'a> {
  string: &'a str,
  index: usize,
  previous_word_case: Option<WordCase>,
}

impl<'a> WordIterator<'a> {
  pub fn new(string: &str) -> WordIterator {
    WordIterator {
      string: string,
      index: 0,
      previous_word_case: None,
    }
  }
}

impl<'a> Iterator for WordIterator<'a> {
  type Item = &'a str;
  fn next(&mut self) -> Option<&'a str> {
    while self.index < self.string.len() && &self.string[self.index..self.index + 1] == "_" {
      self.index = self.index + 1;
    }
    if self.index >= self.string.len() {
      return None;
    }
    let mut i = self.index;
    let mut word_case = WordCase::Lower;
    while i < self.string.len() {
      let current = &self.string[i..i + 1].chars().next().unwrap();
      if current == &'_' {
        break;
      }
      if i - self.index == 0 {
        // first letter
        if current.is_uppercase() {
          word_case = WordCase::Capitalized;
        } else {
          word_case = WordCase::Lower;
        }
      } else if i - self.index == 1 {
        if current.is_uppercase() {
          if word_case == WordCase::Capitalized {
            let next_not_upper = if i + 1 < self.string.len() {
              !self.string[i + 1..i + 2].chars().next().unwrap().is_uppercase()
            } else {
              true
            };
            if next_not_upper || self.previous_word_case == Some(WordCase::Capitalized) {
              break;
            } else {
              word_case = WordCase::Upper;
            }
          } else if word_case == WordCase::Lower {
            break;
          }
        }
      } else {
        match word_case {
          WordCase::Lower | WordCase::Capitalized => {
            if current.is_uppercase() {
              break;
            }
          }
          WordCase::Upper => {
            if !current.is_uppercase() {
              break;
            }
          }
        }
      }
      i = i + 1;
    }
    let result = &self.string[self.index..i];
    self.index = i;
    self.previous_word_case = Some(word_case);
    return Some(result);
  }
}

pub trait CaseOperations {
  fn to_class_case(&self) -> String;
  fn to_snake_case(&self) -> String;
}
pub trait VecCaseOperations {
  fn to_class_case(self) -> String;
  fn to_snake_case(self) -> String;
}

fn iterator_to_class_case<'a, S: AsRef<str>, T: Iterator<Item = S>>(it: T) -> String {
  it.map(|x| {
      format!("{}{}",
              x.as_ref()[0..1].to_uppercase(),
              x.as_ref()[1..].to_lowercase())
    })
    .join("")
}

fn iterator_to_snake_case<'a, S: AsRef<str>, T: Iterator<Item = S>>(it: T) -> String {
  it.map(|x| x.as_ref().to_lowercase()).join("_")
}

fn replace_all_sub_vecs(parts: &mut Vec<String>, needle: Vec<&str>) {
  let mut any_found = true;
  while any_found {
    any_found = false;
    for i in 0..parts.len() + 1 - needle.len() {
      if &parts[i..i + needle.len()] == &needle[..] {
        for _ in 0..needle.len() - 1 {
          parts.remove(i + 1);
        }
        parts[i] = needle.join("");
        any_found = true;
        break;
      }
    }
  }
}

impl CaseOperations for String {
  fn to_class_case(&self) -> Self {
    iterator_to_class_case(WordIterator::new(self))
  }
  fn to_snake_case(&self) -> Self {
    let mut parts: Vec<_> = WordIterator::new(self).map(|x| x.to_lowercase()).collect();
    replace_all_sub_vecs(&mut parts, vec!["na", "n"]);
    replace_all_sub_vecs(&mut parts, vec!["open", "g", "l"]);
    parts.join("_")
  }
}

impl<'a> VecCaseOperations for Vec<&'a str> {
  fn to_class_case(self) -> String {
    iterator_to_class_case(self.into_iter())
  }
  fn to_snake_case(self) -> String {
    iterator_to_snake_case(self.into_iter())
  }
}

pub fn add_to_multihash<K: Eq + Hash + Clone, T, V: Default + Extend<T>>(hash: &mut HashMap<K, V>,
                                                                         key: &K,
                                                                         value: T) {
  if !hash.contains_key(key) {
    hash.insert(key.clone(), Default::default());
  }
  hash.get_mut(key).unwrap().extend(std::iter::once(value));
}
<commit_msg>bugfix<commit_after>use std::path::{Path, PathBuf};
use std::io;
use std::fs;
use std::fs::File;
use std::io::Read;
use std::collections::HashMap;
use std::hash::Hash;
use std;
use log;

#[cfg(all(windows, target_env = "msvc"))]
pub fn is_msvc() -> bool {
  true
}

#[cfg(not(all(windows, target_env = "msvc")))]
pub fn is_msvc() -> bool {
  false
}

pub trait JoinWithString<S> {
  fn join(self, separator: S) -> String;
}

impl<S, S2, X> JoinWithString<S2> for X
  where S: AsRef<str>,
        S2: AsRef<str>,
        X: Iterator<Item = S>
{
  fn join(self, separator: S2) -> String {
    self.fold("".to_string(), |a, b| {
      let m = if a.len() > 0 {
        a + separator.as_ref()
      } else {
        a
      };
      m + b.as_ref()
    })
  }
}

pub trait PathBufPushTweak {
  fn with_added<P: AsRef<Path>>(&self, path: P) -> PathBuf;
}

impl PathBufPushTweak for PathBuf {
  fn with_added<P: AsRef<Path>>(&self, path: P) -> PathBuf {
    let mut p = self.clone();
    p.push(path);
    p
  }
}

impl PathBufPushTweak for Path {
  fn with_added<P: AsRef<Path>>(&self, path: P) -> PathBuf {
    let mut p = self.to_path_buf();
    p.push(path);
    p
  }
}

pub fn move_files(src: &PathBuf, dst: &PathBuf) -> io::Result<()> {
  if src.as_path().is_dir() {
    if !dst.as_path().is_dir() {
      log::info(format!("New dir created: {}", dst.to_str().unwrap()));
      try!(fs::create_dir(dst));
    }

    for item in try!(fs::read_dir(dst)) {
      let item = try!(item);
      if !src.with_added(item.file_name()).as_path().exists() {
        let path = item.path();
        if path.as_path().is_dir() {
          log::info(format!("Old dir removed: {}", path.to_str().unwrap()));
          try!(fs::remove_dir_all(path));
        } else {
          log::info(format!("Old file removed: {}", path.to_str().unwrap()));
          try!(fs::remove_file(path));
        }
      }
    }

    for item in try!(fs::read_dir(src)) {
      let item = try!(item);
      try!(move_files(&item.path().to_path_buf(),
                      &dst.with_added(item.file_name())));
    }
    try!(fs::remove_dir_all(src));
  } else {
    try!(move_one_file(src, dst));
  }
  Ok(())
}

pub fn copy_recursively(src: &PathBuf, dst: &PathBuf) -> io::Result<()> {
  if src.as_path().is_dir() {
    try!(fs::create_dir(dst));
    for item in try!(fs::read_dir(src)) {
      let item = try!(item);
      try!(copy_recursively(&item.path().to_path_buf(),
                            &dst.with_added(item.file_name())));
    }
  } else {
    try!(fs::copy(src, dst));
  }
  Ok(())
}

pub fn move_one_file(old_path: &PathBuf, new_path: &PathBuf) -> io::Result<()> {
  let is_changed = if new_path.as_path().is_file() {
    let mut string1 = String::new();
    let mut string2 = String::new();
    try!(try!(File::open(old_path)).read_to_string(&mut string1));
    try!(try!(File::open(new_path)).read_to_string(&mut string2));
    string1 != string2
  } else {
    true
  };

  if is_changed {
    if new_path.as_path().exists() {
      try!(fs::remove_file(new_path));
    }
    try!(fs::rename(old_path, new_path));
    log::noisy(format!("File changed: {}", new_path.to_str().unwrap()));
  } else {
    try!(fs::remove_file(old_path));
    log::noisy(format!("File not changed: {}", new_path.to_str().unwrap()));
  }
  Ok(())
}

#[derive(PartialEq, Debug)]
enum WordCase {
  Upper,
  Lower,
  Capitalized,
}

pub struct WordIterator<'a> {
  string: &'a str,
  index: usize,
  previous_word_case: Option<WordCase>,
}

impl<'a> WordIterator<'a> {
  pub fn new(string: &str) -> WordIterator {
    WordIterator {
      string: string,
      index: 0,
      previous_word_case: None,
    }
  }
}

impl<'a> Iterator for WordIterator<'a> {
  type Item = &'a str;
  fn next(&mut self) -> Option<&'a str> {
    while self.index < self.string.len() && &self.string[self.index..self.index + 1] == "_" {
      self.index = self.index + 1;
    }
    if self.index >= self.string.len() {
      return None;
    }
    let mut i = self.index;
    let mut word_case = WordCase::Lower;
    while i < self.string.len() {
      let current = &self.string[i..i + 1].chars().next().unwrap();
      if current == &'_' {
        break;
      }
      if i - self.index == 0 {
        // first letter
        if current.is_uppercase() {
          word_case = WordCase::Capitalized;
        } else {
          word_case = WordCase::Lower;
        }
      } else if i - self.index == 1 {
        if current.is_uppercase() {
          if word_case == WordCase::Capitalized {
            let next_not_upper = if i + 1 < self.string.len() {
              !self.string[i + 1..i + 2].chars().next().unwrap().is_uppercase()
            } else {
              true
            };
            if next_not_upper || self.previous_word_case == Some(WordCase::Capitalized) {
              break;
            } else {
              word_case = WordCase::Upper;
            }
          } else if word_case == WordCase::Lower {
            break;
          }
        }
      } else {
        match word_case {
          WordCase::Lower | WordCase::Capitalized => {
            if current.is_uppercase() {
              break;
            }
          }
          WordCase::Upper => {
            if !current.is_uppercase() {
              break;
            }
          }
        }
      }
      i = i + 1;
    }
    let result = &self.string[self.index..i];
    self.index = i;
    self.previous_word_case = Some(word_case);
    return Some(result);
  }
}

pub trait CaseOperations {
  fn to_class_case(&self) -> String;
  fn to_snake_case(&self) -> String;
}
pub trait VecCaseOperations {
  fn to_class_case(self) -> String;
  fn to_snake_case(self) -> String;
}

fn iterator_to_class_case<'a, S: AsRef<str>, T: Iterator<Item = S>>(it: T) -> String {
  it.map(|x| {
      format!("{}{}",
              x.as_ref()[0..1].to_uppercase(),
              x.as_ref()[1..].to_lowercase())
    })
    .join("")
}

fn iterator_to_snake_case<'a, S: AsRef<str>, T: Iterator<Item = S>>(it: T) -> String {
  it.map(|x| x.as_ref().to_lowercase()).join("_")
}

fn replace_all_sub_vecs(parts: &mut Vec<String>, needle: Vec<&str>) {
  let mut any_found = true;
  while any_found {
    any_found = false;
    if parts.len() + 1 >= needle.len() {
      for i in 0..parts.len() + 1 - needle.len() {
        if &parts[i..i + needle.len()] == &needle[..] {
          for _ in 0..needle.len() - 1 {
            parts.remove(i + 1);
          }
          parts[i] = needle.join("");
          any_found = true;
          break;
        }
      }
    }
  }
}

impl CaseOperations for String {
  fn to_class_case(&self) -> Self {
    iterator_to_class_case(WordIterator::new(self))
  }
  fn to_snake_case(&self) -> Self {
    let mut parts: Vec<_> = WordIterator::new(self).map(|x| x.to_lowercase()).collect();
    replace_all_sub_vecs(&mut parts, vec!["na", "n"]);
    replace_all_sub_vecs(&mut parts, vec!["open", "g", "l"]);
    parts.join("_")
  }
}

impl<'a> VecCaseOperations for Vec<&'a str> {
  fn to_class_case(self) -> String {
    iterator_to_class_case(self.into_iter())
  }
  fn to_snake_case(self) -> String {
    iterator_to_snake_case(self.into_iter())
  }
}

pub fn add_to_multihash<K: Eq + Hash + Clone, T, V: Default + Extend<T>>(hash: &mut HashMap<K, V>,
                                                                         key: &K,
                                                                         value: T) {
  if !hash.contains_key(key) {
    hash.insert(key.clone(), Default::default());
  }
  hash.get_mut(key).unwrap().extend(std::iter::once(value));
}
<|endoftext|>


<commit_before>//! Contains the different types of values understood by XML-RPC.

use utils::{escape_xml, format_datetime};

use base64::encode;
use iso8601::DateTime;

use std::collections::BTreeMap;
use std::io::{self, Write};

/// The possible XML-RPC values.
#[derive(Debug, PartialEq)]
pub enum Value {
    /// `<i4>` or `<int>`, 32-bit signed integer.
    Int(i32),
    /// `<i8>`, 64-bit signed integer.
    ///
    /// This is a non-standard feature that may not be supported on all servers
    /// or clients.
    Int64(i64),
    /// `<boolean>`, 0 == `false`, 1 == `true`.
    Bool(bool),
    /// `<string>`
    // FIXME zero-copy? `Cow<'static, ..>`?
    String(String),
    /// `<double>`
    Double(f64),
    /// `<dateTime.iso8601>`, an ISO 8601 formatted date/time value.
    DateTime(DateTime),
    /// `<base64>`, base64-encoded binary data.
    Base64(Vec<u8>),

    /// `<struct>`, a mapping of named values.
    Struct(BTreeMap<String, Value>),
    /// `<array>`, a list of arbitrary (heterogeneous) values.
    Array(Vec<Value>),

    /// `</nil>`
    ///
    /// Ref: https://web.archive.org/web/20050911054235/http://ontosys.com/xml-rpc/extensions.php
    Nil
}

impl Value {
    /// Formats this `Value` as an XML `<value>` element.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        writeln!(fmt, "<value>")?;

        match *self {
            Value::Int(i) => {
                writeln!(fmt, "<i4>{}</i4>", i)?;
            }
            Value::Int64(i) => {
                writeln!(fmt, "<i8>{}</i8>", i)?;
            }
            Value::Bool(b) => {
                writeln!(fmt, "<boolean>{}</boolean>", if b { "1" } else { "0" })?;
            }
            Value::String(ref s) => {
                writeln!(fmt, "<string>{}</string>", escape_xml(s))?;
            }
            Value::Double(d) => {
                writeln!(fmt, "<double>{}</double>", d)?;
            }
            Value::DateTime(date_time) => {
                writeln!(fmt, "<dateTime.iso8601>{}</dateTime.iso8601>", format_datetime(&date_time))?;
            }
            Value::Base64(ref data) => {
                writeln!(fmt, "<base64>{}</base64>", encode(data))?;
            }
            Value::Struct(ref map) => {
                writeln!(fmt, "<struct>")?;
                for (ref name, ref value) in map {
                    writeln!(fmt, "<member>")?;
                    writeln!(fmt, "<name>{}</name>", escape_xml(name))?;
                    value.write_as_xml(fmt)?;
                    writeln!(fmt, "</member>")?;
                }
                writeln!(fmt, "</struct>")?;
            }
            Value::Array(ref array) => {
                writeln!(fmt, "<array>")?;
                writeln!(fmt, "<data>")?;
                for value in array {
                    value.write_as_xml(fmt)?;
                }
                writeln!(fmt, "</data>")?;
                writeln!(fmt, "</array>")?;
            }
            Value::Nil => {
                writeln!(fmt, "<nil/>")?;
            }
        }

        writeln!(fmt, "</value>")?;
        Ok(())
    }
}

impl From<i32> for Value {
    fn from(other: i32) -> Self {
        Value::Int(other)
    }
}

impl From<bool> for Value {
    fn from(other: bool) -> Self {
        Value::Bool(other)
    }
}

impl From<String> for Value {
    fn from(other: String) -> Self {
        Value::String(other)
    }
}

impl<'a> From<&'a str> for Value {
    fn from(other: &'a str) -> Self {
        Value::String(other.to_string())
    }
}

impl From<f64> for Value {
    fn from(other: f64) -> Self {
        Value::Double(other)
    }
}

impl From<DateTime> for Value {
    fn from(other: DateTime) -> Self {
        Value::DateTime(other)
    }
}

impl From<Vec<u8>> for Value {
    fn from(other: Vec<u8>) -> Self {
        Value::Base64(other)
    }
}

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

    #[test]
    fn escapes_strings() {
        let mut output: Vec<u8> = Vec::new();

        Value::from("<xml> string").write_as_xml(&mut output).unwrap();
        assert_eq!(str::from_utf8(&output).unwrap(), "<value>\n<string><xml>&nbsp;string</string>\n</value>\n");
    }

    #[test]
    fn escapes_struct_member_names() {
        let mut output: Vec<u8> = Vec::new();
        let mut map: BTreeMap<String, Value> = BTreeMap::new();
        map.insert("x&<x".to_string(), Value::from(true));

        Value::Struct(map).write_as_xml(&mut output).unwrap();
        assert_eq!(str::from_utf8(&output).unwrap(), "<value>\n<struct>\n<member>\n<name>x&<x</name>\n<value>\n<boolean>1</boolean>\n</value>\n</member>\n</struct>\n</value>\n");
    }
}
<commit_msg>Derive Clone for Value.<commit_after>//! Contains the different types of values understood by XML-RPC.

use utils::{escape_xml, format_datetime};

use base64::encode;
use iso8601::DateTime;

use std::collections::BTreeMap;
use std::io::{self, Write};

/// The possible XML-RPC values.
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
    /// `<i4>` or `<int>`, 32-bit signed integer.
    Int(i32),
    /// `<i8>`, 64-bit signed integer.
    ///
    /// This is a non-standard feature that may not be supported on all servers
    /// or clients.
    Int64(i64),
    /// `<boolean>`, 0 == `false`, 1 == `true`.
    Bool(bool),
    /// `<string>`
    // FIXME zero-copy? `Cow<'static, ..>`?
    String(String),
    /// `<double>`
    Double(f64),
    /// `<dateTime.iso8601>`, an ISO 8601 formatted date/time value.
    DateTime(DateTime),
    /// `<base64>`, base64-encoded binary data.
    Base64(Vec<u8>),

    /// `<struct>`, a mapping of named values.
    Struct(BTreeMap<String, Value>),
    /// `<array>`, a list of arbitrary (heterogeneous) values.
    Array(Vec<Value>),

    /// `</nil>`
    ///
    /// Ref: https://web.archive.org/web/20050911054235/http://ontosys.com/xml-rpc/extensions.php
    Nil
}

impl Value {
    /// Formats this `Value` as an XML `<value>` element.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        writeln!(fmt, "<value>")?;

        match *self {
            Value::Int(i) => {
                writeln!(fmt, "<i4>{}</i4>", i)?;
            }
            Value::Int64(i) => {
                writeln!(fmt, "<i8>{}</i8>", i)?;
            }
            Value::Bool(b) => {
                writeln!(fmt, "<boolean>{}</boolean>", if b { "1" } else { "0" })?;
            }
            Value::String(ref s) => {
                writeln!(fmt, "<string>{}</string>", escape_xml(s))?;
            }
            Value::Double(d) => {
                writeln!(fmt, "<double>{}</double>", d)?;
            }
            Value::DateTime(date_time) => {
                writeln!(fmt, "<dateTime.iso8601>{}</dateTime.iso8601>", format_datetime(&date_time))?;
            }
            Value::Base64(ref data) => {
                writeln!(fmt, "<base64>{}</base64>", encode(data))?;
            }
            Value::Struct(ref map) => {
                writeln!(fmt, "<struct>")?;
                for (ref name, ref value) in map {
                    writeln!(fmt, "<member>")?;
                    writeln!(fmt, "<name>{}</name>", escape_xml(name))?;
                    value.write_as_xml(fmt)?;
                    writeln!(fmt, "</member>")?;
                }
                writeln!(fmt, "</struct>")?;
            }
            Value::Array(ref array) => {
                writeln!(fmt, "<array>")?;
                writeln!(fmt, "<data>")?;
                for value in array {
                    value.write_as_xml(fmt)?;
                }
                writeln!(fmt, "</data>")?;
                writeln!(fmt, "</array>")?;
            }
            Value::Nil => {
                writeln!(fmt, "<nil/>")?;
            }
        }

        writeln!(fmt, "</value>")?;
        Ok(())
    }
}

impl From<i32> for Value {
    fn from(other: i32) -> Self {
        Value::Int(other)
    }
}

impl From<bool> for Value {
    fn from(other: bool) -> Self {
        Value::Bool(other)
    }
}

impl From<String> for Value {
    fn from(other: String) -> Self {
        Value::String(other)
    }
}

impl<'a> From<&'a str> for Value {
    fn from(other: &'a str) -> Self {
        Value::String(other.to_string())
    }
}

impl From<f64> for Value {
    fn from(other: f64) -> Self {
        Value::Double(other)
    }
}

impl From<DateTime> for Value {
    fn from(other: DateTime) -> Self {
        Value::DateTime(other)
    }
}

impl From<Vec<u8>> for Value {
    fn from(other: Vec<u8>) -> Self {
        Value::Base64(other)
    }
}

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

    #[test]
    fn escapes_strings() {
        let mut output: Vec<u8> = Vec::new();

        Value::from("<xml> string").write_as_xml(&mut output).unwrap();
        assert_eq!(str::from_utf8(&output).unwrap(), "<value>\n<string><xml>&nbsp;string</string>\n</value>\n");
    }

    #[test]
    fn escapes_struct_member_names() {
        let mut output: Vec<u8> = Vec::new();
        let mut map: BTreeMap<String, Value> = BTreeMap::new();
        map.insert("x&<x".to_string(), Value::from(true));

        Value::Struct(map).write_as_xml(&mut output).unwrap();
        assert_eq!(str::from_utf8(&output).unwrap(), "<value>\n<struct>\n<member>\n<name>x&<x</name>\n<value>\n<boolean>1</boolean>\n</value>\n</member>\n</struct>\n</value>\n");
    }
}
<|endoftext|>


<commit_before>//! Contains the different types of values understood by XML-RPC.

use utils::{escape_xml, format_datetime};

use base64::encode;
use iso8601::DateTime;

use std::collections::BTreeMap;
use std::io::{self, Write};

/// The possible XML-RPC values.
///
/// Nested values can be accessed by using [`get`](#method.get) method and [square-bracket indexing operator](#impl-Index%3CI%3E).
/// A string index can be used to access a value in a `Struct`, and a usize index can be used to access an element of an `Array`.
///
/// # Examples
///
/// ```
/// # use std::collections::BTreeMap;
/// # use xmlrpc::{Value, Index};
/// let nothing = Value::Nil;
///
/// let person = Value::Struct(vec![
///     ("name".to_string(), Value::from("John Doe")),
///     ("age".to_string(), Value::from(37)),
///     ("children".to_string(), Value::Array(vec![
///         Value::from("Mark"),
///         Value::from("Jennyfer")
///     ])),
/// ].into_iter().collect());
///
/// // get
/// assert_eq!(nothing.get("name"), None);
/// assert_eq!(person.get("name"), Some(&Value::from("John Doe")));
/// assert_eq!(person.get("SSN"), None);
///
/// // index
/// assert_eq!(nothing["name"], Value::Nil);
/// assert_eq!(person["name"], Value::from("John Doe"));
/// assert_eq!(person["age"], Value::Int(37));
/// assert_eq!(person["SSN"], Value::Nil);
/// assert_eq!(person["children"][0], Value::from("Mark"));
/// assert_eq!(person["children"][0]["age"], Value::Nil);
/// assert_eq!(person["children"][2], Value::Nil);
///
/// // extract values
/// assert_eq!(person["name"].as_str(), Some("John Doe"));
/// assert_eq!(person["age"].as_i32(), Some(37));
/// assert_eq!(person["age"].as_bool(), None);
/// assert_eq!(person["children"].as_array().unwrap().len(), 2);
/// ```
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
    /// `<i4>` or `<int>`, 32-bit signed integer.
    Int(i32),
    /// `<i8>`, 64-bit signed integer.
    ///
    /// This is an XMLRPC extension and may not be supported by all clients / servers.
    Int64(i64),
    /// `<boolean>`, 0 == `false`, 1 == `true`.
    Bool(bool),
    /// `<string>`
    // FIXME zero-copy? `Cow<'static, ..>`?
    String(String),
    /// `<double>`
    Double(f64),
    /// `<dateTime.iso8601>`, an ISO 8601 formatted date/time value.
    DateTime(DateTime),
    /// `<base64>`, base64-encoded binary data.
    Base64(Vec<u8>),

    /// `<struct>`, a mapping of named values.
    Struct(BTreeMap<String, Value>),
    /// `<array>`, a list of arbitrary (heterogeneous) values.
    Array(Vec<Value>),

    /// `<nil/>`, the empty (Unit) value.
    ///
    /// This is an XMLRPC [extension][ext] and may not be supported by all clients / servers.
    ///
    /// [ext]: https://web.archive.org/web/20050911054235/http://ontosys.com/xml-rpc/extensions.php
    Nil
}

impl Value {
    /// Formats this `Value` as an XML `<value>` element.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        writeln!(fmt, "<value>")?;

        match *self {
            Value::Int(i) => {
                writeln!(fmt, "<i4>{}</i4>", i)?;
            }
            Value::Int64(i) => {
                writeln!(fmt, "<i8>{}</i8>", i)?;
            }
            Value::Bool(b) => {
                writeln!(fmt, "<boolean>{}</boolean>", if b { "1" } else { "0" })?;
            }
            Value::String(ref s) => {
                writeln!(fmt, "<string>{}</string>", escape_xml(s))?;
            }
            Value::Double(d) => {
                writeln!(fmt, "<double>{}</double>", d)?;
            }
            Value::DateTime(date_time) => {
                writeln!(fmt, "<dateTime.iso8601>{}</dateTime.iso8601>", format_datetime(&date_time))?;
            }
            Value::Base64(ref data) => {
                writeln!(fmt, "<base64>{}</base64>", encode(data))?;
            }
            Value::Struct(ref map) => {
                writeln!(fmt, "<struct>")?;
                for (ref name, ref value) in map {
                    writeln!(fmt, "<member>")?;
                    writeln!(fmt, "<name>{}</name>", escape_xml(name))?;
                    value.write_as_xml(fmt)?;
                    writeln!(fmt, "</member>")?;
                }
                writeln!(fmt, "</struct>")?;
            }
            Value::Array(ref array) => {
                writeln!(fmt, "<array>")?;
                writeln!(fmt, "<data>")?;
                for value in array {
                    value.write_as_xml(fmt)?;
                }
                writeln!(fmt, "</data>")?;
                writeln!(fmt, "</array>")?;
            }
            Value::Nil => {
                writeln!(fmt, "<nil/>")?;
            }
        }

        writeln!(fmt, "</value>")?;
        Ok(())
    }

    /// Returns existing inner value indexed by `index`. Returns None otherwise.
    pub fn get<I: Index>(&self, index: I) -> Option<&Value> {
        index.get(self)
    }

    /// If the `Value` is an integer representable as i32, returns associated value. Returns None otherwise.
    pub fn as_i32(&self) -> Option<i32> {
        match *self {
            Value::Int(i) => Some(i),
            _ => None
        }
    }

    /// If the `Value` is an integer, returns associated value. Returns None otherwise.
    pub fn as_i64(&self) -> Option<i64> {
        match *self {
            Value::Int(i) => Some(i64::from(i)),
            Value::Int64(i) => Some(i),
            _ => None
        }
    }

    /// If the `Value` is a boolean, returns associated value. Returns None otherwise.
    pub fn as_bool(&self) -> Option<bool> {
        match *self {
            Value::Bool(b) => Some(b),
            _ => None
        }
    }

    /// If the `Value` is a string, returns associated value. Returns None otherwise.
    pub fn as_str(&self) -> Option<&str> {
        match *self {
            Value::String(ref s) => Some(s),
            _ => None
        }
    }

    /// If the `Value` is a floating point number, returns associated value. Returns None otherwise.
    pub fn as_f64(&self) -> Option<f64> {
        match *self {
            Value::Double(d) => Some(d),
            _ => None
        }
    }

    /// If the `Value` is a date/time, returns associated value. Returns None otherwise.
    pub fn as_datetime(&self) -> Option<DateTime> {
        match *self {
            Value::DateTime(dt) => Some(dt),
            _ => None
        }
    }

    /// If the `Value` is a binary data, returns associated value. Returns None otherwise.
    pub fn as_bytes(&self) -> Option<&[u8]> {
        match *self {
            Value::Base64(ref data) => Some(data),
            _ => None
        }
    }

    /// If the `Value` is a struct, returns associated map. Returns None otherwise.
    pub fn as_struct(&self) -> Option<&BTreeMap<String, Value>> {
        match *self {
            Value::Struct(ref map) => Some(map),
            _ => None
        }
    }

    /// If the `Value` is an array, returns associated slice. Returns None otherwise.
    pub fn as_array(&self) -> Option<&[Value]> {
        match *self {
            Value::Array(ref array) => Some(array),
            _ => None
        }
    }
}

impl From<i32> for Value {
    fn from(other: i32) -> Self {
        Value::Int(other)
    }
}

impl From<i64> for Value {
    fn from(other: i64) -> Self {
        Value::Int64(other)
    }
}

impl From<bool> for Value {
    fn from(other: bool) -> Self {
        Value::Bool(other)
    }
}

impl From<String> for Value {
    fn from(other: String) -> Self {
        Value::String(other)
    }
}

impl<'a> From<&'a str> for Value {
    fn from(other: &'a str) -> Self {
        Value::String(other.to_string())
    }
}

impl From<f64> for Value {
    fn from(other: f64) -> Self {
        Value::Double(other)
    }
}

impl From<DateTime> for Value {
    fn from(other: DateTime) -> Self {
        Value::DateTime(other)
    }
}

/// A type that can be used to index into a `Value`
pub trait Index {
    /// Gets an inner value of a given value represented by self.
    #[doc(hidden)]
    fn get<'v>(&self, value: &'v Value) -> Option<&'v Value>;
}

impl Index for str {
    fn get<'v>(&self, value: &'v Value) -> Option<&'v Value> {
        if let Value::Struct(ref map) = *value {
            map.get(self)
        } else {
            None
        }
    }
}

impl Index for String {
    fn get<'v>(&self, value: &'v Value) -> Option<&'v Value> {
        if let Value::Struct(ref map) = *value {
            map.get(self)
        } else {
            None
        }
    }
}

impl Index for usize {
    fn get<'v>(&self, value: &'v Value) -> Option<&'v Value> {
        if let Value::Array(ref array) = *value {
            array.get(*self)
        } else {
            None
        }
    }
}

impl<'a, I> Index for &'a I where I: Index + ?Sized {
    fn get<'v>(&self, value: &'v Value) -> Option<&'v Value> {
        (*self).get(value)
    }
}

impl<I> ::std::ops::Index<I> for Value where I: Index {
    type Output = Value;
    fn index(&self, index: I) -> &Self::Output {
        index.get(self).unwrap_or(&Value::Nil)
    }
}

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

    #[test]
    fn escapes_strings() {
        let mut output: Vec<u8> = Vec::new();

        Value::from("<xml> string").write_as_xml(&mut output).unwrap();
        assert_eq!(str::from_utf8(&output).unwrap(), "<value>\n<string><xml>&nbsp;string</string>\n</value>\n");
    }

    #[test]
    fn escapes_struct_member_names() {
        let mut output: Vec<u8> = Vec::new();
        let mut map: BTreeMap<String, Value> = BTreeMap::new();
        map.insert("x&<x".to_string(), Value::from(true));

        Value::Struct(map).write_as_xml(&mut output).unwrap();
        assert_eq!(str::from_utf8(&output).unwrap(), "<value>\n<struct>\n<member>\n<name>x&<x</name>\n<value>\n<boolean>1</boolean>\n</value>\n</member>\n</struct>\n</value>\n");
    }

    #[test]
    fn access_nested_values() {
        let mut map: BTreeMap<String, Value> = BTreeMap::new();
        map.insert("name".to_string(), Value::from("John Doe"));
        map.insert("age".to_string(), Value::from(37));
        map.insert("children".to_string(), Value::Array(vec![Value::from("Mark"), Value::from("Jennyfer")]));
        let value = Value::Struct(map);

        assert_eq!(value.get("name"), Some(&Value::from("John Doe")));
        assert_eq!(value.get("age"), Some(&Value::from(37)));
        assert_eq!(value.get("birthdate"), None);
        assert_eq!(Value::Nil.get("age"), None);
        assert_eq!(value["name"], Value::from("John Doe"));
        assert_eq!(value["age"], Value::from(37));
        assert_eq!(value["birthdate"], Value::Nil);
        assert_eq!(Value::Nil["age"], Value::Nil);
        assert_eq!(value["children"][0], Value::from("Mark"));
        assert_eq!(value["children"][1], Value::from("Jennyfer"));
        assert_eq!(value["children"][2], Value::Nil);

        assert_eq!(value["age"].as_i32(), Some(37));
        assert_eq!(value["children"][0].as_str(), Some("Mark"));
    }
}
<commit_msg>Tweak value/index docs<commit_after>//! Contains the different types of values understood by XML-RPC.

use utils::{escape_xml, format_datetime};

use base64::encode;
use iso8601::DateTime;

use std::collections::BTreeMap;
use std::io::{self, Write};

/// The possible XML-RPC values.
///
/// Nested values can be accessed by using [`get`](#method.get) method and Rust's square-bracket
/// indexing operator.
///
/// A string index can be used to access a value in a `Struct`, and a `usize` index can be used to
/// access an element of an `Array`.
///
/// # Examples
///
/// ```
/// # use std::collections::BTreeMap;
/// # use xmlrpc::{Value, Index};
/// let nothing = Value::Nil;
///
/// let person = Value::Struct(vec![
///     ("name".to_string(), Value::from("John Doe")),
///     ("age".to_string(), Value::from(37)),
///     ("children".to_string(), Value::Array(vec![
///         Value::from("Mark"),
///         Value::from("Jennyfer")
///     ])),
/// ].into_iter().collect());
///
/// // get
/// assert_eq!(nothing.get("name"), None);
/// assert_eq!(person.get("name"), Some(&Value::from("John Doe")));
/// assert_eq!(person.get("SSN"), None);
///
/// // index
/// assert_eq!(nothing["name"], Value::Nil);
/// assert_eq!(person["name"], Value::from("John Doe"));
/// assert_eq!(person["age"], Value::Int(37));
/// assert_eq!(person["SSN"], Value::Nil);
/// assert_eq!(person["children"][0], Value::from("Mark"));
/// assert_eq!(person["children"][0]["age"], Value::Nil);
/// assert_eq!(person["children"][2], Value::Nil);
///
/// // extract values
/// assert_eq!(person["name"].as_str(), Some("John Doe"));
/// assert_eq!(person["age"].as_i32(), Some(37));
/// assert_eq!(person["age"].as_bool(), None);
/// assert_eq!(person["children"].as_array().unwrap().len(), 2);
/// ```
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
    /// `<i4>` or `<int>`, 32-bit signed integer.
    Int(i32),
    /// `<i8>`, 64-bit signed integer.
    ///
    /// This is an XMLRPC extension and may not be supported by all clients / servers.
    Int64(i64),
    /// `<boolean>`, 0 == `false`, 1 == `true`.
    Bool(bool),
    /// `<string>`
    // FIXME zero-copy? `Cow<'static, ..>`?
    String(String),
    /// `<double>`
    Double(f64),
    /// `<dateTime.iso8601>`, an ISO 8601 formatted date/time value.
    DateTime(DateTime),
    /// `<base64>`, base64-encoded binary data.
    Base64(Vec<u8>),

    /// `<struct>`, a mapping of named values.
    Struct(BTreeMap<String, Value>),
    /// `<array>`, a list of arbitrary (heterogeneous) values.
    Array(Vec<Value>),

    /// `<nil/>`, the empty (Unit) value.
    ///
    /// This is an XMLRPC [extension][ext] and may not be supported by all clients / servers.
    ///
    /// [ext]: https://web.archive.org/web/20050911054235/http://ontosys.com/xml-rpc/extensions.php
    Nil,
}

impl Value {
    /// Formats this `Value` as an XML `<value>` element.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        writeln!(fmt, "<value>")?;

        match *self {
            Value::Int(i) => {
                writeln!(fmt, "<i4>{}</i4>", i)?;
            }
            Value::Int64(i) => {
                writeln!(fmt, "<i8>{}</i8>", i)?;
            }
            Value::Bool(b) => {
                writeln!(fmt, "<boolean>{}</boolean>", if b { "1" } else { "0" })?;
            }
            Value::String(ref s) => {
                writeln!(fmt, "<string>{}</string>", escape_xml(s))?;
            }
            Value::Double(d) => {
                writeln!(fmt, "<double>{}</double>", d)?;
            }
            Value::DateTime(date_time) => {
                writeln!(fmt, "<dateTime.iso8601>{}</dateTime.iso8601>", format_datetime(&date_time))?;
            }
            Value::Base64(ref data) => {
                writeln!(fmt, "<base64>{}</base64>", encode(data))?;
            }
            Value::Struct(ref map) => {
                writeln!(fmt, "<struct>")?;
                for (ref name, ref value) in map {
                    writeln!(fmt, "<member>")?;
                    writeln!(fmt, "<name>{}</name>", escape_xml(name))?;
                    value.write_as_xml(fmt)?;
                    writeln!(fmt, "</member>")?;
                }
                writeln!(fmt, "</struct>")?;
            }
            Value::Array(ref array) => {
                writeln!(fmt, "<array>")?;
                writeln!(fmt, "<data>")?;
                for value in array {
                    value.write_as_xml(fmt)?;
                }
                writeln!(fmt, "</data>")?;
                writeln!(fmt, "</array>")?;
            }
            Value::Nil => {
                writeln!(fmt, "<nil/>")?;
            }
        }

        writeln!(fmt, "</value>")?;
        Ok(())
    }

    /// Returns an inner struct or array value indexed by `index`.
    ///
    /// Returns `None` if the member doesn't exist or `self` is neither a struct nor an array.
    ///
    /// You can also use Rust's square-bracket indexing syntax to perform this operation if you want
    /// a default value instead of an `Option`. Refer to the top-level [examples](#examples) for
    /// details.
    pub fn get<I: Index>(&self, index: I) -> Option<&Value> {
        index.get(self)
    }

    /// If the `Value` is a normal integer (`Value::Int`), returns associated value. Returns `None`
    /// otherwise.
    ///
    /// In particular, `None` is also returned if `self` is a `Value::Int64`. Use [`as_i64`] to
    /// handle this case.
    ///
    /// [`as_i64`]: #method.as_i64
    pub fn as_i32(&self) -> Option<i32> {
        match *self {
            Value::Int(i) => Some(i),
            _ => None
        }
    }

    /// If the `Value` is an integer, returns associated value. Returns `None` otherwise.
    ///
    /// This works with both `Value::Int` and `Value::Int64`.
    pub fn as_i64(&self) -> Option<i64> {
        match *self {
            Value::Int(i) => Some(i64::from(i)),
            Value::Int64(i) => Some(i),
            _ => None
        }
    }

    /// If the `Value` is a boolean, returns associated value. Returns `None` otherwise.
    pub fn as_bool(&self) -> Option<bool> {
        match *self {
            Value::Bool(b) => Some(b),
            _ => None
        }
    }

    /// If the `Value` is a string, returns associated value. Returns `None` otherwise.
    pub fn as_str(&self) -> Option<&str> {
        match *self {
            Value::String(ref s) => Some(s),
            _ => None
        }
    }

    /// If the `Value` is a floating point number, returns associated value. Returns `None`
    /// otherwise.
    pub fn as_f64(&self) -> Option<f64> {
        match *self {
            Value::Double(d) => Some(d),
            _ => None
        }
    }

    /// If the `Value` is a date/time, returns associated value. Returns `None` otherwise.
    pub fn as_datetime(&self) -> Option<DateTime> {
        match *self {
            Value::DateTime(dt) => Some(dt),
            _ => None
        }
    }

    /// If the `Value` is a binary data, returns associated value. Returns `None` otherwise.
    pub fn as_bytes(&self) -> Option<&[u8]> {
        match *self {
            Value::Base64(ref data) => Some(data),
            _ => None
        }
    }

    /// If the `Value` is a struct, returns associated map. Returns `None` otherwise.
    pub fn as_struct(&self) -> Option<&BTreeMap<String, Value>> {
        match *self {
            Value::Struct(ref map) => Some(map),
            _ => None
        }
    }

    /// If the `Value` is an array, returns associated slice. Returns `None` otherwise.
    pub fn as_array(&self) -> Option<&[Value]> {
        match *self {
            Value::Array(ref array) => Some(array),
            _ => None
        }
    }
}

impl From<i32> for Value {
    fn from(other: i32) -> Self {
        Value::Int(other)
    }
}

impl From<i64> for Value {
    fn from(other: i64) -> Self {
        Value::Int64(other)
    }
}

impl From<bool> for Value {
    fn from(other: bool) -> Self {
        Value::Bool(other)
    }
}

impl From<String> for Value {
    fn from(other: String) -> Self {
        Value::String(other)
    }
}

impl<'a> From<&'a str> for Value {
    fn from(other: &'a str) -> Self {
        Value::String(other.to_string())
    }
}

impl From<f64> for Value {
    fn from(other: f64) -> Self {
        Value::Double(other)
    }
}

impl From<DateTime> for Value {
    fn from(other: DateTime) -> Self {
        Value::DateTime(other)
    }
}

/// A type that can be used to index into a `Value`.
pub trait Index {
    /// Gets an inner value of a given value represented by self.
    #[doc(hidden)]
    fn get<'v>(&self, value: &'v Value) -> Option<&'v Value>;
}

impl Index for str {
    fn get<'v>(&self, value: &'v Value) -> Option<&'v Value> {
        if let Value::Struct(ref map) = *value {
            map.get(self)
        } else {
            None
        }
    }
}

impl Index for String {
    fn get<'v>(&self, value: &'v Value) -> Option<&'v Value> {
        if let Value::Struct(ref map) = *value {
            map.get(self)
        } else {
            None
        }
    }
}

impl Index for usize {
    fn get<'v>(&self, value: &'v Value) -> Option<&'v Value> {
        if let Value::Array(ref array) = *value {
            array.get(*self)
        } else {
            None
        }
    }
}

impl<'a, I> Index for &'a I where I: Index + ?Sized {
    fn get<'v>(&self, value: &'v Value) -> Option<&'v Value> {
        (*self).get(value)
    }
}

impl<I> ::std::ops::Index<I> for Value where I: Index {
    type Output = Value;
    fn index(&self, index: I) -> &Self::Output {
        index.get(self).unwrap_or(&Value::Nil)
    }
}

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

    #[test]
    fn escapes_strings() {
        let mut output: Vec<u8> = Vec::new();

        Value::from("<xml> string").write_as_xml(&mut output).unwrap();
        assert_eq!(str::from_utf8(&output).unwrap(), "<value>\n<string><xml>&nbsp;string</string>\n</value>\n");
    }

    #[test]
    fn escapes_struct_member_names() {
        let mut output: Vec<u8> = Vec::new();
        let mut map: BTreeMap<String, Value> = BTreeMap::new();
        map.insert("x&<x".to_string(), Value::from(true));

        Value::Struct(map).write_as_xml(&mut output).unwrap();
        assert_eq!(str::from_utf8(&output).unwrap(), "<value>\n<struct>\n<member>\n<name>x&<x</name>\n<value>\n<boolean>1</boolean>\n</value>\n</member>\n</struct>\n</value>\n");
    }

    #[test]
    fn access_nested_values() {
        let mut map: BTreeMap<String, Value> = BTreeMap::new();
        map.insert("name".to_string(), Value::from("John Doe"));
        map.insert("age".to_string(), Value::from(37));
        map.insert("children".to_string(), Value::Array(vec![Value::from("Mark"), Value::from("Jennyfer")]));
        let value = Value::Struct(map);

        assert_eq!(value.get("name"), Some(&Value::from("John Doe")));
        assert_eq!(value.get("age"), Some(&Value::from(37)));
        assert_eq!(value.get("birthdate"), None);
        assert_eq!(Value::Nil.get("age"), None);
        assert_eq!(value["name"], Value::from("John Doe"));
        assert_eq!(value["age"], Value::from(37));
        assert_eq!(value["birthdate"], Value::Nil);
        assert_eq!(Value::Nil["age"], Value::Nil);
        assert_eq!(value["children"][0], Value::from("Mark"));
        assert_eq!(value["children"][1], Value::from("Jennyfer"));
        assert_eq!(value["children"][2], Value::Nil);

        assert_eq!(value["age"].as_i32(), Some(37));
        assert_eq!(value["children"][0].as_str(), Some("Mark"));
    }
}
<|endoftext|>


<commit_before>use iron::prelude::*;
use maud::{Markup, PreEscaped, Render};
use pulldown_cmark;

use user::User;

static STYLES: &'static str = r#"
html {
    box-sizing: border-box;
    font: 100%/1.5 "Liberation Serif", "Times New Roman", serif;
    background: linear-gradient(to bottom, #445 0%, #001 100%);
}

@media (min-width: 450px) {
    html {
        font-size: 125%;
    }
}

@media (min-width: 750px) {
    html {
        font-size: 150%;
    }
}

input, button {
    font-size: 1rem;
    line-height: 1.5;
}

* {
    box-sizing: inherit;
}

body {
    margin: 0 auto;
    max-width: 30rem;
    min-height: 100vh;
    padding: 0.5rem 1.5rem;
    box-shadow: 0 0 0 8px #ccc, 0 0 0 16px #999, 0 0 0 20px #666;
    background: #fff;
}

a {
    text-decoration: none;
    color: #910;
}

a:hover, a:focus, a:active {
    text-shadow: 0 0 2px #fff, 0 0 8px #d60;
}

h1 {
    /* A E S T H E T I C */
    margin: 0 0 1rem;
    white-space: nowrap;
    font-family: "Comic Sans MS", sans-serif;
    font-size: 1.75rem;
    letter-spacing: 0.5rem;
}

@media (min-width: 600px) {
    h1 {
        font-size: 3rem;
        letter-spacing: 0.75rem;
    }
}

h1 a {
    color: #f90;
    text-transform: uppercase;
    text-shadow: 2px 2px #000, -2px -2px #faa;
    transition: text-shadow 0.2s;
}

h1 a:hover, h1 a:focus, h1 a:active {
    text-shadow: 16px 16px #000, -16px -16px #faa;
    transition: text-shadow 0.2s;
}

.karkinos {
    font-family: "Comic Sans MS", sans-serif;
}

#q {
    width: 100%;
    padding: 0.25rem 0.5rem;
    border: 1px solid #000;
    box-shadow: 0 0 0 0 #f90, 0 0 0 0 #666;
    transition: box-shadow 0.2s;
}

#q:focus {
    border-color: #f90;
    box-shadow: -2px -2px 0 2px #f90, 2px 2px 0 3px #666;
    transition: box-shadow 0.2s;
}

table {
    border-spacing: 0.5rem;
}

th {
    text-align: right;
    vertical-align: top;
}
"#;

pub fn layout(r: &Request, title: Option<&str>, body: Markup) -> Markup {
    let url = r.url.clone().into_generic_url();
    html! {
        (PreEscaped("<!DOCTYPE html>"))
        html {
            meta charset="utf-8" /
            title {
                @if let Some(title) = title {
                    (title) " - "
                }
                "Karkinos"
            }
            meta name="viewport" content="width=device-width" /
            style (PreEscaped(STYLES))
            body {
                h1 a href="/" "🦀Karkinos"
                @if let Some(title) = title {
                    h2 a href=(url.path()) title="Link to this page" (title)
                }
                (body)
            }
        }
    }
}

pub fn home(r: &Request) -> Markup {
    layout(r, None, html! {
        form action=(url_for!(r, "search")) {
            input name="q" id="q" type="search" placeholder="Enter a name" /
        }
        script (PreEscaped("document.getElementById('q').select()"))
        p {
            span.karkinos "KARKINOS"
            " is a database of people interested in the "
            a href="https://www.rust-lang.org" "Rust programming language"
            ". It uses the same data as "
            a href="http://rustaceans.org" "rustaceans.org"
            ", but presents it through a different interface."
        }
        p "I created Karkinos for these reasons:"
        ul {
            li "To provide access for users who browse with JavaScript disabled;"
            li "To rewrite the backend in Rust (instead of Node.js);"
            li {
                "As a proving ground for my template engine, "
                a href="https://github.com/lfairy/maud" "Maud"
                ";"
            }
            li "To screw around with CSS (this is the most important reason)."
        }
        p {
            "Karkinos is named after a very special "
            a href="https://en.wikipedia.org/wiki/Cancer_(constellation)#Names" "giant crab"
            "."
        }
        p {
            "The source code for this site can be found on "
            a href="https://github.com/lfairy/karkinos" "GitHub"
            "."
        }
    })
}

pub fn not_found(r: &Request) -> Markup {
    layout(r, Some("Not found"), html! {
        p {
            "The page at "
            strong (r.url)
            " could not be found."
        }
        p a href="/" "‹ Back to home page"
    })
}

pub fn user(r: &Request, id: &str, user: &User) -> Markup {
    let title = if let Some(ref name) = user.name {
        format!("{} ({})", name, id)
    } else {
        id.to_string()
    };
    layout(r, Some(&title), html! {
        table {
            @if let Some(ref nick) = user.irc {
                tr {
                    th "IRC"
                    td {
                        (nick)
                        @if !user.irc_channels.is_empty() {
                            " on "
                            @for (i, channel) in user.irc_channels.iter().enumerate() {
                                @if i > 0 { ", " }
                                a href={ "irc://irc.mozilla.org/" (channel) } {
                                    "#" (channel)
                                }
                            }
                        }
                    }
                }
            }
            @if let Some(ref x) = user.discourse {
                tr {
                    th "Discourse"
                    td a href={ "https://users.rust-lang.org/users/" (x) } (x)
                }
            }
            @if let Some(ref x) = user.reddit {
                tr {
                    th "Reddit"
                    td a href={ "https://reddit.com/user/" (x) } (x)
                }
            }
            @if let Some(ref x) = user.twitter {
                tr {
                    th "Twitter"
                    td a href={ "https://twitter.com/" (x) } (x)
                }
            }
            @if let Some(ref x) = user.website {
                tr {
                    th "Website"
                    td a href=(x) (x)
                }
            }
            @if let Some(ref x) = user.blog {
                tr {
                    th "Blog"
                    td a href=(x) (x)
                }
            }
            @if let Some(ref x) = user.email {
                tr {
                    th "Email"
                    td a href={ "mailto:" (x) } (x)
                }
            }
        }
        @if let Some(ref x) = user.notes {
            div.notes (Markdown(x))
        }
    })
}

pub fn user_not_found(r: &Request, id: &str) -> Markup {
    layout(r, Some(id), html! {
        p {
            "The user "
            strong (id)
            " could not be found."
        }
        p a href="/" "<< Back to home page"
    })
}

struct Markdown<'a>(&'a str);

impl<'a> Render for Markdown<'a> {
    fn render_to(&self, buffer: &mut String) {
        let parser = pulldown_cmark::Parser::new(self.0);
        pulldown_cmark::html::push_html(buffer, parser);
    }
}
<commit_msg>Make it clear that we're searching usernames, not names<commit_after>use iron::prelude::*;
use maud::{Markup, PreEscaped, Render};
use pulldown_cmark;

use user::User;

static STYLES: &'static str = r#"
html {
    box-sizing: border-box;
    font: 100%/1.5 "Liberation Serif", "Times New Roman", serif;
    background: linear-gradient(to bottom, #445 0%, #001 100%);
}

@media (min-width: 450px) {
    html {
        font-size: 125%;
    }
}

@media (min-width: 750px) {
    html {
        font-size: 150%;
    }
}

input, button {
    font-size: 1rem;
    line-height: 1.5;
}

* {
    box-sizing: inherit;
}

body {
    margin: 0 auto;
    max-width: 30rem;
    min-height: 100vh;
    padding: 0.5rem 1.5rem;
    box-shadow: 0 0 0 8px #ccc, 0 0 0 16px #999, 0 0 0 20px #666;
    background: #fff;
}

a {
    text-decoration: none;
    color: #910;
}

a:hover, a:focus, a:active {
    text-shadow: 0 0 2px #fff, 0 0 8px #d60;
}

h1 {
    /* A E S T H E T I C */
    margin: 0 0 1rem;
    white-space: nowrap;
    font-family: "Comic Sans MS", sans-serif;
    font-size: 1.75rem;
    letter-spacing: 0.5rem;
}

@media (min-width: 600px) {
    h1 {
        font-size: 3rem;
        letter-spacing: 0.75rem;
    }
}

h1 a {
    color: #f90;
    text-transform: uppercase;
    text-shadow: 2px 2px #000, -2px -2px #faa;
    transition: text-shadow 0.2s;
}

h1 a:hover, h1 a:focus, h1 a:active {
    text-shadow: 16px 16px #000, -16px -16px #faa;
    transition: text-shadow 0.2s;
}

.karkinos {
    font-family: "Comic Sans MS", sans-serif;
}

#q {
    width: 100%;
    padding: 0.25rem 0.5rem;
    border: 1px solid #000;
    box-shadow: 0 0 0 0 #f90, 0 0 0 0 #666;
    transition: box-shadow 0.2s;
}

#q:focus {
    border-color: #f90;
    box-shadow: -2px -2px 0 2px #f90, 2px 2px 0 3px #666;
    transition: box-shadow 0.2s;
}

table {
    border-spacing: 0.5rem;
}

th {
    text-align: right;
    vertical-align: top;
}
"#;

pub fn layout(r: &Request, title: Option<&str>, body: Markup) -> Markup {
    let url = r.url.clone().into_generic_url();
    html! {
        (PreEscaped("<!DOCTYPE html>"))
        html {
            meta charset="utf-8" /
            title {
                @if let Some(title) = title {
                    (title) " - "
                }
                "Karkinos"
            }
            meta name="viewport" content="width=device-width" /
            style (PreEscaped(STYLES))
            body {
                h1 a href="/" "🦀Karkinos"
                @if let Some(title) = title {
                    h2 a href=(url.path()) title="Link to this page" (title)
                }
                (body)
            }
        }
    }
}

pub fn home(r: &Request) -> Markup {
    layout(r, None, html! {
        form action=(url_for!(r, "search")) {
            input name="q" id="q" type="search" placeholder="Enter a username" /
        }
        script (PreEscaped("document.getElementById('q').select()"))
        p {
            span.karkinos "KARKINOS"
            " is a database of people interested in the "
            a href="https://www.rust-lang.org" "Rust programming language"
            ". It uses the same data as "
            a href="http://rustaceans.org" "rustaceans.org"
            ", but presents it through a different interface."
        }
        p "I created Karkinos for these reasons:"
        ul {
            li "To provide access for users who browse with JavaScript disabled;"
            li "To rewrite the backend in Rust (instead of Node.js);"
            li {
                "As a proving ground for my template engine, "
                a href="https://github.com/lfairy/maud" "Maud"
                ";"
            }
            li "To screw around with CSS (this is the most important reason)."
        }
        p {
            "Karkinos is named after a very special "
            a href="https://en.wikipedia.org/wiki/Cancer_(constellation)#Names" "giant crab"
            "."
        }
        p {
            "The source code for this site can be found on "
            a href="https://github.com/lfairy/karkinos" "GitHub"
            "."
        }
    })
}

pub fn not_found(r: &Request) -> Markup {
    layout(r, Some("Not found"), html! {
        p {
            "The page at "
            strong (r.url)
            " could not be found."
        }
        p a href="/" "‹ Back to home page"
    })
}

pub fn user(r: &Request, id: &str, user: &User) -> Markup {
    let title = if let Some(ref name) = user.name {
        format!("{} ({})", name, id)
    } else {
        id.to_string()
    };
    layout(r, Some(&title), html! {
        table {
            @if let Some(ref nick) = user.irc {
                tr {
                    th "IRC"
                    td {
                        (nick)
                        @if !user.irc_channels.is_empty() {
                            " on "
                            @for (i, channel) in user.irc_channels.iter().enumerate() {
                                @if i > 0 { ", " }
                                a href={ "irc://irc.mozilla.org/" (channel) } {
                                    "#" (channel)
                                }
                            }
                        }
                    }
                }
            }
            @if let Some(ref x) = user.discourse {
                tr {
                    th "Discourse"
                    td a href={ "https://users.rust-lang.org/users/" (x) } (x)
                }
            }
            @if let Some(ref x) = user.reddit {
                tr {
                    th "Reddit"
                    td a href={ "https://reddit.com/user/" (x) } (x)
                }
            }
            @if let Some(ref x) = user.twitter {
                tr {
                    th "Twitter"
                    td a href={ "https://twitter.com/" (x) } (x)
                }
            }
            @if let Some(ref x) = user.website {
                tr {
                    th "Website"
                    td a href=(x) (x)
                }
            }
            @if let Some(ref x) = user.blog {
                tr {
                    th "Blog"
                    td a href=(x) (x)
                }
            }
            @if let Some(ref x) = user.email {
                tr {
                    th "Email"
                    td a href={ "mailto:" (x) } (x)
                }
            }
        }
        @if let Some(ref x) = user.notes {
            div.notes (Markdown(x))
        }
    })
}

pub fn user_not_found(r: &Request, id: &str) -> Markup {
    layout(r, Some(id), html! {
        p {
            "The user "
            strong (id)
            " could not be found."
        }
        p a href="/" "<< Back to home page"
    })
}

struct Markdown<'a>(&'a str);

impl<'a> Render for Markdown<'a> {
    fn render_to(&self, buffer: &mut String) {
        let parser = pulldown_cmark::Parser::new(self.0);
        pulldown_cmark::html::push_html(buffer, parser);
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthias Schorsch
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// import modules
use ffi::*;
use types::{FileAction, FileNotifyChange};
use errors;

use ::libc::{FILE_SHARE_WRITE, FILE_SHARE_READ, FILE_SHARE_DELETE, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS};

// std uses
use std::path::Path;
use std::fmt;
use std::ptr;

// consts
const FILE_LIST_DIRECTORY: DWORD = 0x0001;

#[derive(Debug,Clone)]
pub struct FileNotifyInformation {
    action: FileAction,
    filename: String,
}

impl fmt::Display for FileNotifyInformation {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} -> {}", self.action, self.filename)
    }
}

#[derive(Debug)]
pub struct WinWatch {

    h_directory: HANDLE,
    watch_subdirs: bool,
    buffer_size: u32,

    results_arr: Box<Vec<u16>>,
    dw_notify_filter: DWORD,
}

impl Drop for WinWatch {

    fn drop(&mut self) {
        close_dir_handle(self.h_directory);
    }
}

impl WinWatch {

    pub fn new(directory: &Path, notify_changes: Box<Vec<FileNotifyChange>>, watch_subdirs: bool, buffer_size: u32) -> WinWatch {
        let h_directory = open_dir_handle(directory); //TODO: check errors

        let mut results_arr: Vec<u16> = Vec::with_capacity(buffer_size as usize);
        unsafe {results_arr.set_len(buffer_size as usize)};

        WinWatch {
            h_directory: h_directory,
            watch_subdirs: watch_subdirs,
            buffer_size: buffer_size,
            results_arr: Box::new(results_arr),
            dw_notify_filter: FileNotifyChange::as_u32(notify_changes),
        }
    }

    pub fn watch(&mut self) -> Result<Box<Vec<FileNotifyInformation>>, errors::Error> {
        read_directory_changes(self.h_directory, &mut *self.results_arr, self.buffer_size, self.dw_notify_filter, self.watch_subdirs)
    }
}

fn open_dir_handle(directory: &Path) -> HANDLE {
    let lp_filename = to_lpcwstr(directory);
    let dw_desired_access = FILE_LIST_DIRECTORY;
    let dw_share_mode = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
    let lp_security_attributes: *mut SECURITY_ATTRIBUTES = ptr::null_mut();
    let dw_creation_disposition = OPEN_EXISTING;
    let dw_flags_and_attributes = FILE_FLAG_BACKUP_SEMANTICS;
    let h_template_file: HANDLE = ptr::null_mut();

    unsafe {
        CreateFileW(lp_filename, dw_desired_access,
            dw_share_mode, lp_security_attributes, dw_creation_disposition,
            dw_flags_and_attributes, h_template_file)
    }
}

fn close_dir_handle(handle: HANDLE) -> bool {
    to_bool(unsafe {
        CloseHandle(handle)
    })
}

fn read_directory_changes(h_directory: HANDLE, result_vec: &mut [u16], buffer_size: DWORD,
                          dw_notify_filter: DWORD, watch_subdirs: bool) -> Result<Box<Vec<FileNotifyInformation>>, errors::Error> {
    //
    // prepare parameters
    let handle = h_directory as *mut c_void;
    let lp_buffer = result_vec.as_mut_ptr() as *mut c_void;
    let n_buffer_length: DWORD = buffer_size * 2; // in bytes

    let b_watch_subtree = from_bool(watch_subdirs);
    let mut lp_bytes_returned: DWORD = 0;

    //overlapped io + callback
    let lp_overlapped: LPOVERLAPPED = ptr::null_mut(); // c_null
    let lp_completion_routine: LPOVERLAPPED_COMPLETION_ROUTINE = Option::None; /*unsafe {
        std::mem::transmute(ptr::null_mut::<LPOVERLAPPED_COMPLETION_ROUTINE>())
    };*/

    //
    // watch
    let has_result: bool = to_bool(unsafe {
        ReadDirectoryChangesW(handle,
                              lp_buffer,
                              n_buffer_length,
                              b_watch_subtree,
                              dw_notify_filter,
                              &mut lp_bytes_returned,
                              lp_overlapped,
                              lp_completion_routine)
    });

    //
    // results
    if has_result {
        Result::Ok(from_u16_slice(result_vec))
    } else {
        let error_desc = format!("Failure detected with system error code {}", get_last_error());
        Result::Err(errors::Error::new(error_desc))
    }
}

fn from_u16_slice(v: &[u16]) -> Box<Vec<FileNotifyInformation>> {
    let mut result:Vec<FileNotifyInformation> = Vec::new();
    let mut offset: usize = 0;
    loop {
        let (next_entry_offset, fni) = to_file_notify_information(v, offset);
        result.push(fni);

        // check for 0.
        // 0 indicates that this is the last record
        if next_entry_offset == 0 {
            break;
        }
        offset += next_entry_offset as usize
    }
    Box::new(result)
}

fn to_file_notify_information(v: &[u16], offset: usize) -> (u32, FileNotifyInformation) {
    let next_entry_offset_in_u16 = to_u32le(v, offset) / 2;
    let action = to_u32le(v, offset + 2);
    let file_name_length_in_bytes = to_u32le(v, offset + 4) as usize;
    let filename = to_filename(v, offset + 6, file_name_length_in_bytes);

    let fni = FileNotifyInformation {
        action: FileAction::from_u32(action),
        filename: filename,
    };

    (next_entry_offset_in_u16, fni)
}

fn to_filename(v: &[u16], offset: usize, file_name_length_in_bytes: usize) -> String {
    let result = &v[offset .. offset + (file_name_length_in_bytes / 2)];
    String::from_utf16(result).unwrap()
}

fn to_u32le(v: &[u16], offset: usize) -> u32 {
    (v[offset + 1] as u32) << 16 | (v[offset] as u32) // little endian
}<commit_msg>Buffer handling simplified.<commit_after>// Copyright 2015 Matthias Schorsch
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// import modules
use ffi::*;
use types::{FileAction, FileNotifyChange};
use errors;

use ::libc::{FILE_SHARE_WRITE, FILE_SHARE_READ, FILE_SHARE_DELETE, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS};

// std uses
use std::path::Path;
use std::fmt;
use std::ptr;
use std::mem;
use std::slice;

// consts
const FILE_LIST_DIRECTORY: DWORD = 0x0001;

#[derive(Debug,Clone)]
pub struct FileNotifyInformation {
    action: FileAction,
    filename: String,
}

impl fmt::Display for FileNotifyInformation {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} -> {}", self.action, self.filename)
    }
}

#[derive(Debug)]
pub struct WinWatch {

    h_directory: HANDLE,
    watch_subdirs: bool,
    buffer_size: u32,

    result_buffer: Box<Vec<u8>>,
    dw_notify_filter: DWORD,
}

impl Drop for WinWatch {

    fn drop(&mut self) {
        close_dir_handle(self.h_directory);
    }
}

impl WinWatch {

    pub fn new(directory: &Path, notify_changes: Box<Vec<FileNotifyChange>>, watch_subdirs: bool, buffer_size: u32) -> WinWatch {
        let h_directory = open_dir_handle(directory); //TODO: check errors

        WinWatch {
            h_directory: h_directory,
            watch_subdirs: watch_subdirs,
            buffer_size: buffer_size,
            result_buffer: Box::new(vec![0u8; buffer_size as usize]),
            dw_notify_filter: FileNotifyChange::as_u32(notify_changes),
        }
    }

    pub fn watch(&mut self) -> Result<Box<Vec<FileNotifyInformation>>, errors::Error> {
        read_directory_changes(self.h_directory, &mut *self.result_buffer, self.buffer_size, self.dw_notify_filter, self.watch_subdirs)
    }
}

fn open_dir_handle(directory: &Path) -> HANDLE {
    let lp_filename = to_lpcwstr(directory);
    let dw_desired_access = FILE_LIST_DIRECTORY;
    let dw_share_mode = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
    let lp_security_attributes: *mut SECURITY_ATTRIBUTES = ptr::null_mut();
    let dw_creation_disposition = OPEN_EXISTING;
    let dw_flags_and_attributes = FILE_FLAG_BACKUP_SEMANTICS;
    let h_template_file: HANDLE = ptr::null_mut();

    unsafe {
        CreateFileW(lp_filename, dw_desired_access,
            dw_share_mode, lp_security_attributes, dw_creation_disposition,
            dw_flags_and_attributes, h_template_file)
    }
}

fn close_dir_handle(handle: HANDLE) -> bool {
    to_bool(unsafe {
        CloseHandle(handle)
    })
}

fn read_directory_changes(h_directory: HANDLE, result_buffer: &mut [u8], buffer_size: DWORD,
                          dw_notify_filter: DWORD, watch_subdirs: bool) -> Result<Box<Vec<FileNotifyInformation>>, errors::Error> {
    //
    // prepare parameters
    let handle = h_directory as *mut c_void;
    let lp_buffer = result_buffer.as_mut_ptr() as *mut c_void;
    let n_buffer_length: DWORD = buffer_size; // in bytes

    let b_watch_subtree = from_bool(watch_subdirs);
    let mut lp_bytes_returned: DWORD = 0;

    //overlapped io + callback
    let lp_overlapped: LPOVERLAPPED = ptr::null_mut(); // c_null
    let lp_completion_routine: LPOVERLAPPED_COMPLETION_ROUTINE = Option::None; /*unsafe {
        std::mem::transmute(ptr::null_mut::<LPOVERLAPPED_COMPLETION_ROUTINE>())
    };*/

    //
    // watch
    let has_result: bool = to_bool(unsafe {
        ReadDirectoryChangesW(handle,
                              lp_buffer,
                              n_buffer_length,
                              b_watch_subtree,
                              dw_notify_filter,
                              &mut lp_bytes_returned,
                              lp_overlapped,
                              lp_completion_routine)
    });

    //
    // results
    if has_result {
        Result::Ok(from_u16_slice(result_buffer))
    } else {
        let error_desc = format!("Failure detected with system error code {}", get_last_error());
        Result::Err(errors::Error::new(error_desc))
    }
}

fn from_u16_slice(v: &[u8]) -> Box<Vec<FileNotifyInformation>> {
    let mut result:Vec<FileNotifyInformation> = Vec::new();
    let mut offset: usize = 0;
    loop {
        let next_entry_offset = as_u32le(v, offset);
        result.push(to_file_notify_information(v, offset));

        // check for 0.
        // 0 indicates that this is the last record
        if next_entry_offset == 0 {
            break;
        }
        offset += next_entry_offset as usize
    }
    Box::new(result)
}

fn to_file_notify_information(v: &[u8], offset: usize) -> FileNotifyInformation {
    let action = as_u32le(v, offset + 4);
    let file_name_length = as_u32le(v, offset + 8) as usize;
    let filename = to_filename(v, offset + 12, file_name_length);

    let fni = FileNotifyInformation {
        action: FileAction::from_u32(action),
        filename: filename,
    };

    fni
}

fn to_filename(v: &[u8], offset: usize, file_name_length: usize) -> String {
    let result = &v[offset .. offset + file_name_length];
    String::from_utf16(as_u16_slice(result)).unwrap()
}

fn as_u16_slice(v: &[u8]) -> &[u16] {
    unsafe {
        slice::from_raw_parts(v.as_ptr() as *const u16, v.len() / mem::size_of::<u16>())
    }
}

fn as_u32le(v: &[u8], offset: usize) -> u32 {
     // little endian
    (v[offset + 3] as u32) << 24
        | (v[offset + 2] as u32) << 16
        | (v[offset + 1] as u32) << 8
        | (v[offset] as u32)
}<|endoftext|>