<commit_before>use std::ascii::{AsciiCast, StrAsciiExt};
use std::fmt::{Formatter, FormatError, Show};

/// Status code of a request or response.
#[deriving(Eq, PartialEq, Clone, Show, Ord, PartialOrd)]
#[stable]
pub struct StatusCode(pub uint);

impl StatusCode {
    #[stable]
    /// Returns the status code as a number.
    pub fn as_uint(&self) -> uint {
        match *self { StatusCode(n) => n }
    }

    #[stable]
    /// Returns the default reason phrase for this status code.
    /// For example the status code 404 corresponds to "Not Found".
    pub fn get_default_reason_phrase(&self) -> &'static str {
        match self.as_uint() {
            100 => "Continue",
            101 => "Switching Protocols",
            102 => "Processing",
            118 => "Connection timed out",
            200 => "OK",
            201 => "Created",
            202 => "Accepted",
            203 => "Non-Authoritative Information",
            204 => "No Content",
            205 => "Reset Content",
            206 => "Partial Content",
            207 => "Multi-Status",
            210 => "Content Different",
            300 => "Multiple Choices",
            301 => "Moved Permanently",
            302 => "Found",
            303 => "See Other",
            304 => "Not Modified",
            305 => "Use Proxy",
            307 => "Temporary Redirect",
            400 => "Bad Request",
            401 => "Unauthorized",
            402 => "Payment Required",
            403 => "Forbidden",
            404 => "Not Found",
            405 => "Method Not Allowed",
            406 => "Not Acceptable",
            407 => "Proxy Authentication Required",
            408 => "Request Time-out",
            409 => "Conflict",
            410 => "Gone",
            411 => "Length Required",
            412 => "Precondition Failed",
            413 => "Request Entity Too Large",
            414 => "Reques-URI Too Large",
            415 => "Unsupported Media Type",
            416 => "Request range not satisfiable",
            417 => "Expectation Failed",
            500 => "Internal Server Error",
            501 => "Not Implemented",
            502 => "Bad Gateway",
            503 => "Service Unavailable",
            504 => "Gateway Time-out",
            505 => "HTTP Version not supported",
            _ => "Unknown"
        }
    }
}

impl Equiv<uint> for StatusCode {
    fn equiv(&self, other: &uint) -> bool {
        self.as_uint() == *other
    }
}

/// Represents a HTTP header.
///
/// The easiest way to create a `Header` object is to call `from_str`.
///
/// ```
/// let header: Header = from_str("Content-Type: text/plain").unwrap();
/// ```
#[deriving(Clone)]
#[unstable]
pub struct Header {
    pub field: HeaderField,
    pub value: String,
}

impl ::std::from_str::FromStr for Header {
    fn from_str(input: &str) -> Option<Header> {
        let elems = input.splitn(':', 2).map(|e| e.to_string()).collect::<Vec<String>>();

        if elems.len() <= 1 {
            return None;
        }
        if elems.get(1).as_slice().chars().next() != Some(' ') {
            return None;
        }

        let field = match from_str(elems.get(0).as_slice()) {
            None => return None,
            Some(f) => f
        };

        Some(Header {
            field: field,
            value: elems.get(1).as_slice().slice_from(1).to_string()
        })
    }
}

impl Show for Header {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        (format!("{}: {}", self.field, self.value)).fmt(formatter)
    }
}

/// Field of a header (eg. `Content-Type`, `Content-Length`, etc.)
///
/// Comparaison between two `HeaderField`s ignores case.
#[unstable]
#[deriving(Clone)]
pub struct HeaderField(Vec<Ascii>);

impl HeaderField {
    fn as_str<'a>(&'a self) -> &'a [Ascii] {
        match self { &HeaderField(ref s) => s.as_slice() }
    }
}

impl ::std::from_str::FromStr for HeaderField {
    fn from_str(s: &str) -> Option<HeaderField> {
        s.to_ascii_opt().map(|s| HeaderField(Vec::from_slice(s)))
    }
}

impl IntoStr for HeaderField {
    fn into_string(self) -> String {
        match self { HeaderField(s) => s.into_string() }
    }
}

impl Show for HeaderField {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        let method = self.as_str();
        method.as_str_ascii().fmt(formatter)
    }
}

impl PartialEq for HeaderField {
    fn eq(&self, other: &HeaderField) -> bool {
        self.as_str().eq_ignore_case(other.as_str())
    }
}

impl Eq for HeaderField {}

impl<S: Str> Equiv<S> for HeaderField {
    fn equiv(&self, other: &S) -> bool {
        other.as_slice().eq_ignore_ascii_case(self.as_str().as_str_ascii())
    }
}

/// HTTP method (eg. `GET`, `POST`, etc.)
///
/// The user chooses the method he wants.
///
/// Comparaison between two `Method`s ignores case.
#[unstable]
#[deriving(Clone)]
pub struct Method(Vec<Ascii>);

impl Method {
    fn as_str<'a>(&'a self) -> &'a [Ascii] {
        match self { &Method(ref s) => s.as_slice() }
    }
}

impl ::std::from_str::FromStr for Method {
    fn from_str(s: &str) -> Option<Method> {
        s.to_ascii_opt().map(|s| Method(Vec::from_slice(s)))
    }
}

impl IntoStr for Method {
    fn into_string(self) -> String {
        match self { Method(s) => s.into_string() }
    }
}

impl Show for Method {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        let method = self.as_str();
        method.as_str_ascii().fmt(formatter)
    }
}

impl PartialEq for Method {
    fn eq(&self, other: &Method) -> bool {
        self.as_str().eq_ignore_case(other.as_str())
    }
}

impl Eq for Method {}

impl<S: Str> Equiv<S> for Method {
    fn equiv(&self, other: &S) -> bool {
        other.as_slice().eq_ignore_ascii_case(self.as_str().as_str_ascii())
    }
}

/// HTTP version (usually 1.0 or 1.1).
#[unstable]
#[deriving(Clone, PartialEq, Eq, Ord)]
pub struct HTTPVersion(pub uint, pub uint);

impl Show for HTTPVersion {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        let (major, minor) = match self { &HTTPVersion(m, n) => (m, n) };
        (format!("{}.{}", major, minor)).fmt(formatter)
    }
}

impl PartialOrd for HTTPVersion {
    fn partial_cmp(&self, other: &HTTPVersion) -> Option<Ordering> {
        let (my_major, my_minor) = match self { &HTTPVersion(m, n) => (m, n) };
        let (other_major, other_minor) = match other { &HTTPVersion(m, n) => (m, n) };

        if my_major != other_major {
            return my_major.partial_cmp(&other_major)
        }

        my_minor.partial_cmp(&other_minor)
    }
}

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

    #[test]
    fn test_parse_header() {
        let header: Header = from_str("Content-Type: text/html").unwrap();

        assert!(header.field.equiv(&"content-type"));
        assert!(header.value.as_slice() == "text/html");

        assert!(from_str::<Header>("hello world").is_none());
    }
}
<commit_msg>Parsing headers is now more tolerant towards white spaces<commit_after>use std::ascii::{AsciiCast, StrAsciiExt};
use std::fmt::{Formatter, FormatError, Show};

/// Status code of a request or response.
#[deriving(Eq, PartialEq, Clone, Show, Ord, PartialOrd)]
#[stable]
pub struct StatusCode(pub uint);

impl StatusCode {
    #[stable]
    /// Returns the status code as a number.
    pub fn as_uint(&self) -> uint {
        match *self { StatusCode(n) => n }
    }

    #[stable]
    /// Returns the default reason phrase for this status code.
    /// For example the status code 404 corresponds to "Not Found".
    pub fn get_default_reason_phrase(&self) -> &'static str {
        match self.as_uint() {
            100 => "Continue",
            101 => "Switching Protocols",
            102 => "Processing",
            118 => "Connection timed out",
            200 => "OK",
            201 => "Created",
            202 => "Accepted",
            203 => "Non-Authoritative Information",
            204 => "No Content",
            205 => "Reset Content",
            206 => "Partial Content",
            207 => "Multi-Status",
            210 => "Content Different",
            300 => "Multiple Choices",
            301 => "Moved Permanently",
            302 => "Found",
            303 => "See Other",
            304 => "Not Modified",
            305 => "Use Proxy",
            307 => "Temporary Redirect",
            400 => "Bad Request",
            401 => "Unauthorized",
            402 => "Payment Required",
            403 => "Forbidden",
            404 => "Not Found",
            405 => "Method Not Allowed",
            406 => "Not Acceptable",
            407 => "Proxy Authentication Required",
            408 => "Request Time-out",
            409 => "Conflict",
            410 => "Gone",
            411 => "Length Required",
            412 => "Precondition Failed",
            413 => "Request Entity Too Large",
            414 => "Reques-URI Too Large",
            415 => "Unsupported Media Type",
            416 => "Request range not satisfiable",
            417 => "Expectation Failed",
            500 => "Internal Server Error",
            501 => "Not Implemented",
            502 => "Bad Gateway",
            503 => "Service Unavailable",
            504 => "Gateway Time-out",
            505 => "HTTP Version not supported",
            _ => "Unknown"
        }
    }
}

impl Equiv<uint> for StatusCode {
    fn equiv(&self, other: &uint) -> bool {
        self.as_uint() == *other
    }
}

/// Represents a HTTP header.
///
/// The easiest way to create a `Header` object is to call `from_str`.
///
/// ```
/// let header: Header = from_str("Content-Type: text/plain").unwrap();
/// ```
#[deriving(Clone)]
#[unstable]
pub struct Header {
    pub field: HeaderField,
    pub value: String,
}

impl ::std::from_str::FromStr for Header {
    fn from_str(input: &str) -> Option<Header> {
        let elems = input.splitn(':', 2).map(|e| e.to_string()).collect::<Vec<String>>();

        if elems.len() <= 1 {
            return None;
        }

        let field = match from_str(elems.get(0).as_slice().trim()) {
            None => return None,
            Some(f) => f
        };

        Some(Header {
            field: field,
            value: elems.get(1).as_slice().trim().to_string()
        })
    }
}

