<commit_before>use hb;
use std::ops::{Deref, DerefMut};
use std::borrow::Borrow;

/// A type to represent 4-byte SFNT tags.
///
/// Tables, features, etc. in OpenType and many other font formats use SFNT tags as identifiers.
/// These are 4-bytes long and usually each byte represents an ASCII value. `Tag` provides methods
/// to create such identifiers from individual `chars` or a `str` slice and to get the string
/// representation of a `Tag`.
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct Tag(pub hb::hb_tag_t);

impl Tag {
    /// Create a `Tag` from its four-char textual representation.
    ///
    /// # Examples
    ///
    /// ```
    /// use harfbuzz_rs::Tag;
    /// let cmap_tag = Tag::new('c', 'm', 'a', 'p');
    /// assert_eq!(cmap_tag.to_string(), "cmap")
    /// ```
    ///
    pub fn new(a: char, b: char, c: char, d: char) -> Self {
        Tag(((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | (d as u32))
    }

    fn tag_to_string(self) -> String {
        let mut buf: [u8; 4] = [0; 4];
        unsafe { hb::hb_tag_to_string(self.0, buf.as_mut_ptr() as *mut _) };
        String::from_utf8_lossy(&buf).into()
    }
}

use std::fmt;
use std::fmt::{Debug, Display, Formatter};
impl Debug for Tag {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let string = self.tag_to_string();
        let mut chars = string.chars().chain(std::iter::repeat('\u{FFFD}'));
        write!(
            f,
            "Tag({:?}, {:?}, {:?}, {:?})",
            chars.next().unwrap(),
            chars.next().unwrap(),
            chars.next().unwrap(),
            chars.next().unwrap()
        )
    }
}

impl Display for Tag {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.tag_to_string())
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
/// An Error generated when a `Tag` fails to parse from a `&str` with the `from_str` function.
pub enum TagFromStrErr {
    /// The string contains non-ASCII characters.
    NonAscii,
    /// The string has length zero.
    ZeroLengthString,
}

use std;
use std::str::FromStr;

impl FromStr for Tag {
    type Err = TagFromStrErr;
    /// Parses a `Tag` from a `&str` that contains four or less ASCII characters. When the string's
    /// length is smaller than 4 it is extended with `' '` (Space) characters. The remaining bytes
    /// of strings longer than 4 bytes are ignored.
    ///
    /// # Examples
    ///
    /// ```
    /// use harfbuzz_rs::Tag;
    /// use std::str::FromStr;
    /// let tag1 = Tag::from_str("ABCD").unwrap();
    /// let tag2 = Tag::new('A', 'B', 'C', 'D');
    /// assert_eq!(tag1, tag2);
    /// ```
    ///
    fn from_str(s: &str) -> Result<Tag, TagFromStrErr> {
        if s.is_ascii() == false {
            return Err(TagFromStrErr::NonAscii);
        }
        if s.len() == 0 {
            return Err(TagFromStrErr::ZeroLengthString);
        }
        let len = std::cmp::max(s.len(), 4) as i32;
        unsafe { Ok(Tag(hb::hb_tag_from_string(s.as_ptr() as *mut _, len))) }
    }
}

pub struct Language(pub hb::hb_language_t);

impl Default for Language {
    fn default() -> Language {
        Language(unsafe { hb::hb_language_get_default() })
    }
}

impl Debug for Language {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Language(\"{}\")", self)
    }
}

use std::ffi::CStr;
impl Display for Language {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let string = unsafe {
            let char_ptr = hb::hb_language_to_string(self.0);
            if char_ptr.is_null() {
                return Err(fmt::Error);
            }
            CStr::from_ptr(char_ptr)
                .to_str()
                .expect("String representation of language is not valid utf8.")
        };
        write!(f, "{}", string)
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct InvalidLanguage;

impl FromStr for Language {
    type Err = InvalidLanguage;
    fn from_str(s: &str) -> Result<Language, InvalidLanguage> {
        let len = std::cmp::min(s.len(), std::i32::MAX as _) as i32;
        let lang = unsafe { hb::hb_language_from_string(s.as_ptr() as *mut _, len) };
        if lang.is_null() {
            Err(InvalidLanguage {})
        } else {
            Ok(Language(lang))
        }
    }
}

/// A trait which is implemented for all harffbuzz wrapper structs. It exposes common functionality
/// for converting from and to the underlying raw harfbuzz pointers that are useful for ffi.
pub trait HarfbuzzObject: Sized {
    /// Type of the raw harfbuzz object pointer.
    type Raw;

    /// Creates a reference from a harfbuzz object pointer.
    ///
    /// Unsafe because a raw pointer may be accessed. The reference count is not changed. Should not
    /// be called directly by a library user.
    ///
    /// Use the HbBox and HbArc abstractions instead.
    unsafe fn from_raw<'a>(val: *const Self::Raw) -> &'a Self {
        &*(val as *const Self)
    }

    /// Creates a mutable reference from a harfbuzz object pointer.
    ///
    /// Unsafe because a raw pointer may be accessed. The reference count is not changed. Should not
    /// be called directly by a library user.
    ///
    /// Use the HbBox and HbArc abstractions instead.
    unsafe fn from_raw_mut<'a>(val: *mut Self::Raw) -> &'a mut Self {
        &mut *(val as *mut Self)
    }

    /// Returns the underlying harfbuzz object pointer.
    ///
    /// The caller must ensure, that this pointer is not used after `self`'s destruction.
    fn as_raw(&self) -> *mut Self::Raw {
        (((self as *const Self) as *mut Self) as *mut Self::Raw)
    }

    /// Increases the reference count of the HarfBuzz object.
    ///
    /// Wraps a `hb_TYPE_reference()` call.
    unsafe fn reference(&self);

    /// Decreases the reference count of the HarfBuzz object and destroys it if the reference count
    /// reaches zero.
    ///
    /// Wraps a `hb_TYPE_destroy()` call.
    unsafe fn dereference(&self);

    /// Makes an owned value from the reference.
    fn upgrade(&self) -> HbArc<Self> {
        unsafe {
            self.reference();
            HbArc::from_raw(self.as_raw())
        }
    }
}

/// A smart pointer that wraps an atomically reference counted HarfBuzz object.
///
/// Usually you don't create a `HbArc` yourself, but get it from another function in this crate.
/// You can just use the methods of the wrapped object through its `Deref` implementation.
///
/// A `HbArc` is a safe wrapper for reference counted HarfBuzz objects and provides shared immutable
/// access to its inner object. As HarfBuzz' objects are all thread-safe `HbArc` implements `Send`
/// and `Sync`.
///
/// Tries to mirror the stdlib `Arc` interface where applicable as HarfBuzz' reference counting has
/// similar semantics.
#[derive(Debug, PartialEq, Eq)]
pub struct HbArc<T: HarfbuzzObject> {
    pointer: *mut T::Raw,
}

impl<T: HarfbuzzObject> HbArc<T> {
    /// Creates a `HbArc` from a raw harfbuzz pointer.
    ///
    /// Transfers ownership. _Use of the original pointer is now forbidden!_ Unsafe because
    /// dereferencing a raw pointer is necessary.
    pub unsafe fn from_raw(raw: *mut T::Raw) -> Self {
        HbArc { pointer: raw }
    }

    /// Converts `self` into the underlying harfbuzz object pointer value. The resulting pointer
    /// has to be manually destroyed using `hb_TYPE_destroy` or be converted back into the wrapper
    /// using the `from_raw` function.
    pub fn into_raw(self) -> *mut T::Raw {
        let result = self.pointer;
        std::mem::forget(self);
        result
    }
}

impl<T: HarfbuzzObject> Clone for HbArc<T> {
    fn clone(&self) -> Self {
        unsafe {
            self.reference();
            Self::from_raw(self.pointer)
        }
    }
}

impl<T: HarfbuzzObject> Deref for HbArc<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { T::from_raw(self.pointer) }
    }
}

impl<T: HarfbuzzObject> Borrow<T> for HbArc<T> {
    fn borrow(&self) -> &T {
        self
    }
}

impl<T: HarfbuzzObject> From<HbBox<T>> for HbArc<T> {
    fn from(t: HbBox<T>) -> Self {
        let ptr = t.pointer;
        std::mem::forget(t);
        unsafe { HbArc::from_raw(ptr) }
    }
}

impl<T: HarfbuzzObject> Drop for HbArc<T> {
    fn drop(&mut self) {
        unsafe { self.dereference() }
    }
}

unsafe impl<T: HarfbuzzObject + Sync + Send> Send for HbArc<T> {}
unsafe impl<T: HarfbuzzObject + Sync + Send> Sync for HbArc<T> {}

/// A smart pointer that wraps a singly owned harfbuzz object.
///
/// Usually you don't create a `HbBox` yourself, but get it from another function in this crate.
/// You can just use the methods of the wrapped object through its `Deref` implementation.
///
/// A `HbBox` is used to wrap freshly created owned HarfBuzz objects. It permits mutable, non-shared
/// access to the enclosed HarfBuzz value so it can be used e.g. to set up a `Font` or `Face` after
/// its creation.
///
/// When you are finished mutating the inner value, you usually want to pass it to other HarfBuzz
/// functions that expect shared access. Thus you need to convert the `HbBox` to a `HbArc` pointer
/// using `.into()`. Note however that once a value is
/// converted to  a `HbArc<T>`, it will not possible to mutate it anymore.
#[derive(Debug, PartialEq, Eq)]
pub struct HbBox<T: HarfbuzzObject> {
    pointer: *mut T::Raw,
}

impl<T: HarfbuzzObject> HbBox<T> {
    /// Creates a `HbBox` safely wrapping a raw harfbuzz pointer.
    ///
    /// This fully transfers ownership. _Use of the original pointer is now forbidden!_ Unsafe
    /// because a dereference of a raw pointer is necessary.
    ///
    /// Use this only to wrap freshly created HarfBuzz object!
    pub unsafe fn from_raw(raw: *mut T::Raw) -> Self {
        HbBox { pointer: raw }
    }
}

impl<T: HarfbuzzObject> Drop for HbBox<T> {
    fn drop(&mut self) {
        unsafe { self.dereference() }
    }
}

impl<T: HarfbuzzObject> Deref for HbBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { T::from_raw(self.pointer) }
    }
}

