<commit_before>use std::borrow::Cow;
use std::cell::RefCell;
use std::iter::repeat;

use console::{measure_text_width, Style};

use crate::format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanDuration};
use crate::progress::ProgressState;
use crate::utils::{expand_template, pad_str};

use unicode_segmentation::UnicodeSegmentation;

/// Controls the rendering style of progress bars.
#[derive(Clone, Debug)]
pub struct ProgressStyle {
    tick_strings: Vec<Box<str>>,
    progress_chars: Vec<Box<str>>,
    template: Cow<'static, str>,
}

fn segment(s: &str) -> Vec<Box<str>> {
    UnicodeSegmentation::graphemes(s, true)
        .map(|s| s.into())
        .collect()
}

impl ProgressStyle {
    /// Returns the default progress bar style for bars.
    pub fn default_bar() -> ProgressStyle {
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars: segment("█░"),
            template: Cow::Borrowed("{wide_bar} {pos}/{len}"),
        }
    }

    /// Returns the default progress bar style for spinners.
    pub fn default_spinner() -> ProgressStyle {
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars: segment("█░"),
            template: Cow::Borrowed("{spinner} {msg}"),
        }
    }

    /// Sets the tick character sequence for spinners.
    pub fn tick_chars(mut self, s: &str) -> ProgressStyle {
        self.tick_strings = s.chars().map(|c| c.to_string().into()).collect();
        self
    }

    /// Sets the tick string sequence for spinners.
    pub fn tick_strings(mut self, s: &[&str]) -> ProgressStyle {
        self.tick_strings = s.iter().map(|s| s.to_string().into()).collect();
        self
    }

    /// Sets the progress characters `(filled, current, to do)`.
    /// You can pass more then three for a more detailed display.
    pub fn progress_chars(mut self, s: &str) -> ProgressStyle {
        self.progress_chars = segment(s);
        self
    }

    /// Sets the template string for the progress bar.
    pub fn template(mut self, s: &str) -> ProgressStyle {
        self.template = Cow::Owned(s.into());
        self
    }

    /// Returns the tick char for a given number.
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_tick_str")]
    pub fn get_tick_char(&self, idx: u64) -> char {
        self.get_tick_str(idx).chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for a given number.
    pub fn get_tick_str(&self, idx: u64) -> &str {
        &self.tick_strings[(idx as usize) % (self.tick_strings.len() - 1)]
    }

    /// Returns the tick char for the finished state.
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_final_tick_str")]
    pub fn get_final_tick_char(&self) -> char {
        self.get_final_tick_str().chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for the finished state.
    pub fn get_final_tick_str(&self) -> &str {
        &self.tick_strings[self.tick_strings.len() - 1]
    }

    pub(crate) fn format_bar(
        &self,
        state: &ProgressState,
        width: usize,
        alt_style: Option<&Style>,
    ) -> String {
        let pct = state.fraction();
        let fill = pct * width as f32;
        let head = if pct > 0.0 && (fill as usize) < width {
            1
        } else {
            0
        };

        let pb = repeat(&state.style.progress_chars[0])
            .take(fill as usize)
            .fold(String::new(), |mut acc, new| {
                acc.push_str(&new);
                acc
            });
        let cur = if head == 1 {
            let n = state.style.progress_chars.len().saturating_sub(2);
            let cur_char = if n == 0 {
                1
            } else {
                n.saturating_sub((fill * n as f32) as usize % n)
            };
            state.style.progress_chars[cur_char].to_string()
        } else {
            "".into()
        };
        let bg = width.saturating_sub(fill as usize).saturating_sub(head);
        let rest = repeat(state.style.progress_chars.last().unwrap())
            .take(bg)
            .fold(String::new(), |mut acc, new| {
                acc.push_str(&new);
                acc
            });
        format!(
            "{}{}{}",
            pb,
            cur,
            alt_style.unwrap_or(&Style::new()).apply_to(rest)
        )
    }

    pub(crate) fn format_state(&self, state: &ProgressState) -> Vec<String> {
        let (pos, len) = state.position();
        let mut rv = vec![];

        for line in self.template.lines() {
            let wide_element = RefCell::new(None);

            let s = expand_template(line, |var| {
                let key = var.key;

                match key {
                    "wide_bar" => {
                        *wide_element.borrow_mut() = Some(var.duplicate_for_key("bar"));
                        "\x00".into()
                    }
                    "bar" => {
                        self.format_bar(state, var.width.unwrap_or(20), var.alt_style.as_ref())
                    }
                    "spinner" => state.current_tick_str().to_string(),
                    "wide_msg" => {
                        *wide_element.borrow_mut() = Some(var.duplicate_for_key("msg"));
                        "\x00".into()
                    }
                    "msg" => state.message().to_string(),
                    "prefix" => state.prefix().to_string(),
                    "pos" => pos.to_string(),
                    "len" => len.to_string(),
                    "percent" => format!("{:.*}", 0, state.fraction() * 100f32),
                    "bytes" => format!("{}", HumanBytes(state.pos)),
                    "total_bytes" => format!("{}", HumanBytes(state.len)),
                    "decimal_bytes" => format!("{}", DecimalBytes(state.pos)),
                    "decimal_total_bytes" => format!("{}", DecimalBytes(state.len)),
                    "binary_bytes" => format!("{}", BinaryBytes(state.pos)),
                    "binary_total_bytes" => format!("{}", BinaryBytes(state.len)),
                    "elapsed_precise" => format!("{}", FormattedDuration(state.started.elapsed())),
                    "elapsed" => format!("{:#}", HumanDuration(state.started.elapsed())),
                    "per_sec" => format!("{}/s", state.per_sec()),
                    "bytes_per_sec" => format!("{}/s", HumanBytes(state.per_sec())),
                    "eta_precise" => format!("{}", FormattedDuration(state.eta())),
                    "eta" => format!("{:#}", HumanDuration(state.eta())),
                    _ => "".into(),
                }
            });

            rv.push(if let Some(ref var) = *wide_element.borrow() {
                let total_width = state.width();
                if var.key == "bar" {
                    let bar_width = total_width.saturating_sub(measure_text_width(&s));
                    s.replace(
                        "\x00",
                        &self.format_bar(state, bar_width, var.alt_style.as_ref()),
                    )
                } else if var.key == "msg" {
                    let msg_width = total_width.saturating_sub(measure_text_width(&s));
                    let msg = pad_str(state.message(), msg_width, var.align, true);
                    s.replace(
                        "\x00",
                        if var.last_element {
                            msg.trim_end()
                        } else {
                            &msg
                        },
                    )
                } else {
                    unreachable!()
                }
            } else {
                s.to_string()
            });
        }

        rv
    }
}
<commit_msg>unicode-awareness part 2/2<commit_after>use std::borrow::Cow;
use std::cell::RefCell;
use std::iter::repeat;

use console::{measure_text_width, Style};

use crate::format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanDuration};
use crate::progress::ProgressState;
use crate::utils::{expand_template, pad_str};

use unicode_segmentation::UnicodeSegmentation;

/// Controls the rendering style of progress bars.
#[derive(Clone, Debug)]
pub struct ProgressStyle {
    tick_strings: Vec<Box<str>>,
    progress_chars: Vec<Box<str>>,
    template: Cow<'static, str>,
    // how unicode-big each char in progress_chars is
    char_width: usize,
}

fn segment(s: &str) -> Vec<Box<str>> {
    UnicodeSegmentation::graphemes(s, true)
        .map(|s| s.into())
        .collect()
}

/// finds the unicode-aware width of the passed grapheme cluters
/// panics on an empty parameter, or if the characters are not equal-width
fn width(c: &[Box<str>]) -> usize {
    c.iter()
        .map(|s| unicode_width::UnicodeWidthStr::width(s.as_ref()))
        .fold(None, |acc, new| {
            match acc {
                None => return Some(new),
                Some(old) => assert_eq!(old, new, "got passed un-equal width progress characters"),
            }
            acc
        })
        .unwrap()
}

