<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fmt;

use text_writer::{mod, TextWriter};

use ast::*;
use ast::ComponentValue::*;

pub trait ToCss for Sized? {
    /// Serialize `self` in CSS syntax, writing to `dest`.
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter;

    /// Serialize `self` in CSS syntax and return a string.
    ///
    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
    #[inline]
    fn to_css_string(&self) -> String {
        let mut s = String::new();
        self.to_css(&mut s).unwrap();
        s
    }

    /// Serialize `self` in CSS syntax and return a result compatible with `std::fmt::Show`.
    ///
    /// Typical usage is, for a `Foo` that implements `ToCss`:
    ///
    /// ```{rust,ignore}
    /// use std::fmt;
    /// impl fmt::Show for Foo {
    ///     #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_to_css(f) }
    /// }
    /// ```
    ///
    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
    #[inline]
    fn fmt_to_css<W>(&self, dest: &mut W) -> fmt::Result where W: TextWriter {
        self.to_css(dest).map_err(|_| fmt::WriteError)
    }
}

impl ToCss for ComponentValue {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        match self {
            &Ident(ref value) => try!(serialize_identifier(value.as_slice(), dest)),
            &AtKeyword(ref value) => {
                try!(dest.write_char('@'));
                try!(serialize_identifier(value.as_slice(), dest));
            },
            &Hash(ref value) => {
                try!(dest.write_char('#'));
                for c in value.as_slice().chars() {
                    try!(serialize_char(c, dest, /* is_identifier_start = */ false));
                }
            },
            &IDHash(ref value) => {
                try!(dest.write_char('#'));
                try!(serialize_identifier(value.as_slice(), dest));
            }
            &QuotedString(ref value) => try!(serialize_string(value.as_slice(), dest)),
            &URL(ref value) => {
                try!(dest.write_str("url("));
                try!(serialize_string(value.as_slice(), dest));
                try!(dest.write_char(')'));
            },
            &Delim(value) => try!(dest.write_char(value)),

            &Number(ref value) => try!(dest.write_str(value.representation.as_slice())),
            &Percentage(ref value) => {
                try!(dest.write_str(value.representation.as_slice()));
                try!(dest.write_char('%'));
            },
            &Dimension(ref value, ref unit) => {
                try!(dest.write_str(value.representation.as_slice()));
                // Disambiguate with scientific notation.
                let unit = unit.as_slice();
                if unit == "e" || unit == "E" || unit.starts_with("e-") || unit.starts_with("E-") {
                    try!(dest.write_str("\\65 "));
                    for c in unit.slice_from(1).chars() {
                        try!(serialize_char(c, dest, /* is_identifier_start = */ false));
                    }
                } else {
                    try!(serialize_identifier(unit, dest));
                }
            },

            &UnicodeRange(start, end) => {
                try!(dest.write_str(format!("U+{:X}", start).as_slice()));
                if end != start {
                    try!(dest.write_str(format!("-{:X}", end).as_slice()));
                }
            }

            &WhiteSpace => try!(dest.write_char(' ')),
            &Colon => try!(dest.write_char(':')),
            &Semicolon => try!(dest.write_char(';')),
            &Comma => try!(dest.write_char(',')),
            &IncludeMatch => try!(dest.write_str("~=")),
            &DashMatch => try!(dest.write_str("|=")),
            &PrefixMatch => try!(dest.write_str("^=")),
            &SuffixMatch => try!(dest.write_str("$=")),
            &SubstringMatch => try!(dest.write_str("*=")),
            &Column => try!(dest.write_str("||")),
            &CDO => try!(dest.write_str("<!--")),
            &CDC => try!(dest.write_str("-->")),

            &Function(ref name, ref arguments) => {
                try!(serialize_identifier(name.as_slice(), dest));
                try!(dest.write_char('('));
                try!(arguments.to_css(dest));
                try!(dest.write_char(')'));
            },
            &ParenthesisBlock(ref content) => {
                try!(dest.write_char('('));
                try!(content.to_css(dest));
                try!(dest.write_char(')'));
            },
            &SquareBracketBlock(ref content) => {
                try!(dest.write_char('['));
                try!(content.to_css(dest));
                try!(dest.write_char(']'));
            },
            &CurlyBracketBlock(ref content) => {
                try!(dest.write_char('{'));
                try!(content.to_css(dest));
                try!(dest.write_char('}'));
            },

            &BadURL => try!(dest.write_str("url(<bad url>)")),
            &BadString => try!(dest.write_str("\"<bad string>\n")),
            &CloseParenthesis => try!(dest.write_char(')')),
            &CloseSquareBracket => try!(dest.write_char(']')),
            &CloseCurlyBracket => try!(dest.write_char('}')),
        }
        Ok(())
    }
}

pub fn serialize_identifier<W>(value: &str, dest: &mut W) -> text_writer::Result
where W:TextWriter {
    // TODO: avoid decoding/re-encoding UTF-8?
    let mut iter = value.chars();
    let mut c = iter.next().unwrap();
    if c == '-' {
        c = match iter.next() {
            None => return dest.write_str("\\-"),
            Some(c) => { try!(dest.write_char('-')); c },
        }
    };
    try!(serialize_char(c, dest, /* is_identifier_start = */ true));
    for c in iter {
        try!(serialize_char(c, dest, /* is_identifier_start = */ false));
    }
    Ok(())
}

#[inline]
fn serialize_char<W>(c: char, dest: &mut W, is_identifier_start: bool) -> text_writer::Result
where W: TextWriter {
    match c {
        '0'...'9' if is_identifier_start => try!(dest.write_str(format!("\\3{} ", c).as_slice())),
        '-' if is_identifier_start => try!(dest.write_str("\\-")),
        '0'...'9' | 'A'...'Z' | 'a'...'z' | '_' | '-' => try!(dest.write_char(c)),
        _ if c > '\x7F' => try!(dest.write_char(c)),
        '\n' => try!(dest.write_str("\\A ")),
        '\r' => try!(dest.write_str("\\D ")),
        '\x0C' => try!(dest.write_str("\\C ")),
        _ => { try!(dest.write_char('\\')); try!(dest.write_char(c)) },
    };
    Ok(())
}

pub fn serialize_string<W>(value: &str, dest: &mut W) -> text_writer::Result
where W: TextWriter {
    try!(dest.write_char('"'));
    try!(CssStringWriter::new(dest).write_str(value));
    try!(dest.write_char('"'));
    Ok(())
}

/// A `TextWriter` adaptor that escapes text for writing as a CSS string.
/// Quotes are not included.
///
/// Typical usage:
///
/// ```{rust,ignore}
/// fn write_foo<W>(foo: &Foo, dest: &mut W) -> text_writer::Result where W: TextWriter {
///     try!(dest.write_char('"'));
///     {
///         let mut string_dest = CssStringWriter::new(dest);
///         // Write into string_dest...
///     }
///     try!(dest.write_char('"'));
///     Ok(())
/// }
/// ```
pub struct CssStringWriter<'a, W: 'a> {
    inner: &'a mut W,
}

impl<'a, W> CssStringWriter<'a, W> where W: TextWriter {
    pub fn new(inner: &'a mut W) -> CssStringWriter<'a, W> {
        CssStringWriter { inner: inner }
    }
}

impl<'a, W> TextWriter for CssStringWriter<'a, W> where W: TextWriter {
    fn write_str(&mut self, s: &str) -> text_writer::Result {
        // TODO: avoid decoding/re-encoding UTF-8?
        for c in s.chars() {
            try!(self.write_char(c))
        }
        Ok(())
    }

    fn write_char(&mut self, c: char) -> text_writer::Result {
        match c {
            '"' => self.inner.write_str("\\\""),
            '\\' => self.inner.write_str("\\\\"),
            '\n' => self.inner.write_str("\\A "),
            '\r' => self.inner.write_str("\\D "),
            '\x0C' => self.inner.write_str("\\C "),
            _ => self.inner.write_char(c),
        }
    }
}

impl<'a> ToCss for [ComponentValue] {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        component_values_to_css(self.iter(), dest)
    }
}

impl<'a> ToCss for [Node] {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        let component_values = self.iter().map(|n| match n { &(ref c, _) => c });
        component_values_to_css(component_values, dest)
    }
}

fn component_values_to_css<'a, I, W>(mut iter: I, dest: &mut W) -> text_writer::Result
where I: Iterator<&'a ComponentValue>, W: TextWriter {
    let mut previous = match iter.next() {
        None => return Ok(()),
        Some(first) => { try!(first.to_css(dest)); first }
    };
    macro_rules! matches(
        ($value:expr, $($pattern:pat)|+) => (
            match $value { $($pattern)|+ => true, _ => false }
        );
    );
    // This does not borrow-check: for component_value in iter {
    loop { match iter.next() { None => break, Some(component_value) => {
        let (a, b) = (previous, component_value);
        if (
            matches!(*a, Ident(..) | AtKeyword(..) | Hash(..) | IDHash(..) |
                         Dimension(..) | Delim('#') | Delim('-') | Number(..)) &&
            matches!(*b, Ident(..) | Function(..) | URL(..) | BadURL(..) |
                         Number(..) | Percentage(..) | Dimension(..) | UnicodeRange(..))
        ) || (
            matches!(*a, Ident(..)) &&
            matches!(*b, ParenthesisBlock(..))
        ) || (
            matches!(*a, Ident(..) | AtKeyword(..) | Hash(..) | IDHash(..) | Dimension(..)) &&
            matches!(*b, Delim('-') | CDC)
        ) || (
            matches!(*a, Delim('#') | Delim('-') | Number(..) | Delim('@')) &&
            matches!(*b, Ident(..) | Function(..) | URL(..) | BadURL(..))
        ) || (
            matches!(*a, Delim('@')) &&
            matches!(*b, Ident(..) | Function(..) | URL(..) | BadURL(..) |
                         UnicodeRange(..) | Delim('-'))
        ) || (
            matches!(*a, UnicodeRange(..) | Delim('.') | Delim('+')) &&
            matches!(*b, Number(..) | Percentage(..) | Dimension(..))
        ) || (
            matches!(*a, UnicodeRange(..)) &&
            matches!(*b, Ident(..) | Function(..) | Delim('?'))
        ) || (match (a, b) { (&Delim(a), &Delim(b)) => matches!((a, b),
            ('#', '-') |
            ('$', '=') |
            ('*', '=') |
            ('^', '=') |
            ('~', '=') |
            ('|', '=') |
            ('|', '|') |
            ('/', '*')
        ), _ => false }) {
            try!(dest.write_str("/**/"));
        }
        // Skip whitespace when '\n' was previously written at the previous iteration.
        if !matches!((previous, component_value), (&Delim('\\'), &WhiteSpace)) {
            try!(component_value.to_css(dest));
        }
        if component_value == &Delim('\\') {
            try!(dest.write_char('\n'));
        }
        previous = component_value;
    }}}
    Ok(())
}