impl<T: HarfbuzzObject> DerefMut for HbBox<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { T::from_raw_mut(self.pointer) }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::str::FromStr;
    use std::rc::Rc;
    use std::cell::Cell;
    use std::mem;

    #[test]
    fn test_tag_debugging() {
        let tag = Tag::from_str("ABCD").unwrap();
        assert_eq!("ABCD", format!("{}", tag));
        assert_eq!("Tag('A', 'B', 'C', 'D')", format!("{:?}", tag));
    }

    #[test]
    fn test_tag_creation() {
        assert!(Tag::from_str("∞BCD").is_err());
        assert!(Tag::from_str("").is_err());
        assert_eq!(Tag::from_str("ABCDE"), Tag::from_str("ABCD"));
        assert_eq!(Tag::from_str("abWd").unwrap(), Tag::new('a', 'b', 'W', 'd'));
    }

    #[test]
    fn test_language() {
        assert_eq!(Language::default().to_string(), "c");
        assert_eq!(Language::from_str("ger").unwrap().to_string(), "ger");
        assert_eq!(Language::from_str("ge!").unwrap().to_string(), "ge");
        assert_eq!(Language::from_str("German").unwrap().to_string(), "german");
    }

    #[derive(Debug)]
    struct ReferenceCounter {
        rc: Cell<isize>,
    }

    impl HarfbuzzObject for ReferenceCounter {
        type Raw = Cell<isize>;

        unsafe fn reference(&self) {
            println!("referencing {:?}", self);
            let rc = self.rc.get();
            self.rc.set(rc + 1);
        }

        unsafe fn dereference(&self) {
            println!("dereferencing {:?}", self);
            let rc = self.rc.get();
            self.rc.set(rc - 1);
        }
    }

    #[test]
    fn reference_counting_hbarc() {
        // Mimic a C-API that returns a pointer to a reference counted value.
        let object = Rc::new(ReferenceCounter { rc: Cell::new(1) });
        let raw = Rc::into_raw(object.clone()) as *mut _;

        let arc: HbArc<ReferenceCounter> = unsafe { HbArc::from_raw(raw) };
        assert_eq!(object.rc.get(), 1);
        {
            let arc2 = HbArc::clone(&arc);
            assert_eq!(object.rc.get(), 2);
            mem::drop(arc2);
        }
        assert_eq!(object.rc.get(), 1);
        mem::drop(arc);
        assert_eq!(object.rc.get(), 0);

        // don't leak memory
        let _ = unsafe { Rc::from_raw(raw) };
    }
}
<commit_msg>Clarify some comments<commit_after>use hb;
use std::ops::{Deref, DerefMut};
use std::borrow::Borrow;

/// A type to represent 4-byte SFNT tags.
///
/// Tables, features, etc. in OpenType and many other font formats use SFNT tags as identifiers.
/// These are 4-bytes long and usually each byte represents an ASCII value. `Tag` provides methods
/// to create such identifiers from individual `chars` or a `str` slice and to get the string
/// representation of a `Tag`.
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct Tag(pub hb::hb_tag_t);

impl Tag {
    /// Create a `Tag` from its four-char textual representation.
    ///
    /// # Examples
    ///
    /// ```
    /// use harfbuzz_rs::Tag;
    /// let cmap_tag = Tag::new('c', 'm', 'a', 'p');
    /// assert_eq!(cmap_tag.to_string(), "cmap")
    /// ```
    ///
    pub fn new(a: char, b: char, c: char, d: char) -> Self {
        Tag(((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | (d as u32))
    }

    fn tag_to_string(self) -> String {
        let mut buf: [u8; 4] = [0; 4];
        unsafe { hb::hb_tag_to_string(self.0, buf.as_mut_ptr() as *mut _) };
        String::from_utf8_lossy(&buf).into()
    }
}

use std::fmt;
use std::fmt::{Debug, Display, Formatter};
impl Debug for Tag {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let string = self.tag_to_string();
        let mut chars = string.chars().chain(std::iter::repeat('\u{FFFD}'));
        write!(
            f,
            "Tag({:?}, {:?}, {:?}, {:?})",
            chars.next().unwrap(),
            chars.next().unwrap(),
            chars.next().unwrap(),
            chars.next().unwrap()
        )
    }
}

impl Display for Tag {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.tag_to_string())
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
/// An Error generated when a `Tag` fails to parse from a `&str` with the `from_str` function.
pub enum TagFromStrErr {
    /// The string contains non-ASCII characters.
    NonAscii,
    /// The string has length zero.
    ZeroLengthString,
}

use std;
use std::str::FromStr;

impl FromStr for Tag {
    type Err = TagFromStrErr;
    /// Parses a `Tag` from a `&str` that contains four or less ASCII characters. When the string's
    /// length is smaller than 4 it is extended with `' '` (Space) characters. The remaining bytes
    /// of strings longer than 4 bytes are ignored.
    ///
    /// # Examples
    ///
    /// ```
    /// use harfbuzz_rs::Tag;
    /// use std::str::FromStr;
    /// let tag1 = Tag::from_str("ABCD").unwrap();
    /// let tag2 = Tag::new('A', 'B', 'C', 'D');
    /// assert_eq!(tag1, tag2);
    /// ```
    ///
    fn from_str(s: &str) -> Result<Tag, TagFromStrErr> {
        if s.is_ascii() == false {
            return Err(TagFromStrErr::NonAscii);
        }
        if s.len() == 0 {
            return Err(TagFromStrErr::ZeroLengthString);
        }
        let len = std::cmp::max(s.len(), 4) as i32;
        unsafe { Ok(Tag(hb::hb_tag_from_string(s.as_ptr() as *mut _, len))) }
    }
}

pub struct Language(pub hb::hb_language_t);

impl Default for Language {
    fn default() -> Language {
        Language(unsafe { hb::hb_language_get_default() })
    }
}

impl Debug for Language {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Language(\"{}\")", self)
    }
}

use std::ffi::CStr;
impl Display for Language {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let string = unsafe {
            let char_ptr = hb::hb_language_to_string(self.0);
            if char_ptr.is_null() {
                return Err(fmt::Error);
            }
            CStr::from_ptr(char_ptr)
                .to_str()
                .expect("String representation of language is not valid utf8.")
        };
        write!(f, "{}", string)
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct InvalidLanguage;

impl FromStr for Language {
    type Err = InvalidLanguage;
    fn from_str(s: &str) -> Result<Language, InvalidLanguage> {
        let len = std::cmp::min(s.len(), std::i32::MAX as _) as i32;
        let lang = unsafe { hb::hb_language_from_string(s.as_ptr() as *mut _, len) };
        if lang.is_null() {
            Err(InvalidLanguage {})
        } else {
            Ok(Language(lang))
        }
    }
}

/// A trait which is implemented for all harffbuzz wrapper structs. It exposes common functionality
/// for converting from and to the underlying raw harfbuzz pointers that are useful for ffi.
pub trait HarfbuzzObject: Sized {
    /// Type of the raw harfbuzz object.
    type Raw;

    /// Creates a reference from a harfbuzz object pointer.
    ///
    /// Unsafe because a raw pointer may be accessed. The reference count is not changed. Should not
    /// be called directly by a library user.
    ///
    /// Use the HbBox and HbArc abstractions instead.
    unsafe fn from_raw<'a>(val: *const Self::Raw) -> &'a Self {
        &*(val as *const Self)
    }

    /// Creates a mutable reference from a harfbuzz object pointer.
    ///
    /// Unsafe because a raw pointer may be accessed. The reference count is not changed. Should not
    /// be called directly by a library user.
    ///
    /// Use the HbBox and HbArc abstractions instead.
    unsafe fn from_raw_mut<'a>(val: *mut Self::Raw) -> &'a mut Self {
        &mut *(val as *mut Self)
    }

    /// Returns the underlying harfbuzz object pointer.
    ///
    /// The caller must ensure, that this pointer is not used after `self`'s destruction.
    fn as_raw(&self) -> *mut Self::Raw {
        (((self as *const Self) as *mut Self) as *mut Self::Raw)
    }

    /// Increases the reference count of the HarfBuzz object.
    ///
    /// Wraps a `hb_TYPE_reference()` call.
    unsafe fn reference(&self);

    /// Decreases the reference count of the HarfBuzz object and destroys it if the reference count
    /// reaches zero.
    ///
    /// Wraps a `hb_TYPE_destroy()` call.
    unsafe fn dereference(&self);

    /// Makes an owned value from the reference.
    fn upgrade(&self) -> HbArc<Self> {
        unsafe {
            self.reference();
            HbArc::from_raw(self.as_raw())
        }
    }
}

/// A smart pointer that wraps an atomically reference counted HarfBuzz object.
///
/// Usually you don't create a `HbArc` yourself, but get it from another function in this crate.
/// You can just use the methods of the wrapped object through its `Deref` implementation.
///
/// A `HbArc` is a safe wrapper for reference counted HarfBuzz objects and provides shared immutable
/// access to its inner object. As HarfBuzz' objects are all thread-safe `HbArc` implements `Send`
/// and `Sync`.
///
/// Tries to mirror the stdlib `Arc` interface where applicable as HarfBuzz' reference counting has
/// similar semantics.
#[derive(Debug, PartialEq, Eq)]
pub struct HbArc<T: HarfbuzzObject> {
    pointer: *mut T::Raw,
}

impl<T: HarfbuzzObject> HbArc<T> {
    /// Creates a `HbArc` from a raw harfbuzz pointer.
    ///
    /// Transfers ownership. _Use of the original pointer is now forbidden!_ Unsafe because
    /// dereferencing a raw pointer is necessary.
    pub unsafe fn from_raw(raw: *mut T::Raw) -> Self {
        HbArc { pointer: raw }
    }

    /// Converts `self` into the underlying harfbuzz object pointer value. The resulting pointer
    /// has to be manually destroyed using `hb_TYPE_destroy` or be converted back into the wrapper
    /// using the `from_raw` function to avoid leaking memory.
    pub fn into_raw(self) -> *mut T::Raw {
        let result = self.pointer;
        std::mem::forget(self);
        result
    }
}

impl<T: HarfbuzzObject> Clone for HbArc<T> {
    fn clone(&self) -> Self {
        unsafe {
            self.reference();
            Self::from_raw(self.pointer)
        }
    }
}

impl<T: HarfbuzzObject> Deref for HbArc<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { T::from_raw(self.pointer) }
    }
}

impl<T: HarfbuzzObject> Borrow<T> for HbArc<T> {
    fn borrow(&self) -> &T {
        self
    }
}

impl<T: HarfbuzzObject> From<HbBox<T>> for HbArc<T> {
    fn from(t: HbBox<T>) -> Self {
        let ptr = t.pointer;
        std::mem::forget(t);
        unsafe { HbArc::from_raw(ptr) }
    }
}

impl<T: HarfbuzzObject> Drop for HbArc<T> {
    fn drop(&mut self) {
        unsafe { self.dereference() }
    }
}

unsafe impl<T: HarfbuzzObject + Sync + Send> Send for HbArc<T> {}
unsafe impl<T: HarfbuzzObject + Sync + Send> Sync for HbArc<T> {}

/// A smart pointer that wraps a singly owned harfbuzz object.
///
/// Usually you don't create a `HbBox` yourself, but get it from another function in this crate.
/// You can just use the methods of the wrapped object through its `Deref` implementation.
///
/// A `HbBox` is used to wrap freshly created owned HarfBuzz objects. It permits mutable, non-shared
/// access to the enclosed HarfBuzz value so it can be used e.g. to set up a `Font` or `Face` after
/// its creation.
///
/// When you are finished mutating the inner value, you usually want to pass it to other HarfBuzz
/// functions that expect shared access. Thus you need to convert the `HbBox` to a `HbArc` pointer
/// using `.into()`. Note however that once a value is
/// converted to  a `HbArc<T>`, it will not possible to mutate it anymore.
#[derive(Debug, PartialEq, Eq)]
pub struct HbBox<T: HarfbuzzObject> {
    pointer: *mut T::Raw,
}