impl Show for Header {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        (format!("{}: {}", self.field, self.value)).fmt(formatter)
    }
}

/// Field of a header (eg. `Content-Type`, `Content-Length`, etc.)
///
/// Comparaison between two `HeaderField`s ignores case.
#[unstable]
#[deriving(Clone)]
pub struct HeaderField(Vec<Ascii>);

impl HeaderField {
    fn as_str<'a>(&'a self) -> &'a [Ascii] {
        match self { &HeaderField(ref s) => s.as_slice() }
    }
}

impl ::std::from_str::FromStr for HeaderField {
    fn from_str(s: &str) -> Option<HeaderField> {
        s.trim().to_ascii_opt().map(|s| HeaderField(Vec::from_slice(s)))
    }
}

impl IntoStr for HeaderField {
    fn into_string(self) -> String {
        match self { HeaderField(s) => s.into_string() }
    }
}

impl Show for HeaderField {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        let method = self.as_str();
        method.as_str_ascii().fmt(formatter)
    }
}

impl PartialEq for HeaderField {
    fn eq(&self, other: &HeaderField) -> bool {
        self.as_str().eq_ignore_case(other.as_str())
    }
}

impl Eq for HeaderField {}

impl<S: Str> Equiv<S> for HeaderField {
    fn equiv(&self, other: &S) -> bool {
        other.as_slice().eq_ignore_ascii_case(self.as_str().as_str_ascii())
    }
}

/// HTTP method (eg. `GET`, `POST`, etc.)
///
/// The user chooses the method he wants.
///
/// Comparaison between two `Method`s ignores case.
#[unstable]
#[deriving(Clone)]
pub struct Method(Vec<Ascii>);

impl Method {
    fn as_str<'a>(&'a self) -> &'a [Ascii] {
        match self { &Method(ref s) => s.as_slice() }
    }
}

impl ::std::from_str::FromStr for Method {
    fn from_str(s: &str) -> Option<Method> {
        s.to_ascii_opt().map(|s| Method(Vec::from_slice(s)))
    }
}

impl IntoStr for Method {
    fn into_string(self) -> String {
        match self { Method(s) => s.into_string() }
    }
}

impl Show for Method {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        let method = self.as_str();
        method.as_str_ascii().fmt(formatter)
    }
}

impl PartialEq for Method {
    fn eq(&self, other: &Method) -> bool {
        self.as_str().eq_ignore_case(other.as_str())
    }
}

impl Eq for Method {}

impl<S: Str> Equiv<S> for Method {
    fn equiv(&self, other: &S) -> bool {
        other.as_slice().eq_ignore_ascii_case(self.as_str().as_str_ascii())
    }
}

/// HTTP version (usually 1.0 or 1.1).
#[unstable]
#[deriving(Clone, PartialEq, Eq, Ord)]
pub struct HTTPVersion(pub uint, pub uint);

impl Show for HTTPVersion {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        let (major, minor) = match self { &HTTPVersion(m, n) => (m, n) };
        (format!("{}.{}", major, minor)).fmt(formatter)
    }
}

impl PartialOrd for HTTPVersion {
    fn partial_cmp(&self, other: &HTTPVersion) -> Option<Ordering> {
        let (my_major, my_minor) = match self { &HTTPVersion(m, n) => (m, n) };
        let (other_major, other_minor) = match other { &HTTPVersion(m, n) => (m, n) };

        if my_major != other_major {
            return my_major.partial_cmp(&other_major)
        }

        my_minor.partial_cmp(&other_minor)
    }
}

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

    #[test]
    fn test_parse_header() {
        let header: Header = from_str("Content-Type: text/html").unwrap();

        assert!(header.field.equiv(&"content-type"));
        assert!(header.value.as_slice() == "text/html");

        assert!(from_str::<Header>("hello world").is_none());
    }
}
<|endoftext|>


<commit_before>use hb;

/// A type to represent 4 byte tags that are used in many font formats for naming font tables,
/// font features and similar.
///
/// The user-facing representation is a 4-character ASCII string. `Tag` provides methods to create
/// `Tag`s from such a representation and to get the string representation from 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;
use std::ascii::AsciiExt;

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);
            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))
        }
    }
}

/// All trait all wrappers for harfbuzz objects implement. It exposes common functionality for
/// converting from and to the underlying raw harfbuzz pointers.
pub trait HarfbuzzObject: Clone {
    /// Type of the raw harfbuzz object pointer;
    type Raw;

    /// Creates a value safely wrapping the raw harfbuzz pointer. Transfers ownership. _Use of the
    /// original pointer is now forbidden!_ Unsafe because a dereference of a raw pointer is
    /// necesarry.
    unsafe fn from_raw(val: Self::Raw) -> Self;

    /// Creates a value safely wrapping the raw harfbuzz pointer and references it immediately so
    /// that the existing pointer can still be used as normal. Unsafe because a dereference of a
    /// raw pointer is necesarry.
    unsafe fn from_raw_referenced(val: Self::Raw) -> Self {
        let result = Self::from_raw(val);
        std::mem::forget(result.clone()); // increase reference count
        result
    }

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

    /// Returns the underlying harfbuzz object pointer after referencing the object. The resulting
    /// pointer has to be manually destroyed using `hb_TYPE_destroy` or be converted back into the
    /// wrapper using the `from_raw` function.
    fn as_raw_referenced(&self) -> Self::Raw {
        std::mem::forget(self.clone()); // increase reference count
        self.as_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.
    fn into_raw(self) -> Self::Raw {
        let result = self.as_raw();
        std::mem::forget(self);
        result
    }
}

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

    #[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");
    }
}
<commit_msg>Add null-ptr check.<commit_after>use hb;

/// A type to represent 4 byte tags that are used in many font formats for naming font tables,
/// font features and similar.
///
/// The user-facing representation is a 4-character ASCII string. `Tag` provides methods to create
/// `Tag`s from such a representation and to get the string representation from 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;
use std::ascii::AsciiExt;

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() {
                panic!("hb::hb_language_to_string returned null-pointer");
            }
            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))
        }
    }
}

/// All trait all wrappers for harfbuzz objects implement. It exposes common functionality for
/// converting from and to the underlying raw harfbuzz pointers.
pub trait HarfbuzzObject: Clone {
    /// Type of the raw harfbuzz object pointer;
    type Raw;

    /// Creates a value safely wrapping the raw harfbuzz pointer. Transfers ownership. _Use of the
    /// original pointer is now forbidden!_ Unsafe because a dereference of a raw pointer is
    /// necesarry.
    unsafe fn from_raw(val: Self::Raw) -> Self;

    /// Creates a value safely wrapping the raw harfbuzz pointer and references it immediately so
    /// that the existing pointer can still be used as normal. Unsafe because a dereference of a
    /// raw pointer is necesarry.
    unsafe fn from_raw_referenced(val: Self::Raw) -> Self {
        let result = Self::from_raw(val);
        std::mem::forget(result.clone()); // increase reference count
        result
    }

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

    /// Returns the underlying harfbuzz object pointer after referencing the object. The resulting
    /// pointer has to be manually destroyed using `hb_TYPE_destroy` or be converted back into the
    /// wrapper using the `from_raw` function.
    fn as_raw_referenced(&self) -> Self::Raw {
        std::mem::forget(self.clone()); // increase reference count
        self.as_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.
    fn into_raw(self) -> Self::Raw {
        let result = self.as_raw();
        std::mem::forget(self);
        result
    }
}

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

    #[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");
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

use regex;
use regex::{Regex, RegexBuilder};
use serde_yaml;

error_chain! {
    foreign_links {
        ParseError(::serde_yaml::Error);
        IOError(::std::io::Error);
    }

    errors {
        NoHomeError {
            description("could not locate home directory")
        }

        BadEntryError(browser: String, profile: String) {
            description("bad configuration")
            display("bad configuration: no such browser '{}' defined for profile '{}'",
                    browser,
                    profile)
        }
    }
}

#[doc(hidden)]
/// A regex that is equivalent to the "*." wildcard pattern.
///
/// This regular expression matches any subdomains (and subdomains of subdomains, etc.). It replaces
/// the "*." in entries such as "*.example.com"
static WC_REPLACEMENT: &'static str = r"(?:.+\.)?";

#[doc(hidden)]
/// The prefix for the generated regular expression.
///
/// This prefix will make the regular expression match both HTTP and HTTPs URLs.
static RE_PREFIX: &'static str = r"^https?://(?:";

#[doc(hidden)]
/// The suffix for the generated regular expression.
///
/// This is the corresponding closing parenthesis for the opening parenthesis in `RE_PREFIX`.
static RE_SUFFIX: char = ')';

#[doc(hidden)]
/// Transform a string into a regular expression that will match that URL.
///
/// URLs of the form "*.example.com" will match any subdomains of "example.com" in addition to
/// "example.com".
fn escape_url(url: &str) -> String {
    if url.starts_with("*.") {
        let rest = url.split_at(2).1;
        let mut escaped = String::with_capacity(WC_REPLACEMENT.len() + url.len() - 2);

        escaped.push_str(WC_REPLACEMENT);
        escaped.push_str(&regex::escape(&rest));
        escaped
    } else {
        regex::escape(&url)
    }
}

#[doc(hidden)]
/// Transform a list of URLs to a regular expression that matches them.
///
/// URLs should not include the scheme: the resulting regular expression will match both HTTP and
/// HTTPs URLs.
///
/// If the list is empty, no regular expression is returned.
fn urls_to_regex(urls: &Vec<String>) -> Option<Regex> {
    if urls.is_empty() {
        None
    } else {
        let re_body = urls.iter()
            .map(|url| escape_url(url))
            .collect::<Vec<_>>()
            .join("|");

        let mut pattern = String::with_capacity(RE_PREFIX.len() + re_body.len() + 1);
        pattern.push_str(RE_PREFIX);
        pattern.push_str(&re_body);
        pattern.push(RE_SUFFIX);

        // build() returns a ::std::result::Result<regex::Regex, regex::Error>, but panicing is not
        // possible because we escape all user input (and we therefore cannot generate an invalid
        // regular expression).
        Some(RegexBuilder::new(&pattern)
            .case_insensitive(true)
            .build()
            .unwrap())
    }
}

#[derive(Debug, Deserialize)]
/// A default browser and profile combination.
pub struct DefaultProfile {
    /// The default browser.
    pub browser: String,