impl ToCss for Declaration {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        try!(dest.write_str(self.name.as_slice()));
        try!(dest.write_char(':'));
        try!(self.value.to_css(dest));
        Ok(())
    }
}

impl ToCss for [Declaration] {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        for declaration in self.iter() {
            try!(declaration.to_css(dest));
            try!(dest.write_char(';'));
        }
        Ok(())
    }
}

impl ToCss for QualifiedRule {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        try!(self.prelude.to_css(dest));
        try!(dest.write_char('{'));
        try!(self.block.to_css(dest));
        try!(dest.write_char('}'));
        Ok(())
    }
}

impl ToCss for AtRule {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        try!(dest.write_char('@'));
        try!(dest.write_str(self.name.as_slice()));
        try!(self.prelude.to_css(dest));
        match self.block {
            Some(ref block) => {
                try!(dest.write_char('{'));
                try!(block.to_css(dest));
                try!(dest.write_char('}'));
            }
            None => try!(dest.write_char(';'))
        }
        Ok(())
    }
}

impl ToCss for DeclarationListItem {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        match self {
            &DeclarationListItem::Declaration(ref declaration) => declaration.to_css(dest),
            &DeclarationListItem::AtRule(ref at_rule) => at_rule.to_css(dest),
        }
    }
}

impl ToCss for [DeclarationListItem] {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        for item in self.iter() {
            try!(item.to_css(dest));
            match item {
                &DeclarationListItem::AtRule(_) => {}
                &DeclarationListItem::Declaration(_) => try!(dest.write_char(';'))
            }
        }
        Ok(())
    }
}

impl ToCss for Rule {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        match self {
            &Rule::QualifiedRule(ref rule) => rule.to_css(dest),
            &Rule::AtRule(ref rule) => rule.to_css(dest),
        }
    }
}
<commit_msg>Upgrade to rustc 0.13.0-nightly (99d6956c3 2014-12-18 20:32:07 +0000)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fmt;

use text_writer::{mod, TextWriter};

use ast::*;
use ast::ComponentValue::*;

pub trait ToCss for Sized? {
    /// Serialize `self` in CSS syntax, writing to `dest`.
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter;

    /// Serialize `self` in CSS syntax and return a string.
    ///
    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
    #[inline]
    fn to_css_string(&self) -> String {
        let mut s = String::new();
        self.to_css(&mut s).unwrap();
        s
    }

    /// Serialize `self` in CSS syntax and return a result compatible with `std::fmt::Show`.
    ///
    /// Typical usage is, for a `Foo` that implements `ToCss`:
    ///
    /// ```{rust,ignore}
    /// use std::fmt;
    /// impl fmt::Show for Foo {
    ///     #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_to_css(f) }
    /// }
    /// ```
    ///
    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
    #[inline]
    fn fmt_to_css<W>(&self, dest: &mut W) -> fmt::Result where W: TextWriter {
        self.to_css(dest).map_err(|_| fmt::Error)
    }
}

impl ToCss for ComponentValue {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        match self {
            &Ident(ref value) => try!(serialize_identifier(value.as_slice(), dest)),
            &AtKeyword(ref value) => {
                try!(dest.write_char('@'));
                try!(serialize_identifier(value.as_slice(), dest));
            },
            &Hash(ref value) => {
                try!(dest.write_char('#'));
                for c in value.as_slice().chars() {
                    try!(serialize_char(c, dest, /* is_identifier_start = */ false));
                }
            },
            &IDHash(ref value) => {
                try!(dest.write_char('#'));
                try!(serialize_identifier(value.as_slice(), dest));
            }
            &QuotedString(ref value) => try!(serialize_string(value.as_slice(), dest)),
            &URL(ref value) => {
                try!(dest.write_str("url("));
                try!(serialize_string(value.as_slice(), dest));
                try!(dest.write_char(')'));
            },
            &Delim(value) => try!(dest.write_char(value)),

            &Number(ref value) => try!(dest.write_str(value.representation.as_slice())),
            &Percentage(ref value) => {
                try!(dest.write_str(value.representation.as_slice()));
                try!(dest.write_char('%'));
            },
            &Dimension(ref value, ref unit) => {
                try!(dest.write_str(value.representation.as_slice()));
                // Disambiguate with scientific notation.
                let unit = unit.as_slice();
                if unit == "e" || unit == "E" || unit.starts_with("e-") || unit.starts_with("E-") {
                    try!(dest.write_str("\\65 "));
                    for c in unit.slice_from(1).chars() {
                        try!(serialize_char(c, dest, /* is_identifier_start = */ false));
                    }
                } else {
                    try!(serialize_identifier(unit, dest));
                }
            },

            &UnicodeRange(start, end) => {
                try!(dest.write_str(format!("U+{:X}", start).as_slice()));
                if end != start {
                    try!(dest.write_str(format!("-{:X}", end).as_slice()));
                }
            }

            &WhiteSpace => try!(dest.write_char(' ')),
            &Colon => try!(dest.write_char(':')),
            &Semicolon => try!(dest.write_char(';')),
            &Comma => try!(dest.write_char(',')),
            &IncludeMatch => try!(dest.write_str("~=")),
            &DashMatch => try!(dest.write_str("|=")),
            &PrefixMatch => try!(dest.write_str("^=")),
            &SuffixMatch => try!(dest.write_str("$=")),
            &SubstringMatch => try!(dest.write_str("*=")),
            &Column => try!(dest.write_str("||")),
            &CDO => try!(dest.write_str("<!--")),
            &CDC => try!(dest.write_str("-->")),

            &Function(ref name, ref arguments) => {
                try!(serialize_identifier(name.as_slice(), dest));
                try!(dest.write_char('('));
                try!(arguments.to_css(dest));
                try!(dest.write_char(')'));
            },
            &ParenthesisBlock(ref content) => {
                try!(dest.write_char('('));
                try!(content.to_css(dest));
                try!(dest.write_char(')'));
            },
            &SquareBracketBlock(ref content) => {
                try!(dest.write_char('['));
                try!(content.to_css(dest));
                try!(dest.write_char(']'));
            },
            &CurlyBracketBlock(ref content) => {
                try!(dest.write_char('{'));
                try!(content.to_css(dest));
                try!(dest.write_char('}'));
            },

            &BadURL => try!(dest.write_str("url(<bad url>)")),
            &BadString => try!(dest.write_str("\"<bad string>\n")),
            &CloseParenthesis => try!(dest.write_char(')')),
            &CloseSquareBracket => try!(dest.write_char(']')),
            &CloseCurlyBracket => try!(dest.write_char('}')),
        }
        Ok(())
    }
}

pub fn serialize_identifier<W>(value: &str, dest: &mut W) -> text_writer::Result
where W:TextWriter {
    // TODO: avoid decoding/re-encoding UTF-8?
    let mut iter = value.chars();
    let mut c = iter.next().unwrap();
    if c == '-' {
        c = match iter.next() {
            None => return dest.write_str("\\-"),
            Some(c) => { try!(dest.write_char('-')); c },
        }
    };
    try!(serialize_char(c, dest, /* is_identifier_start = */ true));
    for c in iter {
        try!(serialize_char(c, dest, /* is_identifier_start = */ false));
    }
    Ok(())
}

#[inline]
fn serialize_char<W>(c: char, dest: &mut W, is_identifier_start: bool) -> text_writer::Result
where W: TextWriter {
    match c {
        '0'...'9' if is_identifier_start => try!(dest.write_str(format!("\\3{} ", c).as_slice())),
        '-' if is_identifier_start => try!(dest.write_str("\\-")),
        '0'...'9' | 'A'...'Z' | 'a'...'z' | '_' | '-' => try!(dest.write_char(c)),
        _ if c > '\x7F' => try!(dest.write_char(c)),
        '\n' => try!(dest.write_str("\\A ")),
        '\r' => try!(dest.write_str("\\D ")),
        '\x0C' => try!(dest.write_str("\\C ")),
        _ => { try!(dest.write_char('\\')); try!(dest.write_char(c)) },
    };
    Ok(())
}

pub fn serialize_string<W>(value: &str, dest: &mut W) -> text_writer::Result
where W: TextWriter {
    try!(dest.write_char('"'));
    try!(CssStringWriter::new(dest).write_str(value));
    try!(dest.write_char('"'));
    Ok(())
}

/// A `TextWriter` adaptor that escapes text for writing as a CSS string.
/// Quotes are not included.
///
/// Typical usage:
///
/// ```{rust,ignore}
/// fn write_foo<W>(foo: &Foo, dest: &mut W) -> text_writer::Result where W: TextWriter {
///     try!(dest.write_char('"'));
///     {
///         let mut string_dest = CssStringWriter::new(dest);
///         // Write into string_dest...
///     }
///     try!(dest.write_char('"'));
///     Ok(())
/// }
/// ```
pub struct CssStringWriter<'a, W: 'a> {
    inner: &'a mut W,
}

impl<'a, W> CssStringWriter<'a, W> where W: TextWriter {
    pub fn new(inner: &'a mut W) -> CssStringWriter<'a, W> {
        CssStringWriter { inner: inner }
    }
}

impl<'a, W> TextWriter for CssStringWriter<'a, W> where W: TextWriter {
    fn write_str(&mut self, s: &str) -> text_writer::Result {
        // TODO: avoid decoding/re-encoding UTF-8?
        for c in s.chars() {
            try!(self.write_char(c))
        }
        Ok(())
    }

    fn write_char(&mut self, c: char) -> text_writer::Result {
        match c {
            '"' => self.inner.write_str("\\\""),
            '\\' => self.inner.write_str("\\\\"),
            '\n' => self.inner.write_str("\\A "),
            '\r' => self.inner.write_str("\\D "),
            '\x0C' => self.inner.write_str("\\C "),
            _ => self.inner.write_char(c),
        }
    }
}

impl<'a> ToCss for [ComponentValue] {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        component_values_to_css(self.iter(), dest)
    }
}

impl<'a> ToCss for [Node] {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        let component_values = self.iter().map(|n| match n { &(ref c, _) => c });
        component_values_to_css(component_values, dest)
    }
}

