<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A module for working with borrowed data.

#![stable(feature = "rust1", since = "1.0.0")]

use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::ops::{Add, AddAssign, Deref};

use fmt;
use string::String;

use self::Cow::*;

#[stable(feature = "rust1", since = "1.0.0")]
pub use core::borrow::{Borrow, BorrowMut};

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> Borrow<B> for Cow<'a, B>
    where B: ToOwned,
          <B as ToOwned>::Owned: 'a
{
    fn borrow(&self) -> &B {
        &**self
    }
}

/// A generalization of `Clone` to borrowed data.
///
/// Some types make it possible to go from borrowed to owned, usually by
/// implementing the `Clone` trait. But `Clone` works only for going from `&T`
/// to `T`. The `ToOwned` trait generalizes `Clone` to construct owned data
/// from any borrow of a given type.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait ToOwned {
    #[stable(feature = "rust1", since = "1.0.0")]
    type Owned: Borrow<Self>;

    /// Creates owned data from borrowed data, usually by cloning.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// let s: &str = "a";
    /// let ss: String = s.to_owned();
    ///
    /// let v: &[i32] = &[1, 2];
    /// let vv: Vec<i32> = v.to_owned();
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    fn to_owned(&self) -> Self::Owned;

    /// Uses borrowed data to replace owned data, usually by cloning.
    ///
    /// This is borrow-generalized version of `Clone::clone_from`.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// # #![feature(toowned_clone_into)]
    /// let mut s: String = String::new();
    /// "hello".clone_into(&mut s);
    ///
    /// let mut v: Vec<i32> = Vec::new();
    /// [1, 2][..].clone_into(&mut v);
    /// ```
    #[unstable(feature = "toowned_clone_into",
               reason = "recently added",
               issue = "41263")]
    fn clone_into(&self, target: &mut Self::Owned) {
        *target = self.to_owned();
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> ToOwned for T
    where T: Clone
{
    type Owned = T;
    fn to_owned(&self) -> T {
        self.clone()
    }

    fn clone_into(&self, target: &mut T) {
        target.clone_from(self);
    }
}

/// A clone-on-write smart pointer.
///
/// The type `Cow` is a smart pointer providing clone-on-write functionality: it
/// can enclose and provide immutable access to borrowed data, and clone the
/// data lazily when mutation or ownership is required. The type is designed to
/// work with general borrowed data via the `Borrow` trait.
///
/// `Cow` implements `Deref`, which means that you can call
/// non-mutating methods directly on the data it encloses. If mutation
/// is desired, `to_mut` will obtain a mutable reference to an owned
/// value, cloning if necessary.
///
/// # Examples
///
/// ```
/// use std::borrow::Cow;
///
/// fn abs_all(input: &mut Cow<[i32]>) {
///     for i in 0..input.len() {
///         let v = input[i];
///         if v < 0 {
///             // Clones into a vector if not already owned.
///             input.to_mut()[i] = -v;
///         }
///     }
/// }
///
/// // No clone occurs because `input` doesn't need to be mutated.
/// let slice = [0, 1, 2];
/// let mut input = Cow::from(&slice[..]);
/// abs_all(&mut input);
///
/// // Clone occurs because `input` needs to be mutated.
/// let slice = [-1, 0, 1];
/// let mut input = Cow::from(&slice[..]);
/// abs_all(&mut input);
///
/// // No clone occurs because `input` is already owned.
/// let mut input = Cow::from(vec![-1, 0, 1]);
/// abs_all(&mut input);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub enum Cow<'a, B: ?Sized + 'a>
    where B: ToOwned
{
    /// Borrowed data.
    #[stable(feature = "rust1", since = "1.0.0")]
    Borrowed(#[stable(feature = "rust1", since = "1.0.0")]
             &'a B),

    /// Owned data.
    #[stable(feature = "rust1", since = "1.0.0")]
    Owned(#[stable(feature = "rust1", since = "1.0.0")]
          <B as ToOwned>::Owned),
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> Clone for Cow<'a, B>
    where B: ToOwned
{
    fn clone(&self) -> Cow<'a, B> {
        match *self {
            Borrowed(b) => Borrowed(b),
            Owned(ref o) => {
                let b: &B = o.borrow();
                Owned(b.to_owned())
            }
        }
    }

    fn clone_from(&mut self, source: &Cow<'a, B>) {
        if let Owned(ref mut dest) = *self {
            if let Owned(ref o) = *source {
                o.borrow().clone_into(dest);
                return;
            }
        }

        *self = source.clone();
    }
}

impl<'a, B: ?Sized> Cow<'a, B>
    where B: ToOwned
{
    /// Acquires a mutable reference to the owned form of the data.
    ///
    /// Clones the data if it is not already owned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::borrow::Cow;
    ///
    /// let mut cow: Cow<[_]> = Cow::Owned(vec![1, 2, 3]);
    ///
    /// let hello = cow.to_mut();
    ///
    /// assert_eq!(hello, &[1, 2, 3]);
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn to_mut(&mut self) -> &mut <B as ToOwned>::Owned {
        match *self {
            Borrowed(borrowed) => {
                *self = Owned(borrowed.to_owned());
                match *self {
                    Borrowed(..) => unreachable!(),
                    Owned(ref mut owned) => owned,
                }
            }
            Owned(ref mut owned) => owned,
        }
    }

    /// Extracts the owned data.
    ///
    /// Clones the data if it is not already owned.
    ///
    /// # Examples
    ///
    /// Calling `into_owned` on a `Cow::Borrowed` clones the underlying data
    /// and becomes a `Cow::Owned`:
    ///
    /// ```
    /// use std::borrow::Cow;
    ///
    /// let s = "Hello world!";
    /// let cow = Cow::Borrowed(s);
    ///
    /// assert_eq!(
    ///   cow.into_owned(),
    ///   Cow::Owned(String::from(s))
    /// );
    /// ```
    ///
    /// Calling `into_owned` on a `Cow::Owned` is a no-op:
    ///
    /// ```
    /// use std::borrow::Cow;
    ///
    /// let s = "Hello world!";
    /// let cow: Cow<str> = Cow::Owned(String::from(s));
    ///
    /// assert_eq!(
    ///   cow.into_owned(),
    ///   Cow::Owned(String::from(s))
    /// );
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn into_owned(self) -> <B as ToOwned>::Owned {
        match self {
            Borrowed(borrowed) => borrowed.to_owned(),
            Owned(owned) => owned,
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> Deref for Cow<'a, B>
    where B: ToOwned
{
    type Target = B;

    fn deref(&self) -> &B {
        match *self {
            Borrowed(borrowed) => borrowed,
            Owned(ref owned) => owned.borrow(),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> Eq for Cow<'a, B> where B: Eq + ToOwned {}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> Ord for Cow<'a, B>
    where B: Ord + ToOwned
{
    #[inline]
    fn cmp(&self, other: &Cow<'a, B>) -> Ordering {
        Ord::cmp(&**self, &**other)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, 'b, B: ?Sized, C: ?Sized> PartialEq<Cow<'b, C>> for Cow<'a, B>
    where B: PartialEq<C> + ToOwned,
          C: ToOwned
{
    #[inline]
    fn eq(&self, other: &Cow<'b, C>) -> bool {
        PartialEq::eq(&**self, &**other)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> PartialOrd for Cow<'a, B>
    where B: PartialOrd + ToOwned
{
    #[inline]
    fn partial_cmp(&self, other: &Cow<'a, B>) -> Option<Ordering> {
        PartialOrd::partial_cmp(&**self, &**other)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> fmt::Debug for Cow<'a, B>
    where B: fmt::Debug + ToOwned,
          <B as ToOwned>::Owned: fmt::Debug
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Borrowed(ref b) => fmt::Debug::fmt(b, f),
            Owned(ref o) => fmt::Debug::fmt(o, f),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> fmt::Display for Cow<'a, B>
    where B: fmt::Display + ToOwned,
          <B as ToOwned>::Owned: fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Borrowed(ref b) => fmt::Display::fmt(b, f),
            Owned(ref o) => fmt::Display::fmt(o, f),
        }
    }
}

#[stable(feature = "default", since = "1.11.0")]
impl<'a, B: ?Sized> Default for Cow<'a, B>
    where B: ToOwned,
          <B as ToOwned>::Owned: Default
{
    /// Creates an owned Cow<'a, B> with the default value for the contained owned value.
    fn default() -> Cow<'a, B> {
        Owned(<B as ToOwned>::Owned::default())
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> Hash for Cow<'a, B>
    where B: Hash + ToOwned
{
    #[inline]
    fn hash<H: Hasher>(&self, state: &mut H) {
        Hash::hash(&**self, state)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated)]
impl<'a, T: ?Sized + ToOwned> AsRef<T> for Cow<'a, T> {
    fn as_ref(&self) -> &T {
        self
    }
}

#[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> Add<&'a str> for Cow<'a, str> {
    type Output = Cow<'a, str>;

    #[inline]
    fn add(mut self, rhs: &'a str) -> Self::Output {
        self += rhs;
        self
    }
}

#[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> Add<Cow<'a, str>> for Cow<'a, str> {
    type Output = Cow<'a, str>;

    #[inline]
    fn add(mut self, rhs: Cow<'a, str>) -> Self::Output {
        self += rhs;
        self
    }
}

#[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> AddAssign<&'a str> for Cow<'a, str> {
    fn add_assign(&mut self, rhs: &'a str) {
        if self.is_empty() {
            *self = Cow::Borrowed(rhs)
        } else if rhs.is_empty() {
            return;
        } else {
            if let Cow::Borrowed(lhs) = *self {
                let mut s = String::with_capacity(lhs.len() + rhs.len());
                s.push_str(lhs);
                *self = Cow::Owned(s);
            }
            self.to_mut().push_str(rhs);
        }
    }
}

#[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> AddAssign<Cow<'a, str>> for Cow<'a, str> {
    fn add_assign(&mut self, rhs: Cow<'a, str>) {
        if self.is_empty() {
            *self = rhs
        } else if rhs.is_empty() {
            return;
        } else {
            if let Cow::Borrowed(lhs) = *self {
                let mut s = String::with_capacity(lhs.len() + rhs.len());
                s.push_str(lhs);
                *self = Cow::Owned(s);
            }
            self.to_mut().push_str(&rhs);
        }
    }
}
<commit_msg> Improve doc example for `Cow::to_mut`.<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A module for working with borrowed data.

#![stable(feature = "rust1", since = "1.0.0")]

use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::ops::{Add, AddAssign, Deref};

use fmt;
use string::String;

use self::Cow::*;

#[stable(feature = "rust1", since = "1.0.0")]
pub use core::borrow::{Borrow, BorrowMut};

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> Borrow<B> for Cow<'a, B>
    where B: ToOwned,
          <B as ToOwned>::Owned: 'a
{
    fn borrow(&self) -> &B {
        &**self
    }
}

/// A generalization of `Clone` to borrowed data.
///
/// Some types make it possible to go from borrowed to owned, usually by
/// implementing the `Clone` trait. But `Clone` works only for going from `&T`
/// to `T`. The `ToOwned` trait generalizes `Clone` to construct owned data
/// from any borrow of a given type.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait ToOwned {
    #[stable(feature = "rust1", since = "1.0.0")]
    type Owned: Borrow<Self>;

    /// Creates owned data from borrowed data, usually by cloning.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// let s: &str = "a";
    /// let ss: String = s.to_owned();
    ///
    /// let v: &[i32] = &[1, 2];
    /// let vv: Vec<i32> = v.to_owned();
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    fn to_owned(&self) -> Self::Owned;

    /// Uses borrowed data to replace owned data, usually by cloning.
    ///
    /// This is borrow-generalized version of `Clone::clone_from`.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// # #![feature(toowned_clone_into)]
    /// let mut s: String = String::new();
    /// "hello".clone_into(&mut s);
    ///
    /// let mut v: Vec<i32> = Vec::new();
    /// [1, 2][..].clone_into(&mut v);
    /// ```
    #[unstable(feature = "toowned_clone_into",
               reason = "recently added",
               issue = "41263")]
    fn clone_into(&self, target: &mut Self::Owned) {
        *target = self.to_owned();
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> ToOwned for T
    where T: Clone
{
    type Owned = T;
    fn to_owned(&self) -> T {
        self.clone()
    }

    fn clone_into(&self, target: &mut T) {
        target.clone_from(self);
    }
}

/// A clone-on-write smart pointer.
///
/// The type `Cow` is a smart pointer providing clone-on-write functionality: it
/// can enclose and provide immutable access to borrowed data, and clone the
/// data lazily when mutation or ownership is required. The type is designed to
/// work with general borrowed data via the `Borrow` trait.
///
/// `Cow` implements `Deref`, which means that you can call
/// non-mutating methods directly on the data it encloses. If mutation
/// is desired, `to_mut` will obtain a mutable reference to an owned
/// value, cloning if necessary.
///
/// # Examples
///
/// ```
/// use std::borrow::Cow;
///
/// fn abs_all(input: &mut Cow<[i32]>) {
///     for i in 0..input.len() {
///         let v = input[i];
///         if v < 0 {
///             // Clones into a vector if not already owned.
///             input.to_mut()[i] = -v;
///         }
///     }
/// }
///
/// // No clone occurs because `input` doesn't need to be mutated.
/// let slice = [0, 1, 2];
/// let mut input = Cow::from(&slice[..]);
/// abs_all(&mut input);
///
/// // Clone occurs because `input` needs to be mutated.
/// let slice = [-1, 0, 1];
/// let mut input = Cow::from(&slice[..]);
/// abs_all(&mut input);
///
/// // No clone occurs because `input` is already owned.
/// let mut input = Cow::from(vec![-1, 0, 1]);
/// abs_all(&mut input);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub enum Cow<'a, B: ?Sized + 'a>
    where B: ToOwned
{
    /// Borrowed data.
    #[stable(feature = "rust1", since = "1.0.0")]
    Borrowed(#[stable(feature = "rust1", since = "1.0.0")]
             &'a B),

    /// Owned data.
    #[stable(feature = "rust1", since = "1.0.0")]
    Owned(#[stable(feature = "rust1", since = "1.0.0")]
          <B as ToOwned>::Owned),
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> Clone for Cow<'a, B>
    where B: ToOwned
{
    fn clone(&self) -> Cow<'a, B> {
        match *self {
            Borrowed(b) => Borrowed(b),
            Owned(ref o) => {
                let b: &B = o.borrow();
                Owned(b.to_owned())
            }
        }
    }

    fn clone_from(&mut self, source: &Cow<'a, B>) {
        if let Owned(ref mut dest) = *self {
            if let Owned(ref o) = *source {
                o.borrow().clone_into(dest);
                return;
            }
        }

        *self = source.clone();
    }
}

impl<'a, B: ?Sized> Cow<'a, B>
    where B: ToOwned
{
    /// Acquires a mutable reference to the owned form of the data.
    ///
    /// Clones the data if it is not already owned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::ascii::AsciiExt;
    /// use std::borrow::Cow;
    ///
    /// let mut cow = Cow::Borrowed("foo");
    /// cow.to_mut().make_ascii_uppercase();
    ///
    /// assert_eq!(
    ///   cow,
    ///   Cow::Owned(String::from("FOO")) as Cow<str>
    /// );
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn to_mut(&mut self) -> &mut <B as ToOwned>::Owned {
        match *self {
            Borrowed(borrowed) => {
                *self = Owned(borrowed.to_owned());
                match *self {
                    Borrowed(..) => unreachable!(),
                    Owned(ref mut owned) => owned,
                }
            }
            Owned(ref mut owned) => owned,
        }
    }

    /// Extracts the owned data.
    ///
    /// Clones the data if it is not already owned.
    ///
    /// # Examples
    ///
    /// Calling `into_owned` on a `Cow::Borrowed` clones the underlying data
    /// and becomes a `Cow::Owned`:
    ///
    /// ```
    /// use std::borrow::Cow;
    ///
    /// let s = "Hello world!";
    /// let cow = Cow::Borrowed(s);
    ///
    /// assert_eq!(
    ///   cow.into_owned(),
    ///   Cow::Owned(String::from(s))
    /// );
    /// ```
    ///
    /// Calling `into_owned` on a `Cow::Owned` is a no-op:
    ///
    /// ```
    /// use std::borrow::Cow;
    ///
    /// let s = "Hello world!";
    /// let cow: Cow<str> = Cow::Owned(String::from(s));
    ///
    /// assert_eq!(
    ///   cow.into_owned(),
    ///   Cow::Owned(String::from(s))
    /// );
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn into_owned(self) -> <B as ToOwned>::Owned {
        match self {
            Borrowed(borrowed) => borrowed.to_owned(),
            Owned(owned) => owned,
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> Deref for Cow<'a, B>
    where B: ToOwned
{
    type Target = B;

    fn deref(&self) -> &B {
        match *self {
            Borrowed(borrowed) => borrowed,
            Owned(ref owned) => owned.borrow(),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> Eq for Cow<'a, B> where B: Eq + ToOwned {}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> Ord for Cow<'a, B>
    where B: Ord + ToOwned
{
    #[inline]
    fn cmp(&self, other: &Cow<'a, B>) -> Ordering {
        Ord::cmp(&**self, &**other)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, 'b, B: ?Sized, C: ?Sized> PartialEq<Cow<'b, C>> for Cow<'a, B>
    where B: PartialEq<C> + ToOwned,
          C: ToOwned
{
    #[inline]
    fn eq(&self, other: &Cow<'b, C>) -> bool {
        PartialEq::eq(&**self, &**other)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> PartialOrd for Cow<'a, B>
    where B: PartialOrd + ToOwned
{
    #[inline]
    fn partial_cmp(&self, other: &Cow<'a, B>) -> Option<Ordering> {
        PartialOrd::partial_cmp(&**self, &**other)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> fmt::Debug for Cow<'a, B>
    where B: fmt::Debug + ToOwned,
          <B as ToOwned>::Owned: fmt::Debug
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Borrowed(ref b) => fmt::Debug::fmt(b, f),
            Owned(ref o) => fmt::Debug::fmt(o, f),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> fmt::Display for Cow<'a, B>
    where B: fmt::Display + ToOwned,
          <B as ToOwned>::Owned: fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Borrowed(ref b) => fmt::Display::fmt(b, f),
            Owned(ref o) => fmt::Display::fmt(o, f),
        }
    }
}

#[stable(feature = "default", since = "1.11.0")]
impl<'a, B: ?Sized> Default for Cow<'a, B>
    where B: ToOwned,
          <B as ToOwned>::Owned: Default
{
    /// Creates an owned Cow<'a, B> with the default value for the contained owned value.
    fn default() -> Cow<'a, B> {
        Owned(<B as ToOwned>::Owned::default())
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: ?Sized> Hash for Cow<'a, B>
    where B: Hash + ToOwned
{
    #[inline]
    fn hash<H: Hasher>(&self, state: &mut H) {
        Hash::hash(&**self, state)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated)]
impl<'a, T: ?Sized + ToOwned> AsRef<T> for Cow<'a, T> {
    fn as_ref(&self) -> &T {
        self
    }
}

#[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> Add<&'a str> for Cow<'a, str> {
    type Output = Cow<'a, str>;

    #[inline]
    fn add(mut self, rhs: &'a str) -> Self::Output {
        self += rhs;
        self
    }
}

#[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> Add<Cow<'a, str>> for Cow<'a, str> {
    type Output = Cow<'a, str>;

    #[inline]
    fn add(mut self, rhs: Cow<'a, str>) -> Self::Output {
        self += rhs;
        self
    }
}

#[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> AddAssign<&'a str> for Cow<'a, str> {
    fn add_assign(&mut self, rhs: &'a str) {
        if self.is_empty() {
            *self = Cow::Borrowed(rhs)
        } else if rhs.is_empty() {
            return;
        } else {
            if let Cow::Borrowed(lhs) = *self {
                let mut s = String::with_capacity(lhs.len() + rhs.len());
                s.push_str(lhs);
                *self = Cow::Owned(s);
            }
            self.to_mut().push_str(rhs);
        }
    }
}

#[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> AddAssign<Cow<'a, str>> for Cow<'a, str> {
    fn add_assign(&mut self, rhs: Cow<'a, str>) {
        if self.is_empty() {
            *self = rhs
        } else if rhs.is_empty() {
            return;
        } else {
            if let Cow::Borrowed(lhs) = *self {
                let mut s = String::with_capacity(lhs.len() + rhs.len());
                s.push_str(lhs);
                *self = Cow::Owned(s);
            }
            self.to_mut().push_str(&rhs);
        }
    }
}
<|endoftext|>


<commit_before>use cairo;

use std::f64::consts::*;

use cairo::MatrixTrait;
use cssparser::{ParseError as CssParseError, Parser, ParserInput, Token};

use error::*;
use parsers::{optional_comma, Parse, ParseError};

impl Parse for cairo::Matrix {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _: ()) -> Result<cairo::Matrix, AttributeError> {
        let matrix = parse_transform_list(s)?;

        matrix
            .try_invert()
            .map(|_| matrix)
            .map_err(|_| AttributeError::Value("invalid transformation matrix".to_string()))
    }
}

// This parser is for the "transform" attribute in SVG.
// Its operataion and grammar are described here:
// https://www.w3.org/TR/SVG/coords.html#TransformAttribute

fn parse_transform_list(s: &str) -> Result<cairo::Matrix, AttributeError> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let mut matrix = cairo::Matrix::identity();

    loop {
        if parser.is_exhausted() {
            break;
        }

        let m = parse_transform_command(&mut parser)?;
        matrix = cairo::Matrix::multiply(&m, &matrix);

        optional_comma(&mut parser);
    }

    Ok(matrix)
}

fn make_expected_function_error() -> AttributeError {
    AttributeError::from(ParseError::new(
        "expected matrix|translate|scale|rotate|skewX|skewY",
    ))
}

fn parse_transform_command(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    match parser.next()?.clone() {
        Token::Function(ref name) => parse_transform_function(name, parser),

        Token::Ident(ref name) => {
            parser.expect_parenthesis_block()?;
            parse_transform_function(name, parser)
        }

        _ => Err(make_expected_function_error()),
    }
}

fn parse_transform_function(
    name: &str,
    parser: &mut Parser,
) -> Result<cairo::Matrix, AttributeError> {
    match name {
        "matrix" => parse_matrix_args(parser),
        "translate" => parse_translate_args(parser),
        "scale" => parse_scale_args(parser),
        "rotate" => parse_rotate_args(parser),
        "skewX" => parse_skewx_args(parser),
        "skewY" => parse_skewy_args(parser),
        _ => Err(make_expected_function_error()),
    }
}

fn parse_matrix_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser
        .parse_nested_block(|p| {
            let xx = f64::from(p.expect_number()?);
            optional_comma(p);

            let yx = f64::from(p.expect_number()?);
            optional_comma(p);

            let xy = f64::from(p.expect_number()?);
            optional_comma(p);

            let yy = f64::from(p.expect_number()?);
            optional_comma(p);

            let x0 = f64::from(p.expect_number()?);
            optional_comma(p);

            let y0 = f64::from(p.expect_number()?);

            Ok(cairo::Matrix::new(xx, yx, xy, yy, x0, y0))
        })
        .map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_translate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser
        .parse_nested_block(|p| {
            let tx = f64::from(p.expect_number()?);

            let ty = f64::from(
                p.try(|p| -> Result<f32, CssParseError<()>> {
                    optional_comma(p);
                    Ok(p.expect_number()?)
                }).unwrap_or(0.0),
            );

            Ok(cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx, ty))
        })
        .map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_scale_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser
        .parse_nested_block(|p| {
            let x = f64::from(p.expect_number()?);

            let y =
                p.try(|p| -> Result<f32, CssParseError<()>> {
                    optional_comma(p);
                    Ok(p.expect_number()?)
                }).map(f64::from)
                    .unwrap_or(x);

            Ok(cairo::Matrix::new(x, 0.0, 0.0, y, 0.0, 0.0))
        })
        .map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_rotate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser
        .parse_nested_block(|p| {
            let angle = f64::from(p.expect_number()?) * PI / 180.0;
            let (s, c) = angle.sin_cos();

            let (tx, ty) =
                p.try(|p| -> Result<_, CssParseError<()>> {
                    optional_comma(p);
                    let tx = f64::from(p.expect_number()?);

                    optional_comma(p);
                    let ty = f64::from(p.expect_number()?);

                    Ok((tx, ty))
                }).unwrap_or((0.0, 0.0));

            let mut m = cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx, ty);

            m = cairo::Matrix::multiply(&cairo::Matrix::new(c, s, -s, c, 0.0, 0.0), &m);
            m = cairo::Matrix::multiply(&cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
            Ok(m)
        })
        .map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_skewx_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser
        .parse_nested_block(|p| {
            let a = f64::from(p.expect_number()?) * PI / 180.0;
            Ok(cairo::Matrix::new(1.0, 0.0, a.tan(), 1.0, 0.0, 0.0))
        })
        .map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_skewy_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser
        .parse_nested_block(|p| {
            let a = f64::from(p.expect_number()?) * PI / 180.0;
            Ok(cairo::Matrix::new(1.0, a.tan(), 0.0, 1.0, 0.0, 0.0))
        })
        .map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

#[cfg(test)]
fn make_rotation_matrix(angle_degrees: f64, tx: f64, ty: f64) -> cairo::Matrix {
    let angle = angle_degrees * PI / 180.0;

    let mut m = cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx, ty);

    let mut r = cairo::Matrix::identity();
    r.rotate(angle);
    m = cairo::Matrix::multiply(&r, &m);

    m = cairo::Matrix::multiply(&cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
    m
}

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

    fn parse_transform(s: &str) -> Result<cairo::Matrix, AttributeError> {
        cairo::Matrix::parse(s, ())
    }

    #[test]
    fn parses_valid_transform() {
        let t = cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new(10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix(30.0, 10.0, 10.0);

        let a = cairo::Matrix::multiply(&s, &t);
        assert_eq!(
            parse_transform("translate(20, 30), scale (10) rotate (30 10 10)").unwrap(),
            cairo::Matrix::multiply(&r, &a)
        );
    }

    fn assert_parse_error(s: &str) {
        match parse_transform(s) {
            Err(AttributeError::Parse(_)) => {}
            _ => {
                panic!();
            }
        }
    }

    #[test]
    fn syntax_error_yields_parse_error() {
        assert_parse_error("foo");
        assert_parse_error("matrix (1 2 3 4 5)");
        assert_parse_error("translate(1 2 3 4 5)");
        assert_parse_error("translate (1,)");
        assert_parse_error("scale (1,)");
        assert_parse_error("skewX (1,2)");
        assert_parse_error("skewY ()");
        assert_parse_error("skewY");
    }

    #[test]
    fn invalid_transform_yields_value_error() {
        match parse_transform("matrix (0 0 0 0 0 0)") {
            Err(AttributeError::Value(_)) => {}
            _ => {
                panic!();
            }
        }

        match parse_transform("scale (0), translate (10, 10)") {
            Err(AttributeError::Value(_)) => {}
            _ => {
                panic!();
            }
        }

        match parse_transform("scale (0), skewX (90)") {
            Err(AttributeError::Value(_)) => {}
            _ => {
                panic!();
            }
        }
    }

    #[test]
    fn parses_matrix() {
        assert_eq!(
            parse_transform("matrix (1 2 3 4 5 6)").unwrap(),
            cairo::Matrix::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
        );

        assert_eq!(
            parse_transform("matrix(1,2,3,4 5 6)").unwrap(),
            cairo::Matrix::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
        );

        assert_eq!(
            parse_transform("matrix (1,2.25,-3.25e2,4 5 6)").unwrap(),
            cairo::Matrix::new(1.0, 2.25, -325.0, 4.0, 5.0, 6.0)
        );
    }

    #[test]
    fn parses_translate() {
        assert_eq!(
            parse_transform("translate(-1 -2)").unwrap(),
            cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -1.0, -2.0)
        );

        assert_eq!(
            parse_transform("translate(-1, -2)").unwrap(),
            cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -1.0, -2.0)
        );

        assert_eq!(
            parse_transform("translate(-1)").unwrap(),
            cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -1.0, 0.0)
        );
    }

    #[test]
    fn parses_scale() {
        assert_eq!(
            parse_transform("scale (-1)").unwrap(),
            cairo::Matrix::new(-1.0, 0.0, 0.0, -1.0, 0.0, 0.0)
        );

        assert_eq!(
            parse_transform("scale(-1 -2)").unwrap(),
            cairo::Matrix::new(-1.0, 0.0, 0.0, -2.0, 0.0, 0.0)
        );

        assert_eq!(
            parse_transform("scale(-1, -2)").unwrap(),
            cairo::Matrix::new(-1.0, 0.0, 0.0, -2.0, 0.0, 0.0)
        );
    }

    #[test]
    fn parses_rotate() {
        assert_eq!(
            parse_transform("rotate (30)").unwrap(),
            make_rotation_matrix(30.0, 0.0, 0.0)
        );
        assert_eq!(
            parse_transform("rotate (30,-1,-2)").unwrap(),
            make_rotation_matrix(30.0, -1.0, -2.0)
        );
        assert_eq!(
            parse_transform("rotate(30, -1, -2)").unwrap(),
            make_rotation_matrix(30.0, -1.0, -2.0)
        );
    }

    fn make_skew_x_matrix(angle_degrees: f64) -> cairo::Matrix {
        let a = angle_degrees * PI / 180.0;
        cairo::Matrix::new(1.0, 0.0, a.tan(), 1.0, 0.0, 0.0)
    }

    fn make_skew_y_matrix(angle_degrees: f64) -> cairo::Matrix {
        let mut m = make_skew_x_matrix(angle_degrees);
        m.yx = m.xy;
        m.xy = 0.0;
        m
    }

    #[test]
    fn parses_skew_x() {
        assert_eq!(
            parse_transform("skewX (30)").unwrap(),
            make_skew_x_matrix(30.0)
        );
    }

    #[test]
    fn parses_skew_y() {
        assert_eq!(
            parse_transform("skewY (30)").unwrap(),
            make_skew_y_matrix(30.0)
        );
    }

    #[test]
    fn parses_transform_list() {
        let t = cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new(10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix(30.0, 10.0, 10.0);

        assert_eq!(
            parse_transform("scale(10)rotate(30, 10, 10)").unwrap(),
            cairo::Matrix::multiply(&r, &s)
        );

        assert_eq!(
            parse_transform("translate(20, 30), scale (10)").unwrap(),
            cairo::Matrix::multiply(&s, &t)
        );

        let a = cairo::Matrix::multiply(&s, &t);
        assert_eq!(
            parse_transform("translate(20, 30), scale (10) rotate (30 10 10)").unwrap(),
            cairo::Matrix::multiply(&r, &a)
        );
    }

    #[test]
    fn parses_empty() {
        assert_eq!(parse_transform("").unwrap(), cairo::Matrix::identity());
    }
}
<commit_msg>transform.rs: Use a cssparser from the toplevel impl Parse<commit_after>use cairo;

use std::f64::consts::*;

use cairo::MatrixTrait;
use cssparser::{ParseError as CssParseError, Parser, ParserInput, Token};

use error::*;
use parsers::{optional_comma, Parse, ParseError};

impl Parse for cairo::Matrix {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _: ()) -> Result<cairo::Matrix, AttributeError> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        let matrix = parse_transform_list(&mut parser)?;

        matrix
            .try_invert()
            .map(|_| matrix)
            .map_err(|_| AttributeError::Value("invalid transformation matrix".to_string()))
    }
}

// This parser is for the "transform" attribute in SVG.
// Its operataion and grammar are described here:
// https://www.w3.org/TR/SVG/coords.html#TransformAttribute

fn parse_transform_list(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    let mut matrix = cairo::Matrix::identity();

    loop {
        if parser.is_exhausted() {
            break;
        }

        let m = parse_transform_command(parser)?;
        matrix = cairo::Matrix::multiply(&m, &matrix);

        optional_comma(parser);
    }

    Ok(matrix)
}

fn make_expected_function_error() -> AttributeError {
    AttributeError::from(ParseError::new(
        "expected matrix|translate|scale|rotate|skewX|skewY",
    ))
}

fn parse_transform_command(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    match parser.next()?.clone() {
        Token::Function(ref name) => parse_transform_function(name, parser),

        Token::Ident(ref name) => {
            parser.expect_parenthesis_block()?;
            parse_transform_function(name, parser)
        }

        _ => Err(make_expected_function_error()),
    }
}

fn parse_transform_function(
    name: &str,
    parser: &mut Parser,
) -> Result<cairo::Matrix, AttributeError> {
    match name {
        "matrix" => parse_matrix_args(parser),
        "translate" => parse_translate_args(parser),
        "scale" => parse_scale_args(parser),
        "rotate" => parse_rotate_args(parser),
        "skewX" => parse_skewx_args(parser),
        "skewY" => parse_skewy_args(parser),
        _ => Err(make_expected_function_error()),
    }
}

fn parse_matrix_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser
        .parse_nested_block(|p| {
            let xx = f64::from(p.expect_number()?);
            optional_comma(p);

            let yx = f64::from(p.expect_number()?);
            optional_comma(p);

            let xy = f64::from(p.expect_number()?);
            optional_comma(p);

            let yy = f64::from(p.expect_number()?);
            optional_comma(p);

            let x0 = f64::from(p.expect_number()?);
            optional_comma(p);

            let y0 = f64::from(p.expect_number()?);

            Ok(cairo::Matrix::new(xx, yx, xy, yy, x0, y0))
        })
        .map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_translate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser
        .parse_nested_block(|p| {
            let tx = f64::from(p.expect_number()?);

            let ty = f64::from(
                p.try(|p| -> Result<f32, CssParseError<()>> {
                    optional_comma(p);
                    Ok(p.expect_number()?)
                }).unwrap_or(0.0),
            );

            Ok(cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx, ty))
        })
        .map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_scale_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser
        .parse_nested_block(|p| {
            let x = f64::from(p.expect_number()?);

            let y =
                p.try(|p| -> Result<f32, CssParseError<()>> {
                    optional_comma(p);
                    Ok(p.expect_number()?)
                }).map(f64::from)
                    .unwrap_or(x);

            Ok(cairo::Matrix::new(x, 0.0, 0.0, y, 0.0, 0.0))
        })
        .map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_rotate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser
        .parse_nested_block(|p| {
            let angle = f64::from(p.expect_number()?) * PI / 180.0;
            let (s, c) = angle.sin_cos();

            let (tx, ty) =
                p.try(|p| -> Result<_, CssParseError<()>> {
                    optional_comma(p);
                    let tx = f64::from(p.expect_number()?);

                    optional_comma(p);
                    let ty = f64::from(p.expect_number()?);

                    Ok((tx, ty))
                }).unwrap_or((0.0, 0.0));

            let mut m = cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx, ty);

            m = cairo::Matrix::multiply(&cairo::Matrix::new(c, s, -s, c, 0.0, 0.0), &m);
            m = cairo::Matrix::multiply(&cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
            Ok(m)
        })
        .map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_skewx_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser
        .parse_nested_block(|p| {
            let a = f64::from(p.expect_number()?) * PI / 180.0;
            Ok(cairo::Matrix::new(1.0, 0.0, a.tan(), 1.0, 0.0, 0.0))
        })
        .map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_skewy_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser
        .parse_nested_block(|p| {
            let a = f64::from(p.expect_number()?) * PI / 180.0;
            Ok(cairo::Matrix::new(1.0, a.tan(), 0.0, 1.0, 0.0, 0.0))
        })
        .map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

#[cfg(test)]
fn make_rotation_matrix(angle_degrees: f64, tx: f64, ty: f64) -> cairo::Matrix {
    let angle = angle_degrees * PI / 180.0;

    let mut m = cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx, ty);

    let mut r = cairo::Matrix::identity();
    r.rotate(angle);
    m = cairo::Matrix::multiply(&r, &m);

    m = cairo::Matrix::multiply(&cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
    m
}

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

    fn parse_transform(s: &str) -> Result<cairo::Matrix, AttributeError> {
        cairo::Matrix::parse(s, ())
    }

    #[test]
    fn parses_valid_transform() {
        let t = cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new(10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix(30.0, 10.0, 10.0);

        let a = cairo::Matrix::multiply(&s, &t);
        assert_eq!(
            parse_transform("translate(20, 30), scale (10) rotate (30 10 10)").unwrap(),
            cairo::Matrix::multiply(&r, &a)
        );
    }

    fn assert_parse_error(s: &str) {
        match parse_transform(s) {
            Err(AttributeError::Parse(_)) => {}
            _ => {
                panic!();
            }
        }
    }

    #[test]
    fn syntax_error_yields_parse_error() {
        assert_parse_error("foo");
        assert_parse_error("matrix (1 2 3 4 5)");
        assert_parse_error("translate(1 2 3 4 5)");
        assert_parse_error("translate (1,)");
        assert_parse_error("scale (1,)");
        assert_parse_error("skewX (1,2)");
        assert_parse_error("skewY ()");
        assert_parse_error("skewY");
    }

    #[test]
    fn invalid_transform_yields_value_error() {
        match parse_transform("matrix (0 0 0 0 0 0)") {
            Err(AttributeError::Value(_)) => {}
            _ => {
                panic!();
            }
        }

        match parse_transform("scale (0), translate (10, 10)") {
            Err(AttributeError::Value(_)) => {}
            _ => {
                panic!();
            }
        }

        match parse_transform("scale (0), skewX (90)") {
            Err(AttributeError::Value(_)) => {}
            _ => {
                panic!();
            }
        }
    }

    #[test]
    fn parses_matrix() {
        assert_eq!(
            parse_transform("matrix (1 2 3 4 5 6)").unwrap(),
            cairo::Matrix::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
        );

        assert_eq!(
            parse_transform("matrix(1,2,3,4 5 6)").unwrap(),
            cairo::Matrix::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
        );

        assert_eq!(
            parse_transform("matrix (1,2.25,-3.25e2,4 5 6)").unwrap(),
            cairo::Matrix::new(1.0, 2.25, -325.0, 4.0, 5.0, 6.0)
        );
    }

    #[test]
    fn parses_translate() {
        assert_eq!(
            parse_transform("translate(-1 -2)").unwrap(),
            cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -1.0, -2.0)
        );

        assert_eq!(
            parse_transform("translate(-1, -2)").unwrap(),
            cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -1.0, -2.0)
        );

        assert_eq!(
            parse_transform("translate(-1)").unwrap(),
            cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -1.0, 0.0)
        );
    }

    #[test]
    fn parses_scale() {
        assert_eq!(
            parse_transform("scale (-1)").unwrap(),
            cairo::Matrix::new(-1.0, 0.0, 0.0, -1.0, 0.0, 0.0)
        );

        assert_eq!(
            parse_transform("scale(-1 -2)").unwrap(),
            cairo::Matrix::new(-1.0, 0.0, 0.0, -2.0, 0.0, 0.0)
        );

        assert_eq!(
            parse_transform("scale(-1, -2)").unwrap(),
            cairo::Matrix::new(-1.0, 0.0, 0.0, -2.0, 0.0, 0.0)
        );
    }

    #[test]
    fn parses_rotate() {
        assert_eq!(
            parse_transform("rotate (30)").unwrap(),
            make_rotation_matrix(30.0, 0.0, 0.0)
        );
        assert_eq!(
            parse_transform("rotate (30,-1,-2)").unwrap(),
            make_rotation_matrix(30.0, -1.0, -2.0)
        );
        assert_eq!(
            parse_transform("rotate(30, -1, -2)").unwrap(),
            make_rotation_matrix(30.0, -1.0, -2.0)
        );
    }

    fn make_skew_x_matrix(angle_degrees: f64) -> cairo::Matrix {
        let a = angle_degrees * PI / 180.0;
        cairo::Matrix::new(1.0, 0.0, a.tan(), 1.0, 0.0, 0.0)
    }

    fn make_skew_y_matrix(angle_degrees: f64) -> cairo::Matrix {
        let mut m = make_skew_x_matrix(angle_degrees);
        m.yx = m.xy;
        m.xy = 0.0;
        m
    }

    #[test]
    fn parses_skew_x() {
        assert_eq!(
            parse_transform("skewX (30)").unwrap(),
            make_skew_x_matrix(30.0)
        );
    }

    #[test]
    fn parses_skew_y() {
        assert_eq!(
            parse_transform("skewY (30)").unwrap(),
            make_skew_y_matrix(30.0)
        );
    }

    #[test]
    fn parses_transform_list() {
        let t = cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new(10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix(30.0, 10.0, 10.0);

        assert_eq!(
            parse_transform("scale(10)rotate(30, 10, 10)").unwrap(),
            cairo::Matrix::multiply(&r, &s)
        );

        assert_eq!(
            parse_transform("translate(20, 30), scale (10)").unwrap(),
            cairo::Matrix::multiply(&s, &t)
        );

        let a = cairo::Matrix::multiply(&s, &t);
        assert_eq!(
            parse_transform("translate(20, 30), scale (10) rotate (30 10 10)").unwrap(),
            cairo::Matrix::multiply(&r, &a)
        );
    }

    #[test]
    fn parses_empty() {
        assert_eq!(parse_transform("").unwrap(), cairo::Matrix::identity());
    }
}
<|endoftext|>


<commit_before>use containers::reference::Ref;
use containers::vector::Vector;
use memory::page::Page;

pub struct HashSet<T> {
    _map: Option<Ref<HashMap<T>>>,
}

impl<T> HashSet<T> {
    pub fn from_raw_array(_page: *mut Page, _array: *const T, length: usize) -> HashSet<T> {
        HashSet {
            _map: Some(Ref::new(_page, HashMap::new(_page, length))),
        }
    }
}

struct HashMap<T> {
    _buckets: Vector<i32>,
    _slots: Vector<Slot<T>>,
}

impl<T> HashMap<T> {
    pub fn new(_page: *mut Page, size: usize) -> HashMap<T> {
        HashMap {
            _buckets: Vector::new(_page, size),
            _slots: Vector::new(_page, size),
        }
    }
}

struct Slot<T> {
    _hash_code: i32,
    _next: usize,
    _value: T,
}
<commit_msg>prime stuff<commit_after>use containers::reference::Ref;
use containers::vector::Vector;
use memory::page::Page;

pub struct HashSet<T> {
    _map: Option<Ref<HashMap<T>>>,
}

impl<T> HashSet<T> {
    pub fn from_raw_array(_page: *mut Page, _array: *const T, length: usize) -> HashSet<T> {
        HashSet {
            _map: Some(Ref::new(_page, HashMap::new(_page, length))),
        }
    }
}

struct HashMap<T> {
    _buckets: Vector<i32>,
    _slots: Vector<Slot<T>>,
}

impl<T> HashMap<T> {
    pub fn new(_page: *mut Page, size: usize) -> HashMap<T> {
        let hash_size = HashPrimeHelper::get_prime(size);
        HashMap {
            _buckets: Vector::new(_page, hash_size),
            _slots: Vector::new(_page, hash_size),
        }
    }
}

struct Slot<T> {
    _hash_code: usize,
    _next: usize,
    _value: T,
}

struct HashPrimeHelper {}

// https://planetmath.org/goodhashtableprimes
static HASH_PRIMES: &'static [usize] = &[
    3, 5, 11, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613,
    393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611,
    402653189, 805306457, 1610612741,
];

impl HashPrimeHelper {
    pub fn get_prime(size: usize) -> usize {
        for i in HASH_PRIMES {
            if *i > size {
                return *i;
            }
        }

        let mut i = size | 1;
        while i < std::usize::MAX {
            if HashPrimeHelper::is_prime(i) {
                return i;
            }
            i += 2;
        }
        size
    }

    fn is_prime(candidate: usize) -> bool {
        if (candidate & 1) != 0 {
            let limit = (candidate as f64).sqrt() as usize;
            let mut divisor: usize = 3;
            while divisor <= limit {
                divisor += 2;
                if (candidate % divisor) == 0 {
                    return false;
                }
            }
            return true;
        }

        candidate == 2
    }
}
<|endoftext|>


<commit_before>#![no_std]

use libtock_platform::Syscalls;

/// The low-level debug API provides tools to diagnose userspace issues that
/// make normal debugging workflows (e.g. printing to the console) difficult.
///
/// It allows libraries to print alert codes and apps to print numeric
/// information using only the command system call.
///
/// # Example
/// ```ignore
/// use libtock_runtime::TockSyscalls;
///
/// // Uses the real syscall implementation.
/// type LowLevelDebug = libtock_low_level_debug::LowLevelDebug<TockSyscalls>;
///
/// // Prints 0x45 and the app which called it.
/// LowLevelDebug::print_1(0x45);
/// ```
pub struct LowLevelDebug<S: Syscalls>(S);

impl<S: Syscalls> LowLevelDebug<S> {
    /// Run a check against the low-level debug capsule to ensure it is working.
    ///
    /// Returns `true` if the driver was present and functional.
    #[inline(always)]
    pub fn driver_check() -> bool {
        S::command(DRIVER_ID, DRIVER_CHECK, 0, 0).is_success()
    }

    /// Print one of the predefined alerts in [`AlertCode`].
    #[inline(always)]
    pub fn print_alert_code(code: AlertCode) {
        let _ = S::command(DRIVER_ID, PRINT_ALERT_CODE, code as u32, 0);
    }

    /// Print a single number. The number will be printed in hexadecimal.
    ///
    /// In general, this should only be added temporarily for debugging and
    /// should not be called by released library code.
    #[inline(always)]
    pub fn print_1(x: u32) {
        let _ = S::command(DRIVER_ID, PRINT_1, x, 0);
    }

    /// Print two numbers. The numbers will be printed in hexadecimal.
    ///
    /// Like `print_1`, this is intended for temporary debugging and should not
    /// be called by released library code. If you want to print multiple
    /// values, it is often useful to use the first argument to indicate what
    /// value is being printed.
    #[inline(always)]
    pub fn print_2(x: u32, y: u32) {
        let _ = S::command(DRIVER_ID, PRINT_2, x, y);
    }
}

/// A predefined alert code, for use with [`LowLevelDebug::print_alert_code`].
pub enum AlertCode {
    /// Application panic (e.g. `panic!()` called in Rust code).
    Panic = 0x01,

    /// A statically-linked app was not installed in the correct location in
    /// flash.
    WrongLocation = 0x02,
}

const DRIVER_ID: u32 = 8;

// Command IDs
const DRIVER_CHECK: u32 = 0;
const PRINT_ALERT_CODE: u32 = 1;
const PRINT_1: u32 = 2;
const PRINT_2: u32 = 3;

#[cfg(test)]
mod tests;
<commit_msg>Clarify "driver_check" doc since it only checks for presence<commit_after>#![no_std]

use libtock_platform::Syscalls;

/// The low-level debug API provides tools to diagnose userspace issues that
/// make normal debugging workflows (e.g. printing to the console) difficult.
///
/// It allows libraries to print alert codes and apps to print numeric
/// information using only the command system call.
///
/// # Example
/// ```ignore
/// use libtock_runtime::TockSyscalls;
///
/// // Uses the real syscall implementation.
/// type LowLevelDebug = libtock_low_level_debug::LowLevelDebug<TockSyscalls>;
///
/// // Prints 0x45 and the app which called it.
/// LowLevelDebug::print_1(0x45);
/// ```
pub struct LowLevelDebug<S: Syscalls>(S);

impl<S: Syscalls> LowLevelDebug<S> {
    /// Run a check against the low-level debug capsule to ensure it is present.
    ///
    /// Returns `true` if the driver was present. This does not necessarily mean
    /// that the driver is working, as it may still fail to allocate grant
    /// memory.
    #[inline(always)]
    pub fn driver_check() -> bool {
        S::command(DRIVER_ID, DRIVER_CHECK, 0, 0).is_success()
    }

    /// Print one of the predefined alerts in [`AlertCode`].
    #[inline(always)]
    pub fn print_alert_code(code: AlertCode) {
        let _ = S::command(DRIVER_ID, PRINT_ALERT_CODE, code as u32, 0);
    }

    /// Print a single number. The number will be printed in hexadecimal.
    ///
    /// In general, this should only be added temporarily for debugging and
    /// should not be called by released library code.
    #[inline(always)]
    pub fn print_1(x: u32) {
        let _ = S::command(DRIVER_ID, PRINT_1, x, 0);
    }

    /// Print two numbers. The numbers will be printed in hexadecimal.
    ///
    /// Like `print_1`, this is intended for temporary debugging and should not
    /// be called by released library code. If you want to print multiple
    /// values, it is often useful to use the first argument to indicate what
    /// value is being printed.
    #[inline(always)]
    pub fn print_2(x: u32, y: u32) {
        let _ = S::command(DRIVER_ID, PRINT_2, x, y);
    }
}

/// A predefined alert code, for use with [`LowLevelDebug::print_alert_code`].
pub enum AlertCode {
    /// Application panic (e.g. `panic!()` called in Rust code).
    Panic = 0x01,

    /// A statically-linked app was not installed in the correct location in
    /// flash.
    WrongLocation = 0x02,
}

const DRIVER_ID: u32 = 8;

// Command IDs
const DRIVER_CHECK: u32 = 0;
const PRINT_ALERT_CODE: u32 = 1;
const PRINT_1: u32 = 2;
const PRINT_2: u32 = 3;

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;

mod cli;
mod input;
mod output;
mod size;
mod surface;

use cssparser::Color;
use librsvg::rsvg_convert_only::LegacySize;
use librsvg::{CairoRenderer, Loader, RenderingError, SvgHandle};

use crate::cli::Args;
use crate::output::Stream;
use crate::size::Size;
use crate::surface::Surface;

#[macro_export]
macro_rules! exit {
    () => (exit!("Error"));
    ($($arg:tt)*) => ({
        std::eprintln!("{}", std::format_args!($($arg)*));
        std::process::exit(1);
    })
}

fn size_limit_exceeded() -> ! {
    exit!(
        "The resulting image would be larger than 32767 pixels on either dimension.\n\
           Librsvg currently cannot render to images bigger than that.\n\
           Please specify a smaller size."
    );
}

fn load_stylesheet(args: &Args) -> std::io::Result<Option<String>> {
    match args.stylesheet {
        Some(ref filename) => std::fs::read_to_string(filename).map(Some),
        None => Ok(None),
    }
}

fn get_size(
    _handle: &SvgHandle,
    renderer: &CairoRenderer,
    args: &Args,
) -> Result<Size, RenderingError> {
    renderer
        .legacy_document_size_in_pixels()
        .map(|(w, h)| Size::new(w, h).scale(args.zoom()))
}

fn main() {
    let args = Args::new().unwrap_or_else(|e| e.exit());

    let stylesheet =
        load_stylesheet(&args).unwrap_or_else(|e| exit!("Error reading stylesheet: {}", e));

    let mut target = None;

    for input in args.input() {
        let mut handle = Loader::new()
            .with_unlimited_size(args.unlimited)
            .keep_image_data(args.keep_image_data)
            .read_stream(input.stream(), input.file(), None::<&gio::Cancellable>)
            .unwrap_or_else(|e| exit!("Error reading SVG {}: {}", input, e));

        if let Some(ref css) = stylesheet {
            handle
                .set_stylesheet(&css)
                .unwrap_or_else(|e| exit!("Error applying stylesheet: {}", e));
        }

        let renderer = CairoRenderer::new(&handle).with_dpi(args.dpi.x, args.dpi.y);

        if target.is_none() {
            let size = get_size(&handle, &renderer, &args)
                .unwrap_or_else(|e| exit!("Error rendering SVG {}: {}", input, e));

            target = {
                let output = Stream::new(args.output())
                    .unwrap_or_else(|e| exit!("Error opening output: {}", e));

                match Surface::new(args.format, size, output) {
                    Ok(surface) => Some(surface),
                    Err(cairo::Status::InvalidSize) => size_limit_exceeded(),
                    Err(e) => exit!("Error creating output surface: {}", e),
                }
            };
        }

        if let Some(ref surface) = target {
            let cr = cairo::Context::new(surface);

            if let Some(Color::RGBA(rgba)) = args.background_color {
                cr.set_source_rgba(
                    rgba.red_f32().into(),
                    rgba.green_f32().into(),
                    rgba.blue_f32().into(),
                    rgba.alpha_f32().into(),
                );
            }

            let scale = args.zoom();
            cr.scale(scale.x, scale.y);

            surface
                .render(&renderer, &cr, args.export_id())
                .unwrap_or_else(|e| match e {
                    RenderingError::InvalidId(_) => exit!(
                        "File {} does not have an object with id \"{}\")",
                        input,
                        args.export_id().unwrap()
                    ),
                    _ => exit!("Error rendering SVG {}: {}", input, e),
                });
        }
    }

    if let Some(ref mut surface) = target {
        surface
            .finish()
            .unwrap_or_else(|e| exit!("Error saving output: {}", e));
    }
}
<commit_msg>rsvg_convert: fix empty_svg_yields_error test case<commit_after>#[macro_use]
extern crate clap;

mod cli;
mod input;
mod output;
mod size;
mod surface;

use cssparser::Color;
use librsvg::rsvg_convert_only::LegacySize;
use librsvg::{CairoRenderer, Loader, RenderingError, SvgHandle};

use crate::cli::Args;
use crate::output::Stream;
use crate::size::Size;
use crate::surface::Surface;

#[macro_export]
macro_rules! exit {
    () => (exit!("Error"));
    ($($arg:tt)*) => ({
        std::eprintln!("{}", std::format_args!($($arg)*));
        std::process::exit(1);
    })
}

fn size_limit_exceeded() -> ! {
    exit!(
        "The resulting image would be larger than 32767 pixels on either dimension.\n\
           Librsvg currently cannot render to images bigger than that.\n\
           Please specify a smaller size."
    );
}

fn load_stylesheet(args: &Args) -> std::io::Result<Option<String>> {
    match args.stylesheet {
        Some(ref filename) => std::fs::read_to_string(filename).map(Some),
        None => Ok(None),
    }
}

fn get_size(
    _handle: &SvgHandle,
    renderer: &CairoRenderer,
    args: &Args,
) -> Result<Size, RenderingError> {
    renderer
        .legacy_document_size_in_pixels()
        .map(|(w, h)| Size::new(w, h).scale(args.zoom()))
}

fn main() {
    let args = Args::new().unwrap_or_else(|e| e.exit());

    let stylesheet =
        load_stylesheet(&args).unwrap_or_else(|e| exit!("Error reading stylesheet: {}", e));

    let mut target = None;

    for input in args.input() {
        let mut handle = Loader::new()
            .with_unlimited_size(args.unlimited)
            .keep_image_data(args.keep_image_data)
            .read_stream(input.stream(), input.file(), None::<&gio::Cancellable>)
            .unwrap_or_else(|e| exit!("Error reading SVG {}: {}", input, e));

        if let Some(ref css) = stylesheet {
            handle
                .set_stylesheet(&css)
                .unwrap_or_else(|e| exit!("Error applying stylesheet: {}", e));
        }

        let renderer = CairoRenderer::new(&handle).with_dpi(args.dpi.x, args.dpi.y);

        if target.is_none() {
            let size = get_size(&handle, &renderer, &args)
                .unwrap_or_else(|e| exit!("Error rendering SVG {}: {}", input, e));

            if size.w == 0.0 && size.h == 0.0 {
                exit!("The SVG {} has no dimensions", input);
            }

            target = {
                let output = Stream::new(args.output())
                    .unwrap_or_else(|e| exit!("Error opening output: {}", e));

                match Surface::new(args.format, size, output) {
                    Ok(surface) => Some(surface),
                    Err(cairo::Status::InvalidSize) => size_limit_exceeded(),
                    Err(e) => exit!("Error creating output surface: {}", e),
                }
            };
        }

        if let Some(ref surface) = target {
            let cr = cairo::Context::new(surface);

            if let Some(Color::RGBA(rgba)) = args.background_color {
                cr.set_source_rgba(
                    rgba.red_f32().into(),
                    rgba.green_f32().into(),
                    rgba.blue_f32().into(),
                    rgba.alpha_f32().into(),
                );
            }

            let scale = args.zoom();
            cr.scale(scale.x, scale.y);

            surface
                .render(&renderer, &cr, args.export_id())
                .unwrap_or_else(|e| match e {
                    RenderingError::InvalidId(_) => exit!(
                        "File {} does not have an object with id \"{}\")",
                        input,
                        args.export_id().unwrap()
                    ),
                    _ => exit!("Error rendering SVG {}: {}", input, e),
                });
        }
    }

    if let Some(ref mut surface) = target {
        surface
            .finish()
            .unwrap_or_else(|e| exit!("Error saving output: {}", e));
    }
}
<|endoftext|>


<commit_before>extern crate prng;
use prng::{PRNG, FloatPointsIterator};

pub fn estimate(points: f64) -> f64 {
  let mut inside: f64 = 0.;
  let randseed = 1.;

  let mut generator = PRNG::new(randseed);

  for _ in 0..points as usize {
    let x = generator.next_float();
    let y = generator.next_float();

    if (x * x) + (y * y) <= 1. {
      inside += 1.;
    }
  }

  inside / points * 4.
}

pub fn functional_estimate(points: f64) -> f64 {
  let randseed = 1.;

  let inside = FloatPointsIterator::new(randseed)
    .take(points as usize)
    .map(|(x,y)| (x * x) + (y * y))
    .filter(|&point| point <= 1.)
    .fold(0., |acc, _| acc + 1.);

  inside / points * 4.
}

#[cfg(not(target_os = "emscripten"))]
pub mod ws {
  extern crate rayon;
  use self::rayon::prelude::*;
  use prng::{FloatPointsIterator};
  use std;

  pub fn estimate(points: f64) -> f64 {
    let chunks = 16;
    let total = (1..chunks + 1)
      .into_par_iter()
      .map(|chunk| {
        FloatPointsIterator::new(chunk as f64)
          .take((points / chunks as f64) as usize)
          .map(|(x,y)| (x * x) + (y * y))
          .filter(|&point| point <= 1.)
          .fold(0., |acc, _| acc + 1.)
      })
      .map(|inside| inside / points * 4.)
      .reduce_with(std::ops::Add::add);

    total.unwrap()
  }
}

#[cfg(test)]
mod estimate_tests {
  use estimate;

  #[test]
  fn pi_estimation_test() {
    let pi = estimate(1e6);
    assert!((pi > 2.64) && (pi < 3.64), "estimated pi value is more than 0.5 off");
  }
}

#[cfg(test)]
#[cfg(not(target_os = "emscripten"))]
mod work_stealing_estimation {
  use ws::estimate;
  #[test]
  fn ws_estimation_test() {
      let pi = estimate(1e6);
      assert!((pi > 2.64) && (pi < 3.64), "estimated pi value is more than 0.5 off");
  }
}
<commit_msg>Minor change in test's cfgs order<commit_after>extern crate prng;
use prng::{PRNG, FloatPointsIterator};

pub fn estimate(points: f64) -> f64 {
  let mut inside: f64 = 0.;
  let randseed = 1.;

  let mut generator = PRNG::new(randseed);

  for _ in 0..points as usize {
    let x = generator.next_float();
    let y = generator.next_float();

    if (x * x) + (y * y) <= 1. {
      inside += 1.;
    }
  }

  inside / points * 4.
}

pub fn functional_estimate(points: f64) -> f64 {
  let randseed = 1.;

  let inside = FloatPointsIterator::new(randseed)
    .take(points as usize)
    .map(|(x,y)| (x * x) + (y * y))
    .filter(|&point| point <= 1.)
    .fold(0., |acc, _| acc + 1.);

  inside / points * 4.
}

#[cfg(not(target_os = "emscripten"))]
pub mod ws {
  extern crate rayon;
  use self::rayon::prelude::*;
  use prng::{FloatPointsIterator};
  use std;

  pub fn estimate(points: f64) -> f64 {
    let chunks = 16;
    let total = (1..chunks + 1)
      .into_par_iter()
      .map(|chunk| {
        FloatPointsIterator::new(chunk as f64)
          .take((points / chunks as f64) as usize)
          .map(|(x,y)| (x * x) + (y * y))
          .filter(|&point| point <= 1.)
          .fold(0., |acc, _| acc + 1.)
      })
      .map(|inside| inside / points * 4.)
      .reduce_with(std::ops::Add::add);

    total.unwrap()
  }
}

#[cfg(test)]
mod estimate_tests {
  use estimate;

  #[test]
  fn pi_estimation_test() {
    let pi = estimate(1e6);
    assert!((pi > 2.64) && (pi < 3.64), "estimated pi value is more than 0.5 off");
  }
}

#[cfg(not(target_os = "emscripten"))]
#[cfg(test)]
mod work_stealing_estimation {
  use ws::estimate;
  #[test]
  fn ws_estimation_test() {
      let pi = estimate(1e6);
      assert!((pi > 2.64) && (pi < 3.64), "estimated pi value is more than 0.5 off");
  }
}
<|endoftext|>


<commit_before>use super::{Document,Root,RootChild,Element};

struct Parser;

struct ParsedAttribute<'a> {
    name: &'a str,
    value: &'a str,
}

impl Parser {
    fn new() -> Parser {
        Parser
    }

    fn parse_preamble<'a>(&self, xml: &'a str) -> &'a str {
        // Parse the preamble
        let idx = xml.find_str("?>").expect("No preamble end");
        let end_of_preamble = idx + "?>".len();
        xml.slice_from(end_of_preamble)
    }

    fn optional_space<'a>(&self, xml: &'a str) -> &'a str {
        match xml.slice_space() {
            Some((_, next_xml)) => next_xml,
            None => xml,
        }
    }

    fn parse_attribute<'a>(&self, xml: &'a str) -> Option<(ParsedAttribute<'a>, &'a str)> {
        let (name, xml) = match xml.slice_name() {
            Some(x) => x,
            None => return None,
        };

        let xml = self.optional_space(xml);
        let (_, xml) = xml.slice_literal("=").expect("No equal sign");
        let xml = self.optional_space(xml);

        let (_, xml) = xml.slice_literal("'").expect("No quote"); // single and double quotes
        let (value, xml) = xml.slice_until("'").expect("No value"); // single and double quotes
        let (_, xml) = xml.slice_literal("'").expect("No quote"); // single and double quotes

        Some((ParsedAttribute{name: name, value: value}, xml))
    }

    fn parse_element<'a>(&self, doc: Document, xml: &'a str) -> (Element, &'a str) {
        let (_, after_start_brace) = xml.slice_literal("<").expect("no start brace");

        let (name, after_name) = after_start_brace.slice_name().expect("failed to parse a name!");

        let (_, after_space) = after_name.slice_space().expect("no space after name");

        let mut attrs = Vec::new();
        let after_attr = match self.parse_attribute(after_space) {
            None => after_space,
            Some((attr, after_attr)) => {
                attrs.push(attr);
                after_attr
            },
        };

        let after_attr = self.optional_space(after_attr);

        let (_, after_end_brace) = after_attr.slice_literal("/>").expect("no end brace");

        let e = doc.new_element(name.to_string());
        for attr in attrs.iter() {
            e.set_attribute(attr.name.to_string(), attr.value.to_string());
        }

        (e, after_end_brace)
    }

    fn parse(&self, xml: &str) -> Document {
        let doc = Document::new();

        let after_preamble = self.parse_preamble(xml);

        let (element, _tail) = self.parse_element(doc.clone(), after_preamble);
        doc.root().append_child(element);

        doc
    }
}