impl ProgressStyle {
    /// Returns the default progress bar style for bars.
    pub fn default_bar() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: Cow::Borrowed("{wide_bar} {pos}/{len}"),
        }
    }

    /// Returns the default progress bar style for spinners.
    pub fn default_spinner() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: Cow::Borrowed("{spinner} {msg}"),
        }
    }

    /// Sets the tick character sequence for spinners.
    pub fn tick_chars(mut self, s: &str) -> ProgressStyle {
        self.tick_strings = s.chars().map(|c| c.to_string().into()).collect();
        self
    }

    /// Sets the tick string sequence for spinners.
    pub fn tick_strings(mut self, s: &[&str]) -> ProgressStyle {
        self.tick_strings = s.iter().map(|s| s.to_string().into()).collect();
        self
    }

    /// Sets the progress characters `(filled, current, to do)`.
    /// You can pass more then three for a more detailed display.
    /// All passed grapheme clusters need to be of equal width.
    pub fn progress_chars(mut self, s: &str) -> ProgressStyle {
        self.progress_chars = segment(s);
        self.char_width = width(&self.progress_chars);
        self
    }

    /// Sets the template string for the progress bar.
    pub fn template(mut self, s: &str) -> ProgressStyle {
        self.template = Cow::Owned(s.into());
        self
    }

    /// Returns the tick char for a given number.
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_tick_str")]
    pub fn get_tick_char(&self, idx: u64) -> char {
        self.get_tick_str(idx).chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for a given number.
    pub fn get_tick_str(&self, idx: u64) -> &str {
        &self.tick_strings[(idx as usize) % (self.tick_strings.len() - 1)]
    }

    /// Returns the tick char for the finished state.
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_final_tick_str")]
    pub fn get_final_tick_char(&self) -> char {
        self.get_final_tick_str().chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for the finished state.
    pub fn get_final_tick_str(&self) -> &str {
        &self.tick_strings[self.tick_strings.len() - 1]
    }

    pub(crate) fn format_bar(
        &self,
        state: &ProgressState,
        width: usize,
        alt_style: Option<&Style>,
    ) -> String {
        // todo: this code could really use some comments
        let width = width / state.style.char_width;
        let pct = state.fraction();
        let fill = pct * width as f32;
        let fill = fill as usize;
        let head = if pct > 0.0 && fill < width { 1 } else { 0 };

        let pb = repeat(&state.style.progress_chars[0]).take(fill).fold(
            String::new(),
            |mut acc, new| {
                acc.push_str(&new);
                acc
            },
        );
        let cur = if head == 1 {
            let n = state.style.progress_chars.len().saturating_sub(2);
            let cur_char = if n == 0 {
                1
            } else {
                n.saturating_sub((fill * n) % n)
            };
            state.style.progress_chars[cur_char].to_string()
        } else {
            "".into()
        };
        let bg = width.saturating_sub(fill).saturating_sub(head);
        let rest = repeat(state.style.progress_chars.last().unwrap())
            .take(bg)
            .fold(String::new(), |mut acc, new| {
                acc.push_str(&new);
                acc
            });
        format!(
            "{}{}{}",
            pb,
            cur,
            alt_style.unwrap_or(&Style::new()).apply_to(rest)
        )
    }

    pub(crate) fn format_state(&self, state: &ProgressState) -> Vec<String> {
        let (pos, len) = state.position();
        let mut rv = vec![];

        for line in self.template.lines() {
            let wide_element = RefCell::new(None);

            let s = expand_template(line, |var| {
                let key = var.key;

                match key {
                    "wide_bar" => {
                        *wide_element.borrow_mut() = Some(var.duplicate_for_key("bar"));
                        "\x00".into()
                    }
                    "bar" => {
                        self.format_bar(state, var.width.unwrap_or(20), var.alt_style.as_ref())
                    }
                    "spinner" => state.current_tick_str().to_string(),
                    "wide_msg" => {
                        *wide_element.borrow_mut() = Some(var.duplicate_for_key("msg"));
                        "\x00".into()
                    }
                    "msg" => state.message().to_string(),
                    "prefix" => state.prefix().to_string(),
                    "pos" => pos.to_string(),
                    "len" => len.to_string(),
                    "percent" => format!("{:.*}", 0, state.fraction() * 100f32),
                    "bytes" => format!("{}", HumanBytes(state.pos)),
                    "total_bytes" => format!("{}", HumanBytes(state.len)),
                    "decimal_bytes" => format!("{}", DecimalBytes(state.pos)),
                    "decimal_total_bytes" => format!("{}", DecimalBytes(state.len)),
                    "binary_bytes" => format!("{}", BinaryBytes(state.pos)),
                    "binary_total_bytes" => format!("{}", BinaryBytes(state.len)),
                    "elapsed_precise" => format!("{}", FormattedDuration(state.started.elapsed())),
                    "elapsed" => format!("{:#}", HumanDuration(state.started.elapsed())),
                    "per_sec" => format!("{}/s", state.per_sec()),
                    "bytes_per_sec" => format!("{}/s", HumanBytes(state.per_sec())),
                    "eta_precise" => format!("{}", FormattedDuration(state.eta())),
                    "eta" => format!("{:#}", HumanDuration(state.eta())),
                    _ => "".into(),
                }
            });

            rv.push(if let Some(ref var) = *wide_element.borrow() {
                let total_width = state.width();
                if var.key == "bar" {
                    let bar_width = total_width.saturating_sub(measure_text_width(&s));
                    s.replace(
                        "\x00",
                        &self.format_bar(state, bar_width, var.alt_style.as_ref()),
                    )
                } else if var.key == "msg" {
                    let msg_width = total_width.saturating_sub(measure_text_width(&s));
                    let msg = pad_str(state.message(), msg_width, var.align, true);
                    s.replace(
                        "\x00",
                        if var.last_element {
                            msg.trim_end()
                        } else {
                            &msg
                        },
                    )
                } else {
                    unreachable!()
                }
            } else {
                s.to_string()
            });
        }

        rv
    }
}
<|endoftext|>


<commit_before>/// A style is a collection of properties that can format a string
/// using ANSI escape codes.
#[derive(PartialEq, Clone, Copy)]
pub struct Style {

    /// The style's foreground colour, if it has one.
    pub foreground: Option<Colour>,

    /// The style's background colour, if it has one.
    pub background: Option<Colour>,

    /// Whether this style is bold.
    pub is_bold: bool,

    /// Whether this style is dimmed.
    pub is_dimmed: bool,

    /// Whether this style is italic.
    pub is_italic: bool,

    /// Whether this style is underlined.
    pub is_underline: bool,

    /// Whether this style is blinking.
    pub is_blink: bool,

    /// Whether this style has reverse colours.
    pub is_reverse: bool,

    /// Whether this style is hidden.
    pub is_hidden: bool,

    /// Whether this style is struckthrough.
    pub is_strikethrough: bool
}

impl Style {
    /// Creates a new Style with no differences.
    pub fn new() -> Style {
        Style::default()
    }

    /// Returns a `Style` with the bold property set.
    pub fn bold(&self) -> Style {
        Style { is_bold: true, .. *self }
    }

    /// Returns a `Style` with the dimmed property set.
    pub fn dimmed(&self) -> Style {
        Style { is_dimmed: true, .. *self }
    }

    /// Returns a `Style` with the italic property set.
    pub fn italic(&self) -> Style {
        Style { is_italic: true, .. *self }
    }

    /// Returns a `Style` with the underline property set.
    pub fn underline(&self) -> Style {
        Style { is_underline: true, .. *self }
    }

    /// Returns a `Style` with the blink property set.
    pub fn blink(&self) -> Style {
        Style { is_blink: true, .. *self }
    }

    /// Returns a `Style` with the reverse property set.
    pub fn reverse(&self) -> Style {
        Style { is_reverse: true, .. *self }
    }

    /// Returns a `Style` with the hidden property set.
    pub fn hidden(&self) -> Style {
        Style { is_hidden: true, .. *self }
    }

    /// Returns a `Style` with the hidden property set.
    pub fn strikethrough(&self) -> Style {
        Style { is_strikethrough: true, .. *self }
    }

    /// Returns a `Style` with the foreground colour property set.
    pub fn fg(&self, foreground: Colour) -> Style {
        Style { foreground: Some(foreground), .. *self }
    }

    /// Returns a `Style` with the background colour property set.
    pub fn on(&self, background: Colour) -> Style {
        Style { background: Some(background), .. *self }
    }

    /// Return true if this `Style` has no actual styles, and can be written
    /// without any control characters.
    pub fn is_plain(self) -> bool {
        self == Style::default()
    }
}

impl Default for Style {

    /// Returns a style with *no* properties set. Formatting text using this
    /// style returns the exact same text.
    ///
    /// ```
    /// use ansi_term::Style;
    /// assert_eq!(None,  Style::default().foreground);
    /// assert_eq!(None,  Style::default().background);
    /// assert_eq!(false, Style::default().is_bold);
    /// assert_eq!("txt", Style::default().paint("txt").to_string());
    /// ```
    fn default() -> Style {
        Style {
            foreground: None,
            background: None,
            is_bold: false,
            is_dimmed: false,
            is_italic: false,
            is_underline: false,
            is_blink: false,
            is_reverse: false,
            is_hidden: false,
            is_strikethrough: false,
        }
    }
}

// ---- colours ----

/// A colour is one specific type of ANSI escape code, and can refer
/// to either the foreground or background colour.
///
/// These use the standard numeric sequences.
/// See <http://invisible-island.net/xterm/ctlseqs/ctlseqs.html>
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum Colour {

    /// Colour #0 (foreground code `30`, background code `40`).
    ///
    /// This is not necessarily the background colour, and using it as one may
    /// render the text hard to read on terminals with dark backgrounds.
    Black,

    /// Colour #1 (foreground code `31`, background code `41`).
    Red,

    /// Colour #2 (foreground code `32`, background code `42`).
    Green,

    /// Colour #3 (foreground code `33`, background code `43`).
    Yellow,

    /// Colour #4 (foreground code `34`, background code `44`).
    Blue,

    /// Colour #5 (foreground code `35`, background code `45`).
    Purple,

    /// Colour #6 (foreground code `36`, background code `46`).
    Cyan,

    /// Colour #7 (foreground code `37`, background code `47`).
    ///
    /// As above, this is not necessarily the foreground colour, and may be
    /// hard to read on terminals with light backgrounds.
    White,

    /// A colour number from 0 to 255, for use in 256-colour terminal
    /// environments.
    ///
    /// - Colours 0 to 7 are the `Black` to `White` variants respectively.
    ///   These colours can usually be changed in the terminal emulator.
    /// - Colours 8 to 15 are brighter versions of the eight colours above.
    ///   These can also usually be changed in the terminal emulator, or it
    ///   could be configured to use the original colours and show the text in
    ///   bold instead. It varies depending on the program.
    /// - Colours 16 to 231 contain several palettes of bright colours,
    ///   arranged in six squares measuring six by six each.
    /// - Colours 232 to 255 are shades of grey from black to white.
    ///
    /// It might make more sense to look at a [colour chart][cc].
    ///
    /// [cc]: https://upload.wikimedia.org/wikipedia/en/1/15/Xterm_256color_chart.svg
    Fixed(u8),

    /// A 24-bit RGB color, as specified by ISO-8613-3.
    RGB(u8, u8, u8),
}

impl Colour {
    /// Return a `Style` with the foreground colour set to this colour.
    pub fn normal(self) -> Style {
        Style { foreground: Some(self), .. Style::default() }
    }

    /// Returns a `Style` with the bold property set.
    pub fn bold(self) -> Style {
        Style { foreground: Some(self), is_bold: true, .. Style::default() }
    }

    /// Returns a `Style` with the dimmed property set.
    pub fn dimmed(self) -> Style {
        Style { foreground: Some(self), is_dimmed: true, .. Style::default() }
    }

    /// Returns a `Style` with the italic property set.
    pub fn italic(self) -> Style {
        Style { foreground: Some(self), is_italic: true, .. Style::default() }
    }

    /// Returns a `Style` with the underline property set.
    pub fn underline(self) -> Style {
        Style { foreground: Some(self), is_underline: true, .. Style::default() }
    }

    /// Returns a `Style` with the blink property set.
    pub fn blink(self) -> Style {
        Style { foreground: Some(self), is_blink: true, .. Style::default() }
    }

    /// Returns a `Style` with the reverse property set.
    pub fn reverse(self) -> Style {
        Style { foreground: Some(self), is_reverse: true, .. Style::default() }
    }

    /// Returns a `Style` with the hidden property set.
    pub fn hidden(self) -> Style {
        Style { foreground: Some(self), is_hidden: true, .. Style::default() }
    }

    /// Returns a `Style` with the strikethrough property set.
    pub fn strikethrough(self) -> Style {
        Style { foreground: Some(self), is_strikethrough: true, .. Style::default() }
    }

    /// Returns a `Style` with the background colour property set.
    pub fn on(self, background: Colour) -> Style {
        Style { foreground: Some(self), background: Some(background), .. Style::default() }
    }
}

impl From<Colour> for Style {

    /// You can turn a `Colour` into a `Style` with the foreground colour set
    /// with the `From` trait.
    ///
    /// ```
    /// use ansi_term::{Style, Colour};
    /// let green_foreground = Style::default().fg(Colour::Green);
    /// assert_eq!(green_foreground, Colour::Green.normal());
    /// assert_eq!(green_foreground, Colour::Green.into());
    /// assert_eq!(green_foreground, Style::from(Colour::Green));
    /// ```
    fn from(colour: Colour) -> Style {
        colour.normal()
    }
}
<commit_msg>Fix URL to color chart<commit_after>/// A style is a collection of properties that can format a string
/// using ANSI escape codes.
#[derive(PartialEq, Clone, Copy)]
pub struct Style {

    /// The style's foreground colour, if it has one.
    pub foreground: Option<Colour>,

    /// The style's background colour, if it has one.
    pub background: Option<Colour>,

    /// Whether this style is bold.
    pub is_bold: bool,

    /// Whether this style is dimmed.
    pub is_dimmed: bool,

    /// Whether this style is italic.
    pub is_italic: bool,

    /// Whether this style is underlined.
    pub is_underline: bool,

    /// Whether this style is blinking.
    pub is_blink: bool,

    /// Whether this style has reverse colours.
    pub is_reverse: bool,

    /// Whether this style is hidden.
    pub is_hidden: bool,

    /// Whether this style is struckthrough.
    pub is_strikethrough: bool
}

impl Style {
    /// Creates a new Style with no differences.
    pub fn new() -> Style {
        Style::default()
    }

    /// Returns a `Style` with the bold property set.
    pub fn bold(&self) -> Style {
        Style { is_bold: true, .. *self }
    }

    /// Returns a `Style` with the dimmed property set.
    pub fn dimmed(&self) -> Style {
        Style { is_dimmed: true, .. *self }
    }

    /// Returns a `Style` with the italic property set.
    pub fn italic(&self) -> Style {
        Style { is_italic: true, .. *self }
    }

    /// Returns a `Style` with the underline property set.
    pub fn underline(&self) -> Style {
        Style { is_underline: true, .. *self }
    }

    /// Returns a `Style` with the blink property set.
    pub fn blink(&self) -> Style {
        Style { is_blink: true, .. *self }
    }

    /// Returns a `Style` with the reverse property set.
    pub fn reverse(&self) -> Style {
        Style { is_reverse: true, .. *self }
    }

    /// Returns a `Style` with the hidden property set.
    pub fn hidden(&self) -> Style {
        Style { is_hidden: true, .. *self }
    }

    /// Returns a `Style` with the hidden property set.
    pub fn strikethrough(&self) -> Style {
        Style { is_strikethrough: true, .. *self }
    }

    /// Returns a `Style` with the foreground colour property set.
    pub fn fg(&self, foreground: Colour) -> Style {
        Style { foreground: Some(foreground), .. *self }
    }

    /// Returns a `Style` with the background colour property set.
    pub fn on(&self, background: Colour) -> Style {
        Style { background: Some(background), .. *self }
    }

    /// Return true if this `Style` has no actual styles, and can be written
    /// without any control characters.
    pub fn is_plain(self) -> bool {
        self == Style::default()
    }
}

impl Default for Style {

    /// Returns a style with *no* properties set. Formatting text using this
    /// style returns the exact same text.
    ///
    /// ```
    /// use ansi_term::Style;
    /// assert_eq!(None,  Style::default().foreground);
    /// assert_eq!(None,  Style::default().background);
    /// assert_eq!(false, Style::default().is_bold);
    /// assert_eq!("txt", Style::default().paint("txt").to_string());
    /// ```
    fn default() -> Style {
        Style {
            foreground: None,
            background: None,
            is_bold: false,
            is_dimmed: false,
            is_italic: false,
            is_underline: false,
            is_blink: false,
            is_reverse: false,
            is_hidden: false,
            is_strikethrough: false,
        }
    }
}

// ---- colours ----

/// A colour is one specific type of ANSI escape code, and can refer
/// to either the foreground or background colour.
///
/// These use the standard numeric sequences.
/// See <http://invisible-island.net/xterm/ctlseqs/ctlseqs.html>
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum Colour {

    /// Colour #0 (foreground code `30`, background code `40`).
    ///
    /// This is not necessarily the background colour, and using it as one may
    /// render the text hard to read on terminals with dark backgrounds.
    Black,

    /// Colour #1 (foreground code `31`, background code `41`).
    Red,

    /// Colour #2 (foreground code `32`, background code `42`).
    Green,

    /// Colour #3 (foreground code `33`, background code `43`).
    Yellow,

    /// Colour #4 (foreground code `34`, background code `44`).
    Blue,

    /// Colour #5 (foreground code `35`, background code `45`).
    Purple,

    /// Colour #6 (foreground code `36`, background code `46`).
    Cyan,

    /// Colour #7 (foreground code `37`, background code `47`).
    ///
    /// As above, this is not necessarily the foreground colour, and may be
    /// hard to read on terminals with light backgrounds.
    White,

    /// A colour number from 0 to 255, for use in 256-colour terminal
    /// environments.
    ///
    /// - Colours 0 to 7 are the `Black` to `White` variants respectively.
    ///   These colours can usually be changed in the terminal emulator.
    /// - Colours 8 to 15 are brighter versions of the eight colours above.
    ///   These can also usually be changed in the terminal emulator, or it
    ///   could be configured to use the original colours and show the text in
    ///   bold instead. It varies depending on the program.
    /// - Colours 16 to 231 contain several palettes of bright colours,
    ///   arranged in six squares measuring six by six each.
    /// - Colours 232 to 255 are shades of grey from black to white.
    ///
    /// It might make more sense to look at a [colour chart][cc].
    ///
    /// [cc]: https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg
    Fixed(u8),

    /// A 24-bit RGB color, as specified by ISO-8613-3.
    RGB(u8, u8, u8),
}

impl Colour {
    /// Return a `Style` with the foreground colour set to this colour.
    pub fn normal(self) -> Style {
        Style { foreground: Some(self), .. Style::default() }
    }

    /// Returns a `Style` with the bold property set.
    pub fn bold(self) -> Style {
        Style { foreground: Some(self), is_bold: true, .. Style::default() }
    }

    /// Returns a `Style` with the dimmed property set.
    pub fn dimmed(self) -> Style {
        Style { foreground: Some(self), is_dimmed: true, .. Style::default() }
    }

    /// Returns a `Style` with the italic property set.
    pub fn italic(self) -> Style {
        Style { foreground: Some(self), is_italic: true, .. Style::default() }
    }

    /// Returns a `Style` with the underline property set.
    pub fn underline(self) -> Style {
        Style { foreground: Some(self), is_underline: true, .. Style::default() }
    }

    /// Returns a `Style` with the blink property set.
    pub fn blink(self) -> Style {
        Style { foreground: Some(self), is_blink: true, .. Style::default() }
    }

    /// Returns a `Style` with the reverse property set.
    pub fn reverse(self) -> Style {
        Style { foreground: Some(self), is_reverse: true, .. Style::default() }
    }

    /// Returns a `Style` with the hidden property set.
    pub fn hidden(self) -> Style {
        Style { foreground: Some(self), is_hidden: true, .. Style::default() }
    }

    /// Returns a `Style` with the strikethrough property set.
    pub fn strikethrough(self) -> Style {
        Style { foreground: Some(self), is_strikethrough: true, .. Style::default() }
    }

    /// Returns a `Style` with the background colour property set.
    pub fn on(self, background: Colour) -> Style {
        Style { foreground: Some(self), background: Some(background), .. Style::default() }
    }
}

impl From<Colour> for Style {

    /// You can turn a `Colour` into a `Style` with the foreground colour set
    /// with the `From` trait.
    ///
    /// ```
    /// use ansi_term::{Style, Colour};
    /// let green_foreground = Style::default().fg(Colour::Green);
    /// assert_eq!(green_foreground, Colour::Green.normal());
    /// assert_eq!(green_foreground, Colour::Green.into());
    /// assert_eq!(green_foreground, Style::from(Colour::Green));
    /// ```
    fn from(colour: Colour) -> Style {
        colour.normal()
    }
}
<|endoftext|>


<commit_before>//! Contains Lea's table type

#![macro_use]

use value::{Value, TNil};
use mem::GcRef;

use std::collections::HashMap;
use std::ops::{Deref, DerefMut};

/// A table that maps Lea values to Lea values.
#[derive(RustcEncodable, Debug)]
pub struct Table {
    data: HashMap<Value, Value>,
    metatable: Option<GcRef<Table>>,
    weak_keys: bool,
    weak_vals: bool,
}

impl Table {
    /// Creates a new table, using the given `HashMap<Value, Value>` as the internal storage.
    pub fn new(data: HashMap<Value, Value>) -> Table {
        Table {
            data: data,
            metatable: None,
            weak_keys: false,
            weak_vals: false,
        }
    }

    pub fn weak_keys(&self) -> bool {
        self.weak_keys
    }

    pub fn weak_values(&self) -> bool {
        self.weak_vals
    }

    /// Equivalent to assignment by Lea code. If the value is nil, the mapping is deleted. Returns
    /// the old value stored with the given key or `Err(())` if the key is nil (this is not
    /// supported).
    pub fn set(&mut self, k: Value, v: Value) -> Result<Option<Value>, ()> {
        if k == TNil { return Err(()); }

        if v == TNil {
            Ok(self.data.remove(&k))
        } else {
            Ok(self.data.insert(k, v))
        }
    }
}

impl Deref for Table {
    type Target = HashMap<Value, Value>;

    fn deref(&self) -> &<Self as Deref>::Target {
        &self.data
    }
}

impl DerefMut for Table {
    fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
        &mut self.data
    }
}