fn component_values_to_css<'a, I, W>(mut iter: I, dest: &mut W) -> text_writer::Result
where I: Iterator<&'a ComponentValue>, W: TextWriter {
    let mut previous = match iter.next() {
        None => return Ok(()),
        Some(first) => { try!(first.to_css(dest)); first }
    };
    macro_rules! matches(
        ($value:expr, $($pattern:pat)|+) => (
            match $value { $($pattern)|+ => true, _ => false }
        );
    );
    // This does not borrow-check: for component_value in iter {
    loop { match iter.next() { None => break, Some(component_value) => {
        let (a, b) = (previous, component_value);
        if (
            matches!(*a, Ident(..) | AtKeyword(..) | Hash(..) | IDHash(..) |
                         Dimension(..) | Delim('#') | Delim('-') | Number(..)) &&
            matches!(*b, Ident(..) | Function(..) | URL(..) | BadURL(..) |
                         Number(..) | Percentage(..) | Dimension(..) | UnicodeRange(..))
        ) || (
            matches!(*a, Ident(..)) &&
            matches!(*b, ParenthesisBlock(..))
        ) || (
            matches!(*a, Ident(..) | AtKeyword(..) | Hash(..) | IDHash(..) | Dimension(..)) &&
            matches!(*b, Delim('-') | CDC)
        ) || (
            matches!(*a, Delim('#') | Delim('-') | Number(..) | Delim('@')) &&
            matches!(*b, Ident(..) | Function(..) | URL(..) | BadURL(..))
        ) || (
            matches!(*a, Delim('@')) &&
            matches!(*b, Ident(..) | Function(..) | URL(..) | BadURL(..) |
                         UnicodeRange(..) | Delim('-'))
        ) || (
            matches!(*a, UnicodeRange(..) | Delim('.') | Delim('+')) &&
            matches!(*b, Number(..) | Percentage(..) | Dimension(..))
        ) || (
            matches!(*a, UnicodeRange(..)) &&
            matches!(*b, Ident(..) | Function(..) | Delim('?'))
        ) || (match (a, b) { (&Delim(a), &Delim(b)) => matches!((a, b),
            ('#', '-') |
            ('$', '=') |
            ('*', '=') |
            ('^', '=') |
            ('~', '=') |
            ('|', '=') |
            ('|', '|') |
            ('/', '*')
        ), _ => false }) {
            try!(dest.write_str("/**/"));
        }
        // Skip whitespace when '\n' was previously written at the previous iteration.
        if !matches!((previous, component_value), (&Delim('\\'), &WhiteSpace)) {
            try!(component_value.to_css(dest));
        }
        if component_value == &Delim('\\') {
            try!(dest.write_char('\n'));
        }
        previous = component_value;
    }}}
    Ok(())
}

impl ToCss for Declaration {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        try!(dest.write_str(self.name.as_slice()));
        try!(dest.write_char(':'));
        try!(self.value.to_css(dest));
        Ok(())
    }
}

impl ToCss for [Declaration] {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        for declaration in self.iter() {
            try!(declaration.to_css(dest));
            try!(dest.write_char(';'));
        }
        Ok(())
    }
}

impl ToCss for QualifiedRule {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        try!(self.prelude.to_css(dest));
        try!(dest.write_char('{'));
        try!(self.block.to_css(dest));
        try!(dest.write_char('}'));
        Ok(())
    }
}

impl ToCss for AtRule {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        try!(dest.write_char('@'));
        try!(dest.write_str(self.name.as_slice()));
        try!(self.prelude.to_css(dest));
        match self.block {
            Some(ref block) => {
                try!(dest.write_char('{'));
                try!(block.to_css(dest));
                try!(dest.write_char('}'));
            }
            None => try!(dest.write_char(';'))
        }
        Ok(())
    }
}

impl ToCss for DeclarationListItem {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        match self {
            &DeclarationListItem::Declaration(ref declaration) => declaration.to_css(dest),
            &DeclarationListItem::AtRule(ref at_rule) => at_rule.to_css(dest),
        }
    }
}

impl ToCss for [DeclarationListItem] {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        for item in self.iter() {
            try!(item.to_css(dest));
            match item {
                &DeclarationListItem::AtRule(_) => {}
                &DeclarationListItem::Declaration(_) => try!(dest.write_char(';'))
            }
        }
        Ok(())
    }
}

impl ToCss for Rule {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        match self {
            &Rule::QualifiedRule(ref rule) => rule.to_css(dest),
            &Rule::AtRule(ref rule) => rule.to_css(dest),
        }
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use blockdev::BlockDevs;

use engine::Engine;

use pool::StratisPool;

use std::path::Path;
use std::collections::BTreeMap;

use types::StratisResult;

pub struct SimEngine {
    pub pools: BTreeMap<String, Box<StratisPool>>,
}

impl SimEngine {
    pub fn new() -> SimEngine {
        SimEngine { pools: BTreeMap::new() }
    }
}

impl Engine for SimEngine {
    fn create_pool(&mut self,
                   name: &str,
                   blockdev_paths: &[&Path],
                   raid_level: u16)
                   -> StratisResult<()> {

        let pool = SimPool::new_pool(name, blockdev_paths, raid_level);

        self.pools.insert(name.to_owned(), pool);

        Ok(())
    }

    fn destroy_pool(&mut self, name: &str) -> StratisResult<()> {

        let deleted_pool = self.pools.remove(name);

        Ok(())
    }

    fn list_pools(&self) -> StratisResult<BTreeMap<String, Box<StratisPool>>> {

        let mut stratis_pools = BTreeMap::new();

        for (name, pool) in &self.pools {

            stratis_pools.insert(name.clone(), pool.copy());
        }

        Ok(stratis_pools)
    }
}

pub struct SimPool {
    pub name: String,
    pub block_devs: BlockDevs,
    pub raid_level: u16,
    pub online: bool,
    pub checking: bool,
}

impl SimPool {
    pub fn new_pool(name: &str, blockdev_paths: &[&Path], raid_level: u16) -> Box<StratisPool> {

        let status = BlockDevs::new(blockdev_paths);

        let block_devs = status.unwrap();

        let new_pool = SimPool {
            name: name.to_owned(),
            block_devs: block_devs.to_owned(),
            raid_level: raid_level,
            online: true,
            checking: false,
        };

        Box::new(new_pool)
    }
}

impl StratisPool for SimPool {
    fn add_blockdev(&mut self, path: &str) -> StratisResult<()> {
        println!("sim: pool::add_blockdev");
        Ok(())
    }

    fn add_cachedev(&mut self, path: &str) -> StratisResult<()> {
        println!("sim: pool::add_cachedev");
        Ok(())
    }

    fn destroy(&mut self) -> StratisResult<()> {
        println!("sim: pool::destroy");
        Ok(())
    }

    fn get_name(&mut self) -> String {
        self.name.clone()
    }
    fn copy(&self) -> Box<StratisPool> {
        let pool_copy = SimPool {
            name: self.name.clone(),
            block_devs: self.block_devs.clone(),
            raid_level: self.raid_level.clone(),
            online: true,
            checking: false,
        };
        Box::new(pool_copy)
    }
}
<commit_msg>There is no point in checking the return value of remove, so don't.<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use blockdev::BlockDevs;

use engine::Engine;

use pool::StratisPool;

use std::path::Path;
use std::collections::BTreeMap;

use types::StratisResult;

pub struct SimEngine {
    pub pools: BTreeMap<String, Box<StratisPool>>,
}

impl SimEngine {
    pub fn new() -> SimEngine {
        SimEngine { pools: BTreeMap::new() }
    }
}

impl Engine for SimEngine {
    fn create_pool(&mut self,
                   name: &str,
                   blockdev_paths: &[&Path],
                   raid_level: u16)
                   -> StratisResult<()> {

        let pool = SimPool::new_pool(name, blockdev_paths, raid_level);

        self.pools.insert(name.to_owned(), pool);

        Ok(())
    }

    fn destroy_pool(&mut self, name: &str) -> StratisResult<()> {

        self.pools.remove(name);

        Ok(())
    }

    fn list_pools(&self) -> StratisResult<BTreeMap<String, Box<StratisPool>>> {

        let mut stratis_pools = BTreeMap::new();

        for (name, pool) in &self.pools {

            stratis_pools.insert(name.clone(), pool.copy());
        }

        Ok(stratis_pools)
    }
}

pub struct SimPool {
    pub name: String,
    pub block_devs: BlockDevs,
    pub raid_level: u16,
    pub online: bool,
    pub checking: bool,
}

impl SimPool {
    pub fn new_pool(name: &str, blockdev_paths: &[&Path], raid_level: u16) -> Box<StratisPool> {

        let status = BlockDevs::new(blockdev_paths);

        let block_devs = status.unwrap();

        let new_pool = SimPool {
            name: name.to_owned(),
            block_devs: block_devs.to_owned(),
            raid_level: raid_level,
            online: true,
            checking: false,
        };

        Box::new(new_pool)
    }
}

impl StratisPool for SimPool {
    fn add_blockdev(&mut self, path: &str) -> StratisResult<()> {
        println!("sim: pool::add_blockdev");
        Ok(())
    }

    fn add_cachedev(&mut self, path: &str) -> StratisResult<()> {
        println!("sim: pool::add_cachedev");
        Ok(())
    }

    fn destroy(&mut self) -> StratisResult<()> {
        println!("sim: pool::destroy");
        Ok(())
    }