trait XmlStr<'a> {
    fn slice_at(&self, position: uint) -> (&'a str, &'a str);
    fn slice_until(&self, s: &str) -> Option<(&'a str, &'a str)>;
    fn slice_literal(&self, expected: &str) -> Option<(&'a str, &'a str)>;
    fn slice_start_rest(&self, is_first: |char| -> bool, is_rest: |char| -> bool) -> Option<(&'a str, &'a str)>;
    fn slice_name(&self) -> Option<(&'a str, &'a str)>;
    fn slice_space(&self) -> Option<(&'a str, &'a str)>;
}

impl<'a> XmlStr<'a> for &'a str {
    fn slice_at(&self, position: uint) -> (&'a str, &'a str) {
        (self.slice_to(position), self.slice_from(position))
    }

    fn slice_until(&self, s: &str) -> Option<(&'a str, &'a str)> {
        match self.find_str(s) {
            Some(position) => Some(self.slice_at(position)),
            None => None
        }
    }

    fn slice_literal(&self, expected: &str) -> Option<(&'a str, &'a str)> {
        if self.starts_with(expected) {
            Some(self.slice_at(expected.len()))
        } else {
            None
        }
    }

    fn slice_start_rest(&self,
                        is_first: |char| -> bool,
                        is_rest: |char| -> bool)
                        -> Option<(&'a str, &'a str)>
    {
        let mut positions = self.char_indices();

        match positions.next() {
            Some((_, c)) if is_first(c) => (),
            Some((_, _)) => return None,
            None => return None,
        };

        let mut positions = positions.skip_while(|&(_, c)| is_rest(c));
        match positions.next() {
            Some((offset, _)) => Some(self.slice_at(offset)),
            None => Some((self.clone(), "")),
        }
    }

    fn slice_name(&self) -> Option<(&'a str, &'a str)> {
        self.slice_start_rest(|c| c.is_name_start_char(), |c| c.is_name_char())
    }

    fn slice_space(&self) -> Option<(&'a str, &'a str)> {
        self.slice_start_rest(|c| c.is_space_char(), |c| c.is_space_char())
    }
}