macro_rules! hashmap {
    ( $($k:expr => $v:expr),* ) => {{
        let mut m = ::std::collections::HashMap::new();
        $( m.insert($k, $v); )*
        m
    }};
    ( $($k:expr => $v:expr,)* ) => {
        hashmap!($($k => $v),*)
    };
}

macro_rules! table {
    ( $($t:tt)* ) => {
        $crate::table::Table::new(hashmap!($($t)*))
    };
}

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

    #[test]
    fn basic() {
        let mut t = table! {
            TFloat(HashedFloat(0.5)) => TInt(42),
            TBool(true) => TInt(24),
        };

        assert_eq!(t.set(TBool(false), TFloat(HashedFloat(123f64))), Ok(None));
        assert_eq!(t.data, hashmap! {
            TFloat(HashedFloat(0.5)) => TInt(42),
            TBool(true) => TInt(24),
            TBool(false) => TFloat(HashedFloat(123f64)),
        });

        assert_eq!(t.set(TBool(true), TNil), Ok(Some(TInt(24))));
        assert_eq!(t.data, hashmap! {
            TFloat(HashedFloat(0.5)) => TInt(42),
            TBool(false) => TFloat(HashedFloat(123f64)),
        });

        assert_eq!(t.set(TNil, TNil), Err(()));
    }

    #[test]
    fn macros() {
        assert_eq!(Table::new(hashmap! {
            TBool(true) => TInt(9),
        }).data, table! {
            TBool(true) => TInt(9),
        }.data);
    }
}
<commit_msg>Add setters for table weakness<commit_after>//! Contains Lea's table type

#![macro_use]

use value::{Value, TNil};
use mem::GcRef;

use std::collections::HashMap;
use std::ops::{Deref, DerefMut};

/// A table that maps Lea values to Lea values.
#[derive(RustcEncodable, Debug)]
pub struct Table {
    data: HashMap<Value, Value>,
    metatable: Option<GcRef<Table>>,
    weak_keys: bool,
    weak_vals: bool,
}

impl Table {
    /// Creates a new table, using the given `HashMap<Value, Value>` as the internal storage.
    pub fn new(data: HashMap<Value, Value>) -> Table {
        Table {
            data: data,
            metatable: None,
            weak_keys: false,
            weak_vals: false,
        }
    }

    pub fn get_weak_keys(&self) -> bool {
        self.weak_keys
    }

    pub fn set_weak_keys(&mut self, weak: bool) {
        self.weak_keys = weak;
    }

    pub fn get_weak_values(&self) -> bool {
        self.weak_vals
    }

    pub fn set_weak_values(&mut self, weak: bool) {
        self.weak_vals = weak;
    }

    /// Equivalent to assignment by Lea code. If the value is nil, the mapping is deleted. Returns
    /// the old value stored with the given key or `Err(())` if the key is nil (this is not
    /// supported).
    pub fn set(&mut self, k: Value, v: Value) -> Result<Option<Value>, ()> {
        if k == TNil { return Err(()); }

        if v == TNil {
            Ok(self.data.remove(&k))
        } else {
            Ok(self.data.insert(k, v))
        }
    }
}

impl Deref for Table {
    type Target = HashMap<Value, Value>;

    fn deref(&self) -> &<Self as Deref>::Target {
        &self.data
    }
}

impl DerefMut for Table {
    fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
        &mut self.data
    }
}

macro_rules! hashmap {
    ( $($k:expr => $v:expr),* ) => {{
        let mut m = ::std::collections::HashMap::new();
        $( m.insert($k, $v); )*
        m
    }};
    ( $($k:expr => $v:expr,)* ) => {
        hashmap!($($k => $v),*)
    };
}

macro_rules! table {
    ( $($t:tt)* ) => {
        $crate::table::Table::new(hashmap!($($t)*))
    };
}

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

    #[test]
    fn test() {
        let mut t = table! {
            TFloat(HashedFloat(0.5)) => TInt(42),
            TBool(true) => TInt(24),
        };

        assert_eq!(t.set(TBool(false), TFloat(HashedFloat(123f64))), Ok(None));
        assert_eq!(t.data, hashmap! {
            TFloat(HashedFloat(0.5)) => TInt(42),
            TBool(true) => TInt(24),
            TBool(false) => TFloat(HashedFloat(123f64)),
        });

        assert_eq!(t.set(TBool(true), TNil), Ok(Some(TInt(24))));
        assert_eq!(t.data, hashmap! {
            TFloat(HashedFloat(0.5)) => TInt(42),
            TBool(false) => TFloat(HashedFloat(123f64)),
        });

        assert_eq!(t.set(TNil, TNil), Err(()));
    }
}
<|endoftext|>


<commit_before>extern crate etcd;

use etcd::{Client, Error};