    /// The default profile to use for the browser.
    pub profile: String,
}

#[derive(Debug, Deserialize)]
/// A configuration entry.
///
/// Each entry maps a regular expression to a browser and profile. If a URL matches the given
/// regular expression it will be launched using the corresponding browser and profile.
pub struct Entry {
    /// The browser to use.
    ///
    /// This must be defined in the list of browsers in the parent [`Config`](struct.Config.html)
    /// struct.
    pub browser: String,

    /// The profile to use.
    pub profile: String,

    /// The list of URLs that this profile matches.
    ///
    /// This list is used to generate the regular expression.
    pub urls: Vec<String>,

    /// The regular expression to match against.
    ///
    /// If a URL matches this regular expression, the corresponding browser will be launched with
    /// the corresponding profile.
    ///
    /// If the list of URLs is empty, this will be `None`.
    #[serde(skip_deserializing)]
    pub regex: Option<Regex>,
}

#[derive(Debug, Deserialize)]
/// Metabrowser configuration.
///
/// The configuration specifies the set of browsers and how they can be launched, as well as the
pub struct Config {
    /// A mapping of browser names to their command line parameters for launching with a specified
    /// profile.
    pub browsers: HashMap<String, Vec<String>>,

    /// The default browser and profile to use for URLs that do not match any regular expressions in
    /// the list of profiles.
    pub default: DefaultProfile,

    /// The specified browser-profile pairs and their corresponding regular expressions.
    #[serde(rename="profiles")]
    pub entries: Vec<Entry>,
}

impl Config {
    /// Read the configuration from the file.
    pub fn from_file(p: &Path) -> Result<Config> {

        let mut buf = String::new();
        let mut f = try!(File::open(p));
        try!(f.read_to_string(&mut buf));

        let mut config = try!(serde_yaml::from_str::<Self>(&buf)
            .map_err(Error::from)
            .and_then(Config::validate));

        for entry in &mut config.entries {
            entry.regex = urls_to_regex(&entry.urls);
        }

        Ok(config)
    }

    #[doc(hidden)]
    /// Validate a parsed configuration.
    ///
    /// This function will ensure that all entries match up with the list of available browsers.
    fn validate(mut self) -> Result<Self> {
        for i in 0..self.entries.len() {
            if !self.browsers.contains_key(&self.entries[i].browser) {
                let browser_profile = self.entries.swap_remove(i);
                return Err(Error::from(ErrorKind::BadEntryError(browser_profile.browser,
                                                                browser_profile.profile)));
            }
        }

        Ok(self)
    }
}
<commit_msg>Formatting config.rs<commit_after>use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

use regex;
use regex::{Regex, RegexBuilder};
use serde_yaml;

error_chain! {
    foreign_links {
        ParseError(::serde_yaml::Error);
        IOError(::std::io::Error);
    }

    errors {
        NoHomeError {
            description("could not locate home directory")
        }

        BadEntryError(browser: String, profile: String) {
            description("bad configuration")
            display("bad configuration: no such browser '{}' defined for profile '{}'",
                    browser,
                    profile)
        }
    }
}

#[doc(hidden)]
/// A regex that is equivalent to the "*." wildcard pattern.
///
/// This regular expression matches any subdomains (and subdomains of subdomains, etc.). It replaces
/// the "*." in entries such as "*.example.com"
static WC_REPLACEMENT: &'static str = r"(?:.+\.)?";

#[doc(hidden)]
/// The prefix for the generated regular expression.
///
/// This prefix will make the regular expression match both HTTP and HTTPs URLs.
static RE_PREFIX: &'static str = r"^https?://(?:";

#[doc(hidden)]
/// The suffix for the generated regular expression.
///
/// This is the corresponding closing parenthesis for the opening parenthesis in `RE_PREFIX`.
static RE_SUFFIX: char = ')';

#[doc(hidden)]
/// Transform a string into a regular expression that will match that URL.
///
/// URLs of the form "*.example.com" will match any subdomains of "example.com" in addition to
/// "example.com".
fn escape_url(url: &str) -> String {
    if url.starts_with("*.") {
        let rest = url.split_at(2).1;
        let mut escaped = String::with_capacity(WC_REPLACEMENT.len() + url.len() - 2);

        escaped.push_str(WC_REPLACEMENT);
        escaped.push_str(&regex::escape(&rest));
        escaped
    } else {
        regex::escape(&url)
    }
}

#[doc(hidden)]
/// Transform a list of URLs to a regular expression that matches them.
///
/// URLs should not include the scheme: the resulting regular expression will match both HTTP and
/// HTTPs URLs.
///
/// If the list is empty, no regular expression is returned.
fn urls_to_regex(urls: &Vec<String>) -> Option<Regex> {
    if urls.is_empty() {
        None
    } else {
        let re_body = urls.iter()
            .map(|url| escape_url(url))
            .collect::<Vec<_>>()
            .join("|");

        let mut pattern = String::with_capacity(RE_PREFIX.len() + re_body.len() + 1);
        pattern.push_str(RE_PREFIX);
        pattern.push_str(&re_body);
        pattern.push(RE_SUFFIX);

        // build() returns a ::std::result::Result<regex::Regex, regex::Error>, but panicing is not
        // possible because we escape all user input (and we therefore cannot generate an invalid
        // regular expression).
        Some(RegexBuilder::new(&pattern)
            .case_insensitive(true)
            .build()
            .unwrap())
    }
}

#[derive(Debug, Deserialize)]
/// A default browser and profile combination.
pub struct DefaultProfile {
    /// The default browser.
    pub browser: String,

    /// The default profile to use for the browser.
    pub profile: String,
}

#[derive(Debug, Deserialize)]
/// A configuration entry.
///
/// Each entry maps a regular expression to a browser and profile. If a URL matches the given
/// regular expression it will be launched using the corresponding browser and profile.
pub struct Entry {
    /// The browser to use.
    ///
    /// This must be defined in the list of browsers in the parent [`Config`](struct.Config.html)
    /// struct.
    pub browser: String,

    /// The profile to use.
    pub profile: String,

    /// The list of URLs that this profile matches.
    ///
    /// This list is used to generate the regular expression.
    pub urls: Vec<String>,

    /// The regular expression to match against.
    ///
    /// If a URL matches this regular expression, the corresponding browser will be launched with
    /// the corresponding profile.
    ///
    /// If the list of URLs is empty, this will be `None`.
    #[serde(skip_deserializing)]
    pub regex: Option<Regex>,
}

#[derive(Debug, Deserialize)]
/// Metabrowser configuration.
///
/// The configuration specifies the set of browsers and how they can be launched, as well as the
pub struct Config {
    /// A mapping of browser names to their command line parameters for launching with a specified
    /// profile.
    pub browsers: HashMap<String, Vec<String>>,

    /// The default browser and profile to use for URLs that do not match any regular expressions in
    /// the list of profiles.
    pub default: DefaultProfile,

    /// The specified browser-profile pairs and their corresponding regular expressions.
    #[serde(rename="profiles")]
    pub entries: Vec<Entry>,
}

impl Config {
    /// Read the configuration from the file.
    pub fn from_file(p: &Path) -> Result<Config> {

        let mut buf = String::new();
        let mut f = try!(File::open(p));
        try!(f.read_to_string(&mut buf));

        let mut config = try!(serde_yaml::from_str::<Self>(&buf)
            .map_err(Error::from)
            .and_then(Config::validate));

        for entry in &mut config.entries {
            entry.regex = urls_to_regex(&entry.urls);
        }

        Ok(config)
    }