trait XmlChar {
    fn is_name_start_char(&self) -> bool;
    fn is_name_char(&self) -> bool;
    fn is_space_char(&self) -> bool;
}

impl XmlChar for char {
    fn is_name_start_char(&self) -> bool {
        match *self {
            ':'                        |
            'A'..'Z'                   |
            '_'                        |
            'a'..'z'                   |
            '\U000000C0'..'\U000000D6' |
            '\U000000D8'..'\U000000F6' |
            '\U000000F8'..'\U000002FF' |
            '\U00000370'..'\U0000037D' |
            '\U0000037F'..'\U00001FFF' |
            '\U0000200C'..'\U0000200D' |
            '\U00002070'..'\U0000218F' |
            '\U00002C00'..'\U00002FEF' |
            '\U00003001'..'\U0000D7FF' |
            '\U0000F900'..'\U0000FDCF' |
            '\U0000FDF0'..'\U0000FFFD' |
            '\U00010000'..'\U000EFFFF' => true,
            _ => false,
        }
    }

    fn is_name_char(&self) -> bool {
        if self.is_name_start_char() { return true; }
        match *self {
            '-'                |
            '.'                |
            '0'..'9'           |
            '\u00B7'           |
            '\u0300'..'\u036F' |
            '\u203F'..'\u2040' => true,
            _ => false
        }
    }