/// Wrapper around Client that automatically cleans up etcd after each test.
struct TestClient {
    c: Client,
}

impl TestClient {
    /// Creates a new client for a test.
    fn new() -> TestClient {
        TestClient {
            c: Client::new("http://etcd:2379").unwrap(),
        }
    }
}

impl Drop for TestClient {
    #[allow(unused_must_use)]
    fn drop(&mut self) {
        self.c.delete("/test", true);
    }
}

#[test]
fn create() {
    let client = TestClient::new();

    let response = match client.c.create("/test/foo", "bar", Some(60)) {
        Ok(response) => response,
        Err(error) => panic!("{:?}", error),
    };

    assert_eq!(response.action, "create".to_string());
    assert_eq!(response.node.value.unwrap(), "bar".to_string());
    assert_eq!(response.node.ttl.unwrap(), 60);
}

#[test]
fn create_does_not_replace_existing_key() {
    let client = TestClient::new();

    client.c.create("/test/foo", "bar", Some(60)).ok().unwrap();

    match client.c.create("/test/foo", "bar", None).err().unwrap() {
        Error::Etcd(error) => assert_eq!(error.message, "Key already exists".to_string()),
        _ => panic!("expected EtcdError due to pre-existing key"),
    };
}

#[test]
fn get() {
    let client = TestClient::new();
    client.c.create("/test/foo", "bar", Some(60)).ok().unwrap();

    let response = client.c.get("/test/foo", false, false).ok().unwrap();

    assert_eq!(response.action, "get".to_string());
    assert_eq!(response.node.value.unwrap(), "bar".to_string());
    assert_eq!(response.node.ttl.unwrap(), 60);

}

#[test]
fn get_recursive() {
    let client = TestClient::new();

    client.c.set("/test/dir/baz", "blah", None).ok();
    client.c.set("/test/foo", "bar", None).ok();

    let mut response = client.c.get("/test", true, false).ok().unwrap();

    assert_eq!(response.node.dir.unwrap(), true);

    let mut nodes = response.node.nodes.unwrap();

    assert_eq!(nodes[0].clone().key.unwrap(), "/test/dir".to_string());
    assert_eq!(nodes[0].clone().dir.unwrap(), true);
    assert_eq!(nodes[1].clone().key.unwrap(), "/test/foo".to_string());
    assert_eq!(nodes[1].clone().value.unwrap(), "bar".to_string());

    response = client.c.get("/test", true, true).ok().unwrap();
    nodes = response.node.nodes.unwrap();

    assert_eq!(
        nodes[0].clone().nodes.unwrap()[0].clone().value.unwrap(),
        "blah".to_string()
    );
}

#[test]
fn set() {
    let client = TestClient::new();

    let response = client.c.set("/test/foo", "baz", None).ok().unwrap();

    assert_eq!(response.action, "set".to_string());
    assert_eq!(response.node.value.unwrap(), "baz".to_string());
    assert!(response.node.ttl.is_none());
}

#[test]
fn set_dir() {
    let client = TestClient::new();

    assert!(client.c.set_dir("/test", None).is_ok());
    assert!(client.c.set_dir("/test", None).is_err());

    client.c.set("/test/foo", "bar", None).ok().unwrap();

    assert!(client.c.set_dir("/test/foo", None).is_ok());
}

#[test]
fn update() {
    let client = TestClient::new();
    client.c.create("/test/foo", "bar", None).ok().unwrap();

    let response = client.c.update("/test/foo", "blah", Some(30)).ok().unwrap();

    assert_eq!(response.action, "update".to_string());
    assert_eq!(response.node.value.unwrap(), "blah".to_string());
    assert_eq!(response.node.ttl.unwrap(), 30);
}

#[test]
fn update_requires_existing_key() {
    let client = TestClient::new();

    match client.c.update("/test/foo", "bar", None).err().unwrap() {
        Error::Etcd(error) => assert_eq!(error.message, "Key not found".to_string()),
        _ => panic!("expected EtcdError due to missing key"),
    };
}

#[test]
fn update_dir() {
    let client = TestClient::new();

    client.c.create_dir("/test", None).ok().unwrap();

    let response = client.c.update_dir("/test", Some(60)).ok().unwrap();

    assert_eq!(response.node.ttl.unwrap(), 60);
}

#[test]
fn update_dir_replaces_key() {
    let client = TestClient::new();

    client.c.set("/test/foo", "bar", None).ok().unwrap();

    let response = client.c.update_dir("/test/foo", Some(60)).ok().unwrap();

    assert_eq!(response.node.value.unwrap(), "");
    assert_eq!(response.node.ttl.unwrap(), 60);
}

#[test]
fn update_dir_requires_existing_dir() {
    let client = TestClient::new();

    assert!(client.c.update_dir("/test", None).is_err());
}

#[test]
fn delete() {
    let client = TestClient::new();
    client.c.create("/test/foo", "bar", None).ok().unwrap();

    let response = client.c.delete("/test/foo", false).ok().unwrap();

    assert_eq!(response.action, "delete");
}

#[test]
fn create_dir() {
    let client = TestClient::new();

    let response = client.c.create_dir("/test/dir", None).ok().unwrap();

    assert_eq!(response.action, "create".to_string());
    assert!(response.node.dir.unwrap());
    assert!(response.node.value.is_none());
}

#[test]
fn delete_dir() {
    let client = TestClient::new();
    client.c.create_dir("/test/dir", None).ok().unwrap();

    let response = client.c.delete_dir("/test/dir").ok().unwrap();

    assert_eq!(response.action, "delete");
}

#[test]
fn leader_stats() {
    let client = TestClient::new();

    client.c.leader_stats().unwrap();
}
<commit_msg>Split get recursive test into two.<commit_after>extern crate etcd;

use etcd::{Client, Error};

/// Wrapper around Client that automatically cleans up etcd after each test.
struct TestClient {
    c: Client,
}

impl TestClient {
    /// Creates a new client for a test.
    fn new() -> TestClient {
        TestClient {
            c: Client::new("http://etcd:2379").unwrap(),
        }
    }
}

impl Drop for TestClient {
    #[allow(unused_must_use)]
    fn drop(&mut self) {
        self.c.delete("/test", true);
    }
}

#[test]
fn create() {
    let client = TestClient::new();

    let response = match client.c.create("/test/foo", "bar", Some(60)) {
        Ok(response) => response,
        Err(error) => panic!("{:?}", error),
    };

    assert_eq!(response.action, "create".to_string());
    assert_eq!(response.node.value.unwrap(), "bar".to_string());
    assert_eq!(response.node.ttl.unwrap(), 60);
}

#[test]
fn create_does_not_replace_existing_key() {
    let client = TestClient::new();

    client.c.create("/test/foo", "bar", Some(60)).ok().unwrap();

    match client.c.create("/test/foo", "bar", None).err().unwrap() {
        Error::Etcd(error) => assert_eq!(error.message, "Key already exists".to_string()),
        _ => panic!("expected EtcdError due to pre-existing key"),
    };
}

#[test]
fn get() {
    let client = TestClient::new();
    client.c.create("/test/foo", "bar", Some(60)).ok().unwrap();

    let response = client.c.get("/test/foo", false, false).ok().unwrap();

    assert_eq!(response.action, "get".to_string());
    assert_eq!(response.node.value.unwrap(), "bar".to_string());
    assert_eq!(response.node.ttl.unwrap(), 60);

}

#[test]
fn get_non_recursive() {
    let client = TestClient::new();

    client.c.set("/test/dir/baz", "blah", None).ok();
    client.c.set("/test/foo", "bar", None).ok();

    let response = client.c.get("/test", true, false).ok().unwrap();

    assert_eq!(response.node.dir.unwrap(), true);

    let nodes = response.node.nodes.unwrap();

    assert_eq!(nodes[0].clone().key.unwrap(), "/test/dir".to_string());
    assert_eq!(nodes[0].clone().dir.unwrap(), true);
    assert_eq!(nodes[1].clone().key.unwrap(), "/test/foo".to_string());
    assert_eq!(nodes[1].clone().value.unwrap(), "bar".to_string());
}

#[test]
fn get_recursive() {
    let client = TestClient::new();

    client.c.set("/test/dir/baz", "blah", None).ok();

    let response = client.c.get("/test", true, true).ok().unwrap();
    let nodes = response.node.nodes.unwrap();

    assert_eq!(
        nodes[0].clone().nodes.unwrap()[0].clone().value.unwrap(),
        "blah".to_string()
    );
}

#[test]
fn set() {
    let client = TestClient::new();

    let response = client.c.set("/test/foo", "baz", None).ok().unwrap();

    assert_eq!(response.action, "set".to_string());
    assert_eq!(response.node.value.unwrap(), "baz".to_string());
    assert!(response.node.ttl.is_none());
}

#[test]
fn set_dir() {
    let client = TestClient::new();

    assert!(client.c.set_dir("/test", None).is_ok());
    assert!(client.c.set_dir("/test", None).is_err());

    client.c.set("/test/foo", "bar", None).ok().unwrap();

    assert!(client.c.set_dir("/test/foo", None).is_ok());
}

#[test]
fn update() {
    let client = TestClient::new();
    client.c.create("/test/foo", "bar", None).ok().unwrap();

    let response = client.c.update("/test/foo", "blah", Some(30)).ok().unwrap();

    assert_eq!(response.action, "update".to_string());
    assert_eq!(response.node.value.unwrap(), "blah".to_string());
    assert_eq!(response.node.ttl.unwrap(), 30);
}

#[test]
fn update_requires_existing_key() {
    let client = TestClient::new();

    match client.c.update("/test/foo", "bar", None).err().unwrap() {
        Error::Etcd(error) => assert_eq!(error.message, "Key not found".to_string()),
        _ => panic!("expected EtcdError due to missing key"),
    };
}

#[test]
fn update_dir() {
    let client = TestClient::new();

    client.c.create_dir("/test", None).ok().unwrap();

    let response = client.c.update_dir("/test", Some(60)).ok().unwrap();

    assert_eq!(response.node.ttl.unwrap(), 60);
}

#[test]
fn update_dir_replaces_key() {
    let client = TestClient::new();

    client.c.set("/test/foo", "bar", None).ok().unwrap();

    let response = client.c.update_dir("/test/foo", Some(60)).ok().unwrap();

    assert_eq!(response.node.value.unwrap(), "");
    assert_eq!(response.node.ttl.unwrap(), 60);
}

#[test]
fn update_dir_requires_existing_dir() {
    let client = TestClient::new();

    assert!(client.c.update_dir("/test", None).is_err());
}

#[test]
fn delete() {
    let client = TestClient::new();
    client.c.create("/test/foo", "bar", None).ok().unwrap();

    let response = client.c.delete("/test/foo", false).ok().unwrap();

    assert_eq!(response.action, "delete");
}

#[test]
fn create_dir() {
    let client = TestClient::new();

    let response = client.c.create_dir("/test/dir", None).ok().unwrap();

    assert_eq!(response.action, "create".to_string());
    assert!(response.node.dir.unwrap());
    assert!(response.node.value.is_none());
}

#[test]
fn delete_dir() {
    let client = TestClient::new();
    client.c.create_dir("/test/dir", None).ok().unwrap();

    let response = client.c.delete_dir("/test/dir").ok().unwrap();

    assert_eq!(response.action, "delete");
}

#[test]
fn leader_stats() {
    let client = TestClient::new();

    client.c.leader_stats().unwrap();
}
<|endoftext|>


<commit_before>// Copyright 2017 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{cmp, ptr, usize};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::ffi::CString;
use std::sync::Arc;
use std::time::Duration;

use libc::{c_char, c_int};
use grpc_sys::{self, GprTimespec, GrpcChannel, GrpcChannelArgs};

use CallOption;
use call::{Call, Method};
use credentials::ChannelCredentials;
use cq::CompletionQueue;
use env::Environment;