impl<T: HarfbuzzObject> HbBox<T> {
    /// Creates a `HbBox` safely wrapping a raw harfbuzz pointer.
    ///
    /// This fully transfers ownership. _Use of the original pointer is now forbidden!_ Unsafe
    /// because a dereference of a raw pointer is necessary.
    ///
    /// Use this only to wrap freshly created HarfBuzz object that is not shared!
    pub unsafe fn from_raw(raw: *mut T::Raw) -> Self {
        HbBox { pointer: raw }
    }
}

impl<T: HarfbuzzObject> Drop for HbBox<T> {
    fn drop(&mut self) {
        unsafe { self.dereference() }
    }
}

impl<T: HarfbuzzObject> Deref for HbBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { T::from_raw(self.pointer) }
    }
}

impl<T: HarfbuzzObject> DerefMut for HbBox<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { T::from_raw_mut(self.pointer) }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::str::FromStr;
    use std::rc::Rc;
    use std::cell::Cell;
    use std::mem;

    #[test]
    fn test_tag_debugging() {
        let tag = Tag::from_str("ABCD").unwrap();
        assert_eq!("ABCD", format!("{}", tag));
        assert_eq!("Tag('A', 'B', 'C', 'D')", format!("{:?}", tag));
    }

    #[test]
    fn test_tag_creation() {
        assert!(Tag::from_str("∞BCD").is_err());
        assert!(Tag::from_str("").is_err());
        assert_eq!(Tag::from_str("ABCDE"), Tag::from_str("ABCD"));
        assert_eq!(Tag::from_str("abWd").unwrap(), Tag::new('a', 'b', 'W', 'd'));
    }

    #[test]
    fn test_language() {
        assert_eq!(Language::default().to_string(), "c");
        assert_eq!(Language::from_str("ger").unwrap().to_string(), "ger");
        assert_eq!(Language::from_str("ge!").unwrap().to_string(), "ge");
        assert_eq!(Language::from_str("German").unwrap().to_string(), "german");
    }

    #[derive(Debug)]
    struct ReferenceCounter {
        rc: Cell<isize>,
    }

    impl HarfbuzzObject for ReferenceCounter {
        type Raw = Cell<isize>;

        unsafe fn reference(&self) {
            println!("referencing {:?}", self);
            let rc = self.rc.get();
            self.rc.set(rc + 1);
        }

        unsafe fn dereference(&self) {
            println!("dereferencing {:?}", self);
            let rc = self.rc.get();
            self.rc.set(rc - 1);
        }
    }

    #[test]
    fn reference_counting_hbarc() {
        // Mimic a C-API that returns a pointer to a reference counted value.
        let object = Rc::new(ReferenceCounter { rc: Cell::new(1) });
        let raw = Rc::into_raw(object.clone()) as *mut _;

        let arc: HbArc<ReferenceCounter> = unsafe { HbArc::from_raw(raw) };
        assert_eq!(object.rc.get(), 1);
        {
            let arc2 = HbArc::clone(&arc);
            assert_eq!(object.rc.get(), 2);
            mem::drop(arc2);
        }
        assert_eq!(object.rc.get(), 1);
        mem::drop(arc);
        assert_eq!(object.rc.get(), 0);

        // don't leak memory
        let _ = unsafe { Rc::from_raw(raw) };
    }
}
<|endoftext|>


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

use error::{Error, Result};

/// Find the next string, delimited by quotes `"` (which are not returned),
/// and not stopping at escape quotes `\"`
pub fn find_string(bytes: &[u8]) -> Result<String> {
    let mut begin = None;
    let mut i = 0;
    while i < bytes.len() {
        match bytes[i] {
            b'"' => if begin.is_some() {
                if bytes[i-1] != b'\\' {
                    break
                }
            } else {
                if i + 1 >= bytes.len() {
                    return Err(Error::new(""));
                }
                begin = Some(i + 1);
            },
            _ => (),
        }
        i += 1;
    }
    let begin = if let Some(begin) = begin {
        begin
    } else {
        return Err(Error::new(""));
    };
    Ok(String::from_utf8(bytes[begin..i].to_vec()).unwrap())
}
<commit_msg>Add tests for find_string<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at https://mozilla.org/MPL/2.0/.

use error::{Error, Result};

/// Find the next string, delimited by quotes `"` (which are not returned),
/// and not stopping at escape quotes `\"`
pub fn find_string(bytes: &[u8]) -> Result<String> {
    let mut begin = None;
    let mut i = 0;
    while i < bytes.len() {
        match bytes[i] {
            b'"' => if begin.is_some() {
                if bytes[i-1] != b'\\' {
                    break
                }
            } else {
                if i + 1 >= bytes.len() {
                    return Err(Error::new(""));
                }
                begin = Some(i + 1);
            },
            _ => (),
        }
        i += 1;
    }
    let begin = if let Some(begin) = begin {
        begin
    } else {
        return Err(Error::new(""));
    };
    Ok(String::from_utf8(bytes[begin..i].to_vec()).unwrap())
}

#[test]
fn find_string_1() {
    let s = r#"
"Test"
"#;
    let expected = "Test";
    assert_eq!(&find_string(s.as_bytes()).unwrap(), expected);
}

#[test]
fn find_string_2() {
    let s = r#"
"A \"test\"..."
"#;
    let expected = r#"A \"test\"..."#;
    assert_eq!(&find_string(s.as_bytes()).unwrap(), expected);
}
<|endoftext|>


<commit_before>use std::default::Default;
use std::path::Path;
use std::fs::File;
use std::io::Read;
use error::Result;
use serde_yaml;

use legacy::wildwest;
use syntax_highlight::has_syntax_theme;

arg_enum! {
    #[derive(Debug, PartialEq, Copy, Clone)]
    pub enum Dump {
        DocObject,
        DocTemplate,
        DocLinkObject,
        Document
    }
}

impl Dump {
    pub fn is_doc(&self) -> bool {
        true
    }
}

#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum SortOrder {
    Asc,
    Desc,
}

impl Default for SortOrder {
    fn default() -> SortOrder {
        SortOrder::Desc
    }
}

#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum SassOutputStyle {
    Nested,
    Expanded,
    Compact,
    Compressed,
}

const SASS_IMPORT_DIR: &'static str = "_sass";

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SassOptions {
    #[serde(skip)]
    pub import_dir: &'static str,
    pub style: SassOutputStyle,
}

impl Default for SassOptions {
    fn default() -> SassOptions {
        SassOptions {
            import_dir: SASS_IMPORT_DIR,
            style: SassOutputStyle::Nested,
        }
    }
}

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SyntaxHighlight {
    pub theme: String,
}

impl Default for SyntaxHighlight {
    fn default() -> SyntaxHighlight {
        SyntaxHighlight { theme: "base16-ocean.dark".to_owned() }
    }
}

const DATA_DIR: &'static str = "_data";

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct Config {
    pub source: String,
    pub dest: String,
    pub layouts: String,
    pub drafts: String,
    pub data: &'static str,
    pub include_drafts: bool,
    pub posts: String,
    pub post_path: Option<String>,
    pub post_order: SortOrder,
    pub template_extensions: Vec<String>,
    pub rss: Option<String>,
    pub jsonfeed: Option<String>,
    pub name: Option<String>,
    pub description: Option<String>,
    pub link: Option<String>,
    pub ignore: Vec<String>,
    pub excerpt_separator: String,
    // This is a debug-only field and should be transient rather than persistently set.
    #[serde(skip)]
    pub dump: Vec<Dump>,
    pub syntax_highlight: SyntaxHighlight,
    pub sass: SassOptions,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            source: "./".to_owned(),
            dest: "./".to_owned(),
            layouts: "_layouts".to_owned(),
            drafts: "_drafts".to_owned(),
            data: DATA_DIR,
            include_drafts: false,
            posts: "posts".to_owned(),
            post_path: None,
            post_order: SortOrder::default(),
            template_extensions: vec!["md".to_owned(), "liquid".to_owned()],
            rss: None,
            jsonfeed: None,
            name: None,
            description: None,
            link: None,
            ignore: vec![],
            excerpt_separator: "\n\n".to_owned(),
            dump: vec![],
            syntax_highlight: SyntaxHighlight::default(),
            sass: SassOptions::default(),
        }
    }
}

impl Config {
    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Config> {
        let content = {
            let mut buffer = String::new();
            let mut f = File::open(path)?;
            f.read_to_string(&mut buffer)?;
            buffer
        };

        if content.trim().is_empty() {
            return Ok(Config::default());
        }

        let config: wildwest::GlobalConfig = serde_yaml::from_str(&content)?;
        let mut config: Config = config.into();

        config.link = if let Some(ref link) = config.link {
            let mut link = link.to_owned();
            if !link.ends_with('/') {
                link += "/";
            }
            Some(link)
        } else {
            None
        };

        let result: Result<()> = match has_syntax_theme(&config.syntax_highlight.theme) {
            Ok(true) => Ok(()),
            Ok(false) => {
                Err(format!("Syntax theme '{}' is unsupported",
                            config.syntax_highlight.theme)
                        .into())
            }
            Err(err) => {
                warn!("Syntax theme named '{}' ignored. Reason: {}",
                      config.syntax_highlight.theme,
                      err);
                Ok(())
            }
        };
        result?;

        Ok(config)
    }
}

#[test]
fn test_from_file_ok() {
    let result = Config::from_file("tests/fixtures/config/.cobalt.yml").unwrap();
    assert_eq!(result,
               Config {
                   dest: "./dest".to_owned(),
                   layouts: "_my_layouts".to_owned(),
                   posts: "_my_posts".to_owned(),
                   ..Default::default()
               });
}

#[test]
fn test_from_file_rss() {
    let result = Config::from_file("tests/fixtures/config/rss.yml").unwrap();
    assert_eq!(result,
               Config {
                   rss: Some("rss.xml".to_owned()),
                   name: Some("My blog!".to_owned()),
                   description: Some("Blog description".to_owned()),
                   link: Some("http://example.com/".to_owned()),
                   ..Default::default()
               });
}

#[test]
fn test_from_file_empty() {
    let result = Config::from_file("tests/fixtures/config/empty.yml").unwrap();
    assert_eq!((result), Config { ..Default::default() });
}

#[test]
fn test_from_file_invalid_syntax() {
    let result = Config::from_file("tests/fixtures/config/invalid_syntax.yml");
    assert!(result.is_err());
}

#[test]
fn test_from_file_not_found() {
    let result = Config::from_file("tests/fixtures/config/config_does_not_exist.yml");
    assert!(result.is_err());
}
<commit_msg>fix: Don't dump a flag that isn't meant to be<commit_after>use std::default::Default;
use std::path::Path;
use std::fs::File;
use std::io::Read;
use error::Result;
use serde_yaml;

use legacy::wildwest;
use syntax_highlight::has_syntax_theme;

arg_enum! {
    #[derive(Debug, PartialEq, Copy, Clone)]
    pub enum Dump {
        DocObject,
        DocTemplate,
        DocLinkObject,
        Document
    }
}

impl Dump {
    pub fn is_doc(&self) -> bool {
        true
    }
}

#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum SortOrder {
    Asc,
    Desc,
}

impl Default for SortOrder {
    fn default() -> SortOrder {
        SortOrder::Desc
    }
}