    fn is_space_char(&self) -> bool {
        match *self {
            '\x20' |
            '\x09' |
            '\x0D' |
            '\x0A' => true,
            _ => false,
        }
    }
}

trait Hax {
    fn first_child(&self) -> Option<RootChild>;
}

impl Hax for Root {
    fn first_child(&self) -> Option<RootChild> {
        self.children().remove(0)
    }
}

#[test]
fn parses_a_document_with_a_single_element() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello />");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.name().as_slice(), "hello");
}

#[test]
fn parses_an_element_with_an_attribute() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello scope='world'/>");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.get_attribute("scope").unwrap().as_slice(), "world");
}
<commit_msg>Single or double quoted attribute values<commit_after>use super::{Document,Root,RootChild,Element};

struct Parser;

struct ParsedAttribute<'a> {
    name: &'a str,
    value: &'a str,
}

impl Parser {
    fn new() -> Parser {
        Parser
    }

    fn parse_preamble<'a>(&self, xml: &'a str) -> &'a str {
        // Parse the preamble
        let idx = xml.find_str("?>").expect("No preamble end");
        let end_of_preamble = idx + "?>".len();
        xml.slice_from(end_of_preamble)
    }

    fn optional_space<'a>(&self, xml: &'a str) -> &'a str {
        match xml.slice_space() {
            Some((_, next_xml)) => next_xml,
            None => xml,
        }
    }

    fn parse_attribute_value_quote<'a>(&self, xml: &'a str, quote: &str) -> Option<(&'a str, &'a str)> {
        let (_, xml) = match xml.slice_literal(quote) {
            None => return None,
            Some(x) => x,
        };
        // TODO: don't consume & or <
        let (value, xml) = xml.slice_until(quote).expect("No value");
        let (_, xml) = xml.slice_literal(quote).expect("No quote");

        Some((value, xml))
    }

    fn parse_attribute<'a>(&self, xml: &'a str) -> Option<(ParsedAttribute<'a>, &'a str)> {
        let (name, xml) = match xml.slice_name() {
            Some(x) => x,
            None => return None,
        };

        let xml = self.optional_space(xml);
        let (_, xml) = xml.slice_literal("=").expect("No equal sign");
        let xml = self.optional_space(xml);

        // Pattern: alternate
        let (value, xml) = match self.parse_attribute_value_quote(xml, "'") {
            Some(x) => x,
            None => match self.parse_attribute_value_quote(xml, "\"") {
                Some(x) => x,
                None => fail!("No attribute value"),
            },
        };

        Some((ParsedAttribute{name: name, value: value}, xml))
    }

    fn parse_element<'a>(&self, doc: Document, xml: &'a str) -> (Element, &'a str) {
        let (_, after_start_brace) = xml.slice_literal("<").expect("no start brace");

        let (name, after_name) = after_start_brace.slice_name().expect("failed to parse a name!");

        let (_, after_space) = after_name.slice_space().expect("no space after name");

        let mut attrs = Vec::new();
        let after_attr = match self.parse_attribute(after_space) {
            None => after_space,
            Some((attr, after_attr)) => {
                attrs.push(attr);
                after_attr
            },
        };

        let after_attr = self.optional_space(after_attr);

        let (_, after_end_brace) = after_attr.slice_literal("/>").expect("no end brace");

        let e = doc.new_element(name.to_string());
        for attr in attrs.iter() {
            e.set_attribute(attr.name.to_string(), attr.value.to_string());
        }

        (e, after_end_brace)
    }

    fn parse(&self, xml: &str) -> Document {
        let doc = Document::new();

        let after_preamble = self.parse_preamble(xml);

        let (element, _tail) = self.parse_element(doc.clone(), after_preamble);
        doc.root().append_child(element);

        doc
    }
}