// hack: add a '\0' to be compatible with c string without extra allocation.
const OPT_DEFAULT_AUTHORITY: &'static [u8] = b"grpc.default_authority\0";
const OPT_MAX_CONCURRENT_STREAMS: &'static [u8] = b"grpc.max_concurrent_streams\0";
const OPT_MAX_RECEIVE_MESSAGE_LENGTH: &'static [u8] = b"grpc.max_receive_message_length\0";
const OPT_MAX_SEND_MESSAGE_LENGTH: &'static [u8] = b"grpc.max_send_message_length\0";
const OPT_MAX_RECONNECT_BACKOFF_MS: &'static [u8] = b"grpc.max_reconnect_backoff_ms\0";
const OPT_INITIAL_RECONNECT_BACKOFF_MS: &'static [u8] = b"grpc.initial_reconnect_backoff_ms\0";
const OPT_HTTP2_INITIAL_SEQUENCE_NUMBER: &'static [u8] = b"grpc.http2.initial_sequence_number\0";
const OPT_SO_REUSE_PORT: &'static [u8] = b"grpc.so_reuseport\0";
const OPT_SSL_TARGET_NAME_OVERRIDE: &'static [u8] = b"grpc.ssl_target_name_override\0";
const PRIMARY_USER_AGENT_STRING: &'static [u8] = b"grpc.primary_user_agent\0";

/// Ref: http://www.grpc.io/docs/guides/wire.html#user-agents
fn format_user_agent_string(agent: &str) -> CString {
    let version = env!("CARGO_PKG_VERSION");
    let trimed_agent = agent.trim();
    let val = if trimed_agent.is_empty() {
        format!("grpc-rust/{}", version)
    } else {
        format!("{} grpc-rust/{}", trimed_agent, version)
    };
    CString::new(val).unwrap()
}

fn dur_to_ms(dur: Duration) -> usize {
    let millis = dur.as_secs() * 1000 + dur.subsec_nanos() as u64 / 1_000_000;
    cmp::min(usize::MAX as u64, millis) as usize
}

enum Options {
    Integer(usize),
    String(CString),
}

/// Channel configuration object.
pub struct ChannelBuilder {
    env: Arc<Environment>,
    options: HashMap<&'static [u8], Options>,
}

impl ChannelBuilder {
    pub fn new(env: Arc<Environment>) -> ChannelBuilder {
        ChannelBuilder {
            env: env,
            options: HashMap::new(),
        }
    }

    /// Default authority to pass if none specified on call construction.
    pub fn default_authority<S: Into<Vec<u8>>>(mut self, authority: S) -> ChannelBuilder {
        let authority = CString::new(authority).unwrap();
        self.options
            .insert(OPT_DEFAULT_AUTHORITY, Options::String(authority));
        self
    }

    /// Maximum number of concurrent incoming streams to allow on a http2 connection.
    pub fn max_concurrent_stream(mut self, num: usize) -> ChannelBuilder {
        self.options
            .insert(OPT_MAX_CONCURRENT_STREAMS, Options::Integer(num));
        self
    }

    /// Maximum message length that the channel can receive. usize::MAX means unlimited.
    pub fn max_receive_message_len(mut self, len: usize) -> ChannelBuilder {
        self.options
            .insert(OPT_MAX_RECEIVE_MESSAGE_LENGTH, Options::Integer(len));
        self
    }

    /// Maximum message length that the channel can send. -1 means unlimited.
    pub fn max_send_message_len(mut self, len: usize) -> ChannelBuilder {
        self.options
            .insert(OPT_MAX_SEND_MESSAGE_LENGTH, Options::Integer(len));
        self
    }

    /// The maximum time between subsequent connection attempts.
    pub fn max_reconnect_backoff(mut self, backoff: Duration) -> ChannelBuilder {
        self.options
            .insert(OPT_MAX_RECONNECT_BACKOFF_MS,
                    Options::Integer(dur_to_ms(backoff)));
        self
    }

    /// The time between the first and second connection attempts.
    pub fn initial_reconnect_backoff(mut self, backoff: Duration) -> ChannelBuilder {
        self.options
            .insert(OPT_INITIAL_RECONNECT_BACKOFF_MS,
                    Options::Integer(dur_to_ms(backoff)));
        self
    }

    /// Initial sequence number for http2 transports.
    pub fn https_initial_seq_number(mut self, number: usize) -> ChannelBuilder {
        self.options
            .insert(OPT_HTTP2_INITIAL_SEQUENCE_NUMBER, Options::Integer(number));
        self
    }

    /// Primary user agent: goes at the start of the user-agent metadata sent on each request.
    pub fn primary_user_agent(mut self, agent: &str) -> ChannelBuilder {
        let agent_string = format_user_agent_string(agent);
        self.options
            .insert(PRIMARY_USER_AGENT_STRING, Options::String(agent_string));
        self
    }

    /// If enable, allow the use of SO_REUSEPORT if it's available (default true).
    pub fn reuse_port(mut self, reuse: bool) -> ChannelBuilder {
        let opt = if reuse { 1 } else { 0 };
        self.options
            .insert(OPT_SO_REUSE_PORT, Options::Integer(opt));
        self
    }

    /// The caller of the secure_channel_create functions may override the target name used for SSL
    /// host name checking using this channel argument. This *should* be used for testing only.
    pub fn override_ssl_target<S: Into<Vec<u8>>>(mut self, target: S) -> ChannelBuilder {
        let target = CString::new(target).unwrap();
        self.options
            .insert(OPT_SSL_TARGET_NAME_OVERRIDE, Options::String(target));
        self
    }

    /// Build a channel args from the current configuration.
    pub fn build_args(&self) -> ChannelArgs {
        let args = unsafe { grpc_sys::grpcwrap_channel_args_create(self.options.len()) };
        for (i, (k, v)) in self.options.iter().enumerate() {
            let key = k.as_ptr() as *const c_char;
            match *v {
                Options::Integer(val) => unsafe {
                    grpc_sys::grpcwrap_channel_args_set_integer(args, i, key, val as c_int)
                },
                Options::String(ref val) => {
                    unsafe {
                        grpc_sys::grpcwrap_channel_args_set_string(args, i, key, val.as_ptr())
                    }
                }
            }
        }
        ChannelArgs { args: args }
    }

    /// Build an insure connection to the address.
    pub fn connect(self, addr: &str) -> Channel {
        self.connect_with_creds(addr, None)
    }

    fn connect_with_creds(mut self, addr: &str, creds: Option<ChannelCredentials>) -> Channel {
        let addr = CString::new(addr).unwrap();
        if let Entry::Vacant(e) = self.options.entry(PRIMARY_USER_AGENT_STRING) {
            e.insert(Options::String(format_user_agent_string("")));
        }
        let args = self.build_args();
        let addr_ptr = addr.as_ptr();
        let channel = unsafe {
            match creds {
                None => {
                    grpc_sys::grpc_insecure_channel_create(addr_ptr, args.args, ptr::null_mut())
                }
                Some(mut creds) => {
                    grpc_sys::grpc_secure_channel_create(creds.as_mut_ptr(),
                                                         addr_ptr,
                                                         args.args,
                                                         ptr::null_mut())
                }
            }
        };

        Channel {
            cq: self.env.pick_cq(),
            inner: Arc::new(ChannelInner {
                                _env: self.env,
                                channel: channel,
                            }),
        }
    }

    pub fn secure_connect(self, addr: &str, creds: ChannelCredentials) -> Channel {
        self.connect_with_creds(addr, Some(creds))
    }
}

pub struct ChannelArgs {
    args: *mut GrpcChannelArgs,
}

impl ChannelArgs {
    pub fn as_ptr(&self) -> *const GrpcChannelArgs {
        self.args
    }
}

impl Drop for ChannelArgs {
    fn drop(&mut self) {
        unsafe { grpc_sys::grpcwrap_channel_args_destroy(self.args) }
    }
}

struct ChannelInner {
    _env: Arc<Environment>,
    channel: *mut GrpcChannel,
}

impl Drop for ChannelInner {
    fn drop(&mut self) {
        unsafe {
            grpc_sys::grpc_channel_destroy(self.channel);
        }
    }
}

/// The Channel struct allows creation of Call objects.
#[derive(Clone)]
pub struct Channel {
    inner: Arc<ChannelInner>,
    cq: Arc<CompletionQueue>,
}

unsafe impl Send for Channel {}
unsafe impl Sync for Channel {}

impl Channel {
    /// Create a call using the method and option.
    pub fn create_call<P, Q>(&self, method: &Method<P, Q>, opt: &CallOption) -> Call {
        let raw_call = unsafe {
            let ch = self.inner.channel;
            let cq = self.cq.as_ptr();
            let method_ptr = method.name.as_ptr();
            let method_len = method.name.len();
            let timeout = opt.get_timeout()
                .map_or_else(GprTimespec::inf_future, GprTimespec::from);
            grpc_sys::grpcwrap_channel_create_call(ch,
                                                   ptr::null_mut(),
                                                   0,
                                                   cq,
                                                   method_ptr as *const _,
                                                   method_len,
                                                   ptr::null(),
                                                   0,
                                                   timeout,
                                                   ptr::null_mut())
        };

        unsafe { Call::from_raw(raw_call) }
    }