    fn get_name(&mut self) -> String {
        self.name.clone()
    }
    fn copy(&self) -> Box<StratisPool> {
        let pool_copy = SimPool {
            name: self.name.clone(),
            block_devs: self.block_devs.clone(),
            raid_level: self.raid_level.clone(),
            online: true,
            checking: false,
        };
        Box::new(pool_copy)
    }
}
<|endoftext|>


<commit_before>extern crate winapi;
extern crate ole32;

use std::ptr;
use std::mem;

use ::libc;

use self::winapi::*;

pub struct AudioSource {
    audio_client: &'static mut IAudioClient,
    render_client: &'static mut IAudioRenderClient,
    channels: u32,
    max_frames_in_buffer: u32,
    bytes_per_frame: u32,
    bytes_per_sample: u32,
    samples_per_second: u32,
}

impl AudioSource {
    /// Stream samples to the audio buffer.
    ///
    /// # Params
    ///
    /// - data_source: An iterator that will provide the samples to be written.
    /// - max_time: The maximum amount of time in seconds that should be written to the buffer.
    ///
    /// # Returns
    ///
    /// The number of samples written to the audio buffer.
    pub fn stream<T: Iterator<Item = u16>>(&mut self, data_source: &mut T, max_time: f32) -> usize { unsafe {
        let frames_available = {
            let mut padding = mem::uninitialized();
            let hresult = self.audio_client.GetCurrentPadding(&mut padding);
            if hresult != S_OK {
                panic!("IAudioClient::GetCurrentPadding() failed with code 0x{:x}", hresult);
            }
            self.max_frames_in_buffer - padding
        };

        if frames_available == 0 {
            return 0
        }

        let max_samples = max_time * (self.samples_per_second * self.channels) as f32;
        let frames_available = ::std::cmp::min(
            frames_available,
            max_samples as u32 * self.bytes_per_sample / self.bytes_per_frame);
        assert!(frames_available != 0);

        // loading buffer
        let mut buffer = {
            let mut buffer: *mut BYTE = mem::uninitialized();
            let hresult =
                self.render_client.GetBuffer(
                    frames_available,
                    &mut buffer as *mut *mut libc::c_uchar);
            if hresult != S_OK {
                panic!("IAudioRenderClient::GetBuffer() failed with code 0x{:x}", hresult);
            }
            assert!(!buffer.is_null());

            ::std::slice::from_raw_parts_mut(
                buffer as *mut u16,
                (frames_available as usize * self.bytes_per_frame as usize) / self.bytes_per_sample as usize)
        };

        let mut samples_written = 0;
        for (dest, source) in buffer.iter_mut().zip(data_source) {
            *dest = source;
            samples_written += 1;
        }

        assert!(samples_written % self.channels == 0);
        let hresult = self.render_client.ReleaseBuffer(samples_written / self.channels, 0);
        if hresult != S_OK {
            panic!("IAudioRenderClient::ReleaseBuffer() failed with code 0x{:x}", hresult);
        }

        self.audio_client.Start();

        samples_written as usize
    } }
}

impl Drop for AudioSource {
    fn drop(&mut self) { unsafe {
        self.audio_client.Release();
        self.render_client.Release();
    } }
}

pub fn init() -> Result<AudioSource, String> { unsafe {
    // TODO: Initialize with multithreading support once for better performance.
    let hresult = ole32::CoInitializeEx(ptr::null_mut(), COINIT_APARTMENTTHREADED);
    if hresult != S_OK {
        return Err(format!("ole32::CoInitializeEx() failed with error code 0x{:x}", hresult))
    }

    // Build the devices enumerator.
    let enumerator = {
        let mut enumerator: *mut IMMDeviceEnumerator = mem::uninitialized();

        let hresult =
            ole32::CoCreateInstance(
                &CLSID_MMDeviceEnumerator,
                ptr::null_mut(),
                CLSCTX_ALL,
                &IID_IMMDeviceEnumerator,
                mem::transmute(&mut enumerator));

        if hresult != S_OK {
           return Err(format!("ole32::CoCreateInstance() failed with error code 0x{:x}", hresult))
        }
        &mut *enumerator
    };

    // Get the default endpoint.
    let device = {
        let mut device: *mut IMMDevice = mem::uninitialized();

        let hresult = enumerator.GetDefaultAudioEndpoint(
            EDataFlow::eRender,
            ERole::eConsole,
            mem::transmute(&mut device));

        if hresult != S_OK {
           return Err(format!("IMMDeviceEnumerator::GetDefaultAudioEndpoint() failed with error code 0x{:x}", hresult))
        }
        &mut *device
    };

    // Get an `IAudioClient` from the device.
    let audio_client: &mut IAudioClient = {
        let mut audio_client: *mut IAudioClient = mem::uninitialized();

        let hresult =
            device.Activate(&IID_IAudioClient,
                             CLSCTX_ALL,
                             ptr::null_mut(),
                             mem::transmute(&mut audio_client));

        if hresult != S_OK {
            return Err(format!("IAudioClient::Activate() failed with error code 0x{:x}", hresult))
        }
        &mut *audio_client
    };

    // computing the format and initializing the device
    // TODO: Support other audio formats.
    let format = {
        let format_attempt = WAVEFORMATEX {
            wFormatTag: WAVE_FORMAT_PCM,
            nChannels: 2,
            nSamplesPerSec: 44100,
            nAvgBytesPerSec: 2 * 44100 * 2,
            nBlockAlign: (2 * 16) / 8,
            wBitsPerSample: 16,
            cbSize: 0,
        };

        // Query the system to see if the desired format is supported. If it is not it will
        // set format_ptr to point to the closest valid format.
        println!("checking if audio client is supported");
        let mut format_ptr: *mut WAVEFORMATEX = mem::uninitialized();
        let hresult = audio_client.IsFormatSupported(
            AUDCLNT_SHAREMODE::AUDCLNT_SHAREMODE_SHARED,
           &format_attempt,
           &mut format_ptr);
        if hresult != S_OK
        && hresult != S_FALSE
        {
            return if hresult == AUDCLNT_E_UNSUPPORTED_FORMAT {
                Err(format!("The specified audio format is not supported and no similar one can be found"))
            } else {
                Err(format!("IAudioClient::IsFormatSupported() return failure code {:x}", hresult))
            }
        }

        // Set format_copy to be a copy of whichever valid format IsFormatSupported() chooses.
        let format = if format_ptr.is_null() {
            &format_attempt
        } else {
            &*format_ptr
        };
        let format_copy = ptr::read(format);

        // Initialize the audio client with the chosen format.
        println!("initializing audio client");
        let hresult = audio_client.Initialize(
            AUDCLNT_SHAREMODE::AUDCLNT_SHAREMODE_SHARED,
            0,
            10000000,
            0,
            format,
            ptr::null());

        // Free the format created by IsFormatSupported().
        if !format_ptr.is_null() {
            ole32::CoTaskMemFree(format_ptr as *mut libc::c_void);
        }

        match hresult {
            S_OK => println!("successfully initialized the audio client"),
            _ => println!("IAudioClient::Initialize() failed with hresult 0x{:x}", hresult),
        }

        format_copy
    };

    let max_frames_in_buffer = {
        let mut max_frames_in_buffer = mem::uninitialized();
        let hresult = audio_client.GetBufferSize(&mut max_frames_in_buffer);
        if hresult != S_OK {
           return Err(format!("IAudioClient::GetBufferSize() failed with error code 0x{:x}", hresult))
        }
        max_frames_in_buffer
    };

    let render_client = {
        let mut render_client: *mut IAudioRenderClient = mem::uninitialized();
        let hresult = audio_client.GetService(&IID_IAudioRenderClient,
                        mem::transmute(&mut render_client));
        if hresult != S_OK {
           return Err(format!("IAudioClient::GetService() failed with error code 0x{:x}", hresult))
        }
        &mut *render_client
    };

    // let num_channels = format.nChannels;

    Ok(AudioSource {
        audio_client: audio_client,
        render_client: render_client,
        channels: format.nChannels as u32,
        max_frames_in_buffer: max_frames_in_buffer,
        bytes_per_frame: format.nBlockAlign as u32,
        bytes_per_sample: mem::size_of::<u16>() as u32,
        samples_per_second: format.nSamplesPerSec,
    })
} }
<commit_msg>Print Audio Latency on Start<commit_after>extern crate winapi;
extern crate ole32;

use std::ptr;
use std::mem;

use ::libc;

use self::winapi::*;

pub struct AudioSource {
    audio_client: &'static mut IAudioClient,
    render_client: &'static mut IAudioRenderClient,
    channels: u32,
    max_frames_in_buffer: u32,
    bytes_per_frame: u32,
    bytes_per_sample: u32,
    samples_per_second: u32,
}

impl AudioSource {
    /// Stream samples to the audio buffer.
    ///
    /// # Params
    ///
    /// - data_source: An iterator that will provide the samples to be written.
    /// - max_time: The maximum amount of time in seconds that should be written to the buffer.
    ///
    /// # Returns
    ///
    /// The number of samples written to the audio buffer.
    pub fn stream<T: Iterator<Item = u16>>(&mut self, data_source: &mut T, max_time: f32) -> usize { unsafe {
        let frames_available = {
            let mut padding = mem::uninitialized();
            let hresult = self.audio_client.GetCurrentPadding(&mut padding);
            if hresult != S_OK {
                panic!("IAudioClient::GetCurrentPadding() failed with code 0x{:x}", hresult);
            }
            self.max_frames_in_buffer - padding
        };

        if frames_available == 0 {
            return 0
        }

        let max_samples = max_time * (self.samples_per_second * self.channels) as f32;
        let frames_available = ::std::cmp::min(
            frames_available,
            max_samples as u32 * self.bytes_per_sample / self.bytes_per_frame);
        assert!(frames_available != 0);

        // loading buffer
        let mut buffer = {
            let mut buffer: *mut BYTE = mem::uninitialized();
            let hresult =
                self.render_client.GetBuffer(
                    frames_available,
                    &mut buffer as *mut *mut libc::c_uchar);
            if hresult != S_OK {
                panic!("IAudioRenderClient::GetBuffer() failed with code 0x{:x}", hresult);
            }
            assert!(!buffer.is_null());

            ::std::slice::from_raw_parts_mut(
                buffer as *mut u16,
                (frames_available as usize * self.bytes_per_frame as usize) / self.bytes_per_sample as usize)
        };

        let mut samples_written = 0;
        for (dest, source) in buffer.iter_mut().zip(data_source) {
            *dest = source;
            samples_written += 1;
        }

        assert!(samples_written % self.channels == 0);
        let hresult = self.render_client.ReleaseBuffer(samples_written / self.channels, 0);
        if hresult != S_OK {
            panic!("IAudioRenderClient::ReleaseBuffer() failed with code 0x{:x}", hresult);
        }

        self.audio_client.Start();

        samples_written as usize
    } }
}

impl Drop for AudioSource {
    fn drop(&mut self) { unsafe {
        self.audio_client.Release();
        self.render_client.Release();
    } }
}

pub fn init() -> Result<AudioSource, String> { unsafe {
    // TODO: Initialize with multithreading support once for better performance.
    let hresult = ole32::CoInitializeEx(ptr::null_mut(), COINIT_APARTMENTTHREADED);
    if hresult != S_OK {
        return Err(format!("ole32::CoInitializeEx() failed with error code 0x{:x}", hresult))
    }

    // Build the devices enumerator.
    let enumerator = {
        let mut enumerator: *mut IMMDeviceEnumerator = mem::uninitialized();

        let hresult =
            ole32::CoCreateInstance(
                &CLSID_MMDeviceEnumerator,
                ptr::null_mut(),
                CLSCTX_ALL,
                &IID_IMMDeviceEnumerator,
                mem::transmute(&mut enumerator));

        if hresult != S_OK {
           return Err(format!("ole32::CoCreateInstance() failed with error code 0x{:x}", hresult))
        }
        &mut *enumerator
    };

    // Get the default endpoint.
    let device = {
        let mut device: *mut IMMDevice = mem::uninitialized();

        let hresult = enumerator.GetDefaultAudioEndpoint(
            EDataFlow::eRender,
            ERole::eConsole,
            mem::transmute(&mut device));

        if hresult != S_OK {
           return Err(format!("IMMDeviceEnumerator::GetDefaultAudioEndpoint() failed with error code 0x{:x}", hresult))
        }
        &mut *device
    };

    // Get an `IAudioClient` from the device.
    let audio_client: &mut IAudioClient = {
        let mut audio_client: *mut IAudioClient = mem::uninitialized();

        let hresult =
            device.Activate(&IID_IAudioClient,
                             CLSCTX_ALL,
                             ptr::null_mut(),
                             mem::transmute(&mut audio_client));

        if hresult != S_OK {
            return Err(format!("IAudioClient::Activate() failed with error code 0x{:x}", hresult))
        }
        &mut *audio_client
    };

    // computing the format and initializing the device
    // TODO: Support other audio formats.
    let format = {
        let format_attempt = WAVEFORMATEX {
            wFormatTag: WAVE_FORMAT_PCM,
            nChannels: 2,
            nSamplesPerSec: 44100,
            nAvgBytesPerSec: 2 * 44100 * 2,
            nBlockAlign: (2 * 16) / 8,
            wBitsPerSample: 16,
            cbSize: 0,
        };

        // Query the system to see if the desired format is supported. If it is not it will
        // set format_ptr to point to the closest valid format.
        println!("checking if audio client is supported");
        let mut format_ptr: *mut WAVEFORMATEX = mem::uninitialized();
        let hresult = audio_client.IsFormatSupported(
            AUDCLNT_SHAREMODE::AUDCLNT_SHAREMODE_SHARED,
           &format_attempt,
           &mut format_ptr);
        if hresult != S_OK
        && hresult != S_FALSE
        {
            return if hresult == AUDCLNT_E_UNSUPPORTED_FORMAT {
                Err(format!("The specified audio format is not supported and no similar one can be found"))
            } else {
                Err(format!("IAudioClient::IsFormatSupported() return failure code {:x}", hresult))
            }
        }

        // Set format_copy to be a copy of whichever valid format IsFormatSupported() chooses.
        let format = if format_ptr.is_null() {
            &format_attempt
        } else {
            &*format_ptr
        };
        let format_copy = ptr::read(format);

        // Initialize the audio client with the chosen format.
        println!("initializing audio client");
        let hresult = audio_client.Initialize(
            AUDCLNT_SHAREMODE::AUDCLNT_SHAREMODE_SHARED,
            0,
            10000000,
            0,
            format,
            ptr::null());

        // Free the format created by IsFormatSupported().
        if !format_ptr.is_null() {
            ole32::CoTaskMemFree(format_ptr as *mut libc::c_void);
        }

        match hresult {
            S_OK => println!("successfully initialized the audio client"),
            _ => println!("IAudioClient::Initialize() failed with hresult 0x{:x}", hresult),
        }

        format_copy
    };

    let max_frames_in_buffer = {
        let mut max_frames_in_buffer = mem::uninitialized();
        let hresult = audio_client.GetBufferSize(&mut max_frames_in_buffer);
        if hresult != S_OK {
           return Err(format!("IAudioClient::GetBufferSize() failed with error code 0x{:x}", hresult))
        }
        max_frames_in_buffer
    };

    let render_client = {
        let mut render_client: *mut IAudioRenderClient = mem::uninitialized();
        let hresult = audio_client.GetService(&IID_IAudioRenderClient,
                        mem::transmute(&mut render_client));
        if hresult != S_OK {
           return Err(format!("IAudioClient::GetService() failed with error code 0x{:x}", hresult))
        }
        &mut *render_client
    };

    let latency_ms = {
        let mut latency: REFERENCE_TIME = 0;
        audio_client.GetStreamLatency(&mut latency);
        latency as f32 / 10000.0
    };
    println!("max audio latency: {}ms", latency_ms);

    // let num_channels = format.nChannels;

    Ok(AudioSource {
        audio_client: audio_client,
        render_client: render_client,
        channels: format.nChannels as u32,
        max_frames_in_buffer: max_frames_in_buffer,
        bytes_per_frame: format.nBlockAlign as u32,
        bytes_per_sample: mem::size_of::<u16>() as u32,
        samples_per_second: format.nSamplesPerSec,
    })
} }
<|endoftext|>


<commit_before>extern crate compiletest_rs as compiletest;

use std::path::{PathBuf, Path};
use std::io::Write;

fn run_mode(dir: &'static str, mode: &'static str, sysroot: &str) {
    // Disable rustc's new error fomatting. It breaks these tests.
    std::env::remove_var("RUST_NEW_ERROR_FORMAT");
    let flags = format!("--sysroot {} -Dwarnings", sysroot);
    for_all_targets(sysroot, |target| {
        let mut config = compiletest::default_config();
        config.host_rustcflags = Some(flags.clone());
        config.mode = mode.parse().expect("Invalid mode");
        config.run_lib_path = Path::new(sysroot).join("lib").join("rustlib").join(&target).join("lib");
        config.rustc_path = "target/debug/miri".into();
        config.src_base = PathBuf::from(format!("tests/{}", dir));
        config.target = target.to_owned();
        config.target_rustcflags = Some(flags.clone());
        compiletest::run_tests(&config);
    });
}

fn for_all_targets<F: FnMut(String)>(sysroot: &str, mut f: F) {
    for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() {
        let target = target.unwrap();
        if !target.metadata().unwrap().is_dir() {
            continue;
        }
        let target = target.file_name().into_string().unwrap();
        if target == "etc" {
            continue;
        }
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "running tests for target {}", target).unwrap();
        f(target);
    }
}