trait XmlStr<'a> {
    fn slice_at(&self, position: uint) -> (&'a str, &'a str);
    fn slice_until(&self, s: &str) -> Option<(&'a str, &'a str)>;
    fn slice_literal(&self, expected: &str) -> Option<(&'a str, &'a str)>;
    fn slice_start_rest(&self, is_first: |char| -> bool, is_rest: |char| -> bool) -> Option<(&'a str, &'a str)>;
    fn slice_name(&self) -> Option<(&'a str, &'a str)>;
    fn slice_space(&self) -> Option<(&'a str, &'a str)>;
}

impl<'a> XmlStr<'a> for &'a str {
    fn slice_at(&self, position: uint) -> (&'a str, &'a str) {
        (self.slice_to(position), self.slice_from(position))
    }

    fn slice_until(&self, s: &str) -> Option<(&'a str, &'a str)> {
        match self.find_str(s) {
            Some(position) => Some(self.slice_at(position)),
            None => None
        }
    }

    fn slice_literal(&self, expected: &str) -> Option<(&'a str, &'a str)> {
        if self.starts_with(expected) {
            Some(self.slice_at(expected.len()))
        } else {
            None
        }
    }

    fn slice_start_rest(&self,
                        is_first: |char| -> bool,
                        is_rest: |char| -> bool)
                        -> Option<(&'a str, &'a str)>
    {
        let mut positions = self.char_indices();

        match positions.next() {
            Some((_, c)) if is_first(c) => (),
            Some((_, _)) => return None,
            None => return None,
        };

        let mut positions = positions.skip_while(|&(_, c)| is_rest(c));
        match positions.next() {
            Some((offset, _)) => Some(self.slice_at(offset)),
            None => Some((self.clone(), "")),
        }
    }

    fn slice_name(&self) -> Option<(&'a str, &'a str)> {
        self.slice_start_rest(|c| c.is_name_start_char(), |c| c.is_name_char())
    }

    fn slice_space(&self) -> Option<(&'a str, &'a str)> {
        self.slice_start_rest(|c| c.is_space_char(), |c| c.is_space_char())
    }
}