#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum SassOutputStyle {
    Nested,
    Expanded,
    Compact,
    Compressed,
}

const SASS_IMPORT_DIR: &'static str = "_sass";

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SassOptions {
    #[serde(skip)]
    pub import_dir: &'static str,
    pub style: SassOutputStyle,
}

impl Default for SassOptions {
    fn default() -> SassOptions {
        SassOptions {
            import_dir: SASS_IMPORT_DIR,
            style: SassOutputStyle::Nested,
        }
    }
}

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SyntaxHighlight {
    pub theme: String,
}

impl Default for SyntaxHighlight {
    fn default() -> SyntaxHighlight {
        SyntaxHighlight { theme: "base16-ocean.dark".to_owned() }
    }
}

const DATA_DIR: &'static str = "_data";

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct Config {
    pub source: String,
    pub dest: String,
    pub layouts: String,
    pub drafts: String,
    #[serde(skip)]
    pub data: &'static str,
    pub include_drafts: bool,
    pub posts: String,
    pub post_path: Option<String>,
    pub post_order: SortOrder,
    pub template_extensions: Vec<String>,
    pub rss: Option<String>,
    pub jsonfeed: Option<String>,
    pub name: Option<String>,
    pub description: Option<String>,
    pub link: Option<String>,
    pub ignore: Vec<String>,
    pub excerpt_separator: String,
    // This is a debug-only field and should be transient rather than persistently set.
    #[serde(skip)]
    pub dump: Vec<Dump>,
    pub syntax_highlight: SyntaxHighlight,
    pub sass: SassOptions,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            source: "./".to_owned(),
            dest: "./".to_owned(),
            layouts: "_layouts".to_owned(),
            drafts: "_drafts".to_owned(),
            data: DATA_DIR,
            include_drafts: false,
            posts: "posts".to_owned(),
            post_path: None,
            post_order: SortOrder::default(),
            template_extensions: vec!["md".to_owned(), "liquid".to_owned()],
            rss: None,
            jsonfeed: None,
            name: None,
            description: None,
            link: None,
            ignore: vec![],
            excerpt_separator: "\n\n".to_owned(),
            dump: vec![],
            syntax_highlight: SyntaxHighlight::default(),
            sass: SassOptions::default(),
        }
    }
}

impl Config {
    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Config> {
        let content = {
            let mut buffer = String::new();
            let mut f = File::open(path)?;
            f.read_to_string(&mut buffer)?;
            buffer
        };

        if content.trim().is_empty() {
            return Ok(Config::default());
        }

        let config: wildwest::GlobalConfig = serde_yaml::from_str(&content)?;
        let mut config: Config = config.into();

        config.link = if let Some(ref link) = config.link {
            let mut link = link.to_owned();
            if !link.ends_with('/') {
                link += "/";
            }
            Some(link)
        } else {
            None
        };

        let result: Result<()> = match has_syntax_theme(&config.syntax_highlight.theme) {
            Ok(true) => Ok(()),
            Ok(false) => {
                Err(format!("Syntax theme '{}' is unsupported",
                            config.syntax_highlight.theme)
                        .into())
            }
            Err(err) => {
                warn!("Syntax theme named '{}' ignored. Reason: {}",
                      config.syntax_highlight.theme,
                      err);
                Ok(())
            }
        };
        result?;

        Ok(config)
    }
}

#[test]
fn test_from_file_ok() {
    let result = Config::from_file("tests/fixtures/config/.cobalt.yml").unwrap();
    assert_eq!(result,
               Config {
                   dest: "./dest".to_owned(),
                   layouts: "_my_layouts".to_owned(),
                   posts: "_my_posts".to_owned(),
                   ..Default::default()
               });
}

#[test]
fn test_from_file_rss() {
    let result = Config::from_file("tests/fixtures/config/rss.yml").unwrap();
    assert_eq!(result,
               Config {
                   rss: Some("rss.xml".to_owned()),
                   name: Some("My blog!".to_owned()),
                   description: Some("Blog description".to_owned()),
                   link: Some("http://example.com/".to_owned()),
                   ..Default::default()
               });
}

#[test]
fn test_from_file_empty() {
    let result = Config::from_file("tests/fixtures/config/empty.yml").unwrap();
    assert_eq!((result), Config { ..Default::default() });
}

#[test]
fn test_from_file_invalid_syntax() {
    let result = Config::from_file("tests/fixtures/config/invalid_syntax.yml");
    assert!(result.is_err());
}