    #[doc(hidden)]
    /// Validate a parsed configuration.
    ///
    /// This function will ensure that all entries match up with the list of available browsers.
    fn validate(mut self) -> Result<Self> {
        for i in 0..self.entries.len() {
            if !self.browsers.contains_key(&self.entries[i].browser) {
                let browser_profile = self.entries.swap_remove(i);
                return Err(Error::from(ErrorKind::BadEntryError(browser_profile.browser,
                                                                browser_profile.profile)));
            }
        }

        Ok(self)
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fmt;

#[derive(Debug, Default, Deserialize)]
pub struct Error {
    pub detail: String,
}

#[derive(Debug, Deserialize)]
pub struct CrateLinks {
    pub owners: Option<String>,
    pub reverse_dependencies: String,
    pub version_downloads: String,
    pub versions: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct Crate {
    pub created_at: String,
    pub description: Option<String>,
    pub documentation: Option<String>,
    pub downloads: i32,
    pub homepage: Option<String>,
    pub id: String,
    pub keywords: Option<Vec<String>>,
    pub license: Option<String>,
    pub links: CrateLinks,
    pub max_version: String,
    pub name: String,
    pub repository: Option<String>,
    pub updated_at: String,
    pub versions: Option<Vec<u64>>,
}

#[derive(Debug, Deserialize)]
pub struct Keyword {
    pub crates_cnt: u64,
    pub created_at: String,
    pub id: String,
    pub keyword: String,
}

#[derive(Debug, Deserialize)]
pub struct VersionLinks {
    pub authors: String,
    pub dependencies: String,
    pub version_downloads: String,
}

#[derive(Debug, Deserialize)]
pub struct Version {
    #[serde(rename="crate")]
    pub krate: String,
    pub created_at: String,
    pub dl_path: String,
    pub downloads: i32,
    pub features: HashMap<String, Vec<String>>,
    pub id: i32,
    pub links: VersionLinks,
    pub num: String,
    pub updated_at: String,
    pub yanked: bool,
}

#[derive(Debug, Deserialize)]
pub struct Reply {
    #[serde(default)]
    pub errors: Error,
    #[serde(rename="crate")]
    pub krate: Crate,
    pub keywords: Vec<Keyword>,
    pub versions: Vec<Version>,
}

impl fmt::Display for Crate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let empty = &String::new();
        let description = self.description.as_ref().unwrap_or(empty);
        let documentation = self.documentation.as_ref().unwrap_or(empty);
        let homepage = self.homepage.as_ref().unwrap_or(empty);
        let repository = self.repository.as_ref().unwrap_or(empty);

        write!(f,
               "{}\n{}\n{}\n{}\n{}\n{}",
               format_args!("{:<16}{}", "Crate:", self.name),
               format_args!("{:<16}{}", "Version:", self.max_version),
               format_args!("{:<16}{}", "Description:", description),
               format_args!("{:<16}{}", "Homepage:", homepage),
               format_args!("{:<16}{}", "Documentation:", documentation),
               format_args!("{:<16}{}", "Repository:", repository))
    }
}

pub type CratesReply = HashMap<String, String>;

#[derive(Deserialize, Debug)]
struct GenericResponse {
    args: HashMap<String, String>,
    data: Option<String>,
    files: Option<HashMap<String, String>>,
    form: Option<HashMap<String, String>>,
    headers: HashMap<String, String>,
    json: Option<String>,
    origin: String,
    url: String,
}
<commit_msg>Add download counter report [#1]<commit_after>use std::collections::HashMap;
use std::fmt;

#[derive(Debug, Default, Deserialize)]
pub struct Error {
    pub detail: String,
}

#[derive(Debug, Deserialize)]
pub struct CrateLinks {
    pub owners: Option<String>,
    pub reverse_dependencies: String,
    pub version_downloads: String,
    pub versions: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct Crate {
    pub created_at: String,
    pub description: Option<String>,
    pub documentation: Option<String>,
    pub downloads: i32,
    pub homepage: Option<String>,
    pub id: String,
    pub keywords: Option<Vec<String>>,
    pub license: Option<String>,
    pub links: CrateLinks,
    pub max_version: String,
    pub name: String,
    pub repository: Option<String>,
    pub updated_at: String,
    pub versions: Option<Vec<u64>>,
}

#[derive(Debug, Deserialize)]
pub struct Keyword {
    pub crates_cnt: u64,
    pub created_at: String,
    pub id: String,
    pub keyword: String,
}

#[derive(Debug, Deserialize)]
pub struct VersionLinks {
    pub authors: String,
    pub dependencies: String,
    pub version_downloads: String,
}

#[derive(Debug, Deserialize)]
pub struct Version {
    #[serde(rename="crate")]
    pub krate: String,
    pub created_at: String,
    pub dl_path: String,
    pub downloads: i32,
    pub features: HashMap<String, Vec<String>>,
    pub id: i32,
    pub links: VersionLinks,
    pub num: String,
    pub updated_at: String,
    pub yanked: bool,
}

#[derive(Debug, Deserialize)]
pub struct Reply {
    #[serde(default)]
    pub errors: Error,
    #[serde(rename="crate")]
    pub krate: Crate,
    pub keywords: Vec<Keyword>,
    pub versions: Vec<Version>,
}

impl fmt::Display for Crate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let empty = &String::new();
        let description = self.description.as_ref().unwrap_or(empty);
        let documentation = self.documentation.as_ref().unwrap_or(empty);
        let homepage = self.homepage.as_ref().unwrap_or(empty);
        let repository = self.repository.as_ref().unwrap_or(empty);

        write!(f,
               "{}\n{}\n{}\n{}\n{}\n{}\n{}",
               format_args!("{:<16}{}", "Crate:", self.name),
               format_args!("{:<16}{}", "Version:", self.max_version),
               format_args!("{:<16}{}", "Description:", description),
               format_args!("{:<16}{}", "Downloads:", self.downloads),
               format_args!("{:<16}{}", "Homepage:", homepage),
               format_args!("{:<16}{}", "Documentation:", documentation),
               format_args!("{:<16}{}", "Repository:", repository))
    }
}

pub type CratesReply = HashMap<String, String>;

#[derive(Deserialize, Debug)]
struct GenericResponse {
    args: HashMap<String, String>,
    data: Option<String>,
    files: Option<HashMap<String, String>>,
    form: Option<HashMap<String, String>>,
    headers: HashMap<String, String>,
    json: Option<String>,
    origin: String,
    url: String,
}
<|endoftext|>


<commit_before>//! This module contains utilities for turning SyntaxNodes and HIR types
//! into things that may be used to render in a UI.

mod function_signature;
mod navigation_target;
mod structure;

use crate::db::RootDatabase;
use ra_syntax::{ast::{self, AstNode, TypeParamsOwner}, SyntaxKind::{ATTR, COMMENT}};

pub use navigation_target::NavigationTarget;
pub use structure::{StructureNode, file_structure};
pub use function_signature::FunctionSignature;

pub(crate) fn function_label(node: &ast::FnDef) -> String {
    FunctionSignature::from(node).to_string()
}

pub(crate) fn const_label(node: &ast::ConstDef) -> String {
    let label: String = node
        .syntax()
        .children_with_tokens()
        .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
        .map(|node| node.to_string())
        .collect();

    label.trim().to_owned()
}

pub(crate) fn type_label(node: &ast::TypeAliasDef) -> String {
    let label: String = node
        .syntax()
        .children_with_tokens()
        .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
        .map(|node| node.to_string())
        .collect();

    label.trim().to_owned()
}

pub(crate) fn generic_parameters<N: TypeParamsOwner>(node: &N) -> Vec<String> {
    let mut res = vec![];
    if let Some(type_params) = node.type_param_list() {
        res.extend(type_params.lifetime_params().map(|p| p.syntax().text().to_string()));
        res.extend(type_params.type_params().map(|p| p.syntax().text().to_string()));
    }
    res
}

pub(crate) fn where_predicates<N: TypeParamsOwner>(node: &N) -> Vec<String> {
    let mut res = vec![];
    if let Some(clause) = node.where_clause() {
        res.extend(clause.predicates().map(|p| p.syntax().text().to_string()));
    }
    res
}

pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String {
    rust_code_markup_with_doc::<_, &str>(val, None)
}

pub(crate) fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String
where
    CODE: AsRef<str>,
    DOC: AsRef<str>,
{
    if let Some(doc) = doc {
        format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref())
    } else {
        format!("```rust\n{}\n```", val.as_ref())
    }
}

// FIXME: this should not really use navigation target. Rather, approximately
// resolved symbol should return a `DefId`.
pub(crate) fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> {
    match (nav.description(db), nav.docs(db)) {
        (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)),
        (None, Some(docs)) => Some(docs),
        _ => None,
    }
}
<commit_msg>Fix doc comment<commit_after>//! This module contains utilities for turning SyntaxNodes and HIR types
//! into types that may be used to render in a UI.

mod function_signature;
mod navigation_target;
mod structure;

use crate::db::RootDatabase;
use ra_syntax::{ast::{self, AstNode, TypeParamsOwner}, SyntaxKind::{ATTR, COMMENT}};

pub use navigation_target::NavigationTarget;
pub use structure::{StructureNode, file_structure};
pub use function_signature::FunctionSignature;

pub(crate) fn function_label(node: &ast::FnDef) -> String {
    FunctionSignature::from(node).to_string()
}

pub(crate) fn const_label(node: &ast::ConstDef) -> String {
    let label: String = node
        .syntax()
        .children_with_tokens()
        .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
        .map(|node| node.to_string())
        .collect();

    label.trim().to_owned()
}

pub(crate) fn type_label(node: &ast::TypeAliasDef) -> String {
    let label: String = node
        .syntax()
        .children_with_tokens()
        .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
        .map(|node| node.to_string())
        .collect();

    label.trim().to_owned()
}

pub(crate) fn generic_parameters<N: TypeParamsOwner>(node: &N) -> Vec<String> {
    let mut res = vec![];
    if let Some(type_params) = node.type_param_list() {
        res.extend(type_params.lifetime_params().map(|p| p.syntax().text().to_string()));
        res.extend(type_params.type_params().map(|p| p.syntax().text().to_string()));
    }
    res
}

pub(crate) fn where_predicates<N: TypeParamsOwner>(node: &N) -> Vec<String> {
    let mut res = vec![];
    if let Some(clause) = node.where_clause() {
        res.extend(clause.predicates().map(|p| p.syntax().text().to_string()));
    }
    res
}

pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String {
    rust_code_markup_with_doc::<_, &str>(val, None)
}

pub(crate) fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String
where
    CODE: AsRef<str>,
    DOC: AsRef<str>,
{
    if let Some(doc) = doc {
        format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref())
    } else {
        format!("```rust\n{}\n```", val.as_ref())
    }
}

// FIXME: this should not really use navigation target. Rather, approximately
// resolved symbol should return a `DefId`.
pub(crate) fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> {
    match (nav.description(db), nav.docs(db)) {
        (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)),
        (None, Some(docs)) => Some(docs),
        _ => None,
    }
}
<|endoftext|>


<commit_before>use crate::{
    backend::renderer::{
        element::{
            surface::{render_elements_from_surface_tree, WaylandSurfaceRenderElement},
            AsRenderElements,
        },
        utils::RendererSurfaceStateUserData,
        ImportAll, Renderer,
    },
    desktop::{space::SpaceElement, PopupManager, Window, WindowSurfaceType},
    output::{Output, WeakOutput},
    utils::{Logical, Physical, Point, Rectangle, Scale},
    wayland::compositor::{with_surface_tree_downward, TraversalAction},
};
use wayland_server::{protocol::wl_surface::WlSurface, Resource, Weak as WlWeak};