trait XmlChar {
    fn is_name_start_char(&self) -> bool;
    fn is_name_char(&self) -> bool;
    fn is_space_char(&self) -> bool;
}

impl XmlChar for char {
    fn is_name_start_char(&self) -> bool {
        match *self {
            ':'                        |
            'A'..'Z'                   |
            '_'                        |
            'a'..'z'                   |
            '\U000000C0'..'\U000000D6' |
            '\U000000D8'..'\U000000F6' |
            '\U000000F8'..'\U000002FF' |
            '\U00000370'..'\U0000037D' |
            '\U0000037F'..'\U00001FFF' |
            '\U0000200C'..'\U0000200D' |
            '\U00002070'..'\U0000218F' |
            '\U00002C00'..'\U00002FEF' |
            '\U00003001'..'\U0000D7FF' |
            '\U0000F900'..'\U0000FDCF' |
            '\U0000FDF0'..'\U0000FFFD' |
            '\U00010000'..'\U000EFFFF' => true,
            _ => false,
        }
    }

    fn is_name_char(&self) -> bool {
        if self.is_name_start_char() { return true; }
        match *self {
            '-'                |
            '.'                |
            '0'..'9'           |
            '\u00B7'           |
            '\u0300'..'\u036F' |
            '\u203F'..'\u2040' => true,
            _ => false
        }
    }