#[test]
fn test_from_file_not_found() {
    let result = Config::from_file("tests/fixtures/config/config_does_not_exist.yml");
    assert!(result.is_err());
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Tibor Benke <ihrwein@gmail.com>
//
// 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. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use handlebars::Template;
use uuid::Uuid;

use config::action::ActionType;
use conditions::Conditions;

mod deser;
pub mod action;

pub struct ContextConfig {
    pub name: Option<String>,
    pub uuid: Uuid,
    pub conditions: Conditions,
    pub context_id: Option<Template>,
    pub actions: Vec<ActionType>,
    pub patterns: Vec<String>
}

pub struct ContextConfigBuilder {
    name: Option<String>,
    uuid: Uuid,
    conditions: Conditions,
    context_id: Option<Template>,
    actions: Vec<ActionType>,
    patterns: Vec<String>
}

impl ContextConfigBuilder {
    pub fn new(uuid: Uuid, conditions: Conditions) -> ContextConfigBuilder {
        ContextConfigBuilder {
            name: None,
            uuid: uuid,
            conditions: conditions,
            context_id: None,
            actions: Vec::new(),
            patterns: Vec::new()
        }
    }

    pub fn context_id(mut self, context_id: Option<Template>) -> ContextConfigBuilder {
        self.context_id = context_id;
        self
    }

    pub fn actions(mut self, actions: Vec<ActionType>) -> ContextConfigBuilder {
        self.actions = actions;
        self
    }

    pub fn name(mut self, name: String) -> ContextConfigBuilder {
        self.name = Some(name);
        self
    }

    pub fn build(self) -> ContextConfig {
        ContextConfig {
            name: self.name,
            uuid: self.uuid,
            conditions: self.conditions,
            context_id: self.context_id,
            actions: self.actions,
            patterns: self.patterns
        }
    }
}
<commit_msg>ContextConfigBuilder: add patterns() method<commit_after>// Copyright (c) 2016 Tibor Benke <ihrwein@gmail.com>
//
// 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. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use handlebars::Template;
use uuid::Uuid;

use config::action::ActionType;
use conditions::Conditions;

mod deser;
pub mod action;

pub struct ContextConfig {
    pub name: Option<String>,
    pub uuid: Uuid,
    pub conditions: Conditions,
    pub context_id: Option<Template>,
    pub actions: Vec<ActionType>,
    pub patterns: Vec<String>
}

pub struct ContextConfigBuilder {
    name: Option<String>,
    uuid: Uuid,
    conditions: Conditions,
    context_id: Option<Template>,
    actions: Vec<ActionType>,
    patterns: Vec<String>
}

impl ContextConfigBuilder {
    pub fn new(uuid: Uuid, conditions: Conditions) -> ContextConfigBuilder {
        ContextConfigBuilder {
            name: None,
            uuid: uuid,
            conditions: conditions,
            context_id: None,
            actions: Vec::new(),
            patterns: Vec::new()
        }
    }

    pub fn context_id(mut self, context_id: Option<Template>) -> ContextConfigBuilder {
        self.context_id = context_id;
        self
    }

    pub fn actions(mut self, actions: Vec<ActionType>) -> ContextConfigBuilder {
        self.actions = actions;
        self
    }

    pub fn name(mut self, name: String) -> ContextConfigBuilder {
        self.name = Some(name);
        self
    }

    pub fn patterns(mut self, patterns: Vec<String>) -> ContextConfigBuilder {
        self.patterns = patterns;
        self
    }

    pub fn build(self) -> ContextConfig {
        ContextConfig {
            name: self.name,
            uuid: self.uuid,
            conditions: self.conditions,
            context_id: self.context_id,
            actions: self.actions,
            patterns: self.patterns
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::Read;
use toml::{self, Value};

use errors::*;

pub mod bookconfig;
pub mod htmlconfig;
pub mod playpenconfig;
pub mod tomlconfig;
pub mod jsonconfig;

// Re-export the config structs
pub use self::bookconfig::BookConfig;
pub use self::htmlconfig::HtmlConfig;
pub use self::playpenconfig::PlaypenConfig;
pub use self::tomlconfig::TomlConfig;

/// The overall configuration object for MDBook.
#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct Config {
    /// Metadata about the book.
    pub book: BookConfig_,
    /// Arbitrary information which renderers can use during the rendering
    /// stage.
    pub output: BTreeMap<String, Value>,
    /// Information for use by preprocessors.
    pub preprocess: BTreeMap<String, Value>,
    /// Information for use by postprocessors.
    pub postprocess: BTreeMap<String, Value>,
}

impl Config {
    /// Load a `Config` from some string.
    pub fn from_str(src: &str) -> Result<Config> {
        toml::from_str(src).chain_err(|| Error::from("Invalid configuration file"))
    }

    /// Load the configuration file from disk.
    pub fn from_disk<P: AsRef<Path>>(config_file: P) -> Result<Config> {
        let mut buffer = String::new();
        File::open(config_file)
            .chain_err(|| "Unable to open the configuration file")?
            .read_to_string(&mut buffer)
            .chain_err(|| "Couldn't read the file")?;

        Config::from_str(&buffer)
    }

    /// Convenience method for getting the html renderer's configuration.
    ///
    /// # Note
    ///
    /// This is for compatibility only. It will be removed completely once the
    /// rendering and plugin system is established.
    pub fn html_config(&self) -> Option<HtmlConfig_> {
        self.output
            .get("html")
            .and_then(|value| HtmlConfig_::from_toml(value).ok())
    }
}

/// Configuration options which are specific to the book and required for
/// loading it from disk.
#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(default, rename_all = "kebab-case")]
pub struct BookConfig_ {
    /// The book's title.
    pub title: Option<String>,
    /// The book's authors.
    pub authors: Vec<String>,
    /// An optional description for the book.
    pub description: Option<String>,
    /// Location of the book source, relative to the book's root directory.
    pub src: PathBuf,
    /// Where to put built artefacts, relative to the book's root directory.
    pub build_dir: PathBuf,
    /// Does this book support more than one language?
    pub multilingual: bool,
}

#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(default, rename_all = "kebab-case")]
pub struct HtmlConfig_ {
    pub theme: Option<PathBuf>,
    pub curly_quotes: bool,
    pub mathjax_support: bool,
    pub google_analytics: Option<String>,
    pub additional_css: Vec<PathBuf>,
    pub additional_js: Vec<PathBuf>,
    pub playpen: Playpen,
}

impl HtmlConfig_ {
    pub fn from_toml(value: &Value) -> Result<HtmlConfig_> {
        value
            .clone()
            .try_into()
            .chain_err(|| "Unable to deserialize the HTML config")
    }
}

#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct Playpen;
<commit_msg>Added some basic deserializing tests and helpers<commit_after>use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::Read;
use toml::{self, Value};
use serde::Deserialize;

use errors::*;

pub mod bookconfig;
pub mod htmlconfig;
pub mod playpenconfig;
pub mod tomlconfig;
pub mod jsonconfig;

// Re-export the config structs
pub use self::bookconfig::BookConfig;
pub use self::htmlconfig::HtmlConfig;
pub use self::playpenconfig::PlaypenConfig;
pub use self::tomlconfig::TomlConfig;

/// The overall configuration object for MDBook.
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct Config {
    /// Metadata about the book.
    pub book: BookConfig_,
    /// Arbitrary information which renderers can use during the rendering
    /// stage.
    pub output: BTreeMap<String, Value>,
    /// Information for use by preprocessors.
    pub preprocess: BTreeMap<String, Value>,
    /// Information for use by postprocessors.
    pub postprocess: BTreeMap<String, Value>,
}

impl Config {
    /// Load a `Config` from some string.
    pub fn from_str(src: &str) -> Result<Config> {
        toml::from_str(src).chain_err(|| Error::from("Invalid configuration file"))
    }

    /// Load the configuration file from disk.
    pub fn from_disk<P: AsRef<Path>>(config_file: P) -> Result<Config> {
        let mut buffer = String::new();
        File::open(config_file)
            .chain_err(|| "Unable to open the configuration file")?
            .read_to_string(&mut buffer)
            .chain_err(|| "Couldn't read the file")?;

        Config::from_str(&buffer)
    }

    /// Convenience method for getting the html renderer's configuration.
    ///
    /// # Note
    ///
    /// This is for compatibility only. It will be removed completely once the
    /// rendering and plugin system is established.
    pub fn html_config(&self) -> Option<HtmlConfig_> {
        self.try_get_output("html").ok()
    }

    /// Try to get an output and deserialize it as a `T`.
    pub fn try_get_output<'de, T: Deserialize<'de>, S: AsRef<str>>(&self, name: S) -> Result<T> {
        get_deserialized(name, &self.output)
    }

    /// Try to get the configuration for a preprocessor, deserializing it as a
    /// `T`.
    pub fn try_get_preprocessor<'de, T: Deserialize<'de>, S: AsRef<str>>(&self, name: S) -> Result<T> {
        get_deserialized(name, &self.preprocess)
    }

    /// Try to get the configuration for a postprocessor, deserializing it as a
    /// `T`.
    pub fn try_get_postprocessor<'de, T: Deserialize<'de>, S: AsRef<str>>(&self, name: S) -> Result<T> {
        get_deserialized(name, &self.postprocess)
    }
}

/// Convenience function to load a value from some table then deserialize it.
fn get_deserialized<'de, T: Deserialize<'de>, S: AsRef<str>>(name: S, table: &BTreeMap<String, Value>) -> Result<T> {
    match table.get(name.as_ref()) {
        Some(output) => output
            .clone()
            .try_into()
            .chain_err(|| "Couldn't deserialize the value"),
        None => bail!("Key Not Found"),
    }
}

/// Configuration options which are specific to the book and required for
/// loading it from disk.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(default, rename_all = "kebab-case")]
pub struct BookConfig_ {
    /// The book's title.
    pub title: Option<String>,
    /// The book's authors.
    pub authors: Vec<String>,
    /// An optional description for the book.
    pub description: Option<String>,
    /// Location of the book source, relative to the book's root directory.
    pub src: PathBuf,
    /// Where to put built artefacts, relative to the book's root directory.
    pub build_dir: PathBuf,
    /// Does this book support more than one language?
    pub multilingual: bool,
}

impl Default for BookConfig_ {
    fn default() -> BookConfig_ {
        BookConfig_ {
            title: None,
            authors: Vec::new(),
            description: None,
            src: PathBuf::from("src"),
            build_dir: PathBuf::from("build"),
            multilingual: false,
        }
    }
}

#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(default, rename_all = "kebab-case")]
pub struct HtmlConfig_ {
    pub theme: Option<PathBuf>,
    pub curly_quotes: bool,
    pub mathjax_support: bool,
    pub google_analytics: Option<String>,
    pub additional_css: Vec<PathBuf>,
    pub additional_js: Vec<PathBuf>,
    pub playpen: Playpen,
}

#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct Playpen;

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

    #[test]
    fn load_a_complex_config_file() {
        let src = r#"
        [book]
        title = "Some Book"
        authors = ["Michael-F-Bryan <michaelfbryan@gmail.com>"]
        description = "A completely useless book"
        multilingual = true
        src = "source"
        build-dir = "outputs"

        [output.html]
        curly-quotes = true
        google-analytics = "123456"
        additional-css = ["./foo/bar/baz.css"]
        "#;

        let book_should_be = BookConfig_ {
            title: Some(String::from("Some Book")),
            authors: vec![String::from("Michael-F-Bryan <michaelfbryan@gmail.com>")],
            description: Some(String::from("A completely useless book")),
            multilingual: true,
            src: PathBuf::from("source"),
            build_dir: PathBuf::from("outputs"),
            ..Default::default()
        };
        let html_should_be = HtmlConfig_ {
            curly_quotes: true,
            google_analytics: Some(String::from("123456")),
            additional_css: vec![PathBuf::from("./foo/bar/baz.css")],
            ..Default::default()
        };

        let got = Config::from_str(src).unwrap();

        assert_eq!(got.book, book_should_be);
        assert_eq!(got.html_config().unwrap(), html_should_be);
    }

    #[test]
    fn load_arbitrary_output_type() {
        #[derive(Debug, Deserialize, PartialEq)]
        struct RandomOutput {
            foo: u32,
            bar: String,
            baz: Vec<bool>,
        }

        let src = r#"
        [output.random]
        foo = 5
        bar = "Hello World"
        baz = [true, true, false]
        "#;

        let should_be = RandomOutput {
            foo: 5,
            bar: String::from("Hello World"),
            baz: vec![true, true, false],
        };

        let cfg = Config::from_str(src).unwrap();
        let got: RandomOutput = cfg.try_get_output("random").unwrap();

        assert_eq!(got, should_be);
    }
}
<|endoftext|>


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

// Handles invoking external binaries.
// This module assumes that, for a given machine, there is only one place
// where the desired executable might be installed. It expects the engine
// to identify that place at its initialization by invoking verify_binaries(),
// and to exit immediately if verify_binaries() return an error. If this
// protocol is followed then when any command is executed the unique absolute
// path of the binary for this machine will already have been identified.
// However stratisd may run for a while and it is possible for the binary
// to be caused to be uninstalled while stratisd is being run. Therefore,
// the existence of the file is checked before the command is invoked, and
// an explicit error is returned if the executable can not be found.

use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::process::Command;

use uuid::Uuid;

use stratis::{ErrorEnum, StratisError, StratisResult};

/// Find the binary with the given name by looking in likely locations.
/// Return None if no binary was found.
/// Search an explicit list of directories rather than the user's PATH
/// environment variable. stratisd may be running when there is no PATH
/// variable set.
fn find_binary(name: &str) -> Option<PathBuf> {
    ["/usr/sbin", "/sbin", "/usr/bin", "/bin"]
        .iter()
        .map(|pre| [pre, name].iter().collect::<PathBuf>())
        .find(|path| path.exists())
}

// These are the external binaries that stratisd relies on.
// Any change in this list requires a corresponding change to BINARIES,
// and vice-versa.
const MKFS_XFS: &str = "mkfs.xfs";
const THIN_CHECK: &str = "thin_check";
const THIN_REPAIR: &str = "thin_repair";
const XFS_ADMIN: &str = "xfs_admin";
const XFS_GROWFS: &str = "xfs_growfs";

lazy_static! {
    static ref BINARIES: HashMap<String, Option<PathBuf>> = [
        (MKFS_XFS.to_string(), find_binary(MKFS_XFS)),
        (THIN_CHECK.to_string(), find_binary(THIN_CHECK)),
        (THIN_REPAIR.to_string(), find_binary(THIN_REPAIR)),
        (XFS_ADMIN.to_string(), find_binary(XFS_ADMIN)),
        (XFS_GROWFS.to_string(), find_binary(XFS_GROWFS))
    ].iter()
        .cloned()
        .collect();
}

/// Verify that all binaries that the engine might invoke are available at some
/// path. Return an error if any are missing. Required to be called on engine
/// initialization.
pub fn verify_binaries() -> StratisResult<()> {
    match BINARIES.iter().find(|(_, ref path)| path.is_none()) {
        None => Ok(()),
        Some((ref name, _)) => Err(StratisError::Error(format!(
            "Unable to find absolute path for \"{}\"",
            name
        ))),
    }
}

/// Common function to call a command line utility, returning an Result with an error message which
/// also includes stdout & stderr if it fails.
fn execute_cmd(cmd: &mut Command, error_msg: &str) -> StratisResult<()> {
    let result = cmd.output()?;
    if result.status.success() {
        Ok(())
    } else {
        let std_out_txt = String::from_utf8_lossy(&result.stdout);
        let std_err_txt = String::from_utf8_lossy(&result.stderr);
        let err_msg = format!(
            "{} stdout: {} stderr: {}",
            error_msg, std_out_txt, std_err_txt
        );
        Err(StratisError::Engine(ErrorEnum::Error, err_msg))
    }
}

/// Get an absolute path for the executable with the given name.
/// Precondition: verify_binaries() has already been invoked.
fn get_executable(name: &str) -> StratisResult<PathBuf> {
    let executable = BINARIES
        .get(name)
        .expect("name arguments are all constants defined with BINARIES, lookup can not fail")
        .as_ref()
        .expect("verify_binaries() was previously called and returned no error");
    if !executable.exists() {
        return Err(StratisError::Error(format!(
            "Executable previously located at \"{}\" seems to have been removed since stratisd was started",
            executable.to_str().expect("All parts of path are constructed from strictly ASCII components")
        )));
    }
    Ok(executable.to_path_buf())
}

/// Create a filesystem on devnode.
pub fn create_fs(devnode: &Path, uuid: Uuid) -> StratisResult<()> {
    execute_cmd(
        Command::new(get_executable(MKFS_XFS)?.as_os_str())
            .arg("-f")
            .arg("-q")
            .arg(&devnode)
            .arg("-m")
            .arg(format!("uuid={}", uuid)),
        &format!("Failed to create new filesystem at {:?}", devnode),
    )
}

/// Use the xfs_growfs command to expand a filesystem mounted at the given
/// mount point.
pub fn xfs_growfs(mount_point: &Path) -> StratisResult<()> {
    execute_cmd(
        Command::new(get_executable(XFS_GROWFS)?.as_os_str())
            .arg(mount_point)
            .arg("-d"),
        &format!("Failed to expand filesystem {:?}", mount_point),
    )
}

/// Set a new UUID for filesystem on the devnode.
pub fn set_uuid(devnode: &Path, uuid: Uuid) -> StratisResult<()> {
    execute_cmd(
        Command::new(get_executable(XFS_ADMIN)?.as_os_str())
            .arg("-U")
            .arg(format!("{}", uuid))
            .arg(&devnode),
        &format!("Failed to set UUID for filesystem {:?}", devnode),
    )
}

/// Call thin_check on a thinpool
pub fn thin_check(devnode: &Path) -> StratisResult<()> {
    execute_cmd(
        Command::new(get_executable(THIN_CHECK)?.as_os_str())
            .arg("-q")
            .arg(devnode),
        &format!("thin_check for thin pool meta device {:?} failed", devnode),
    )
}

/// Call thin_repair on a thinpool
pub fn thin_repair(meta_dev: &Path, new_meta_dev: &Path) -> StratisResult<()> {
    execute_cmd(
        Command::new(get_executable(THIN_REPAIR)?.as_os_str())
            .arg("-i")
            .arg(meta_dev)
            .arg("-o")
            .arg(new_meta_dev),
        &format!("thin_repair of thin pool meta device {:?} failed", meta_dev),
    )
}
<commit_msg>Strip message argument out of execute_cmd<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Handles invoking external binaries.
// This module assumes that, for a given machine, there is only one place
// where the desired executable might be installed. It expects the engine
// to identify that place at its initialization by invoking verify_binaries(),
// and to exit immediately if verify_binaries() return an error. If this
// protocol is followed then when any command is executed the unique absolute
// path of the binary for this machine will already have been identified.
// However stratisd may run for a while and it is possible for the binary
// to be caused to be uninstalled while stratisd is being run. Therefore,
// the existence of the file is checked before the command is invoked, and
// an explicit error is returned if the executable can not be found.

use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::process::Command;

use uuid::Uuid;

use stratis::{StratisError, StratisResult};

/// Find the binary with the given name by looking in likely locations.
/// Return None if no binary was found.
/// Search an explicit list of directories rather than the user's PATH
/// environment variable. stratisd may be running when there is no PATH
/// variable set.
fn find_binary(name: &str) -> Option<PathBuf> {
    ["/usr/sbin", "/sbin", "/usr/bin", "/bin"]
        .iter()
        .map(|pre| [pre, name].iter().collect::<PathBuf>())
        .find(|path| path.exists())
}

// These are the external binaries that stratisd relies on.
// Any change in this list requires a corresponding change to BINARIES,
// and vice-versa.
const MKFS_XFS: &str = "mkfs.xfs";
const THIN_CHECK: &str = "thin_check";
const THIN_REPAIR: &str = "thin_repair";
const XFS_ADMIN: &str = "xfs_admin";
const XFS_GROWFS: &str = "xfs_growfs";

lazy_static! {
    static ref BINARIES: HashMap<String, Option<PathBuf>> = [
        (MKFS_XFS.to_string(), find_binary(MKFS_XFS)),
        (THIN_CHECK.to_string(), find_binary(THIN_CHECK)),
        (THIN_REPAIR.to_string(), find_binary(THIN_REPAIR)),
        (XFS_ADMIN.to_string(), find_binary(XFS_ADMIN)),
        (XFS_GROWFS.to_string(), find_binary(XFS_GROWFS))
    ].iter()
        .cloned()
        .collect();
}

/// Verify that all binaries that the engine might invoke are available at some
/// path. Return an error if any are missing. Required to be called on engine
/// initialization.
pub fn verify_binaries() -> StratisResult<()> {
    match BINARIES.iter().find(|(_, ref path)| path.is_none()) {
        None => Ok(()),
        Some((ref name, _)) => Err(StratisError::Error(format!(
            "Unable to find absolute path for \"{}\"",
            name
        ))),
    }
}

/// Invoke the specified command. Return an error if invoking the command
/// fails or if the command itself fails.
fn execute_cmd(cmd: &mut Command) -> StratisResult<()> {
    match cmd.output() {
        Err(err) => Err(StratisError::Error(format!(
            "Failed to execute command {:?}, err: {:?}",
            cmd, err
        ))),
        Ok(result) => {
            if result.status.success() {
                Ok(())
            } else {
                let std_out_txt = String::from_utf8_lossy(&result.stdout);
                let std_err_txt = String::from_utf8_lossy(&result.stderr);
                let err_msg = format!(
                    "Command failed: cmd: {:?}, stdout: {} stderr: {}",
                    cmd, std_out_txt, std_err_txt
                );
                Err(StratisError::Error(err_msg))
            }
        }
    }
}

/// Get an absolute path for the executable with the given name.
/// Precondition: verify_binaries() has already been invoked.
fn get_executable(name: &str) -> StratisResult<PathBuf> {
    Ok(BINARIES
        .get(name)
        .expect("name arguments are all constants defined with BINARIES, lookup can not fail")
        .as_ref()
        .expect("verify_binaries() was previously called and returned no error")
        .to_path_buf())
}

/// Create a filesystem on devnode.
pub fn create_fs(devnode: &Path, uuid: Uuid) -> StratisResult<()> {
    execute_cmd(
        Command::new(get_executable(MKFS_XFS)?.as_os_str())
            .arg("-f")
            .arg("-q")
            .arg(&devnode)
            .arg("-m")
            .arg(format!("uuid={}", uuid)),
    )
}

/// Use the xfs_growfs command to expand a filesystem mounted at the given
/// mount point.
pub fn xfs_growfs(mount_point: &Path) -> StratisResult<()> {
    execute_cmd(
        Command::new(get_executable(XFS_GROWFS)?.as_os_str())
            .arg(mount_point)
            .arg("-d"),
    )
}

/// Set a new UUID for filesystem on the devnode.
pub fn set_uuid(devnode: &Path, uuid: Uuid) -> StratisResult<()> {
    execute_cmd(
        Command::new(get_executable(XFS_ADMIN)?.as_os_str())
            .arg("-U")
            .arg(format!("{}", uuid))
            .arg(&devnode),
    )
}

/// Call thin_check on a thinpool
pub fn thin_check(devnode: &Path) -> StratisResult<()> {
    execute_cmd(
        Command::new(get_executable(THIN_CHECK)?.as_os_str())
            .arg("-q")
            .arg(devnode),
    )
}

/// Call thin_repair on a thinpool
pub fn thin_repair(meta_dev: &Path, new_meta_dev: &Path) -> StratisResult<()> {
    execute_cmd(
        Command::new(get_executable(THIN_REPAIR)?.as_os_str())
            .arg("-i")
            .arg(meta_dev)
            .arg("-o")
            .arg(new_meta_dev),
    )
}
<|endoftext|>


<commit_before>//! Elements of the WebAssembly binary format.

use crate::rust::{fmt, vec::Vec, format, string::String};
use crate::io;

macro_rules! buffered_read {
	($buffer_size: expr, $length: expr, $reader: expr) => {
		{
			let mut vec_buf = Vec::new();
			let mut total_read = 0;
			let mut buf = [0u8; $buffer_size];
			while total_read < $length {
				let next_to_read = if $length - total_read > $buffer_size { $buffer_size } else { $length - total_read };
				$reader.read(&mut buf[0..next_to_read])?;
				vec_buf.extend_from_slice(&buf[0..next_to_read]);
				total_read += next_to_read;
			}
			vec_buf
		}
	}
}

mod primitives;
mod module;
mod section;
mod types;
mod import_entry;
mod export_entry;
mod global_entry;
mod ops;
mod func;
mod segment;
mod index_map;
mod name_section;
mod reloc_section;

pub use self::module::{Module, peek_size, ImportCountType};
pub use self::section::{
	Section, FunctionSection, CodeSection, MemorySection, DataSection,
	ImportSection, ExportSection, GlobalSection, TypeSection, ElementSection,
	TableSection, CustomSection,
};
pub use self::import_entry::{ImportEntry, ResizableLimits, MemoryType, TableType, GlobalType, External};
pub use self::export_entry::{ExportEntry, Internal};
pub use self::global_entry::GlobalEntry;
pub use self::primitives::{
	VarUint32, VarUint7, Uint8, VarUint1, VarInt7, Uint32, VarInt32, VarInt64,
	Uint64, VarUint64, CountedList, CountedWriter, CountedListWriter,
};
pub use self::types::{Type, ValueType, BlockType, FunctionType, TableElementType};
pub use self::ops::{Instruction, Instructions, InitExpr, opcodes, BrTableData};

#[cfg(feature="atomics")]
pub use self::ops::AtomicsInstruction;

#[cfg(feature="simd")]
pub use self::ops::SimdInstruction;

#[cfg(feature="sign_ext")]
pub use self::ops::SignExtInstruction;

#[cfg(feature="bulk")]
pub use self::ops::BulkInstruction;

#[cfg(any(feature="simd", feature="atomics"))]
pub use self::ops::MemArg;

pub use self::func::{Func, FuncBody, Local};
pub use self::segment::{ElementSegment, DataSegment};
pub use self::index_map::IndexMap;
pub use self::name_section::{
	NameMap, NameSection, ModuleNameSubsection, FunctionNameSubsection,
	LocalNameSubsection,
};
pub use self::reloc_section::{
	RelocSection, RelocationEntry,
};

/// Deserialization from serial i/o.
pub trait Deserialize : Sized {
	/// Serialization error produced by deserialization routine.
	type Error: From<io::Error>;
	/// Deserialize type from serial i/o
	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error>;
}

/// Serialization to serial i/o. Takes self by value to consume less memory
/// (parity-wasm IR is being partially freed by filling the result buffer).
pub trait Serialize {
	/// Serialization error produced by serialization routine.
	type Error: From<io::Error>;
	/// Serialize type to serial i/o
	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error>;
}

/// Deserialization/serialization error
#[derive(Debug, Clone)]
pub enum Error {
	/// Unexpected end of input.
	UnexpectedEof,
	/// Invalid magic.
	InvalidMagic,
	/// Unsupported version.
	UnsupportedVersion(u32),
	/// Inconsistence between declared and actual length.
	InconsistentLength {
		/// Expected length of the definition.
		expected: usize,
		/// Actual length of the definition.
		actual: usize
	},
	/// Other static error.
	Other(&'static str),
	/// Other allocated error.
	HeapOther(String),
	/// Invalid/unknown value type declaration.
	UnknownValueType(i8),
	/// Invalid/unknown table element type declaration.
	UnknownTableElementType(i8),
	/// Non-utf8 string.
	NonUtf8String,
	/// Unknown external kind code.
	UnknownExternalKind(u8),
	/// Unknown internal kind code.
	UnknownInternalKind(u8),
	/// Unknown opcode encountered.
	UnknownOpcode(u8),
	#[cfg(feature="simd")]
	/// Unknown SIMD opcode encountered.
	UnknownSimdOpcode(u32),
	/// Invalid VarUint1 value.
	InvalidVarUint1(u8),
	/// Invalid VarInt32 value.
	InvalidVarInt32,
	/// Invalid VarInt64 value.
	InvalidVarInt64,
	/// Invalid VarUint32 value.
	InvalidVarUint32,
	/// Invalid VarUint64 value.
	InvalidVarUint64,
	/// Inconsistent metadata.
	InconsistentMetadata,
	/// Invalid section id.
	InvalidSectionId(u8),
	/// Sections are out of order.
	SectionsOutOfOrder,
	/// Duplicated sections.
	DuplicatedSections(u8),
	/// Invalid memory reference (should be 0).
	InvalidMemoryReference(u8),
	/// Invalid table reference (should be 0).
	InvalidTableReference(u8),
	/// Invalid value used for flags in limits type.
	InvalidLimitsFlags(u8),
	/// Unknown function form (should be 0x60).
	UnknownFunctionForm(u8),
	/// Invalid varint7 (should be in -64..63 range).
	InvalidVarInt7(u8),
	/// Number of function body entries and signatures does not match.
	InconsistentCode,
	/// Only flags 0, 1, and 2 are accepted on segments.
	InvalidSegmentFlags(u32),
	/// Sum of counts of locals is greater than 2^32.
	TooManyLocals,
	/// Duplicated name subsections.
	DuplicatedNameSubsections(u8),
	/// Unknown name subsection type.
	UnknownNameSubsectionType(u8),
}

impl fmt::Display for Error {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		match *self {
			Error::UnexpectedEof => write!(f, "Unexpected end of input"),
			Error::InvalidMagic => write!(f, "Invalid magic number at start of file"),
			Error::UnsupportedVersion(v) => write!(f, "Unsupported wasm version {}", v),
			Error::InconsistentLength { expected, actual } => {
				write!(f, "Expected length {}, found {}", expected, actual)
			}
			Error::Other(msg) => write!(f, "{}", msg),
			Error::HeapOther(ref msg) => write!(f, "{}", msg),
			Error::UnknownValueType(ty) => write!(f, "Invalid or unknown value type {}", ty),
			Error::UnknownTableElementType(ty) => write!(f, "Unknown table element type {}", ty),
			Error::NonUtf8String => write!(f, "Non-UTF-8 string"),
			Error::UnknownExternalKind(kind) => write!(f, "Unknown external kind {}", kind),
			Error::UnknownInternalKind(kind) => write!(f, "Unknown internal kind {}", kind),
			Error::UnknownOpcode(opcode) => write!(f, "Unknown opcode {}", opcode),
			#[cfg(feature="simd")]
			Error::UnknownSimdOpcode(opcode) => write!(f, "Unknown SIMD opcode {}", opcode),
			Error::InvalidVarUint1(val) => write!(f, "Not an unsigned 1-bit integer: {}", val),
			Error::InvalidVarInt7(val) => write!(f, "Not a signed 7-bit integer: {}", val),
			Error::InvalidVarInt32 => write!(f, "Not a signed 32-bit integer"),
			Error::InvalidVarUint32 => write!(f, "Not an unsigned 32-bit integer"),
			Error::InvalidVarInt64 => write!(f, "Not a signed 64-bit integer"),
			Error::InvalidVarUint64 => write!(f, "Not an unsigned 64-bit integer"),
			Error::InconsistentMetadata =>  write!(f, "Inconsistent metadata"),
			Error::InvalidSectionId(ref id) =>  write!(f, "Invalid section id: {}", id),
			Error::SectionsOutOfOrder =>  write!(f, "Sections out of order"),
			Error::DuplicatedSections(ref id) =>  write!(f, "Duplicated sections ({})", id),
			Error::InvalidMemoryReference(ref mem_ref) =>  write!(f, "Invalid memory reference ({})", mem_ref),
			Error::InvalidTableReference(ref table_ref) =>  write!(f, "Invalid table reference ({})", table_ref),
			Error::InvalidLimitsFlags(ref flags) =>  write!(f, "Invalid limits flags ({})", flags),
			Error::UnknownFunctionForm(ref form) =>  write!(f, "Unknown function form ({})", form),
			Error::InconsistentCode =>  write!(f, "Number of function body entries and signatures does not match"),
			Error::InvalidSegmentFlags(n) =>  write!(f, "Invalid segment flags: {}", n),
			Error::TooManyLocals => write!(f, "Too many locals"),
			Error::DuplicatedNameSubsections(n) =>  write!(f, "Duplicated name subsections: {}", n),
			Error::UnknownNameSubsectionType(n) => write!(f, "Unknown subsection type: {}", n),
		}
	}
}

#[cfg(feature = "std")]
impl ::std::error::Error for Error {
	fn description(&self) -> &str {
		match *self {
			Error::UnexpectedEof => "Unexpected end of input",
			Error::InvalidMagic => "Invalid magic number at start of file",
			Error::UnsupportedVersion(_) => "Unsupported wasm version",
			Error::InconsistentLength { .. } => "Inconsistent length",
			Error::Other(msg) => msg,
			Error::HeapOther(ref msg) => &msg[..],
			Error::UnknownValueType(_) => "Invalid or unknown value type",
			Error::UnknownTableElementType(_) => "Unknown table element type",
			Error::NonUtf8String => "Non-UTF-8 string",
			Error::UnknownExternalKind(_) => "Unknown external kind",
			Error::UnknownInternalKind(_) => "Unknown internal kind",
			Error::UnknownOpcode(_) => "Unknown opcode",
			#[cfg(feature="simd")]
			Error::UnknownSimdOpcode(_) => "Unknown SIMD opcode",
			Error::InvalidVarUint1(_) => "Not an unsigned 1-bit integer",
			Error::InvalidVarInt32 => "Not a signed 32-bit integer",
			Error::InvalidVarInt7(_) => "Not a signed 7-bit integer",
			Error::InvalidVarUint32 => "Not an unsigned 32-bit integer",
			Error::InvalidVarInt64 => "Not a signed 64-bit integer",
			Error::InvalidVarUint64 => "Not an unsigned 64-bit integer",
			Error::InconsistentMetadata => "Inconsistent metadata",
			Error::InvalidSectionId(_) =>  "Invalid section id",
			Error::SectionsOutOfOrder =>  "Sections out of order",
			Error::DuplicatedSections(_) =>  "Duplicated section",
			Error::InvalidMemoryReference(_) =>  "Invalid memory reference",
			Error::InvalidTableReference(_) =>  "Invalid table reference",
			Error::InvalidLimitsFlags(_) => "Invalid limits flags",
			Error::UnknownFunctionForm(_) =>  "Unknown function form",
			Error::InconsistentCode =>  "Number of function body entries and signatures does not match",
			Error::InvalidSegmentFlags(_) =>  "Invalid segment flags",
			Error::TooManyLocals => "Too many locals",
			Error::DuplicatedNameSubsections(_) =>  "Duplicated name subsections",
			Error::UnknownNameSubsectionType(_) => "Unknown name subsections type",
		}
	}
}

impl From<io::Error> for Error {
	fn from(err: io::Error) -> Self {
		Error::HeapOther(format!("I/O Error: {:?}", err))
	}
}

// These are emitted by section parsers, such as `parse_names` and `parse_reloc`.
impl From<(Vec<(usize, Error)>, Module)> for Error {
	fn from(err: (Vec<(usize, Error)>, Module)) -> Self {
		let ret = err.0.iter()
			.fold(
				String::new(),
				|acc, item| acc + &format!("In section {}: {}\n", item.0, item.1)
			);
		Error::HeapOther(ret)
	}
}

/// Unparsed part of the module/section.
pub struct Unparsed(pub Vec<u8>);

impl Deserialize for Unparsed {
	type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let len = VarUint32::deserialize(reader)?.into();
		let mut vec = vec![0u8; len];
		reader.read(&mut vec[..])?;
		Ok(Unparsed(vec))
	}
}

impl From<Unparsed> for Vec<u8> {
	fn from(u: Unparsed) -> Vec<u8> {
		u.0
	}
}

/// Deserialize deserializable type from buffer.
pub fn deserialize_buffer<T: Deserialize>(contents: &[u8]) -> Result<T, T::Error> {
	let mut reader = io::Cursor::new(contents);
	let result = T::deserialize(&mut reader)?;
	if reader.position() != contents.len() {
		// It's a TrailingData, since if there is not enough data then
		// UnexpectedEof must have been returned earlier in T::deserialize.
		return Err(io::Error::TrailingData.into())
	}
	Ok(result)
}

/// Create buffer with serialized value.
pub fn serialize<T: Serialize>(val: T) -> Result<Vec<u8>, T::Error> {
	let mut buf = Vec::new();
	val.serialize(&mut buf)?;
	Ok(buf)
}

/// Deserialize module from the file.
#[cfg(feature = "std")]
pub fn deserialize_file<P: AsRef<::std::path::Path>>(p: P) -> Result<Module, Error> {
	let mut f = ::std::fs::File::open(p)
		.map_err(|e| Error::HeapOther(format!("Can't read from the file: {:?}", e)))?;

	Module::deserialize(&mut f)
}

/// Serialize module to the file
#[cfg(feature = "std")]
pub fn serialize_to_file<P: AsRef<::std::path::Path>>(p: P, module: Module) -> Result<(), Error> {
	let mut io = ::std::fs::File::create(p)
		.map_err(|e|
			Error::HeapOther(format!("Can't create the file: {:?}", e))
		)?;

	module.serialize(&mut io)?;
	Ok(())
}
<commit_msg>Optimize string creation in from trait<commit_after>//! Elements of the WebAssembly binary format.

use crate::rust::{fmt, vec::Vec, format, string::String};
use crate::io;

macro_rules! buffered_read {
	($buffer_size: expr, $length: expr, $reader: expr) => {
		{
			let mut vec_buf = Vec::new();
			let mut total_read = 0;
			let mut buf = [0u8; $buffer_size];
			while total_read < $length {
				let next_to_read = if $length - total_read > $buffer_size { $buffer_size } else { $length - total_read };
				$reader.read(&mut buf[0..next_to_read])?;
				vec_buf.extend_from_slice(&buf[0..next_to_read]);
				total_read += next_to_read;
			}
			vec_buf
		}
	}
}

mod primitives;
mod module;
mod section;
mod types;
mod import_entry;
mod export_entry;
mod global_entry;
mod ops;
mod func;
mod segment;
mod index_map;
mod name_section;
mod reloc_section;

pub use self::module::{Module, peek_size, ImportCountType};
pub use self::section::{
	Section, FunctionSection, CodeSection, MemorySection, DataSection,
	ImportSection, ExportSection, GlobalSection, TypeSection, ElementSection,
	TableSection, CustomSection,
};
pub use self::import_entry::{ImportEntry, ResizableLimits, MemoryType, TableType, GlobalType, External};
pub use self::export_entry::{ExportEntry, Internal};
pub use self::global_entry::GlobalEntry;
pub use self::primitives::{
	VarUint32, VarUint7, Uint8, VarUint1, VarInt7, Uint32, VarInt32, VarInt64,
	Uint64, VarUint64, CountedList, CountedWriter, CountedListWriter,
};
pub use self::types::{Type, ValueType, BlockType, FunctionType, TableElementType};
pub use self::ops::{Instruction, Instructions, InitExpr, opcodes, BrTableData};

#[cfg(feature="atomics")]
pub use self::ops::AtomicsInstruction;

#[cfg(feature="simd")]
pub use self::ops::SimdInstruction;

#[cfg(feature="sign_ext")]
pub use self::ops::SignExtInstruction;

#[cfg(feature="bulk")]
pub use self::ops::BulkInstruction;

#[cfg(any(feature="simd", feature="atomics"))]
pub use self::ops::MemArg;

pub use self::func::{Func, FuncBody, Local};
pub use self::segment::{ElementSegment, DataSegment};
pub use self::index_map::IndexMap;
pub use self::name_section::{
	NameMap, NameSection, ModuleNameSubsection, FunctionNameSubsection,
	LocalNameSubsection,
};
pub use self::reloc_section::{
	RelocSection, RelocationEntry,
};

/// Deserialization from serial i/o.
pub trait Deserialize : Sized {
	/// Serialization error produced by deserialization routine.
	type Error: From<io::Error>;
	/// Deserialize type from serial i/o
	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error>;
}

/// Serialization to serial i/o. Takes self by value to consume less memory
/// (parity-wasm IR is being partially freed by filling the result buffer).
pub trait Serialize {
	/// Serialization error produced by serialization routine.
	type Error: From<io::Error>;
	/// Serialize type to serial i/o
	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error>;
}

/// Deserialization/serialization error
#[derive(Debug, Clone)]
pub enum Error {
	/// Unexpected end of input.
	UnexpectedEof,
	/// Invalid magic.
	InvalidMagic,
	/// Unsupported version.
	UnsupportedVersion(u32),
	/// Inconsistence between declared and actual length.
	InconsistentLength {
		/// Expected length of the definition.
		expected: usize,
		/// Actual length of the definition.
		actual: usize
	},
	/// Other static error.
	Other(&'static str),
	/// Other allocated error.
	HeapOther(String),
	/// Invalid/unknown value type declaration.
	UnknownValueType(i8),
	/// Invalid/unknown table element type declaration.
	UnknownTableElementType(i8),
	/// Non-utf8 string.
	NonUtf8String,
	/// Unknown external kind code.
	UnknownExternalKind(u8),
	/// Unknown internal kind code.
	UnknownInternalKind(u8),
	/// Unknown opcode encountered.
	UnknownOpcode(u8),
	#[cfg(feature="simd")]
	/// Unknown SIMD opcode encountered.
	UnknownSimdOpcode(u32),
	/// Invalid VarUint1 value.
	InvalidVarUint1(u8),
	/// Invalid VarInt32 value.
	InvalidVarInt32,
	/// Invalid VarInt64 value.
	InvalidVarInt64,
	/// Invalid VarUint32 value.
	InvalidVarUint32,
	/// Invalid VarUint64 value.
	InvalidVarUint64,
	/// Inconsistent metadata.
	InconsistentMetadata,
	/// Invalid section id.
	InvalidSectionId(u8),
	/// Sections are out of order.
	SectionsOutOfOrder,
	/// Duplicated sections.
	DuplicatedSections(u8),
	/// Invalid memory reference (should be 0).
	InvalidMemoryReference(u8),
	/// Invalid table reference (should be 0).
	InvalidTableReference(u8),
	/// Invalid value used for flags in limits type.
	InvalidLimitsFlags(u8),
	/// Unknown function form (should be 0x60).
	UnknownFunctionForm(u8),
	/// Invalid varint7 (should be in -64..63 range).
	InvalidVarInt7(u8),
	/// Number of function body entries and signatures does not match.
	InconsistentCode,
	/// Only flags 0, 1, and 2 are accepted on segments.
	InvalidSegmentFlags(u32),
	/// Sum of counts of locals is greater than 2^32.
	TooManyLocals,
	/// Duplicated name subsections.
	DuplicatedNameSubsections(u8),
	/// Unknown name subsection type.
	UnknownNameSubsectionType(u8),
}

impl fmt::Display for Error {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		match *self {
			Error::UnexpectedEof => write!(f, "Unexpected end of input"),
			Error::InvalidMagic => write!(f, "Invalid magic number at start of file"),
			Error::UnsupportedVersion(v) => write!(f, "Unsupported wasm version {}", v),
			Error::InconsistentLength { expected, actual } => {
				write!(f, "Expected length {}, found {}", expected, actual)
			}
			Error::Other(msg) => write!(f, "{}", msg),
			Error::HeapOther(ref msg) => write!(f, "{}", msg),
			Error::UnknownValueType(ty) => write!(f, "Invalid or unknown value type {}", ty),
			Error::UnknownTableElementType(ty) => write!(f, "Unknown table element type {}", ty),
			Error::NonUtf8String => write!(f, "Non-UTF-8 string"),
			Error::UnknownExternalKind(kind) => write!(f, "Unknown external kind {}", kind),
			Error::UnknownInternalKind(kind) => write!(f, "Unknown internal kind {}", kind),
			Error::UnknownOpcode(opcode) => write!(f, "Unknown opcode {}", opcode),
			#[cfg(feature="simd")]
			Error::UnknownSimdOpcode(opcode) => write!(f, "Unknown SIMD opcode {}", opcode),
			Error::InvalidVarUint1(val) => write!(f, "Not an unsigned 1-bit integer: {}", val),
			Error::InvalidVarInt7(val) => write!(f, "Not a signed 7-bit integer: {}", val),
			Error::InvalidVarInt32 => write!(f, "Not a signed 32-bit integer"),
			Error::InvalidVarUint32 => write!(f, "Not an unsigned 32-bit integer"),
			Error::InvalidVarInt64 => write!(f, "Not a signed 64-bit integer"),
			Error::InvalidVarUint64 => write!(f, "Not an unsigned 64-bit integer"),
			Error::InconsistentMetadata =>  write!(f, "Inconsistent metadata"),
			Error::InvalidSectionId(ref id) =>  write!(f, "Invalid section id: {}", id),
			Error::SectionsOutOfOrder =>  write!(f, "Sections out of order"),
			Error::DuplicatedSections(ref id) =>  write!(f, "Duplicated sections ({})", id),
			Error::InvalidMemoryReference(ref mem_ref) =>  write!(f, "Invalid memory reference ({})", mem_ref),
			Error::InvalidTableReference(ref table_ref) =>  write!(f, "Invalid table reference ({})", table_ref),
			Error::InvalidLimitsFlags(ref flags) =>  write!(f, "Invalid limits flags ({})", flags),
			Error::UnknownFunctionForm(ref form) =>  write!(f, "Unknown function form ({})", form),
			Error::InconsistentCode =>  write!(f, "Number of function body entries and signatures does not match"),
			Error::InvalidSegmentFlags(n) =>  write!(f, "Invalid segment flags: {}", n),
			Error::TooManyLocals => write!(f, "Too many locals"),
			Error::DuplicatedNameSubsections(n) =>  write!(f, "Duplicated name subsections: {}", n),
			Error::UnknownNameSubsectionType(n) => write!(f, "Unknown subsection type: {}", n),
		}
	}
}

#[cfg(feature = "std")]
impl ::std::error::Error for Error {
	fn description(&self) -> &str {
		match *self {
			Error::UnexpectedEof => "Unexpected end of input",
			Error::InvalidMagic => "Invalid magic number at start of file",
			Error::UnsupportedVersion(_) => "Unsupported wasm version",
			Error::InconsistentLength { .. } => "Inconsistent length",
			Error::Other(msg) => msg,
			Error::HeapOther(ref msg) => &msg[..],
			Error::UnknownValueType(_) => "Invalid or unknown value type",
			Error::UnknownTableElementType(_) => "Unknown table element type",
			Error::NonUtf8String => "Non-UTF-8 string",
			Error::UnknownExternalKind(_) => "Unknown external kind",
			Error::UnknownInternalKind(_) => "Unknown internal kind",
			Error::UnknownOpcode(_) => "Unknown opcode",
			#[cfg(feature="simd")]
			Error::UnknownSimdOpcode(_) => "Unknown SIMD opcode",
			Error::InvalidVarUint1(_) => "Not an unsigned 1-bit integer",
			Error::InvalidVarInt32 => "Not a signed 32-bit integer",
			Error::InvalidVarInt7(_) => "Not a signed 7-bit integer",
			Error::InvalidVarUint32 => "Not an unsigned 32-bit integer",
			Error::InvalidVarInt64 => "Not a signed 64-bit integer",
			Error::InvalidVarUint64 => "Not an unsigned 64-bit integer",
			Error::InconsistentMetadata => "Inconsistent metadata",
			Error::InvalidSectionId(_) =>  "Invalid section id",
			Error::SectionsOutOfOrder =>  "Sections out of order",
			Error::DuplicatedSections(_) =>  "Duplicated section",
			Error::InvalidMemoryReference(_) =>  "Invalid memory reference",
			Error::InvalidTableReference(_) =>  "Invalid table reference",
			Error::InvalidLimitsFlags(_) => "Invalid limits flags",
			Error::UnknownFunctionForm(_) =>  "Unknown function form",
			Error::InconsistentCode =>  "Number of function body entries and signatures does not match",
			Error::InvalidSegmentFlags(_) =>  "Invalid segment flags",
			Error::TooManyLocals => "Too many locals",
			Error::DuplicatedNameSubsections(_) =>  "Duplicated name subsections",
			Error::UnknownNameSubsectionType(_) => "Unknown name subsections type",
		}
	}
}

impl From<io::Error> for Error {
	fn from(err: io::Error) -> Self {
		Error::HeapOther(format!("I/O Error: {:?}", err))
	}
}

// These are emitted by section parsers, such as `parse_names` and `parse_reloc`.
impl From<(Vec<(usize, Error)>, Module)> for Error {
	fn from(err: (Vec<(usize, Error)>, Module)) -> Self {
		let ret = err.0.iter()
			.fold(
				String::new(),
				|mut acc, item| { acc.push_str(&format!("In section {}: {}\n", item.0, item.1)); acc }
			);
		Error::HeapOther(ret)
	}
}

/// Unparsed part of the module/section.
pub struct Unparsed(pub Vec<u8>);

impl Deserialize for Unparsed {
	type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let len = VarUint32::deserialize(reader)?.into();
		let mut vec = vec![0u8; len];
		reader.read(&mut vec[..])?;
		Ok(Unparsed(vec))
	}
}