use std::{
    cell::{RefCell, RefMut},
    collections::{HashMap, HashSet},
};

type OutputSurfacesUserdata = RefCell<HashSet<WlWeak<WlSurface>>>;
fn output_surfaces(o: &Output) -> RefMut<'_, HashSet<WlWeak<WlSurface>>> {
    let userdata = o.user_data();
    userdata.insert_if_missing(OutputSurfacesUserdata::default);
    let mut surfaces = userdata.get::<OutputSurfacesUserdata>().unwrap().borrow_mut();
    surfaces.retain(|s| s.upgrade().is_ok());
    surfaces
}

fn output_update(
    output: &Output,
    output_overlap: Rectangle<i32, Logical>,
    surface: &WlSurface,
    logger: &slog::Logger,
) {
    let mut surface_list = output_surfaces(output);

    with_surface_tree_downward(
        surface,
        (Point::from((0, 0)), false),
        |_, states, (location, parent_unmapped)| {
            let mut location = *location;
            let data = states.data_map.get::<RendererSurfaceStateUserData>();

            // If the parent is unmapped we still have to traverse
            // our children to send a leave events
            if *parent_unmapped {
                TraversalAction::DoChildren((location, true))
            } else if let Some(surface_view) = data.and_then(|d| d.borrow().surface_view) {
                location += surface_view.offset;
                TraversalAction::DoChildren((location, false))
            } else {
                // If we are unmapped we still have to traverse
                // our children to send leave events
                TraversalAction::DoChildren((location, true))
            }
        },
        |wl_surface, states, (location, parent_unmapped)| {
            let mut location = *location;

            if *parent_unmapped {
                // The parent is unmapped, just send a leave event
                // if we were previously mapped and exit early
                output_leave(output, &mut *surface_list, wl_surface, logger);
                return;
            }
            let data = states.data_map.get::<RendererSurfaceStateUserData>();

            if let Some(surface_view) = data.and_then(|d| d.borrow().surface_view) {
                location += surface_view.offset;
                let surface_rectangle = Rectangle::from_loc_and_size(location, surface_view.dst);
                if output_overlap.overlaps(surface_rectangle) {
                    // We found a matching output, check if we already sent enter
                    output_enter(output, &mut *surface_list, wl_surface, logger);
                } else {
                    // Surface does not match output, if we sent enter earlier
                    // we should now send leave
                    output_leave(output, &mut *surface_list, wl_surface, logger);
                }
            } else {
                // Maybe the the surface got unmapped, send leave on output
                output_leave(output, &mut *surface_list, wl_surface, logger);
            }
        },
        |_, _, _| true,
    );
}

fn output_enter(
    output: &Output,
    surface_list: &mut HashSet<WlWeak<WlSurface>>,
    surface: &WlSurface,
    logger: &slog::Logger,
) {
    let weak = surface.downgrade();
    if !surface_list.contains(&weak) {
        slog::debug!(
            logger,
            "surface ({:?}) entering output {:?}",
            surface,
            output.name()
        );
        output.enter(surface);
        surface_list.insert(weak);
    }
}

fn output_leave(
    output: &Output,
    surface_list: &mut HashSet<WlWeak<WlSurface>>,
    surface: &WlSurface,
    logger: &slog::Logger,
) {
    let weak = surface.downgrade();
    if surface_list.contains(&weak) {
        slog::debug!(
            logger,
            "surface ({:?}) leaving output {:?}",
            surface,
            output.name()
        );
        output.leave(surface);
        surface_list.remove(&weak);
    }
}

#[derive(Debug, Default)]
struct WindowOutputState {
    output_overlap: HashMap<WeakOutput, Rectangle<i32, Logical>>,
}

type WindowOutputUserData = RefCell<WindowOutputState>;

impl SpaceElement for Window {
    fn geometry(&self) -> Rectangle<i32, Logical> {
        self.geometry()
    }

    fn bbox(&self) -> Rectangle<i32, Logical> {
        self.bbox_with_popups()
    }

    fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
        self.surface_under(*point, WindowSurfaceType::ALL).is_some()
    }

    fn z_index(&self) -> u8 {
        self.0.z_index.load(std::sync::atomic::Ordering::SeqCst)
    }

    fn set_activate(&self, activated: bool) {
        self.set_activated(activated);
    }
    fn output_enter(&self, output: &Output, overlap: Rectangle<i32, Logical>) {
        self.user_data().insert_if_missing(WindowOutputUserData::default);
        {
            let mut state = self
                .user_data()
                .get::<WindowOutputUserData>()
                .unwrap()
                .borrow_mut();
            state.output_overlap.insert(output.downgrade(), overlap);
            state.output_overlap.retain(|weak, _| weak.upgrade().is_some());
        }
        self.refresh()
    }
    fn output_leave(&self, output: &Output) {
        if let Some(state) = self.user_data().get::<WindowOutputUserData>() {
            state.borrow_mut().output_overlap.retain(|weak, _| weak == output);
        }

        let mut surface_list = output_surfaces(output);
        with_surface_tree_downward(
            self.toplevel().wl_surface(),
            (),
            |_, _, _| TraversalAction::DoChildren(()),
            |wl_surface, _, _| {
                output_leave(
                    output,
                    &mut *surface_list,
                    wl_surface,
                    &crate::slog_or_fallback(None),
                );
            },
            |_, _, _| true,
        );
        for (popup, _) in PopupManager::popups_for_surface(self.toplevel().wl_surface()) {
            with_surface_tree_downward(
                popup.wl_surface(),
                (),
                |_, _, _| TraversalAction::DoChildren(()),
                |wl_surface, _, _| {
                    output_leave(
                        output,
                        &mut *surface_list,
                        wl_surface,
                        &crate::slog_or_fallback(None),
                    );
                },
                |_, _, _| true,
            );
        }
    }

    fn refresh(&self) {
        self.user_data().insert_if_missing(WindowOutputUserData::default);
        let state = self.user_data().get::<WindowOutputUserData>().unwrap().borrow();

        for (weak, overlap) in state.output_overlap.iter() {
            if let Some(output) = weak.upgrade() {
                output_update(
                    &output,
                    *overlap,
                    self.toplevel().wl_surface(),
                    &crate::slog_or_fallback(None),
                );
                for (popup, location) in PopupManager::popups_for_surface(self.toplevel().wl_surface()) {
                    let mut overlap = *overlap;
                    overlap.loc -= location;
                    output_update(
                        &output,
                        overlap,
                        popup.wl_surface(),
                        &crate::slog_or_fallback(None),
                    );
                }
            }
        }
    }
}

impl<R> AsRenderElements<R> for Window
where
    R: Renderer + ImportAll,
    <R as Renderer>::TextureId: 'static,
{
    type RenderElement = WaylandSurfaceRenderElement;

    fn render_elements<C: From<WaylandSurfaceRenderElement>>(
        &self,
        location: Point<i32, Physical>,
        scale: Scale<f64>,
    ) -> Vec<C> {
        let surface = self.toplevel().wl_surface();

        let mut render_elements: Vec<C> = Vec::new();
        let popup_render_elements =
            PopupManager::popups_for_surface(surface).flat_map(|(popup, popup_offset)| {
                let offset = (self.geometry().loc + popup_offset - popup.geometry().loc)
                    .to_physical_precise_round(scale);

                render_elements_from_surface_tree(popup.wl_surface(), location + offset, scale)
            });

        render_elements.extend(popup_render_elements);

        render_elements.extend(render_elements_from_surface_tree(surface, location, scale));

        render_elements
    }
}
<commit_msg>desktop: fix output overlap cleanup on leave<commit_after>use crate::{
    backend::renderer::{
        element::{
            surface::{render_elements_from_surface_tree, WaylandSurfaceRenderElement},
            AsRenderElements,
        },
        utils::RendererSurfaceStateUserData,
        ImportAll, Renderer,
    },
    desktop::{space::SpaceElement, PopupManager, Window, WindowSurfaceType},
    output::{Output, WeakOutput},
    utils::{Logical, Physical, Point, Rectangle, Scale},
    wayland::compositor::{with_surface_tree_downward, TraversalAction},
};
use wayland_server::{protocol::wl_surface::WlSurface, Resource, Weak as WlWeak};

use std::{
    cell::{RefCell, RefMut},
    collections::{HashMap, HashSet},
};

type OutputSurfacesUserdata = RefCell<HashSet<WlWeak<WlSurface>>>;
fn output_surfaces(o: &Output) -> RefMut<'_, HashSet<WlWeak<WlSurface>>> {
    let userdata = o.user_data();
    userdata.insert_if_missing(OutputSurfacesUserdata::default);
    let mut surfaces = userdata.get::<OutputSurfacesUserdata>().unwrap().borrow_mut();
    surfaces.retain(|s| s.upgrade().is_ok());
    surfaces
}

fn output_update(
    output: &Output,
    output_overlap: Rectangle<i32, Logical>,
    surface: &WlSurface,
    logger: &slog::Logger,
) {
    let mut surface_list = output_surfaces(output);

    with_surface_tree_downward(
        surface,
        (Point::from((0, 0)), false),
        |_, states, (location, parent_unmapped)| {
            let mut location = *location;
            let data = states.data_map.get::<RendererSurfaceStateUserData>();

            // If the parent is unmapped we still have to traverse
            // our children to send a leave events
            if *parent_unmapped {
                TraversalAction::DoChildren((location, true))
            } else if let Some(surface_view) = data.and_then(|d| d.borrow().surface_view) {
                location += surface_view.offset;
                TraversalAction::DoChildren((location, false))
            } else {
                // If we are unmapped we still have to traverse
                // our children to send leave events
                TraversalAction::DoChildren((location, true))
            }
        },
        |wl_surface, states, (location, parent_unmapped)| {
            let mut location = *location;

            if *parent_unmapped {
                // The parent is unmapped, just send a leave event
                // if we were previously mapped and exit early
                output_leave(output, &mut *surface_list, wl_surface, logger);
                return;
            }
            let data = states.data_map.get::<RendererSurfaceStateUserData>();

            if let Some(surface_view) = data.and_then(|d| d.borrow().surface_view) {
                location += surface_view.offset;
                let surface_rectangle = Rectangle::from_loc_and_size(location, surface_view.dst);
                if output_overlap.overlaps(surface_rectangle) {
                    // We found a matching output, check if we already sent enter
                    output_enter(output, &mut *surface_list, wl_surface, logger);
                } else {
                    // Surface does not match output, if we sent enter earlier
                    // we should now send leave
                    output_leave(output, &mut *surface_list, wl_surface, logger);
                }
            } else {
                // Maybe the the surface got unmapped, send leave on output
                output_leave(output, &mut *surface_list, wl_surface, logger);
            }
        },
        |_, _, _| true,
    );
}