    fn is_space_char(&self) -> bool {
        match *self {
            '\x20' |
            '\x09' |
            '\x0D' |
            '\x0A' => true,
            _ => false,
        }
    }
}

trait Hax {
    fn first_child(&self) -> Option<RootChild>;
}

impl Hax for Root {
    fn first_child(&self) -> Option<RootChild> {
        self.children().remove(0)
    }
}

#[test]
fn parses_a_document_with_a_single_element() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello />");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.name().as_slice(), "hello");
}

#[test]
fn parses_an_element_with_an_attribute() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello scope='world'/>");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.get_attribute("scope").unwrap().as_slice(), "world");
}

#[test]
fn parses_an_element_with_an_attribute_using_double_quotes() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello scope=\"world\"/>");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.get_attribute("scope").unwrap().as_slice(), "world");
}
<|endoftext|>


<commit_before>// Extracting metadata from crate files

import driver::session;
import syntax::ast;
import lib::llvm::False;
import lib::llvm::llvm;
import lib::llvm::mk_object_file;
import lib::llvm::mk_section_iter;
import front::attr;
import middle::resolve;
import syntax::walk;
import syntax::codemap::span;
import back::x86;
import util::common;
import std::ivec;
import std::str;
import std::vec;
import std::ebml;
import std::fs;
import std::io;
import std::option;
import std::option::none;
import std::option::some;
import std::map::hashmap;
import syntax::print::pprust;
import common::*;

export read_crates;
export list_file_metadata;

// Traverses an AST, reading all the information about use'd crates and native
// libraries necessary for later resolving, typechecking, linking, etc.
fn read_crates(session::session sess,
               &ast::crate crate) {
    auto e =
        @rec(sess=sess,
             crate_cache=@std::map::new_str_hash[int](),
             library_search_paths=sess.get_opts().library_search_paths,
             mutable next_crate_num=1);
    auto v =
        rec(visit_view_item_pre=bind visit_view_item(e, _),
            visit_item_pre=bind visit_item(e, _)
            with walk::default_visitor());
    walk::walk_crate(v, crate);
}

type env =
    @rec(session::session sess,
         @hashmap[str, int] crate_cache,
         vec[str] library_search_paths,
         mutable ast::crate_num next_crate_num);

fn visit_view_item(env e, &@ast::view_item i) {
    alt (i.node) {
        case (ast::view_item_use(?ident, ?meta_items, ?id)) {
            auto cnum;
            if (!e.crate_cache.contains_key(ident)) {
                cnum = e.next_crate_num;
                load_library_crate(e.sess, i.span, cnum, ident,
                                   meta_items, e.library_search_paths);
                e.crate_cache.insert(ident, e.next_crate_num);
                e.next_crate_num += 1;
            } else { cnum = e.crate_cache.get(ident); }
            cstore::add_use_stmt_cnum(e.sess.get_cstore(), id, cnum);
        }
        case (_) { }
    }
}

fn visit_item(env e, &@ast::item i) {
    alt (i.node) {
        case (ast::item_native_mod(?m)) {
            if (m.abi != ast::native_abi_rust &&
                m.abi != ast::native_abi_cdecl) {
                ret;
            }
            auto cstore = e.sess.get_cstore();
            if (!cstore::add_used_library(cstore, m.native_name)) {
                ret;
            }
            for (ast::attribute a in
                     attr::find_attrs_by_name(i.attrs, "link_args")) {
                alt (attr::get_meta_item_value_str(attr::attr_meta(a))) {
                    case (some(?linkarg)) {
                        cstore::add_used_link_args(cstore, linkarg);
                    }
                    case (none) { /* fallthrough */ }
                }
            }
        }
        case (_) {
        }
    }
}

// A diagnostic function for dumping crate metadata to an output stream
fn list_file_metadata(str path, io::writer out) {
    alt (get_metadata_section(path)) {
        case (option::some(?bytes)) {
            decoder::list_crate_metadata(bytes, out);
        }
        case (option::none) {
            out.write_str("Could not find metadata in " + path + ".\n");
        }
    }
}

fn metadata_matches(&vec[u8] crate_data,
                    &(@ast::meta_item)[] metas) -> bool {
    auto attrs = decoder::get_crate_attributes(crate_data);
    auto linkage_metas = attr::find_linkage_metas(attrs);

    log #fmt("matching %u metadata requirements against %u items",
             ivec::len(metas), ivec::len(linkage_metas));

    for (@ast::meta_item needed in metas) {
        if (!attr::contains(linkage_metas, needed)) {
            log #fmt("missing %s", pprust::meta_item_to_str(*needed));
            ret false;
        }
    }
    ret true;
}

fn default_native_lib_naming(session::session sess, bool static) ->
   rec(str prefix, str suffix) {
    if (static) {
        ret rec(prefix="lib", suffix=".rlib");
    }
    alt (sess.get_targ_cfg().os) {
        case (session::os_win32) { ret rec(prefix="", suffix=".dll"); }
        case (session::os_macos) { ret rec(prefix="lib", suffix=".dylib"); }
        case (session::os_linux) { ret rec(prefix="lib", suffix=".so"); }
    }
}

fn find_library_crate(&session::session sess, &ast::ident ident,
                      &(@ast::meta_item)[] metas,
                      &vec[str] library_search_paths) ->
   option::t[tup(str, vec[u8])] {

    attr::require_unique_names(sess, metas);

    auto crate_name = {
        auto name_items = attr::find_meta_items_by_name(metas, "name");
        alt (ivec::last(name_items)) {
            case (some(?i)) {
                alt (attr::get_meta_item_value_str(i)) {
                    case (some(?n)) { n }
                    case (_) {
                        // FIXME: Probably want a warning here since the user
                        // is using the wrong type of meta item
                        ident
                    }
                }
            }
            case (none) { ident }
        }
    };

    auto nn = default_native_lib_naming(sess, sess.get_opts().static);
    auto x = find_library_crate_aux(nn, crate_name, metas,
                                    library_search_paths);
    if (x != none || sess.get_opts().static) {
        ret x;
    }
    auto nn2 = default_native_lib_naming(sess, true);
    ret find_library_crate_aux(nn2, crate_name, metas, library_search_paths);
}