#[test]
fn compile_test() {
    let mut failed = false;
    // Taken from https://github.com/Manishearth/rust-clippy/pull/911.
    let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
    let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
    let sysroot = match (home, toolchain) {
        (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
        _ => option_env!("RUST_SYSROOT")
            .expect("need to specify RUST_SYSROOT env var or use rustup or multirust")
            .to_owned(),
    };
    run_mode("compile-fail", "compile-fail", &sysroot);
    for_all_targets(&sysroot, |target| {
        for file in std::fs::read_dir("tests/run-pass").unwrap() {
            let file = file.unwrap();
            if !file.metadata().unwrap().is_file() {
                continue;
            }
            let file = file.path();
            let stderr = std::io::stderr();
            write!(stderr.lock(), "test [miri-pass] {} ", file.to_str().unwrap()).unwrap();
            let mut cmd = std::process::Command::new("target/debug/miri");
            cmd.arg(file);
            cmd.arg(format!("--sysroot={}", sysroot));
            cmd.arg("-Dwarnings");
            cmd.arg(format!("--target={}", target));
            let libs = Path::new(&sysroot).join("lib");
            let sysroot = libs.join("rustlib").join(&target).join("lib");
            let paths = std::env::join_paths(&[libs, sysroot]).unwrap();
            cmd.env(compiletest::procsrv::dylib_env_var(), paths);
            match cmd.output() {
                Ok(ref output) if output.status.success() => writeln!(stderr.lock(), "ok").unwrap(),
                Ok(output) => {
                    failed = true;
                    writeln!(stderr.lock(), "FAILED with exit code {}", output.status.code().unwrap_or(0)).unwrap();
                    writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap();
                    writeln!(stderr.lock(), "stderr: \n {}", std::str::from_utf8(&output.stderr).unwrap()).unwrap();
                }
                Err(e) => {
                    failed = true;
                    writeln!(stderr.lock(), "FAILED: {}", e).unwrap();
                },
            }
        }
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "").unwrap();
    });
    if failed {
        panic!("some tests failed");
    }
}
<commit_msg>miri knows about `--sysroot`<commit_after>extern crate compiletest_rs as compiletest;

use std::path::{PathBuf, Path};
use std::io::Write;

fn run_mode(dir: &'static str, mode: &'static str, sysroot: &str) {
    // Disable rustc's new error fomatting. It breaks these tests.
    std::env::remove_var("RUST_NEW_ERROR_FORMAT");
    let flags = format!("--sysroot {} -Dwarnings", sysroot);
    for_all_targets(sysroot, |target| {
        let mut config = compiletest::default_config();
        config.host_rustcflags = Some(flags.clone());
        config.mode = mode.parse().expect("Invalid mode");
        config.run_lib_path = Path::new(sysroot).join("lib").join("rustlib").join(&target).join("lib");
        config.rustc_path = "target/debug/miri".into();
        config.src_base = PathBuf::from(format!("tests/{}", dir));
        config.target = target.to_owned();
        config.target_rustcflags = Some(flags.clone());
        compiletest::run_tests(&config);
    });
}

fn for_all_targets<F: FnMut(String)>(sysroot: &str, mut f: F) {
    for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() {
        let target = target.unwrap();
        if !target.metadata().unwrap().is_dir() {
            continue;
        }
        let target = target.file_name().into_string().unwrap();
        if target == "etc" {
            continue;
        }
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "running tests for target {}", target).unwrap();
        f(target);
    }
}