fn output_enter(
    output: &Output,
    surface_list: &mut HashSet<WlWeak<WlSurface>>,
    surface: &WlSurface,
    logger: &slog::Logger,
) {
    let weak = surface.downgrade();
    if !surface_list.contains(&weak) {
        slog::debug!(
            logger,
            "surface ({:?}) entering output {:?}",
            surface,
            output.name()
        );
        output.enter(surface);
        surface_list.insert(weak);
    }
}

fn output_leave(
    output: &Output,
    surface_list: &mut HashSet<WlWeak<WlSurface>>,
    surface: &WlSurface,
    logger: &slog::Logger,
) {
    let weak = surface.downgrade();
    if surface_list.contains(&weak) {
        slog::debug!(
            logger,
            "surface ({:?}) leaving output {:?}",
            surface,
            output.name()
        );
        output.leave(surface);
        surface_list.remove(&weak);
    }
}

#[derive(Debug, Default)]
struct WindowOutputState {
    output_overlap: HashMap<WeakOutput, Rectangle<i32, Logical>>,
}

type WindowOutputUserData = RefCell<WindowOutputState>;

impl SpaceElement for Window {
    fn geometry(&self) -> Rectangle<i32, Logical> {
        self.geometry()
    }

    fn bbox(&self) -> Rectangle<i32, Logical> {
        self.bbox_with_popups()
    }

    fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
        self.surface_under(*point, WindowSurfaceType::ALL).is_some()
    }

    fn z_index(&self) -> u8 {
        self.0.z_index.load(std::sync::atomic::Ordering::SeqCst)
    }

    fn set_activate(&self, activated: bool) {
        self.set_activated(activated);
    }
    fn output_enter(&self, output: &Output, overlap: Rectangle<i32, Logical>) {
        self.user_data().insert_if_missing(WindowOutputUserData::default);
        {
            let mut state = self
                .user_data()
                .get::<WindowOutputUserData>()
                .unwrap()
                .borrow_mut();
            state.output_overlap.insert(output.downgrade(), overlap);
            state.output_overlap.retain(|weak, _| weak.upgrade().is_some());
        }
        self.refresh()
    }
    fn output_leave(&self, output: &Output) {
        if let Some(state) = self.user_data().get::<WindowOutputUserData>() {
            state.borrow_mut().output_overlap.retain(|weak, _| weak != output);
        }

        let mut surface_list = output_surfaces(output);
        with_surface_tree_downward(
            self.toplevel().wl_surface(),
            (),
            |_, _, _| TraversalAction::DoChildren(()),
            |wl_surface, _, _| {
                output_leave(
                    output,
                    &mut *surface_list,
                    wl_surface,
                    &crate::slog_or_fallback(None),
                );
            },
            |_, _, _| true,
        );
        for (popup, _) in PopupManager::popups_for_surface(self.toplevel().wl_surface()) {
            with_surface_tree_downward(
                popup.wl_surface(),
                (),
                |_, _, _| TraversalAction::DoChildren(()),
                |wl_surface, _, _| {
                    output_leave(
                        output,
                        &mut *surface_list,
                        wl_surface,
                        &crate::slog_or_fallback(None),
                    );
                },
                |_, _, _| true,
            );
        }
    }

    fn refresh(&self) {
        self.user_data().insert_if_missing(WindowOutputUserData::default);
        let state = self.user_data().get::<WindowOutputUserData>().unwrap().borrow();

        for (weak, overlap) in state.output_overlap.iter() {
            if let Some(output) = weak.upgrade() {
                output_update(
                    &output,
                    *overlap,
                    self.toplevel().wl_surface(),
                    &crate::slog_or_fallback(None),
                );
                for (popup, location) in PopupManager::popups_for_surface(self.toplevel().wl_surface()) {
                    let mut overlap = *overlap;
                    overlap.loc -= location;
                    output_update(
                        &output,
                        overlap,
                        popup.wl_surface(),
                        &crate::slog_or_fallback(None),
                    );
                }
            }
        }
    }
}

impl<R> AsRenderElements<R> for Window
where
    R: Renderer + ImportAll,
    <R as Renderer>::TextureId: 'static,
{
    type RenderElement = WaylandSurfaceRenderElement;

    fn render_elements<C: From<WaylandSurfaceRenderElement>>(
        &self,
        location: Point<i32, Physical>,
        scale: Scale<f64>,
    ) -> Vec<C> {
        let surface = self.toplevel().wl_surface();

        let mut render_elements: Vec<C> = Vec::new();
        let popup_render_elements =
            PopupManager::popups_for_surface(surface).flat_map(|(popup, popup_offset)| {
                let offset = (self.geometry().loc + popup_offset - popup.geometry().loc)
                    .to_physical_precise_round(scale);

                render_elements_from_surface_tree(popup.wl_surface(), location + offset, scale)
            });

        render_elements.extend(popup_render_elements);

        render_elements.extend(render_elements_from_surface_tree(surface, location, scale));

        render_elements
    }
}
<|endoftext|>


<commit_before>//! This module defines the voxel volume API, as well as data
//! types for reading volumes from files.
//! An integration with `ndarray` allows for more elegant and
//! efficient approaches, and should be preferred when possible.
//! In order to do so, you must add the `ndarray_volumes` feature
//! to this crate.

use std::io::{Read, BufReader};
use std::fs::File;
use std::path::Path;
use header::NiftiHeader;
use extension::{ExtensionSequence, Extender};
use error::{NiftiError, Result};
use util::{Endianness, raw_to_value};
use byteorder::{LittleEndian, BigEndian};
use flate2::bufread::GzDecoder;
use typedef::NiftiType;
use num::FromPrimitive;

#[cfg(feature = "ndarray_volumes")] use ndarray::{Array, Ix, IxDyn, ShapeBuilder};
#[cfg(feature = "ndarray_volumes")] use std::ops::{Add, Mul};
#[cfg(feature = "ndarray_volumes")] use num::Num;

/// Public API for NIFTI volume data, exposed as a multi-dimensional
/// voxel array.
///
/// This API is currently experimental and will likely be subjected to
/// various changes and additions in future versions.
pub trait NiftiVolume {
    /// Get the dimensions of the volume. Unlike how NIFTI-1
    /// stores dimensions, the returned slice does not include
    /// `dim[0]` and is clipped to the effective number of dimensions.
    fn dim(&self) -> &[u16];

    /// Get the volume's number of dimensions. In a fully compliant file,
    /// this is equivalent to the corresponding header's `dim[0]` field
    /// (with byte swapping already applied).
    fn dimensionality(&self) -> usize {
        self.dim().len()
    }

    /// Fetch a single voxel's value in the given voxel index coordinates
    /// as a single precision floating point value.
    /// All necessary conversions and transformations are made
    /// when reading the voxel, including scaling. Note that using this
    /// function continuously to traverse the volume is inefficient.
    /// Prefer using iterators or the `ndarray` API for volume traversal.
    ///
    /// # Errors
    ///
    /// - `NiftiError::OutOfBounds` if the given coordinates surpass this
    /// volume's boundaries.
    fn get_f32(&self, coords: &[u16]) -> Result<f32>;

    /// Fetch a single voxel's value in the given voxel index coordinates
    /// as a double precision floating point value.
    /// All necessary conversions and transformations are made
    /// when reading the voxel, including scaling. Note that using this
    /// function continuously to traverse the volume is inefficient.
    /// Prefer using iterators or the `ndarray` API for volume traversal.
    ///
    /// # Errors
    ///
    /// - `NiftiError::OutOfBounds` if the given coordinates surpass this
    /// volume's boundaries.
    fn get_f64(&self, coords: &[u16]) -> Result<f64>;

    /// Get this volume's data type.
    fn data_type(&self) -> NiftiType;
}

/// A data type for a NIFTI-1 volume contained in memory.
/// Objects of this type contain raw image data, which
/// is converted automatically when using reading methods
/// or converting it to an `ndarray` (with the
/// `ndarray_volumes` feature).
#[derive(Debug, PartialEq, Clone)]
pub struct InMemNiftiVolume {
    dim: [u16; 8],
    datatype: NiftiType,
    scl_slope: f32,
    scl_inter: f32,
    raw_data: Vec<u8>,
    endianness: Endianness,
}

impl InMemNiftiVolume {

    /// Read a NIFTI volume from a stream of data. The header and expected byte order
    /// of the volume's data must be known in advance. It it also expected that the
    /// following bytes represent the first voxels of the volume (and not part of the
    /// extensions).
    pub fn from_stream<R: Read>(mut source: R, header: &NiftiHeader, endianness: Endianness) -> Result<Self> {
        let ndims = header.dim[0];
        let resolution: usize = header.dim[1..(ndims+1) as usize].iter()
            .map(|d| *d as usize)
            .product();
        let nbytes = resolution * header.bitpix as usize / 8;
        println!("Reading volume of {:?} bytes", nbytes);
        let mut raw_data = vec![0u8; nbytes];
        source.read_exact(raw_data.as_mut_slice()).unwrap();

        let datatype: NiftiType = NiftiType::from_i16(header.datatype)
            .ok_or_else(|| NiftiError::InvalidFormat)?;

        Ok(InMemNiftiVolume {
            dim: header.dim,
            datatype,
            scl_slope: header.scl_slope,
            scl_inter: header.scl_inter,
            raw_data,
            endianness,
        })
    }