fn find_library_crate_aux(&rec(str prefix, str suffix) nn, str crate_name,
                          &(@ast::meta_item)[] metas,
                          &vec[str] library_search_paths) ->
                          option::t[tup(str, vec[u8])] {
    let str prefix = nn.prefix + crate_name;
    // FIXME: we could probably use a 'glob' function in std::fs but it will
    // be much easier to write once the unsafe module knows more about FFI
    // tricks. Currently the glob(3) interface is a bit more than we can
    // stomach from here, and writing a C++ wrapper is more work than just
    // manually filtering fs::list_dir here.

    for (str library_search_path in library_search_paths) {
        log #fmt("searching %s", library_search_path);
        for (str path in fs::list_dir(library_search_path)) {
            log #fmt("searching %s", path);
            let str f = fs::basename(path);
            if (!(str::starts_with(f, prefix) &&
                      str::ends_with(f, nn.suffix))) {
                log #fmt("skipping %s, doesn't look like %s*%s", path, prefix,
                         nn.suffix);
                cont;
            }
            alt (get_metadata_section(path)) {
                case (option::some(?cvec)) {
                    if (!metadata_matches(cvec, metas)) {
                        log #fmt("skipping %s, metadata doesn't match", path);
                        cont;
                    }
                    log #fmt("found %s with matching metadata", path);
                    ret some(tup(path, cvec));
                }
                case (_) { }
            }
        }
    }
    ret none;
}

fn get_metadata_section(str filename) -> option::t[vec[u8]] {
    auto b = str::buf(filename);
    auto mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(b);
    if (mb as int == 0) { ret option::none[vec[u8]]; }
    auto of = mk_object_file(mb);
    auto si = mk_section_iter(of.llof);
    while (llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False) {
        auto name_buf = llvm::LLVMGetSectionName(si.llsi);
        auto name = str::str_from_cstr(name_buf);
        if (str::eq(name, x86::get_meta_sect_name())) {
            auto cbuf = llvm::LLVMGetSectionContents(si.llsi);
            auto csz = llvm::LLVMGetSectionSize(si.llsi);
            auto cvbuf = cbuf as vec::vbuf;
            ret option::some[vec[u8]](vec::vec_from_vbuf[u8](cvbuf, csz));
        }
        llvm::LLVMMoveToNextSection(si.llsi);
    }
    ret option::none[vec[u8]];
}

fn load_library_crate(&session::session sess, span span, ast::crate_num cnum,
                      &ast::ident ident, &(@ast::meta_item)[] metas,
                      &vec[str] library_search_paths) {
    alt (find_library_crate(sess, ident, metas, library_search_paths)) {
        case (some(?t)) {
            auto cstore = sess.get_cstore();
            cstore::set_crate_data(cstore, cnum,
                                   rec(name=ident, data=t._1));
            cstore::add_used_crate_file(cstore, t._0);
            ret;
        }
        case (_) { }
    }
    sess.span_fatal(span, #fmt("can't find crate for '%s'", ident));
}

// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Extract resolve_crate from creader::visit_view_item<commit_after>// Extracting metadata from crate files

import driver::session;
import syntax::ast;
import lib::llvm::False;
import lib::llvm::llvm;
import lib::llvm::mk_object_file;
import lib::llvm::mk_section_iter;
import front::attr;
import middle::resolve;
import syntax::walk;
import syntax::codemap::span;
import back::x86;
import util::common;
import std::ivec;
import std::str;
import std::vec;
import std::ebml;
import std::fs;
import std::io;
import std::option;
import std::option::none;
import std::option::some;
import std::map::hashmap;
import syntax::print::pprust;
import common::*;

export read_crates;
export list_file_metadata;

// Traverses an AST, reading all the information about use'd crates and native
// libraries necessary for later resolving, typechecking, linking, etc.
fn read_crates(session::session sess,
               &ast::crate crate) {
    auto e =
        @rec(sess=sess,
             crate_cache=@std::map::new_str_hash[int](),
             library_search_paths=sess.get_opts().library_search_paths,
             mutable next_crate_num=1);
    auto v =
        rec(visit_view_item_pre=bind visit_view_item(e, _),
            visit_item_pre=bind visit_item(e, _)
            with walk::default_visitor());
    walk::walk_crate(v, crate);
}

type env =
    @rec(session::session sess,
         @hashmap[str, int] crate_cache,
         vec[str] library_search_paths,
         mutable ast::crate_num next_crate_num);

fn visit_view_item(env e, &@ast::view_item i) {
    alt (i.node) {
        case (ast::view_item_use(?ident, ?meta_items, ?id)) {
            auto cnum = resolve_crate(e, ident, meta_items, i.span);
            cstore::add_use_stmt_cnum(e.sess.get_cstore(), id, cnum);
        }
        case (_) { }
    }
}

fn visit_item(env e, &@ast::item i) {
    alt (i.node) {
        case (ast::item_native_mod(?m)) {
            if (m.abi != ast::native_abi_rust &&
                m.abi != ast::native_abi_cdecl) {
                ret;
            }
            auto cstore = e.sess.get_cstore();
            if (!cstore::add_used_library(cstore, m.native_name)) {
                ret;
            }
            for (ast::attribute a in
                     attr::find_attrs_by_name(i.attrs, "link_args")) {
                alt (attr::get_meta_item_value_str(attr::attr_meta(a))) {
                    case (some(?linkarg)) {
                        cstore::add_used_link_args(cstore, linkarg);
                    }
                    case (none) { /* fallthrough */ }
                }
            }
        }
        case (_) {
        }
    }
}

// A diagnostic function for dumping crate metadata to an output stream
fn list_file_metadata(str path, io::writer out) {
    alt (get_metadata_section(path)) {
        case (option::some(?bytes)) {
            decoder::list_crate_metadata(bytes, out);
        }
        case (option::none) {
            out.write_str("Could not find metadata in " + path + ".\n");
        }
    }
}

fn metadata_matches(&vec[u8] crate_data,
                    &(@ast::meta_item)[] metas) -> bool {
    auto attrs = decoder::get_crate_attributes(crate_data);
    auto linkage_metas = attr::find_linkage_metas(attrs);

    log #fmt("matching %u metadata requirements against %u items",
             ivec::len(metas), ivec::len(linkage_metas));

    for (@ast::meta_item needed in metas) {
        if (!attr::contains(linkage_metas, needed)) {
            log #fmt("missing %s", pprust::meta_item_to_str(*needed));
            ret false;
        }
    }
    ret true;
}

fn default_native_lib_naming(session::session sess, bool static) ->
   rec(str prefix, str suffix) {
    if (static) {
        ret rec(prefix="lib", suffix=".rlib");
    }
    alt (sess.get_targ_cfg().os) {
        case (session::os_win32) { ret rec(prefix="", suffix=".dll"); }
        case (session::os_macos) { ret rec(prefix="lib", suffix=".dylib"); }
        case (session::os_linux) { ret rec(prefix="lib", suffix=".so"); }
    }
}

fn find_library_crate(&session::session sess, &ast::ident ident,
                      &(@ast::meta_item)[] metas,
                      &vec[str] library_search_paths) ->
   option::t[tup(str, vec[u8])] {

    attr::require_unique_names(sess, metas);

    auto crate_name = {
        auto name_items = attr::find_meta_items_by_name(metas, "name");
        alt (ivec::last(name_items)) {
            case (some(?i)) {
                alt (attr::get_meta_item_value_str(i)) {
                    case (some(?n)) { n }
                    case (_) {
                        // FIXME: Probably want a warning here since the user
                        // is using the wrong type of meta item
                        ident
                    }
                }
            }
            case (none) { ident }
        }
    };

    auto nn = default_native_lib_naming(sess, sess.get_opts().static);
    auto x = find_library_crate_aux(nn, crate_name, metas,
                                    library_search_paths);
    if (x != none || sess.get_opts().static) {
        ret x;
    }
    auto nn2 = default_native_lib_naming(sess, true);
    ret find_library_crate_aux(nn2, crate_name, metas, library_search_paths);
}

fn find_library_crate_aux(&rec(str prefix, str suffix) nn, str crate_name,
                          &(@ast::meta_item)[] metas,
                          &vec[str] library_search_paths) ->
                          option::t[tup(str, vec[u8])] {
    let str prefix = nn.prefix + crate_name;
    // FIXME: we could probably use a 'glob' function in std::fs but it will
    // be much easier to write once the unsafe module knows more about FFI
    // tricks. Currently the glob(3) interface is a bit more than we can
    // stomach from here, and writing a C++ wrapper is more work than just
    // manually filtering fs::list_dir here.

    for (str library_search_path in library_search_paths) {
        log #fmt("searching %s", library_search_path);
        for (str path in fs::list_dir(library_search_path)) {
            log #fmt("searching %s", path);
            let str f = fs::basename(path);
            if (!(str::starts_with(f, prefix) &&
                      str::ends_with(f, nn.suffix))) {
                log #fmt("skipping %s, doesn't look like %s*%s", path, prefix,
                         nn.suffix);
                cont;
            }
            alt (get_metadata_section(path)) {
                case (option::some(?cvec)) {
                    if (!metadata_matches(cvec, metas)) {
                        log #fmt("skipping %s, metadata doesn't match", path);
                        cont;
                    }
                    log #fmt("found %s with matching metadata", path);
                    ret some(tup(path, cvec));
                }
                case (_) { }
            }
        }
    }
    ret none;
}

fn get_metadata_section(str filename) -> option::t[vec[u8]] {
    auto b = str::buf(filename);
    auto mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(b);
    if (mb as int == 0) { ret option::none[vec[u8]]; }
    auto of = mk_object_file(mb);
    auto si = mk_section_iter(of.llof);
    while (llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False) {
        auto name_buf = llvm::LLVMGetSectionName(si.llsi);
        auto name = str::str_from_cstr(name_buf);
        if (str::eq(name, x86::get_meta_sect_name())) {
            auto cbuf = llvm::LLVMGetSectionContents(si.llsi);
            auto csz = llvm::LLVMGetSectionSize(si.llsi);
            auto cvbuf = cbuf as vec::vbuf;
            ret option::some[vec[u8]](vec::vec_from_vbuf[u8](cvbuf, csz));
        }
        llvm::LLVMMoveToNextSection(si.llsi);
    }
    ret option::none[vec[u8]];
}

fn load_library_crate(&session::session sess, span span, ast::crate_num cnum,
                      &ast::ident ident, &(@ast::meta_item)[] metas,
                      &vec[str] library_search_paths) {
    alt (find_library_crate(sess, ident, metas, library_search_paths)) {
        case (some(?t)) {
            auto cstore = sess.get_cstore();
            cstore::set_crate_data(cstore, cnum,
                                   rec(name=ident, data=t._1));
            cstore::add_used_crate_file(cstore, t._0);
            ret;
        }
        case (_) { }
    }
    sess.span_fatal(span, #fmt("can't find crate for '%s'", ident));
}

fn resolve_crate(env e, ast::ident ident, (@ast::meta_item)[] metas,
                 span span) -> ast::crate_num {
    if (!e.crate_cache.contains_key(ident)) {
        auto cnum = e.next_crate_num;
        load_library_crate(e.sess, span, cnum, ident,
                           metas, e.library_search_paths);
        e.crate_cache.insert(ident, e.next_crate_num);
        e.next_crate_num += 1;
        ret cnum;
    } else {
        ret e.crate_cache.get(ident);
    }
 }

// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>