    pub fn cq(&self) -> &CompletionQueue {
        self.cq.as_ref()
    }
}
<commit_msg>channel: support window size (#51)<commit_after>// Copyright 2017 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{cmp, ptr, usize};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::ffi::CString;
use std::sync::Arc;
use std::time::Duration;

use libc::{c_char, c_int};
use grpc_sys::{self, GprTimespec, GrpcChannel, GrpcChannelArgs};

use CallOption;
use call::{Call, Method};
use credentials::ChannelCredentials;
use cq::CompletionQueue;
use env::Environment;

// hack: add a '\0' to be compatible with c string without extra allocation.
const OPT_DEFAULT_AUTHORITY: &'static [u8] = b"grpc.default_authority\0";
const OPT_MAX_CONCURRENT_STREAMS: &'static [u8] = b"grpc.max_concurrent_streams\0";
const OPT_MAX_RECEIVE_MESSAGE_LENGTH: &'static [u8] = b"grpc.max_receive_message_length\0";
const OPT_MAX_SEND_MESSAGE_LENGTH: &'static [u8] = b"grpc.max_send_message_length\0";
const OPT_MAX_RECONNECT_BACKOFF_MS: &'static [u8] = b"grpc.max_reconnect_backoff_ms\0";
const OPT_INITIAL_RECONNECT_BACKOFF_MS: &'static [u8] = b"grpc.initial_reconnect_backoff_ms\0";
const OPT_HTTP2_INITIAL_SEQUENCE_NUMBER: &'static [u8] = b"grpc.http2.initial_sequence_number\0";
const OPT_SO_REUSE_PORT: &'static [u8] = b"grpc.so_reuseport\0";
const OPT_SSL_TARGET_NAME_OVERRIDE: &'static [u8] = b"grpc.ssl_target_name_override\0";
const OPT_STREAM_INITIAL_WINDOW_SIZE: &'static [u8] = b"grpc.http2.lookahead_bytes\0";
const PRIMARY_USER_AGENT_STRING: &'static [u8] = b"grpc.primary_user_agent\0";

/// Ref: http://www.grpc.io/docs/guides/wire.html#user-agents
fn format_user_agent_string(agent: &str) -> CString {
    let version = env!("CARGO_PKG_VERSION");
    let trimed_agent = agent.trim();
    let val = if trimed_agent.is_empty() {
        format!("grpc-rust/{}", version)
    } else {
        format!("{} grpc-rust/{}", trimed_agent, version)
    };
    CString::new(val).unwrap()
}

fn dur_to_ms(dur: Duration) -> usize {
    let millis = dur.as_secs() * 1000 + dur.subsec_nanos() as u64 / 1_000_000;
    cmp::min(usize::MAX as u64, millis) as usize
}

enum Options {
    Integer(usize),
    String(CString),
}

/// Channel configuration object.
pub struct ChannelBuilder {
    env: Arc<Environment>,
    options: HashMap<&'static [u8], Options>,
}

impl ChannelBuilder {
    pub fn new(env: Arc<Environment>) -> ChannelBuilder {
        ChannelBuilder {
            env: env,
            options: HashMap::new(),
        }
    }

    /// Default authority to pass if none specified on call construction.
    pub fn default_authority<S: Into<Vec<u8>>>(mut self, authority: S) -> ChannelBuilder {
        let authority = CString::new(authority).unwrap();
        self.options
            .insert(OPT_DEFAULT_AUTHORITY, Options::String(authority));
        self
    }

    /// Maximum number of concurrent incoming streams to allow on a http2 connection.
    pub fn max_concurrent_stream(mut self, num: usize) -> ChannelBuilder {
        self.options
            .insert(OPT_MAX_CONCURRENT_STREAMS, Options::Integer(num));
        self
    }

    /// Maximum message length that the channel can receive. usize::MAX means unlimited.
    pub fn max_receive_message_len(mut self, len: usize) -> ChannelBuilder {
        self.options
            .insert(OPT_MAX_RECEIVE_MESSAGE_LENGTH, Options::Integer(len));
        self
    }

    /// Maximum message length that the channel can send. -1 means unlimited.
    pub fn max_send_message_len(mut self, len: usize) -> ChannelBuilder {
        self.options
            .insert(OPT_MAX_SEND_MESSAGE_LENGTH, Options::Integer(len));
        self
    }

    /// The maximum time between subsequent connection attempts.
    pub fn max_reconnect_backoff(mut self, backoff: Duration) -> ChannelBuilder {
        self.options
            .insert(OPT_MAX_RECONNECT_BACKOFF_MS,
                    Options::Integer(dur_to_ms(backoff)));
        self
    }

    /// The time between the first and second connection attempts.
    pub fn initial_reconnect_backoff(mut self, backoff: Duration) -> ChannelBuilder {
        self.options
            .insert(OPT_INITIAL_RECONNECT_BACKOFF_MS,
                    Options::Integer(dur_to_ms(backoff)));
        self
    }

    /// Initial sequence number for http2 transports.
    pub fn https_initial_seq_number(mut self, number: usize) -> ChannelBuilder {
        self.options
            .insert(OPT_HTTP2_INITIAL_SEQUENCE_NUMBER, Options::Integer(number));
        self
    }

    /// Amount to read ahead on individual streams. Defaults to 64kb, larger
    /// values can help throughput on high-latency connections.
    pub fn stream_initial_window_size(mut self, window_size: usize) -> ChannelBuilder {
        self.options
            .insert(OPT_STREAM_INITIAL_WINDOW_SIZE,
                    Options::Integer(window_size));
        self
    }

    /// Primary user agent: goes at the start of the user-agent metadata sent on each request.
    pub fn primary_user_agent(mut self, agent: &str) -> ChannelBuilder {
        let agent_string = format_user_agent_string(agent);
        self.options
            .insert(PRIMARY_USER_AGENT_STRING, Options::String(agent_string));
        self
    }

    /// If enable, allow the use of SO_REUSEPORT if it's available (default true).
    pub fn reuse_port(mut self, reuse: bool) -> ChannelBuilder {
        let opt = if reuse { 1 } else { 0 };
        self.options
            .insert(OPT_SO_REUSE_PORT, Options::Integer(opt));
        self
    }

    /// The caller of the secure_channel_create functions may override the target name used for SSL
    /// host name checking using this channel argument. This *should* be used for testing only.
    pub fn override_ssl_target<S: Into<Vec<u8>>>(mut self, target: S) -> ChannelBuilder {
        let target = CString::new(target).unwrap();
        self.options
            .insert(OPT_SSL_TARGET_NAME_OVERRIDE, Options::String(target));
        self
    }

    /// Build a channel args from the current configuration.
    pub fn build_args(&self) -> ChannelArgs {
        let args = unsafe { grpc_sys::grpcwrap_channel_args_create(self.options.len()) };
        for (i, (k, v)) in self.options.iter().enumerate() {
            let key = k.as_ptr() as *const c_char;
            match *v {
                Options::Integer(val) => unsafe {
                    grpc_sys::grpcwrap_channel_args_set_integer(args, i, key, val as c_int)
                },
                Options::String(ref val) => {
                    unsafe {
                        grpc_sys::grpcwrap_channel_args_set_string(args, i, key, val.as_ptr())
                    }
                }
            }
        }
        ChannelArgs { args: args }
    }

    /// Build an insure connection to the address.
    pub fn connect(self, addr: &str) -> Channel {
        self.connect_with_creds(addr, None)
    }

    fn connect_with_creds(mut self, addr: &str, creds: Option<ChannelCredentials>) -> Channel {
        let addr = CString::new(addr).unwrap();
        if let Entry::Vacant(e) = self.options.entry(PRIMARY_USER_AGENT_STRING) {
            e.insert(Options::String(format_user_agent_string("")));
        }
        let args = self.build_args();
        let addr_ptr = addr.as_ptr();
        let channel = unsafe {
            match creds {
                None => {
                    grpc_sys::grpc_insecure_channel_create(addr_ptr, args.args, ptr::null_mut())
                }
                Some(mut creds) => {
                    grpc_sys::grpc_secure_channel_create(creds.as_mut_ptr(),
                                                         addr_ptr,
                                                         args.args,
                                                         ptr::null_mut())
                }
            }
        };

        Channel {
            cq: self.env.pick_cq(),
            inner: Arc::new(ChannelInner {
                                _env: self.env,
                                channel: channel,
                            }),
        }
    }

    pub fn secure_connect(self, addr: &str, creds: ChannelCredentials) -> Channel {
        self.connect_with_creds(addr, Some(creds))
    }
}

pub struct ChannelArgs {
    args: *mut GrpcChannelArgs,
}

impl ChannelArgs {
    pub fn as_ptr(&self) -> *const GrpcChannelArgs {
        self.args
    }
}

impl Drop for ChannelArgs {
    fn drop(&mut self) {
        unsafe { grpc_sys::grpcwrap_channel_args_destroy(self.args) }
    }
}

struct ChannelInner {
    _env: Arc<Environment>,
    channel: *mut GrpcChannel,
}

impl Drop for ChannelInner {
    fn drop(&mut self) {
        unsafe {
            grpc_sys::grpc_channel_destroy(self.channel);
        }
    }
}

/// The Channel struct allows creation of Call objects.
#[derive(Clone)]
pub struct Channel {
    inner: Arc<ChannelInner>,
    cq: Arc<CompletionQueue>,
}

unsafe impl Send for Channel {}
unsafe impl Sync for Channel {}

impl Channel {
    /// Create a call using the method and option.
    pub fn create_call<P, Q>(&self, method: &Method<P, Q>, opt: &CallOption) -> Call {
        let raw_call = unsafe {
            let ch = self.inner.channel;
            let cq = self.cq.as_ptr();
            let method_ptr = method.name.as_ptr();
            let method_len = method.name.len();
            let timeout = opt.get_timeout()
                .map_or_else(GprTimespec::inf_future, GprTimespec::from);
            grpc_sys::grpcwrap_channel_create_call(ch,
                                                   ptr::null_mut(),
                                                   0,
                                                   cq,
                                                   method_ptr as *const _,
                                                   method_len,
                                                   ptr::null(),
                                                   0,
                                                   timeout,
                                                   ptr::null_mut())
        };

        unsafe { Call::from_raw(raw_call) }
    }

    pub fn cq(&self) -> &CompletionQueue {
        self.cq.as_ref()
    }
}
<|endoftext|>


<commit_before>use std::fmt;

const LETTERS: [&'static str; 52] = [
        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
        "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
        "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
        "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
    ];

const DIGITS: [&'static str; 10] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TokenType {
    Pad,
    ForwardSlash,
    Colon,
    Comma,
    Digit,
    Letter,
    PlusSign,
    WhiteSpace,
    OtherUtf8,
    ColonSpace,
    NewLine,
    FiveDashes,
    Begin,
    End,
    Version,
    Comment,
    MessageID,
    Hash,
    Charset,
    BlankLine,
    PGPMessage,
    PGPPublicKeyBlock,
    PGPPrivateKeyBlock,
    PGPMessagePart,
    PGPSignature,
    Eof,
}

impl TokenType {
    pub fn armor_string(self) -> Option<&'static str> {
        match self {
            TokenType::Pad => Some("="),
            TokenType::ForwardSlash => Some("/"),
            TokenType::Colon => Some(":"),
            TokenType::Comma => Some(","),
            TokenType::PlusSign => Some("+"),
            TokenType::WhiteSpace => Some(" "),
            TokenType::ColonSpace => Some(": "),
            TokenType::NewLine => Some("\n"),
            TokenType::FiveDashes => Some("-----"),
            TokenType::Begin => Some("BEGIN "),
            TokenType::End => Some("END "),
            TokenType::Version => Some("Version"),
            TokenType::Comment => Some("Comment"),
            TokenType::MessageID => Some("MessageID"),
            TokenType::Hash => Some("Hash"),
            TokenType::Charset => Some("Charset"),
            TokenType::Eof => Some("EOF"),
            TokenType::PGPMessage => Some("PGP MESSAGE"),
            TokenType::PGPPublicKeyBlock => Some("PGP PUBLIC KEY BLOCK"),
            TokenType::PGPPrivateKeyBlock => Some("PGP PRIVATE KEY BLOCK"),
            TokenType::PGPMessagePart => Some("PGP MESSAGE, PART "),
            TokenType::PGPSignature => Some("PGP SIGNATURE"),
            _ => None,
        }
    }
}