    /// Read a NIFTI volume, and extensions, from a stream of data. The header,
    /// extender code and expected byte order of the volume's data must be
    /// known in advance.
    pub fn from_stream_with_extensions<R>(mut source: R,
                                          header: &NiftiHeader,
                                          extender: Extender,
                                          endianness: Endianness)
                                          -> Result<(Self, ExtensionSequence)>
        where R: Read
    {
        // fetch extensions
        let len = header.vox_offset as usize;
        let len = if len < 352 {
            0
        } else {
            len - 352
        };

        let ext = match endianness {
            Endianness::LE => ExtensionSequence::from_stream::<LittleEndian, _>(extender, &mut source, len),
            Endianness::BE => ExtensionSequence::from_stream::<BigEndian, _>(extender, &mut source, len),
        }?;

        // fetch volume (rest of file)
        Ok((Self::from_stream(source, &header, endianness)?, ext))
    }

    /// Read a NIFTI volume from an image file. NIFTI-1 volume files usually have the
    /// extension ".img" or ".img.gz". In the latter case, the file is automatically
    /// decoded as a Gzip stream.
    pub fn from_file<P: AsRef<Path>>(path: P, header: &NiftiHeader, endianness: Endianness) -> Result<Self> {
        let gz = path.as_ref().extension()
            .map(|a| a.to_string_lossy() == "gz")
            .unwrap_or(false);
        let file = BufReader::new(File::open(path)?);
        if gz {
            InMemNiftiVolume::from_stream(GzDecoder::new(file)?, &header, endianness)
        } else {
            InMemNiftiVolume::from_stream(file, &header, endianness)
        }
    }

    /// Read a NIFTI volume, along with the extensions, from an image file. NIFTI-1 volume
    /// files usually have the extension ".img" or ".img.gz". In the latter case, the file
    /// is automatically decoded as a Gzip stream.
    pub fn from_file_with_extensions<P>(path: P,
                                        header: &NiftiHeader,
                                        endianness: Endianness,
                                        extender: Extender)
                                        -> Result<(Self, ExtensionSequence)>
        where P: AsRef<Path>
    {
        let gz = path.as_ref().extension()
            .map(|a| a.to_string_lossy() == "gz")
            .unwrap_or(false);
        let stream = BufReader::new(File::open(path)?);

        if gz {
            InMemNiftiVolume::from_stream_with_extensions(GzDecoder::new(stream)?, &header, extender, endianness)
        } else {
            InMemNiftiVolume::from_stream_with_extensions(stream, &header, extender, endianness)
        }
    }

    /// Retrieve the raw data, consuming the volume.
    pub fn to_raw_data(self) -> Vec<u8> {
        self.raw_data
    }

    /// Retrieve a reference to the raw data.
    pub fn get_raw_data(&self) -> &[u8] {
        &self.raw_data
    }

    /// Retrieve a mutable reference to the raw data.
    pub fn get_raw_data_mut(&mut self) -> &mut [u8] {
        &mut self.raw_data
    }
}

#[cfg(feature = "ndarray_volumes")]
// ndarray dependent impl
impl InMemNiftiVolume {

    /// Consume the volume into an ndarray.
    pub fn to_ndarray<T>(self) -> Result<Array<T, IxDyn>>
        where T: From<u8>,
              T: From<f32>,
              T: Clone,
              T: Num,
              T: Mul<Output = T>,
              T: Add<Output = T>,
    {
        if self.datatype != NiftiType::Uint8 {
            return Err(NiftiError::UnsupportedDataType(self.datatype));
        }

        let slope: T = self.scl_slope.into();
        let inter: T = self.scl_inter.into();
        let dim: Vec<_> = self.dim().iter()
            .map(|d| *d as Ix).collect();
        let a = Array::from_shape_vec(IxDyn(&dim).f(), self.raw_data)
            .expect("Inconsistent raw data size")
            .mapv(|v| raw_to_value(v, slope.clone(), inter.clone()));
        Ok(a)
    }
}

impl NiftiVolume for InMemNiftiVolume {
    fn dim(&self) -> &[u16] {
        &self.dim[1..(self.dim[0] + 1) as usize]
    }

    fn dimensionality(&self) -> usize {
        self.dim[0] as usize
    }

    fn data_type(&self) -> NiftiType {
        self.datatype
    }

    fn get_f32(&self, coords: &[u16]) -> Result<f32> {
        let index = coords_to_index(coords, self.dim())?;
        if self.datatype == NiftiType::Uint8 {
            let byte = self.raw_data[index];
            Ok(raw_to_value(byte as f32, self.scl_slope, self.scl_inter))
        } else {
            let range = &self.raw_data[index..];
            self.datatype.read_primitive_value(range, self.endianness, self.scl_slope, self.scl_inter)
        }
    }

    fn get_f64(&self, coords: &[u16]) -> Result<f64> {
        let index = coords_to_index(coords, self.dim())?;
        if self.datatype == NiftiType::Uint8 {
            let byte = self.raw_data[index];
            Ok(raw_to_value(byte as f64, self.scl_slope as f64, self.scl_inter as f64))
        } else {
            let range = &self.raw_data[index..];
            self.datatype.read_primitive_value(range, self.endianness, self.scl_slope, self.scl_inter)
        }
    }
}

fn coords_to_index(coords: &[u16], dim: &[u16]) -> Result<usize> {
    if coords.len() != dim.len() || coords.is_empty() {
        return Err(NiftiError::IncorrectVolumeDimensionality(
            dim.len() as u16,
            coords.len() as u16
        ))
    }

    if !coords.iter().zip(dim).all(|(i, d)| {
        *i < (*d) as u16
    }) {
        return Err(NiftiError::OutOfBounds(Vec::from(coords)));
    }

    let mut crds = coords.into_iter();
    let start = *crds.next_back().unwrap() as usize;
    let index = crds.zip(dim).rev()
        .fold(start, |a, b| {
            a * *b.1 as usize + *b.0 as usize
    });

    Ok(index)
}

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

    #[test]
    fn test_coords_to_index() {
        assert!(coords_to_index(&[0, 0], &[10, 10, 5]).is_err());
        assert!(coords_to_index(&[0, 0, 0, 0], &[10, 10, 5]).is_err());
        assert_eq!(
            coords_to_index(&[0, 0, 0], &[10, 10, 5]).unwrap(),
            0
        );

        assert_eq!(
            coords_to_index(&[1, 0, 0], &[16, 16, 3]).unwrap(),
            1
        );
        assert_eq!(
            coords_to_index(&[0, 1, 0], &[16, 16, 3]).unwrap(),
            16
        );
        assert_eq!(
            coords_to_index(&[0, 0, 1], &[16, 16, 3]).unwrap(),
            256
        );
        assert_eq!(
            coords_to_index(&[1, 1, 1], &[16, 16, 3]).unwrap(),
            273
        );

        assert_eq!(
            coords_to_index(&[15, 15, 2], &[16, 16, 3]).unwrap(),
            16 * 16 * 3 - 1
        );

        assert!(coords_to_index(&[16, 15, 2], &[16, 16, 3]).is_err());
    }
}
<commit_msg>Add Sliceable trait<commit_after>//! This module defines the voxel volume API, as well as data
//! types for reading volumes from files.
//! An integration with `ndarray` allows for more elegant and
//! efficient approaches, and should be preferred when possible.
//! In order to do so, you must add the `ndarray_volumes` feature
//! to this crate.

use std::io::{Read, BufReader};
use std::fs::File;
use std::path::Path;
use header::NiftiHeader;
use extension::{ExtensionSequence, Extender};
use error::{NiftiError, Result};
use util::{Endianness, raw_to_value};
use byteorder::{LittleEndian, BigEndian};
use flate2::bufread::GzDecoder;
use typedef::NiftiType;
use num::FromPrimitive;

#[cfg(feature = "ndarray_volumes")] use ndarray::{Array, Ix, IxDyn, ShapeBuilder};
#[cfg(feature = "ndarray_volumes")] use std::ops::{Add, Mul};
#[cfg(feature = "ndarray_volumes")] use num::Num;

/// Public API for NIFTI volume data, exposed as a multi-dimensional
/// voxel array.
///
/// This API is currently experimental and will likely be subjected to
/// various changes and additions in future versions.
pub trait NiftiVolume {
    /// Get the dimensions of the volume. Unlike how NIFTI-1
    /// stores dimensions, the returned slice does not include
    /// `dim[0]` and is clipped to the effective number of dimensions.
    fn dim(&self) -> &[u16];

    /// Get the volume's number of dimensions. In a fully compliant file,
    /// this is equivalent to the corresponding header's `dim[0]` field
    /// (with byte swapping already applied).
    fn dimensionality(&self) -> usize {
        self.dim().len()
    }

    /// Fetch a single voxel's value in the given voxel index coordinates
    /// as a double precision floating point value.
    /// All necessary conversions and transformations are made
    /// when reading the voxel, including scaling. Note that using this
    /// function continuously to traverse the volume is inefficient.
    /// Prefer using iterators or the `ndarray` API for volume traversal.
    ///
    /// # Errors
    ///
    /// - `NiftiError::OutOfBounds` if the given coordinates surpass this
    /// volume's boundaries.
    fn get_f64(&self, coords: &[u16]) -> Result<f64>;

    /// Get this volume's data type.
    fn data_type(&self) -> NiftiType;