#[test]
fn compile_test() {
    let mut failed = false;
    // Taken from https://github.com/Manishearth/rust-clippy/pull/911.
    let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
    let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
    let sysroot = match (home, toolchain) {
        (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
        _ => option_env!("RUST_SYSROOT")
            .expect("need to specify RUST_SYSROOT env var or use rustup or multirust")
            .to_owned(),
    };
    run_mode("compile-fail", "compile-fail", &sysroot);
    for_all_targets(&sysroot, |target| {
        for file in std::fs::read_dir("tests/run-pass").unwrap() {
            let file = file.unwrap();
            if !file.metadata().unwrap().is_file() {
                continue;
            }
            let file = file.path();
            let stderr = std::io::stderr();
            write!(stderr.lock(), "test [miri-pass] {} ", file.to_str().unwrap()).unwrap();
            let mut cmd = std::process::Command::new("target/debug/miri");
            cmd.arg(file);
            cmd.arg("-Dwarnings");
            cmd.arg(format!("--target={}", target));
            let libs = Path::new(&sysroot).join("lib");
            let sysroot = libs.join("rustlib").join(&target).join("lib");
            let paths = std::env::join_paths(&[libs, sysroot]).unwrap();
            cmd.env(compiletest::procsrv::dylib_env_var(), paths);
            match cmd.output() {
                Ok(ref output) if output.status.success() => writeln!(stderr.lock(), "ok").unwrap(),
                Ok(output) => {
                    failed = true;
                    writeln!(stderr.lock(), "FAILED with exit code {}", output.status.code().unwrap_or(0)).unwrap();
                    writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap();
                    writeln!(stderr.lock(), "stderr: \n {}", std::str::from_utf8(&output.stderr).unwrap()).unwrap();
                }
                Err(e) => {
                    failed = true;
                    writeln!(stderr.lock(), "FAILED: {}", e).unwrap();
                },
            }
        }
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "").unwrap();
    });
    if failed {
        panic!("some tests failed");
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::compiler_state::SourceSet;
use crate::config::{Config, SchemaLocation};
use relay_typegen::TypegenLanguage;
use std::path::PathBuf;
use watchman_client::prelude::*;

pub fn get_watchman_expr(config: &Config) -> Expr {
    let mut sources_conditions = vec![expr_any(
        config
            .sources
            .iter()
            .flat_map(|(path, name)| match name {
                SourceSet::SourceSetName(name) => vec![(path, &config.projects[name])],
                SourceSet::SourceSetNames(names) => names
                    .iter()
                    .map(|name| (path, &config.projects[name]))
                    .collect::<Vec<_>>(),
            })
            .map(|(path, project)| {
                Expr::All(vec![
                    // Ending in *.js(x) or *.ts(x) depending on the project language.
                    Expr::Suffix(match &project.typegen_config.language {
                        TypegenLanguage::Flow => vec![PathBuf::from("js"), PathBuf::from("jsx")],
                        TypegenLanguage::TypeScript => {
                            vec![PathBuf::from("ts"), PathBuf::from("tsx")]
                        }
                    }),
                    // In the related source root.
                    Expr::DirName(DirNameTerm {
                        path: path.clone(),
                        depth: None,
                    }),
                ])
            })
            .collect(),
    )];
    // not excluded by any glob
    if !config.excludes.is_empty() {
        sources_conditions.push(Expr::Not(Box::new(expr_any(
            config
                .excludes
                .iter()
                .map(|item| {
                    Expr::Match(MatchTerm {
                        glob: item.into(),
                        wholename: true,
                        ..Default::default()
                    })
                })
                .collect(),
        ))));
    }
    let sources_expr = Expr::All(sources_conditions);

    let mut expressions = vec![sources_expr];

    let output_dir_paths = get_output_dir_paths(config);
    if !output_dir_paths.is_empty() {
        let output_dir_expr = expr_files_in_dirs(output_dir_paths);
        expressions.push(output_dir_expr);
    }

    let schema_file_paths = get_schema_file_paths(config);
    if !schema_file_paths.is_empty() {
        let schema_file_expr = Expr::Name(NameTerm {
            paths: get_schema_file_paths(config),
            wholename: true,
        });
        expressions.push(schema_file_expr);
    }

    let schema_dir_paths = get_schema_dir_paths(config);
    if !schema_dir_paths.is_empty() {
        let schema_dir_expr = expr_graphql_files_in_dirs(schema_dir_paths);
        expressions.push(schema_dir_expr);
    }

    let extension_roots = get_extension_roots(config);
    if !extension_roots.is_empty() {
        let extensions_expr = expr_graphql_files_in_dirs(extension_roots);
        expressions.push(extensions_expr);
    }

    Expr::All(vec![
        // we generally only care about regular files
        Expr::FileType(FileType::Regular),
        Expr::Any(expressions),
    ])
}

/// Compute all root paths that we need to query Watchman with. All files
/// relevant to the compiler should be in these directories.
pub fn get_all_roots(config: &Config) -> Vec<PathBuf> {
    let source_roots = get_source_roots(config);
    let output_roots = get_output_dir_paths(config);
    let extension_roots = get_extension_roots(config);
    let schema_file_roots = get_schema_file_roots(config);
    let schema_dir_roots = get_schema_dir_paths(config);
    unify_roots(
        source_roots
            .into_iter()
            .chain(output_roots)
            .chain(extension_roots)
            .chain(schema_file_roots)
            .chain(schema_dir_roots)
            .collect(),
    )
}

/// Returns all root directories of JS source files for the config.
fn get_source_roots(config: &Config) -> Vec<PathBuf> {
    config.sources.keys().cloned().collect()
}

/// Returns all root directories of GraphQL schema extension files for the
/// config.
fn get_extension_roots(config: &Config) -> Vec<PathBuf> {
    config
        .projects
        .values()
        .flat_map(|project_config| project_config.schema_extensions.iter().cloned())
        .collect()
}

/// Returns all output and extra artifact output directories for the config.
fn get_output_dir_paths(config: &Config) -> Vec<PathBuf> {
    let output_dirs = config
        .projects
        .values()
        .filter_map(|project_config| project_config.output.clone());

    let extra_artifact_output_dirs = config
        .projects
        .values()
        .filter_map(|project_config| project_config.extra_artifacts_output.clone());

    output_dirs.chain(extra_artifact_output_dirs).collect()
}

/// Returns all paths that contain GraphQL schema files for the config.
fn get_schema_file_paths(config: &Config) -> Vec<PathBuf> {
    config
        .projects
        .values()
        .filter_map(|project_config| match &project_config.schema_location {
            SchemaLocation::File(schema_file) => Some(schema_file.clone()),
            SchemaLocation::Directory(_) => None,
        })
        .collect()
}

/// Returns all GraphQL schema directories for the config.
fn get_schema_dir_paths(config: &Config) -> Vec<PathBuf> {
    config
        .projects
        .values()
        .filter_map(|project_config| match &project_config.schema_location {
            SchemaLocation::File(_) => None,
            SchemaLocation::Directory(schema_dir) => Some(schema_dir.clone()),
        })
        .collect()
}

/// Returns root directories that contain GraphQL schema files.
fn get_schema_file_roots(config: &Config) -> impl Iterator<Item = PathBuf> {
    get_schema_file_paths(config)
        .into_iter()
        .map(|schema_path| {
            schema_path
                .parent()
                .expect("A schema in the project root directory is currently not supported.")
                .to_owned()
        })
}

fn expr_files_in_dirs(roots: Vec<PathBuf>) -> Expr {
    expr_any(
        roots
            .into_iter()
            .map(|path| Expr::DirName(DirNameTerm { path, depth: None }))
            .collect(),
    )
}

fn expr_graphql_files_in_dirs(roots: Vec<PathBuf>) -> Expr {
    Expr::All(vec![
        // ending in *.graphql
        Expr::Suffix(vec!["graphql".into()]),
        // in one of the extension directories
        expr_files_in_dirs(roots),
    ])
}

/// Helper to create an `anyof` expression if multiple items are passed or just
/// return the expression for a single item input `Vec`.
/// Panics for empty expressions. These are not valid in Watchman. We could
/// return `Expr::false`, but for that case the caller should just skip this
/// expression.
fn expr_any(expressions: Vec<Expr>) -> Expr {
    match expressions.len() {
        0 => panic!("expr_any called with empty expressions, this is an invalid query."),
        1 => expressions.into_iter().next().unwrap(),
        _ => Expr::Any(expressions),
    }
}

/// Finds the roots of a set of paths. This filters any paths
/// that are a subdirectory of other paths in the input.
fn unify_roots(mut paths: Vec<PathBuf>) -> Vec<PathBuf> {
    paths.sort();
    let mut roots = Vec::new();
    for path in paths {
        match roots.last() {
            Some(prev) if path.starts_with(&prev) => {
                // skip
            }
            _ => {
                roots.push(path);
            }
        }
    }
    roots
}

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

    #[test]
    fn test_unify_roots() {
        assert_eq!(unify_roots(vec![]).len(), 0);
        assert_eq!(
            unify_roots(vec!["Apps".into(), "Libraries".into()]),
            &[PathBuf::from("Apps"), PathBuf::from("Libraries")]
        );
        assert_eq!(
            unify_roots(vec!["Apps".into(), "Apps/Foo".into()]),
            &[PathBuf::from("Apps")]
        );
        assert_eq!(
            unify_roots(vec!["Apps/Foo".into(), "Apps".into()]),
            &[PathBuf::from("Apps")]
        );
        assert_eq!(
            unify_roots(vec!["Foo".into(), "Foo2".into()]),
            &[PathBuf::from("Foo"), PathBuf::from("Foo2"),]
        );
    }
}
<commit_msg>Watch js,jsx files with watchman with typescript (#3684)<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::compiler_state::SourceSet;
use crate::config::{Config, SchemaLocation};
use relay_typegen::TypegenLanguage;
use std::path::PathBuf;
use watchman_client::prelude::*;

pub fn get_watchman_expr(config: &Config) -> Expr {
    let mut sources_conditions = vec![expr_any(
        config
            .sources
            .iter()
            .flat_map(|(path, name)| match name {
                SourceSet::SourceSetName(name) => vec![(path, &config.projects[name])],
                SourceSet::SourceSetNames(names) => names
                    .iter()
                    .map(|name| (path, &config.projects[name]))
                    .collect::<Vec<_>>(),
            })
            .map(|(path, project)| {
                Expr::All(vec![
                    // Ending in *.js(x) or *.ts(x) depending on the project language.
                    Expr::Suffix(match &project.typegen_config.language {
                        TypegenLanguage::Flow => vec![PathBuf::from("js"), PathBuf::from("jsx")],
                        TypegenLanguage::TypeScript => {
                            vec![
                                PathBuf::from("js"),
                                PathBuf::from("jsx"),
                                PathBuf::from("ts"),
                                PathBuf::from("tsx"),
                            ]
                        }
                    }),
                    // In the related source root.
                    Expr::DirName(DirNameTerm {
                        path: path.clone(),
                        depth: None,
                    }),
                ])
            })
            .collect(),
    )];
    // not excluded by any glob
    if !config.excludes.is_empty() {
        sources_conditions.push(Expr::Not(Box::new(expr_any(
            config
                .excludes
                .iter()
                .map(|item| {
                    Expr::Match(MatchTerm {
                        glob: item.into(),
                        wholename: true,
                        ..Default::default()
                    })
                })
                .collect(),
        ))));
    }
    let sources_expr = Expr::All(sources_conditions);

    let mut expressions = vec![sources_expr];

    let output_dir_paths = get_output_dir_paths(config);
    if !output_dir_paths.is_empty() {
        let output_dir_expr = expr_files_in_dirs(output_dir_paths);
        expressions.push(output_dir_expr);
    }

    let schema_file_paths = get_schema_file_paths(config);
    if !schema_file_paths.is_empty() {
        let schema_file_expr = Expr::Name(NameTerm {
            paths: get_schema_file_paths(config),
            wholename: true,
        });
        expressions.push(schema_file_expr);
    }

    let schema_dir_paths = get_schema_dir_paths(config);
    if !schema_dir_paths.is_empty() {
        let schema_dir_expr = expr_graphql_files_in_dirs(schema_dir_paths);
        expressions.push(schema_dir_expr);
    }

    let extension_roots = get_extension_roots(config);
    if !extension_roots.is_empty() {
        let extensions_expr = expr_graphql_files_in_dirs(extension_roots);
        expressions.push(extensions_expr);
    }

    Expr::All(vec![
        // we generally only care about regular files
        Expr::FileType(FileType::Regular),
        Expr::Any(expressions),
    ])
}

/// Compute all root paths that we need to query Watchman with. All files
/// relevant to the compiler should be in these directories.
pub fn get_all_roots(config: &Config) -> Vec<PathBuf> {
    let source_roots = get_source_roots(config);
    let output_roots = get_output_dir_paths(config);
    let extension_roots = get_extension_roots(config);
    let schema_file_roots = get_schema_file_roots(config);
    let schema_dir_roots = get_schema_dir_paths(config);
    unify_roots(
        source_roots
            .into_iter()
            .chain(output_roots)
            .chain(extension_roots)
            .chain(schema_file_roots)
            .chain(schema_dir_roots)
            .collect(),
    )
}

/// Returns all root directories of JS source files for the config.
fn get_source_roots(config: &Config) -> Vec<PathBuf> {
    config.sources.keys().cloned().collect()
}

/// Returns all root directories of GraphQL schema extension files for the
/// config.
fn get_extension_roots(config: &Config) -> Vec<PathBuf> {
    config
        .projects
        .values()
        .flat_map(|project_config| project_config.schema_extensions.iter().cloned())
        .collect()
}

/// Returns all output and extra artifact output directories for the config.
fn get_output_dir_paths(config: &Config) -> Vec<PathBuf> {
    let output_dirs = config
        .projects
        .values()
        .filter_map(|project_config| project_config.output.clone());

    let extra_artifact_output_dirs = config
        .projects
        .values()
        .filter_map(|project_config| project_config.extra_artifacts_output.clone());

    output_dirs.chain(extra_artifact_output_dirs).collect()
}

/// Returns all paths that contain GraphQL schema files for the config.
fn get_schema_file_paths(config: &Config) -> Vec<PathBuf> {
    config
        .projects
        .values()
        .filter_map(|project_config| match &project_config.schema_location {
            SchemaLocation::File(schema_file) => Some(schema_file.clone()),
            SchemaLocation::Directory(_) => None,
        })
        .collect()
}

/// Returns all GraphQL schema directories for the config.
fn get_schema_dir_paths(config: &Config) -> Vec<PathBuf> {
    config
        .projects
        .values()
        .filter_map(|project_config| match &project_config.schema_location {
            SchemaLocation::File(_) => None,
            SchemaLocation::Directory(schema_dir) => Some(schema_dir.clone()),
        })
        .collect()
}

/// Returns root directories that contain GraphQL schema files.
fn get_schema_file_roots(config: &Config) -> impl Iterator<Item = PathBuf> {
    get_schema_file_paths(config)
        .into_iter()
        .map(|schema_path| {
            schema_path
                .parent()
                .expect("A schema in the project root directory is currently not supported.")
                .to_owned()
        })
}

fn expr_files_in_dirs(roots: Vec<PathBuf>) -> Expr {
    expr_any(
        roots
            .into_iter()
            .map(|path| Expr::DirName(DirNameTerm { path, depth: None }))
            .collect(),
    )
}

fn expr_graphql_files_in_dirs(roots: Vec<PathBuf>) -> Expr {
    Expr::All(vec![
        // ending in *.graphql
        Expr::Suffix(vec!["graphql".into()]),
        // in one of the extension directories
        expr_files_in_dirs(roots),
    ])
}

/// Helper to create an `anyof` expression if multiple items are passed or just
/// return the expression for a single item input `Vec`.
/// Panics for empty expressions. These are not valid in Watchman. We could
/// return `Expr::false`, but for that case the caller should just skip this
/// expression.
fn expr_any(expressions: Vec<Expr>) -> Expr {
    match expressions.len() {
        0 => panic!("expr_any called with empty expressions, this is an invalid query."),
        1 => expressions.into_iter().next().unwrap(),
        _ => Expr::Any(expressions),
    }
}

/// Finds the roots of a set of paths. This filters any paths
/// that are a subdirectory of other paths in the input.
fn unify_roots(mut paths: Vec<PathBuf>) -> Vec<PathBuf> {
    paths.sort();
    let mut roots = Vec::new();
    for path in paths {
        match roots.last() {
            Some(prev) if path.starts_with(&prev) => {
                // skip
            }
            _ => {
                roots.push(path);
            }
        }
    }
    roots
}

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

    #[test]
    fn test_unify_roots() {
        assert_eq!(unify_roots(vec![]).len(), 0);
        assert_eq!(
            unify_roots(vec!["Apps".into(), "Libraries".into()]),
            &[PathBuf::from("Apps"), PathBuf::from("Libraries")]
        );
        assert_eq!(
            unify_roots(vec!["Apps".into(), "Apps/Foo".into()]),
            &[PathBuf::from("Apps")]
        );
        assert_eq!(
            unify_roots(vec!["Apps/Foo".into(), "Apps".into()]),
            &[PathBuf::from("Apps")]
        );
        assert_eq!(
            unify_roots(vec!["Foo".into(), "Foo2".into()]),
            &[PathBuf::from("Foo"), PathBuf::from("Foo2"),]
        );
    }
}
<|endoftext|>


<commit_before>use std::borrow::Borrow;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ptr;
use std::ops::{Deref, DerefMut};
use std::str;
use std::str::Utf8Error;
use std::slice;

use array::{Array, ArrayExt};
use array::Index;
use CapacityError;
use odds::char::encode_utf8;

/// A string with a fixed capacity.
///
/// The `ArrayString` is a string backed by a fixed size array. It keeps track
/// of its length.
///
/// The string is a contiguous value that you can store directly on the stack
/// if needed.
#[derive(Copy)]
pub struct ArrayString<A: Array<Item=u8>> {
    xs: A,
    len: A::Index,
}

impl<A: Array<Item=u8>> ArrayString<A> {
    /// Create a new empty `ArrayString`.
    ///
    /// Capacity is inferred from the type parameter.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 16]>::new();
    /// string.push_str("foo");
    /// assert_eq!(&string[..], "foo");
    /// assert_eq!(string.capacity(), 16);
    /// ```
    pub fn new() -> ArrayString<A> {
        unsafe {
            ArrayString {
                xs: ::new_array(),
                len: Index::from(0),
            }
        }
    }

    /// Create a new `ArrayString` from a `str`.
    ///
    /// Capacity is inferred from the type parameter.
    ///
    /// **Errors** if the backing array is not large enough to fit the string.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 3]>::from("foo").unwrap();
    /// assert_eq!(&string[..], "foo");
    /// assert_eq!(string.len(), 3);
    /// assert_eq!(string.capacity(), 3);
    /// ```
    pub fn from(s: &str) -> Result<Self, CapacityError<&str>> {
        let mut arraystr = Self::new();
        try!(arraystr.push_str(s));
        Ok(arraystr)
    }

    /// Create a new `ArrayString` from a byte string literal.
    ///
    /// **Errors** if the byte string literal is not valid UTF-8.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let string = ArrayString::from_byte_string(b"hello world").unwrap();
    /// ```
    pub fn from_byte_string(b: &A) -> Result<Self, Utf8Error> {
        let mut arraystr = Self::new();
        let s = try!(str::from_utf8(b.as_slice()));
        let _result = arraystr.push_str(s);
        debug_assert!(_result.is_ok());
        Ok(arraystr)
    }

    /// Return the capacity of the `ArrayString`.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let string = ArrayString::<[_; 3]>::new();
    /// assert_eq!(string.capacity(), 3);
    /// ```
    #[inline]
    pub fn capacity(&self) -> usize { A::capacity() }

    /// Return if the `ArrayString` is completely filled.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 1]>::new();
    /// assert!(!string.is_full());
    /// string.push_str("A");
    /// assert!(string.is_full());
    /// ```
    pub fn is_full(&self) -> bool { self.len() == self.capacity() }

    /// Adds the given char to the end of the string.
    ///
    /// Returns `Ok` if the push succeeds.
    ///
    /// **Errors** if the backing array is not large enough to fit the additional char.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 2]>::new();
    ///
    /// string.push('a').unwrap();
    /// string.push('b').unwrap();
    /// let overflow = string.push('c');
    ///
    /// assert_eq!(&string[..], "ab");
    /// assert_eq!(overflow.unwrap_err().element(), 'c');
    /// ```
    pub fn push(&mut self, c: char) -> Result<(), CapacityError<char>> {
        let len = self.len();
        unsafe {
            match encode_utf8(c, &mut self.raw_mut_bytes()[len..]) {
                Ok(n) => {
                    self.set_len(len + n);
                    Ok(())
                }
                Err(_) => Err(CapacityError::new(c)),
            }
        }
    }

    /// Adds the given string slice to the end of the string.
    ///
    /// Returns `Ok` if the push succeeds.
    ///
    /// **Errors** if the backing array is not large enough to fit the string.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 2]>::new();
    ///
    /// string.push_str("a").unwrap();
    /// let overflow1 = string.push_str("bc");
    /// string.push_str("d").unwrap();
    /// let overflow2 = string.push_str("ef");
    ///
    /// assert_eq!(&string[..], "ad");
    /// assert_eq!(overflow1.unwrap_err().element(), "bc");
    /// assert_eq!(overflow2.unwrap_err().element(), "ef");
    /// ```
    pub fn push_str<'a>(&mut self, s: &'a str) -> Result<(), CapacityError<&'a str>> {
        if s.len() > self.capacity() - self.len() {
            return Err(CapacityError::new(s));
        }
        unsafe {
            let dst = self.xs.as_mut_ptr().offset(self.len() as isize);
            let src = s.as_ptr();
            ptr::copy_nonoverlapping(src, dst, s.len());
            let newl = self.len() + s.len();
            self.set_len(newl);
        }
        Ok(())
    }

    /// Make the string empty.
    pub fn clear(&mut self) {
        unsafe {
            self.set_len(0);
        }
    }

    /// Set the strings's length.
    ///
    /// May panic if `length` is greater than the capacity.
    ///
    /// This function is `unsafe` because it changes the notion of the
    /// number of “valid” bytes in the string. Use with care.
    #[inline]
    pub unsafe fn set_len(&mut self, length: usize) {
        debug_assert!(length <= self.capacity());
        self.len = Index::from(length);
    }

    /// Return a string slice of the whole `ArrayString`.
    pub fn as_str(&self) -> &str {
        self
    }

    /// Return a mutable slice of the whole string's buffer
    unsafe fn raw_mut_bytes(&mut self) -> &mut [u8] {
        slice::from_raw_parts_mut(self.xs.as_mut_ptr(), self.capacity())
    }
}