pub fn string_to_token_type(token_string: &str) -> Option<TokenType> {
    match token_string {
        "=" => Some(TokenType::Pad),
        "/" => Some(TokenType::ForwardSlash),
        ":" => Some(TokenType::Colon),
        "," => Some(TokenType::Comma),
        "+" => Some(TokenType::PlusSign),
        " " => Some(TokenType::WhiteSpace),
        ": " => Some(TokenType::ColonSpace),
        "\n" => Some(TokenType::NewLine),
        "\r" => Some(TokenType::NewLine),
        "-----" => Some(TokenType::FiveDashes),
        "BEGIN " => Some(TokenType::Begin),
        "END " => Some(TokenType::End),
        "Version" => Some(TokenType::Version),
        "Comment" => Some(TokenType::Comment),
        "MessageID" => Some(TokenType::MessageID),
        "Hash" => Some(TokenType::Hash),
        "Charset" => Some(TokenType::Charset),
        "EOF" => Some(TokenType::Eof),
        "PGP MESSAGE" => Some(TokenType::PGPMessage),
        "PGP PUBLIC KEY BLOCK" => Some(TokenType::PGPPublicKeyBlock),
        "PGP PRIVATE KEY BLOCK" => Some(TokenType::PGPPrivateKeyBlock),
        "PGP MESSAGE, PART " => Some(TokenType::PGPMessagePart),
        "PGP SIGNATURE" => Some(TokenType::PGPSignature),
        _ => None,
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Location {
    pub absolute: isize,
}

impl Location {
    pub fn eof() -> Location {
        Location { absolute: -1 }
    }

    #[inline]
    pub fn increment(&mut self, amount: usize) {
        self.absolute += amount as isize;
    }

    #[inline]
    pub fn decrement(&mut self, amount: usize) {
        self.absolute -= amount as isize;
    }
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Token {
    token_type: TokenType,
    text: String,
    location: Location,
}

impl Token {
    pub fn new(token_type: TokenType, text: &str, location: Location) -> Token {
        Token {
            token_type: token_type,
            text: String::from(text),
            location: location,
        }
    }

    pub fn from_char(token_type: TokenType, ch: char, location: Location) -> Token {
        let mut text = String::new();
        text.push(ch);

        Token {
            token_type: token_type,
            text: text,
            location: location
        }
    }

    pub fn has_token_type(&self, token_type: TokenType) -> bool {
        self.token_type == token_type
    }

    fn is_pad(&self) -> bool {
        (self.token_type == TokenType::Pad) && (self.text == "=")
    }

    fn is_forwardslash(&self) -> bool {
        (self.token_type == TokenType::ForwardSlash) && (self.text == "/")
    }

    fn is_colon(&self) -> bool {
        (self.token_type == TokenType::Colon) && (self.text == ":")
    }

    fn is_comma(&self) -> bool {
        (self.token_type == TokenType::Comma) && (self.text == ",")
    }

    fn is_digit(&self) -> bool {
        (self.token_type == TokenType::Digit) && DIGITS.contains(&self.text.as_str())
    }

    fn is_letter(&self) -> bool {
        (self.token_type == TokenType::Letter) && LETTERS.contains(&self.text.as_str())
    }

    fn is_plussign(&self) -> bool {
        (self.token_type == TokenType::PlusSign) && (self.text == "+")
    }

    fn is_whitespace(&self) -> bool {
        (self.token_type == TokenType::WhiteSpace) && (self.text == " ")
    }

    fn is_other_utf8(&self) -> bool {
        (self.token_type == TokenType::OtherUtf8) && (self.text.len() == 1)
    }

    fn is_colonspace(&self) -> bool {
        (self.token_type == TokenType::ColonSpace) && (self.text == ": ")
    }

    fn is_newline(&self) -> bool {
        (self.token_type == TokenType::NewLine) && (self.text == "\n" || self.text == "\r")
    }

    fn is_fivedashes(&self) -> bool {
        (self.token_type == TokenType::FiveDashes) && (self.text == "-----")
    }

    fn is_begin(&self) -> bool {
        (self.token_type == TokenType::Begin) && (self.text == "BEGIN ")
    }

    fn is_end(&self) -> bool {
        (self.token_type == TokenType::End) && (self.text == "END ")
    }

    fn is_version(&self) -> bool {
        (self.token_type == TokenType::Version) && (self.text == "Version")
    }

    fn is_comment(&self) -> bool {
        (self.token_type == TokenType::Comment) && (self.text == "Comment")
    }

    fn is_messageid(&self) -> bool {
        (self.token_type == TokenType::MessageID) && (self.text == "MessageID")
    }

    fn is_hash(&self) -> bool {
        (self.token_type == TokenType::Hash) && (self.text == "Hash")
    }

    fn is_charset(&self) -> bool {
        (self.token_type == TokenType::Charset) && (self.text == "Charset")
    }

    fn is_blankline(&self) -> bool {
        let mut chars = self.text.chars();
        match chars.next() {
            Some('\n') | Some('\r') => {}
            _ => return false
        }

        loop {
            match chars.next() {
                Some(' ') => continue,
                Some('\n') | Some('\r') => break,
                _ => return false
            }
        }

        match chars.next() {
            Some(_) => return false,
            None    => {}
        }

        self.token_type == TokenType::BlankLine
    }

    fn is_pgp_message(&self) -> bool {
        (self.token_type == TokenType::PGPMessage) && (self.text == "PGP MESSAGE")
    }

    fn is_pgp_publickey_block(&self) -> bool {
        (self.token_type == TokenType::PGPPublicKeyBlock) && (self.text == "PGP PUBLIC KEY BLOCK")
    }

    fn is_pgp_privatekey_block(&self) -> bool {
        (self.token_type == TokenType::PGPPublicKeyBlock) && (self.text == "PGP PRIVATE KEY BLOCK")
    }

    fn is_pgp_message_part(&self) -> bool {
        (self.token_type == TokenType::PGPMessagePart) && (self.text == "PGP MESSAGE, PART ")
    }

    fn is_pgp_signature(&self) -> bool {
        (self.token_type == TokenType::PGPSignature) && (self.text == "PGP SIGNATURE")
    }

    fn is_eof(&self) -> bool {
        (self.token_type == TokenType::Eof) && (self.text == "EOF")
    }

    pub fn is_valid_token(&self) -> bool {
        match self.token_type {
            TokenType::Pad => self.is_pad(),
            TokenType::ForwardSlash => self.is_forwardslash(),
            TokenType::Colon => self.is_colon(),
            TokenType::Comma => self.is_comma(),
            TokenType::Digit => self.is_digit(),
            TokenType::Letter => self.is_letter(),
            TokenType::PlusSign => self.is_plussign(),
            TokenType::WhiteSpace => self.is_whitespace(),
            TokenType::OtherUtf8 => self.is_other_utf8(),
            TokenType::ColonSpace => self.is_colonspace(),
            TokenType::NewLine => self.is_newline(),
            TokenType::FiveDashes => self.is_fivedashes(),
            TokenType::Begin => self.is_begin(),
            TokenType::End => self.is_end(),
            TokenType::Version => self.is_version(),
            TokenType::Comment => self.is_comment(),
            TokenType::MessageID => self.is_messageid(),
            TokenType::Hash => self.is_hash(),
            TokenType::Charset => self.is_charset(),
            TokenType::BlankLine => self.is_blankline(),
            TokenType::PGPMessage => self.is_pgp_message(),
            TokenType::PGPPublicKeyBlock => self.is_pgp_publickey_block(),
            TokenType::PGPPrivateKeyBlock => self.is_pgp_privatekey_block(),
            TokenType::PGPMessagePart => self.is_pgp_message_part(),
            TokenType::PGPSignature => self.is_pgp_signature(),
            TokenType::Eof => self.is_eof()
        }
    }
}
<commit_msg>Implement display for token.<commit_after>use std::fmt;

const LETTERS: [&'static str; 52] = [
        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
        "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
        "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
        "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
    ];

const DIGITS: [&'static str; 10] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TokenType {
    Pad,
    ForwardSlash,
    Colon,
    Comma,
    Digit,
    Letter,
    PlusSign,
    WhiteSpace,
    OtherUtf8,
    ColonSpace,
    NewLine,
    FiveDashes,
    Begin,
    End,
    Version,
    Comment,
    MessageID,
    Hash,
    Charset,
    BlankLine,
    PGPMessage,
    PGPPublicKeyBlock,
    PGPPrivateKeyBlock,
    PGPMessagePart,
    PGPSignature,
    Eof,
}

impl TokenType {
    pub fn armor_string(self) -> Option<&'static str> {
        match self {
            TokenType::Pad => Some("="),
            TokenType::ForwardSlash => Some("/"),
            TokenType::Colon => Some(":"),
            TokenType::Comma => Some(","),
            TokenType::PlusSign => Some("+"),
            TokenType::WhiteSpace => Some(" "),
            TokenType::ColonSpace => Some(": "),
            TokenType::NewLine => Some("\n"),
            TokenType::FiveDashes => Some("-----"),
            TokenType::Begin => Some("BEGIN "),
            TokenType::End => Some("END "),
            TokenType::Version => Some("Version"),
            TokenType::Comment => Some("Comment"),
            TokenType::MessageID => Some("MessageID"),
            TokenType::Hash => Some("Hash"),
            TokenType::Charset => Some("Charset"),
            TokenType::Eof => Some("EOF"),
            TokenType::PGPMessage => Some("PGP MESSAGE"),
            TokenType::PGPPublicKeyBlock => Some("PGP PUBLIC KEY BLOCK"),
            TokenType::PGPPrivateKeyBlock => Some("PGP PRIVATE KEY BLOCK"),
            TokenType::PGPMessagePart => Some("PGP MESSAGE, PART "),
            TokenType::PGPSignature => Some("PGP SIGNATURE"),
            _ => None,
        }
    }
}

pub fn string_to_token_type(token_string: &str) -> Option<TokenType> {
    match token_string {
        "=" => Some(TokenType::Pad),
        "/" => Some(TokenType::ForwardSlash),
        ":" => Some(TokenType::Colon),
        "," => Some(TokenType::Comma),
        "+" => Some(TokenType::PlusSign),
        " " => Some(TokenType::WhiteSpace),
        ": " => Some(TokenType::ColonSpace),
        "\n" => Some(TokenType::NewLine),
        "\r" => Some(TokenType::NewLine),
        "-----" => Some(TokenType::FiveDashes),
        "BEGIN " => Some(TokenType::Begin),
        "END " => Some(TokenType::End),
        "Version" => Some(TokenType::Version),
        "Comment" => Some(TokenType::Comment),
        "MessageID" => Some(TokenType::MessageID),
        "Hash" => Some(TokenType::Hash),
        "Charset" => Some(TokenType::Charset),
        "EOF" => Some(TokenType::Eof),
        "PGP MESSAGE" => Some(TokenType::PGPMessage),
        "PGP PUBLIC KEY BLOCK" => Some(TokenType::PGPPublicKeyBlock),
        "PGP PRIVATE KEY BLOCK" => Some(TokenType::PGPPrivateKeyBlock),
        "PGP MESSAGE, PART " => Some(TokenType::PGPMessagePart),
        "PGP SIGNATURE" => Some(TokenType::PGPSignature),
        _ => None,
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Location {
    pub absolute: isize,
}

impl Location {
    pub fn eof() -> Location {
        Location { absolute: -1 }
    }

    #[inline]
    pub fn increment(&mut self, amount: usize) {
        self.absolute += amount as isize;
    }

    #[inline]
    pub fn decrement(&mut self, amount: usize) {
        self.absolute -= amount as isize;
    }
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Token {
    token_type: TokenType,
    text: String,
    location: Location,
}

impl Token {
    pub fn new(token_type: TokenType, text: &str, location: Location) -> Token {
        Token {
            token_type: token_type,
            text: String::from(text),
            location: location,
        }
    }

    pub fn from_char(token_type: TokenType, ch: char, location: Location) -> Token {
        let mut text = String::new();
        text.push(ch);

        Token {
            token_type: token_type,
            text: text,
            location: location
        }
    }

    pub fn has_token_type(&self, token_type: TokenType) -> bool {
        self.token_type == token_type
    }

    fn is_pad(&self) -> bool {
        (self.token_type == TokenType::Pad) && (self.text == "=")
    }

    fn is_forwardslash(&self) -> bool {
        (self.token_type == TokenType::ForwardSlash) && (self.text == "/")
    }

    fn is_colon(&self) -> bool {
        (self.token_type == TokenType::Colon) && (self.text == ":")
    }

    fn is_comma(&self) -> bool {
        (self.token_type == TokenType::Comma) && (self.text == ",")
    }

    fn is_digit(&self) -> bool {
        (self.token_type == TokenType::Digit) && DIGITS.contains(&self.text.as_str())
    }

    fn is_letter(&self) -> bool {
        (self.token_type == TokenType::Letter) && LETTERS.contains(&self.text.as_str())
    }

    fn is_plussign(&self) -> bool {
        (self.token_type == TokenType::PlusSign) && (self.text == "+")
    }

    fn is_whitespace(&self) -> bool {
        (self.token_type == TokenType::WhiteSpace) && (self.text == " ")
    }

    fn is_other_utf8(&self) -> bool {
        (self.token_type == TokenType::OtherUtf8) && (self.text.len() == 1)
    }

    fn is_colonspace(&self) -> bool {
        (self.token_type == TokenType::ColonSpace) && (self.text == ": ")
    }

    fn is_newline(&self) -> bool {
        (self.token_type == TokenType::NewLine) && (self.text == "\n" || self.text == "\r")
    }

    fn is_fivedashes(&self) -> bool {
        (self.token_type == TokenType::FiveDashes) && (self.text == "-----")
    }

    fn is_begin(&self) -> bool {
        (self.token_type == TokenType::Begin) && (self.text == "BEGIN ")
    }

    fn is_end(&self) -> bool {
        (self.token_type == TokenType::End) && (self.text == "END ")
    }

    fn is_version(&self) -> bool {
        (self.token_type == TokenType::Version) && (self.text == "Version")
    }

    fn is_comment(&self) -> bool {
        (self.token_type == TokenType::Comment) && (self.text == "Comment")
    }

    fn is_messageid(&self) -> bool {
        (self.token_type == TokenType::MessageID) && (self.text == "MessageID")
    }

    fn is_hash(&self) -> bool {
        (self.token_type == TokenType::Hash) && (self.text == "Hash")
    }

    fn is_charset(&self) -> bool {
        (self.token_type == TokenType::Charset) && (self.text == "Charset")
    }

    fn is_blankline(&self) -> bool {
        let mut chars = self.text.chars();
        match chars.next() {
            Some('\n') | Some('\r') => {}
            _ => return false
        }

        loop {
            match chars.next() {
                Some(' ') => continue,
                Some('\n') | Some('\r') => break,
                _ => return false
            }
        }

        match chars.next() {
            Some(_) => return false,
            None    => {}
        }

        self.token_type == TokenType::BlankLine
    }

    fn is_pgp_message(&self) -> bool {
        (self.token_type == TokenType::PGPMessage) && (self.text == "PGP MESSAGE")
    }

    fn is_pgp_publickey_block(&self) -> bool {
        (self.token_type == TokenType::PGPPublicKeyBlock) && (self.text == "PGP PUBLIC KEY BLOCK")
    }

    fn is_pgp_privatekey_block(&self) -> bool {
        (self.token_type == TokenType::PGPPublicKeyBlock) && (self.text == "PGP PRIVATE KEY BLOCK")
    }

    fn is_pgp_message_part(&self) -> bool {
        (self.token_type == TokenType::PGPMessagePart) && (self.text == "PGP MESSAGE, PART ")
    }

    fn is_pgp_signature(&self) -> bool {
        (self.token_type == TokenType::PGPSignature) && (self.text == "PGP SIGNATURE")
    }

    fn is_eof(&self) -> bool {
        (self.token_type == TokenType::Eof) && (self.text == "EOF")
    }

    pub fn is_valid_token(&self) -> bool {
        match self.token_type {
            TokenType::Pad => self.is_pad(),
            TokenType::ForwardSlash => self.is_forwardslash(),
            TokenType::Colon => self.is_colon(),
            TokenType::Comma => self.is_comma(),
            TokenType::Digit => self.is_digit(),
            TokenType::Letter => self.is_letter(),
            TokenType::PlusSign => self.is_plussign(),
            TokenType::WhiteSpace => self.is_whitespace(),
            TokenType::OtherUtf8 => self.is_other_utf8(),
            TokenType::ColonSpace => self.is_colonspace(),
            TokenType::NewLine => self.is_newline(),
            TokenType::FiveDashes => self.is_fivedashes(),
            TokenType::Begin => self.is_begin(),
            TokenType::End => self.is_end(),
            TokenType::Version => self.is_version(),
            TokenType::Comment => self.is_comment(),
            TokenType::MessageID => self.is_messageid(),
            TokenType::Hash => self.is_hash(),
            TokenType::Charset => self.is_charset(),
            TokenType::BlankLine => self.is_blankline(),
            TokenType::PGPMessage => self.is_pgp_message(),
            TokenType::PGPPublicKeyBlock => self.is_pgp_publickey_block(),
            TokenType::PGPPrivateKeyBlock => self.is_pgp_privatekey_block(),
            TokenType::PGPMessagePart => self.is_pgp_message_part(),
            TokenType::PGPSignature => self.is_pgp_signature(),
            TokenType::Eof => self.is_eof()
        }
    }
}

impl fmt::Display for Token {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.token_type {
            TokenType::Pad => write!(f, "Pad({})", self.text),
            TokenType::ForwardSlash => write!(f, "ForwardSlash({})", self.text),
            TokenType::Colon => write!(f, "Colon({})", self.text),
            TokenType::Comma => write!(f, "Comma({})", self.text),
            TokenType::Digit => write!(f, "Digit({})", self.text),
            TokenType::Letter => write!(f, "Letter({})", self.text),
            TokenType::PlusSign => write!(f, "PlusSign({})", self.text),
            TokenType::WhiteSpace => write!(f, "WhiteSpace({})", self.text),
            TokenType::OtherUtf8 => write!(f, "OtherUtf8({})", self.text),
            TokenType::ColonSpace => write!(f, "ColonSpace({})", self.text),
            TokenType::NewLine => write!(f, "NewLine({})", self.text),
            TokenType::FiveDashes => write!(f, "FiveDashes({})", self.text),
            TokenType::Begin => write!(f, "Begin({})", self.text),
            TokenType::End => write!(f, "End({})", self.text),
            TokenType::Version => write!(f, "Version({})", self.text),
            TokenType::Comment => write!(f, "Comment({})", self.text),
            TokenType::MessageID => write!(f, "MessageID({})", self.text),
            TokenType::Hash => write!(f, "Hash({})", self.text),
            TokenType::Charset => write!(f, "Charset({})", self.text),
            TokenType::BlankLine => write!(f, "BlankLine({})", self.text),
            TokenType::PGPMessage => write!(f, "PGPMessage({})", self.text),
            TokenType::PGPPublicKeyBlock => write!(f, "PGPPublicKeyBlock({})", self.text),
            TokenType::PGPPrivateKeyBlock => write!(f, "PGPPrivateKeyBlock({})", self.text),
            TokenType::PGPMessagePart => write!(f, "PGPMessagePart({})", self.text),
            TokenType::PGPSignature => write!(f, "PGPSignature({})", self.text),
            TokenType::Eof => write!(f, "EOF({})", self.text)
        }
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::io;

#[derive(Debug)]
pub enum InesError {
  Io(io::Error),
  InesFormat(String),
  Unsupported(String),
}

impl From<io::Error> for InesError {
    fn from(err: io::Error) -> InesError {
        InesError::Io(err)
    }
}
#[repr(C,packed)]
pub struct InesHeader {
  ines_identifier: u32,
  prg_rom_banks: u8,
  chr_rom_banks: u8,
  flags_6: u8,
  flags_7: u8,
  prg_ram_units: u8,
  padding: [u8;7],
}

// http://wiki.nesdev.com/w/index.php/INES
//
pub fn load_ines<P:AsRef<Path>>(file_path: P) -> Result<Vec<u8>,InesError> {
    let mut file = try!(File::open(&file_path));
    let metadata = try!(::std::fs::metadata(&file_path));
    if metadata.len() < ::std::mem::size_of::<InesHeader>() as u64 {
        return Err(InesError::InesFormat("File is smaller than the ines header.".to_string()));
    }

    let header = unsafe {
        let mut header_buf = [0u8;16];
        file.read(&mut header_buf);

        ::std::mem::transmute::<_,InesHeader>(header_buf)
    };
    if header.ines_identifier != 0x1A53454E {
        return Err(InesError::InesFormat("Did not find ines header identifier".to_string()));
    }
    if header.flags_7 != 0 {
        return Err(InesError::Unsupported("This version of the ines format is not supported".to_string()));
    }
    if header.flags_6 & 0x04 != 0 {
        return Err(InesError::Unsupported("Loading trainers is not supported".to_string()));
    }

    // we really just want to seek back to byte 0, but I couldn't figure out how to do so... the
    // documentation apparently lies, so we close/open the file
    //
    drop(file);
    file = try!(File::open(&file_path));
    let mut file_bytes = Vec::<u8>::new();
    try!(file.read_to_end(&mut file_bytes));

    // https://en.wikibooks.org/wiki/NES_Programming/Memory_Map
    //
    let mut nes_bytes = Vec::<u8>::with_capacity(::std::u16::MAX as usize);
    nes_bytes.resize(::std::u16::MAX as usize,0);

    let prg_addr = 16;
    let prg_page_len = 0xBFFF - 0x8000;

    // NOTE: the below implementation is terrible.  I don't know how to memcpy from 1
    //       vector to another, so we do it manually.  As far as I'm concerned, this is
    //       *more* error prone than simply calling a memcpy function, but I'm still
    //       learning rust and it is what it is.
    //

    // nes has 2 prg-rom pages at 0x8000 and 0xC000
    // if the rom has only a single prg-rom page, then we mirror it
    //
    for i in 0..prg_page_len {
        nes_bytes[0x8000+i] = file_bytes[prg_addr+i];
        if header.prg_rom_banks == 1 {
            nes_bytes[0xC000+i] = file_bytes[prg_addr+i];
        }
        else {
            nes_bytes[0xC000+i] = file_bytes[prg_addr+prg_page_len+i];
        }
    }

    Ok(nes_bytes)
}

<commit_msg>we now seek instead of drop/open'ing the file to get back to position 0<commit_after>use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::io;
use std::io::Seek;
use std::io::SeekFrom;

#[derive(Debug)]
pub enum InesError {
  Io(io::Error),
  InesFormat(String),
  Unsupported(String),
}

impl From<io::Error> for InesError {
    fn from(err: io::Error) -> InesError {
        InesError::Io(err)
    }
}
#[repr(C,packed)]
pub struct InesHeader {
  ines_identifier: u32,
  prg_rom_banks: u8,
  chr_rom_banks: u8,
  flags_6: u8,
  flags_7: u8,
  prg_ram_units: u8,
  padding: [u8;7],
}

// http://wiki.nesdev.com/w/index.php/INES
//
pub fn load_ines<P:AsRef<Path>>(file_path: P) -> Result<Vec<u8>,InesError> {
    let mut file = try!(File::open(&file_path));
    let metadata = try!(::std::fs::metadata(&file_path));
    if metadata.len() < ::std::mem::size_of::<InesHeader>() as u64 {
        return Err(InesError::InesFormat("File is smaller than the ines header.".to_string()));
    }

    let header = unsafe {
        let mut header_buf = [0u8;16];
        file.read(&mut header_buf);

        ::std::mem::transmute::<_,InesHeader>(header_buf)
    };
    if header.ines_identifier != 0x1A53454E {
        return Err(InesError::InesFormat("Did not find ines header identifier".to_string()));
    }
    if header.flags_7 != 0 {
        return Err(InesError::Unsupported("This version of the ines format is not supported".to_string()));
    }
    if header.flags_6 & 0x04 != 0 {
        return Err(InesError::Unsupported("Loading trainers is not supported".to_string()));
    }

    file.seek(SeekFrom::Start(0));

    let mut file_bytes = Vec::<u8>::new();
    try!(file.read_to_end(&mut file_bytes));

    // https://en.wikibooks.org/wiki/NES_Programming/Memory_Map
    //
    let mut nes_bytes = Vec::<u8>::with_capacity(::std::u16::MAX as usize);
    nes_bytes.resize(::std::u16::MAX as usize,0);

    let prg_addr = 16;
    let prg_page_len = 0xBFFF - 0x8000;

    // NOTE: the below implementation is terrible.  I don't know how to memcpy from 1
    //       vector to another, so we do it manually.  As far as I'm concerned, this is
    //       *more* error prone than simply calling a memcpy function, but I'm still
    //       learning rust and it is what it is.
    //

    // nes has 2 prg-rom pages at 0x8000 and 0xC000
    // if the rom has only a single prg-rom page, then we mirror it
    //
    for i in 0..prg_page_len {
        nes_bytes[0x8000+i] = file_bytes[prg_addr+i];
        if header.prg_rom_banks == 1 {
            nes_bytes[0xC000+i] = file_bytes[prg_addr+i];
        }
        else {
            nes_bytes[0xC000+i] = file_bytes[prg_addr+prg_page_len+i];
        }
    }

    Ok(nes_bytes)
}

<|endoftext|>