    /// Fetch a single voxel's value in the given voxel index coordinates
    /// as a single precision floating point value.
    /// All necessary conversions and transformations are made
    /// when reading the voxel, including scaling. Note that using this
    /// function continuously to traverse the volume is inefficient.
    /// Prefer using iterators or the `ndarray` API for volume traversal.
    ///
    /// # Errors
    ///
    /// - `NiftiError::OutOfBounds` if the given coordinates surpass this
    /// volume's boundaries.
    fn get_f32(&self, coords: &[u16]) -> Result<f32> {
        let v = self.get_f64(coords)?;
        v as f32
    }
}

/// Interface for a volume that can be sliced.
impl Sliceable {
    /// The type of the resulting slice, which is also a volume.
    type Slice: NiftiVolume;

    /// Obtain a slice of the volume over a certain axis, yielding a
    /// volume of N-1 dimensions.
    fn get_slice_f64(&self, axis: u16, index: u16) -> Result<Self::Slice>;
}

/// A data type for a NIFTI-1 volume contained in memory.
/// Objects of this type contain raw image data, which
/// is converted automatically when using reading methods
/// or converting it to an `ndarray` (with the
/// `ndarray_volumes` feature).
#[derive(Debug, PartialEq, Clone)]
pub struct InMemNiftiVolumeRef<'v> {
    dim: [u16; 8],
    datatype: NiftiType,
    scl_slope: f32,
    scl_inter: f32,
    raw_data: &'v [u8],
    endianness: Endianness,
}

/// A data type for a NIFTI-1 volume contained in memory.
/// Objects of this type contain raw image data, which
/// is converted automatically when using reading methods
/// or converting it to an `ndarray` (with the
/// `ndarray_volumes` feature).
#[derive(Debug, PartialEq, Clone)]
pub struct InMemNiftiVolume {
    dim: [u16; 8],
    datatype: NiftiType,
    scl_slope: f32,
    scl_inter: f32,
    raw_data: Vec<u8>,
    endianness: Endianness,
}

impl InMemNiftiVolume {

    /// Read a NIFTI volume from a stream of data. The header and expected byte order
    /// of the volume's data must be known in advance. It it also expected that the
    /// following bytes represent the first voxels of the volume (and not part of the
    /// extensions).
    pub fn from_stream<R: Read>(mut source: R, header: &NiftiHeader, endianness: Endianness) -> Result<Self> {
        let ndims = header.dim[0];
        let resolution: usize = header.dim[1..(ndims+1) as usize].iter()
            .map(|d| *d as usize)
            .product();
        let nbytes = resolution * header.bitpix as usize / 8;
        println!("Reading volume of {:?} bytes", nbytes);
        let mut raw_data = vec![0u8; nbytes];
        source.read_exact(raw_data.as_mut_slice()).unwrap();

        let datatype: NiftiType = NiftiType::from_i16(header.datatype)
            .ok_or_else(|| NiftiError::InvalidFormat)?;

        Ok(InMemNiftiVolume {
            dim: header.dim,
            datatype,
            scl_slope: header.scl_slope,
            scl_inter: header.scl_inter,
            raw_data,
            endianness,
        })
    }

    /// Read a NIFTI volume, and extensions, from a stream of data. The header,
    /// extender code and expected byte order of the volume's data must be
    /// known in advance.
    pub fn from_stream_with_extensions<R>(mut source: R,
                                          header: &NiftiHeader,
                                          extender: Extender,
                                          endianness: Endianness)
                                          -> Result<(Self, ExtensionSequence)>
        where R: Read
    {
        // fetch extensions
        let len = header.vox_offset as usize;
        let len = if len < 352 {
            0
        } else {
            len - 352
        };

        let ext = match endianness {
            Endianness::LE => ExtensionSequence::from_stream::<LittleEndian, _>(extender, &mut source, len),
            Endianness::BE => ExtensionSequence::from_stream::<BigEndian, _>(extender, &mut source, len),
        }?;

        // fetch volume (rest of file)
        Ok((Self::from_stream(source, &header, endianness)?, ext))
    }

    /// Read a NIFTI volume from an image file. NIFTI-1 volume files usually have the
    /// extension ".img" or ".img.gz". In the latter case, the file is automatically
    /// decoded as a Gzip stream.
    pub fn from_file<P: AsRef<Path>>(path: P, header: &NiftiHeader, endianness: Endianness) -> Result<Self> {
        let gz = path.as_ref().extension()
            .map(|a| a.to_string_lossy() == "gz")
            .unwrap_or(false);
        let file = BufReader::new(File::open(path)?);
        if gz {
            InMemNiftiVolume::from_stream(GzDecoder::new(file)?, &header, endianness)
        } else {
            InMemNiftiVolume::from_stream(file, &header, endianness)
        }
    }

    /// Read a NIFTI volume, along with the extensions, from an image file. NIFTI-1 volume
    /// files usually have the extension ".img" or ".img.gz". In the latter case, the file
    /// is automatically decoded as a Gzip stream.
    pub fn from_file_with_extensions<P>(path: P,
                                        header: &NiftiHeader,
                                        endianness: Endianness,
                                        extender: Extender)
                                        -> Result<(Self, ExtensionSequence)>
        where P: AsRef<Path>
    {
        let gz = path.as_ref().extension()
            .map(|a| a.to_string_lossy() == "gz")
            .unwrap_or(false);
        let stream = BufReader::new(File::open(path)?);

        if gz {
            InMemNiftiVolume::from_stream_with_extensions(GzDecoder::new(stream)?, &header, extender, endianness)
        } else {
            InMemNiftiVolume::from_stream_with_extensions(stream, &header, extender, endianness)
        }
    }

    /// Retrieve the raw data, consuming the volume.
    pub fn to_raw_data(self) -> Vec<u8> {
        self.raw_data
    }

    /// Retrieve a reference to the raw data.
    pub fn get_raw_data(&self) -> &[u8] {
        &self.raw_data
    }

    /// Retrieve a mutable reference to the raw data.
    pub fn get_raw_data_mut(&mut self) -> &mut [u8] {
        &mut self.raw_data
    }
}

#[cfg(feature = "ndarray_volumes")]
// ndarray dependent impl
impl InMemNiftiVolume {

    /// Consume the volume into an ndarray.
    pub fn to_ndarray<T>(self) -> Result<Array<T, IxDyn>>
        where T: From<u8>,
              T: From<f32>,
              T: Clone,
              T: Num,
              T: Mul<Output = T>,
              T: Add<Output = T>,
    {
        if self.datatype != NiftiType::Uint8 {
            return Err(NiftiError::UnsupportedDataType(self.datatype));
        }

        let slope: T = self.scl_slope.into();
        let inter: T = self.scl_inter.into();
        let dim: Vec<_> = self.dim().iter()
            .map(|d| *d as Ix).collect();
        let a = Array::from_shape_vec(IxDyn(&dim).f(), self.raw_data)
            .expect("Inconsistent raw data size")
            .mapv(|v| raw_to_value(v, slope.clone(), inter.clone()));
        Ok(a)
    }
}

impl NiftiVolume for InMemNiftiVolume {
    fn dim(&self) -> &[u16] {
        &self.dim[1..(self.dim[0] + 1) as usize]
    }

    fn dimensionality(&self) -> usize {
        self.dim[0] as usize
    }

    fn data_type(&self) -> NiftiType {
        self.datatype
    }

    fn get_f32(&self, coords: &[u16]) -> Result<f32> {
        let index = coords_to_index(coords, self.dim())?;
        if self.datatype == NiftiType::Uint8 {
            let byte = self.raw_data[index];
            Ok(raw_to_value(byte as f32, self.scl_slope, self.scl_inter))
        } else {
            let range = &self.raw_data[index..];
            self.datatype.read_primitive_value(range, self.endianness, self.scl_slope, self.scl_inter)
        }
    }

    fn get_f64(&self, coords: &[u16]) -> Result<f64> {
        let index = coords_to_index(coords, self.dim())?;
        if self.datatype == NiftiType::Uint8 {
            let byte = self.raw_data[index];
            Ok(raw_to_value(byte as f64, self.scl_slope as f64, self.scl_inter as f64))
        } else {
            let range = &self.raw_data[index..];
            self.datatype.read_primitive_value(range, self.endianness, self.scl_slope, self.scl_inter)
        }
    }
}

fn coords_to_index(coords: &[u16], dim: &[u16]) -> Result<usize> {
    if coords.len() != dim.len() || coords.is_empty() {
        return Err(NiftiError::IncorrectVolumeDimensionality(
            dim.len() as u16,
            coords.len() as u16
        ))
    }

    if !coords.iter().zip(dim).all(|(i, d)| {
        *i < (*d) as u16
    }) {
        return Err(NiftiError::OutOfBounds(Vec::from(coords)));
    }

    let mut crds = coords.into_iter();
    let start = *crds.next_back().unwrap() as usize;
    let index = crds.zip(dim).rev()
        .fold(start, |a, b| {
            a * *b.1 as usize + *b.0 as usize
    });

    Ok(index)
}

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

    #[test]
    fn test_coords_to_index() {
        assert!(coords_to_index(&[0, 0], &[10, 10, 5]).is_err());
        assert!(coords_to_index(&[0, 0, 0, 0], &[10, 10, 5]).is_err());
        assert_eq!(
            coords_to_index(&[0, 0, 0], &[10, 10, 5]).unwrap(),
            0
        );

        assert_eq!(
            coords_to_index(&[1, 0, 0], &[16, 16, 3]).unwrap(),
            1
        );
        assert_eq!(
            coords_to_index(&[0, 1, 0], &[16, 16, 3]).unwrap(),
            16
        );
        assert_eq!(
            coords_to_index(&[0, 0, 1], &[16, 16, 3]).unwrap(),
            256
        );
        assert_eq!(
            coords_to_index(&[1, 1, 1], &[16, 16, 3]).unwrap(),
            273
        );

        assert_eq!(
            coords_to_index(&[15, 15, 2], &[16, 16, 3]).unwrap(),
            16 * 16 * 3 - 1
        );

        assert!(coords_to_index(&[16, 15, 2], &[16, 16, 3]).is_err());
    }
}
<|endoftext|>