impl<A: Array<Item=u8>> Deref for ArrayString<A> {
    type Target = str;
    #[inline]
    fn deref(&self) -> &str {
        unsafe {
            let sl = slice::from_raw_parts(self.xs.as_ptr(), self.len.to_usize());
            str::from_utf8_unchecked(sl)
        }
    }
}

impl<A: Array<Item=u8>> DerefMut for ArrayString<A> {
    #[inline]
    fn deref_mut(&mut self) -> &mut str {
        unsafe {
            let sl = slice::from_raw_parts_mut(self.xs.as_mut_ptr(), self.len.to_usize());
            // FIXME: Nothing but transmute to do this right now
            mem::transmute(sl)
        }
    }
}

impl<A: Array<Item=u8>> PartialEq for ArrayString<A> {
    fn eq(&self, rhs: &Self) -> bool {
        **self == **rhs
    }
}

impl<A: Array<Item=u8>> PartialEq<str> for ArrayString<A> {
    fn eq(&self, rhs: &str) -> bool {
        &**self == rhs
    }
}

impl<A: Array<Item=u8>> PartialEq<ArrayString<A>> for str {
    fn eq(&self, rhs: &ArrayString<A>) -> bool {
        self == &**rhs
    }
}

impl<A: Array<Item=u8>> Eq for ArrayString<A> { }

impl<A: Array<Item=u8>> Hash for ArrayString<A> {
    fn hash<H: Hasher>(&self, h: &mut H) {
        (**self).hash(h)
    }
}

impl<A: Array<Item=u8>> Borrow<str> for ArrayString<A> {
    fn borrow(&self) -> &str { self }
}

impl<A: Array<Item=u8>> AsRef<str> for ArrayString<A> {
    fn as_ref(&self) -> &str { self }
}