impl From<Unparsed> for Vec<u8> {
	fn from(u: Unparsed) -> Vec<u8> {
		u.0
	}
}

/// Deserialize deserializable type from buffer.
pub fn deserialize_buffer<T: Deserialize>(contents: &[u8]) -> Result<T, T::Error> {
	let mut reader = io::Cursor::new(contents);
	let result = T::deserialize(&mut reader)?;
	if reader.position() != contents.len() {
		// It's a TrailingData, since if there is not enough data then
		// UnexpectedEof must have been returned earlier in T::deserialize.
		return Err(io::Error::TrailingData.into())
	}
	Ok(result)
}

/// Create buffer with serialized value.
pub fn serialize<T: Serialize>(val: T) -> Result<Vec<u8>, T::Error> {
	let mut buf = Vec::new();
	val.serialize(&mut buf)?;
	Ok(buf)
}

/// Deserialize module from the file.
#[cfg(feature = "std")]
pub fn deserialize_file<P: AsRef<::std::path::Path>>(p: P) -> Result<Module, Error> {
	let mut f = ::std::fs::File::open(p)
		.map_err(|e| Error::HeapOther(format!("Can't read from the file: {:?}", e)))?;

	Module::deserialize(&mut f)
}

/// Serialize module to the file
#[cfg(feature = "std")]
pub fn serialize_to_file<P: AsRef<::std::path::Path>>(p: P, module: Module) -> Result<(), Error> {
	let mut io = ::std::fs::File::create(p)
		.map_err(|e|
			Error::HeapOther(format!("Can't create the file: {:?}", e))
		)?;

	module.serialize(&mut io)?;
	Ok(())
}
<|endoftext|>