impl<A: Array<Item=u8>> fmt::Debug for ArrayString<A> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) }
}

impl<A: Array<Item=u8>> fmt::Display for ArrayString<A> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) }
}

/// `Write` appends written data to the end of the string.
impl<A: Array<Item=u8>> fmt::Write for ArrayString<A> {
    fn write_char(&mut self, c: char) -> fmt::Result {
        self.push(c).map_err(|_| fmt::Error)
    }
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.push_str(s).map_err(|_| fmt::Error)
    }
}

impl<A: Array<Item=u8> + Copy> Clone for ArrayString<A> {
    fn clone(&self) -> ArrayString<A> {
        *self
    }

    fn clone_from(&mut self, rhs: &Self) {
        // guaranteed to fit due to types matching.
        self.clear();
        self.push_str(rhs).ok();
    }
}
<commit_msg>Add `PartialOrd` and `Ord` implementation to `ArrayString`<commit_after>use std::borrow::Borrow;
use std::cmp;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ptr;
use std::ops::{Deref, DerefMut};
use std::str;
use std::str::Utf8Error;
use std::slice;

use array::{Array, ArrayExt};
use array::Index;
use CapacityError;
use odds::char::encode_utf8;

/// A string with a fixed capacity.
///
/// The `ArrayString` is a string backed by a fixed size array. It keeps track
/// of its length.
///
/// The string is a contiguous value that you can store directly on the stack
/// if needed.
#[derive(Copy)]
pub struct ArrayString<A: Array<Item=u8>> {
    xs: A,
    len: A::Index,
}

impl<A: Array<Item=u8>> ArrayString<A> {
    /// Create a new empty `ArrayString`.
    ///
    /// Capacity is inferred from the type parameter.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 16]>::new();
    /// string.push_str("foo");
    /// assert_eq!(&string[..], "foo");
    /// assert_eq!(string.capacity(), 16);
    /// ```
    pub fn new() -> ArrayString<A> {
        unsafe {
            ArrayString {
                xs: ::new_array(),
                len: Index::from(0),
            }
        }
    }

    /// Create a new `ArrayString` from a `str`.
    ///
    /// Capacity is inferred from the type parameter.
    ///
    /// **Errors** if the backing array is not large enough to fit the string.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 3]>::from("foo").unwrap();
    /// assert_eq!(&string[..], "foo");
    /// assert_eq!(string.len(), 3);
    /// assert_eq!(string.capacity(), 3);
    /// ```
    pub fn from(s: &str) -> Result<Self, CapacityError<&str>> {
        let mut arraystr = Self::new();
        try!(arraystr.push_str(s));
        Ok(arraystr)
    }

    /// Create a new `ArrayString` from a byte string literal.
    ///
    /// **Errors** if the byte string literal is not valid UTF-8.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let string = ArrayString::from_byte_string(b"hello world").unwrap();
    /// ```
    pub fn from_byte_string(b: &A) -> Result<Self, Utf8Error> {
        let mut arraystr = Self::new();
        let s = try!(str::from_utf8(b.as_slice()));
        let _result = arraystr.push_str(s);
        debug_assert!(_result.is_ok());
        Ok(arraystr)
    }

    /// Return the capacity of the `ArrayString`.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let string = ArrayString::<[_; 3]>::new();
    /// assert_eq!(string.capacity(), 3);
    /// ```
    #[inline]
    pub fn capacity(&self) -> usize { A::capacity() }

    /// Return if the `ArrayString` is completely filled.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 1]>::new();
    /// assert!(!string.is_full());
    /// string.push_str("A");
    /// assert!(string.is_full());
    /// ```
    pub fn is_full(&self) -> bool { self.len() == self.capacity() }

    /// Adds the given char to the end of the string.
    ///
    /// Returns `Ok` if the push succeeds.
    ///
    /// **Errors** if the backing array is not large enough to fit the additional char.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 2]>::new();
    ///
    /// string.push('a').unwrap();
    /// string.push('b').unwrap();
    /// let overflow = string.push('c');
    ///
    /// assert_eq!(&string[..], "ab");
    /// assert_eq!(overflow.unwrap_err().element(), 'c');
    /// ```
    pub fn push(&mut self, c: char) -> Result<(), CapacityError<char>> {
        let len = self.len();
        unsafe {
            match encode_utf8(c, &mut self.raw_mut_bytes()[len..]) {
                Ok(n) => {
                    self.set_len(len + n);
                    Ok(())
                }
                Err(_) => Err(CapacityError::new(c)),
            }
        }
    }

    /// Adds the given string slice to the end of the string.
    ///
    /// Returns `Ok` if the push succeeds.
    ///
    /// **Errors** if the backing array is not large enough to fit the string.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 2]>::new();
    ///
    /// string.push_str("a").unwrap();
    /// let overflow1 = string.push_str("bc");
    /// string.push_str("d").unwrap();
    /// let overflow2 = string.push_str("ef");
    ///
    /// assert_eq!(&string[..], "ad");
    /// assert_eq!(overflow1.unwrap_err().element(), "bc");
    /// assert_eq!(overflow2.unwrap_err().element(), "ef");
    /// ```
    pub fn push_str<'a>(&mut self, s: &'a str) -> Result<(), CapacityError<&'a str>> {
        if s.len() > self.capacity() - self.len() {
            return Err(CapacityError::new(s));
        }
        unsafe {
            let dst = self.xs.as_mut_ptr().offset(self.len() as isize);
            let src = s.as_ptr();
            ptr::copy_nonoverlapping(src, dst, s.len());
            let newl = self.len() + s.len();
            self.set_len(newl);
        }
        Ok(())
    }

    /// Make the string empty.
    pub fn clear(&mut self) {
        unsafe {
            self.set_len(0);
        }
    }

    /// Set the strings's length.
    ///
    /// May panic if `length` is greater than the capacity.
    ///
    /// This function is `unsafe` because it changes the notion of the
    /// number of “valid” bytes in the string. Use with care.
    #[inline]
    pub unsafe fn set_len(&mut self, length: usize) {
        debug_assert!(length <= self.capacity());
        self.len = Index::from(length);
    }

    /// Return a string slice of the whole `ArrayString`.
    pub fn as_str(&self) -> &str {
        self
    }

    /// Return a mutable slice of the whole string's buffer
    unsafe fn raw_mut_bytes(&mut self) -> &mut [u8] {
        slice::from_raw_parts_mut(self.xs.as_mut_ptr(), self.capacity())
    }
}

impl<A: Array<Item=u8>> Deref for ArrayString<A> {
    type Target = str;
    #[inline]
    fn deref(&self) -> &str {
        unsafe {
            let sl = slice::from_raw_parts(self.xs.as_ptr(), self.len.to_usize());
            str::from_utf8_unchecked(sl)
        }
    }
}

impl<A: Array<Item=u8>> DerefMut for ArrayString<A> {
    #[inline]
    fn deref_mut(&mut self) -> &mut str {
        unsafe {
            let sl = slice::from_raw_parts_mut(self.xs.as_mut_ptr(), self.len.to_usize());
            // FIXME: Nothing but transmute to do this right now
            mem::transmute(sl)
        }
    }
}

impl<A: Array<Item=u8>> PartialEq for ArrayString<A> {
    fn eq(&self, rhs: &Self) -> bool {
        **self == **rhs
    }
}

impl<A: Array<Item=u8>> PartialEq<str> for ArrayString<A> {
    fn eq(&self, rhs: &str) -> bool {
        &**self == rhs
    }
}

impl<A: Array<Item=u8>> PartialEq<ArrayString<A>> for str {
    fn eq(&self, rhs: &ArrayString<A>) -> bool {
        self == &**rhs
    }
}

impl<A: Array<Item=u8>> Eq for ArrayString<A> { }

impl<A: Array<Item=u8>> Hash for ArrayString<A> {
    fn hash<H: Hasher>(&self, h: &mut H) {
        (**self).hash(h)
    }
}

impl<A: Array<Item=u8>> Borrow<str> for ArrayString<A> {
    fn borrow(&self) -> &str { self }
}

impl<A: Array<Item=u8>> AsRef<str> for ArrayString<A> {
    fn as_ref(&self) -> &str { self }
}

impl<A: Array<Item=u8>> fmt::Debug for ArrayString<A> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) }
}

impl<A: Array<Item=u8>> fmt::Display for ArrayString<A> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) }
}

/// `Write` appends written data to the end of the string.
impl<A: Array<Item=u8>> fmt::Write for ArrayString<A> {
    fn write_char(&mut self, c: char) -> fmt::Result {
        self.push(c).map_err(|_| fmt::Error)
    }
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.push_str(s).map_err(|_| fmt::Error)
    }
}

impl<A: Array<Item=u8> + Copy> Clone for ArrayString<A> {
    fn clone(&self) -> ArrayString<A> {
        *self
    }
    fn clone_from(&mut self, rhs: &Self) {
        // guaranteed to fit due to types matching.
        self.clear();
        self.push_str(rhs).ok();
    }
}

impl<A: Array<Item=u8>> PartialOrd for ArrayString<A> {
    fn partial_cmp(&self, rhs: &Self) -> Option<cmp::Ordering> {
        (**self).partial_cmp(&**rhs)
    }
    fn lt(&self, rhs: &Self) -> bool { **self < **rhs }
    fn le(&self, rhs: &Self) -> bool { **self <= **rhs }
    fn gt(&self, rhs: &Self) -> bool { **self > **rhs }
    fn ge(&self, rhs: &Self) -> bool { **self >= **rhs }
}

impl<A: Array<Item=u8>> PartialOrd<str> for ArrayString<A> {
    fn partial_cmp(&self, rhs: &str) -> Option<cmp::Ordering> {
        (**self).partial_cmp(rhs)
    }
    fn lt(&self, rhs: &str) -> bool { &**self < rhs }
    fn le(&self, rhs: &str) -> bool { &**self <= rhs }
    fn gt(&self, rhs: &str) -> bool { &**self > rhs }
    fn ge(&self, rhs: &str) -> bool { &**self >= rhs }
}

impl<A: Array<Item=u8>> PartialOrd<ArrayString<A>> for str {
    fn partial_cmp(&self, rhs: &ArrayString<A>) -> Option<cmp::Ordering> {
        self.partial_cmp(&**rhs)
    }
    fn lt(&self, rhs: &ArrayString<A>) -> bool { self < &**rhs }
    fn le(&self, rhs: &ArrayString<A>) -> bool { self <= &**rhs }
    fn gt(&self, rhs: &ArrayString<A>) -> bool { self > &**rhs }
    fn ge(&self, rhs: &ArrayString<A>) -> bool { self >= &**rhs }
}

impl<A: Array<Item=u8>> Ord for ArrayString<A> {
    fn cmp(&self, rhs: &Self) -> cmp::Ordering {
        (**self).cmp(&**rhs)
    }
}
<|endoftext|>