<commit_before>use {Camera, Error, Projective3, Result, ScanPosition};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use xmltree::Element;

const PROJECT_RSP: &'static str = "project.rsp";

/// A RiSCAN Pro project.
///
/// This project isn't a one-to-one mapping to Riegl's XML structure. We've chosen to cut cornerns
/// in order to easily support *our* use case. Specifically:
///
/// - Only one or zero camera calibrations are supported, not more than one.
#[derive(Debug, PartialEq)]
pub struct Project {
    root: Element,
}

impl Project {
    /// Reads a project from a path.
    ///
    /// This path can either be the `.RiSCAN` directory, or the underlying `project.rsp` file.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project1 = Project::from_path("data/project.RiSCAN").unwrap();
    /// let project2 = Project::from_path("data/project.RiSCAN/project.rsp").unwrap();
    /// assert_eq!(project1, project2);
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Project> {
        use std::fs::File;

        let path = rsp_path(path)?;
        let mut file = File::open(path)?;
        let element = Element::parse(&mut file)?;
        if element.name != "project" {
            Err(Error::InvalidRspRoot(element.name))
        } else {
            Ok(Project { root: element })
        }
    }

    /// Returns this project's POP.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// let pop = project.pop();
    /// ```
    pub fn pop(&self) -> Result<Projective3> {
        self.get_projective3("pop/matrix")
    }

    /// Returns this project's camera calibration, if it exists.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// let camera = project.camera().unwrap();
    /// ```
    pub fn camera(&self) -> Result<Option<Camera>> {
        let camcalibs = self.get_element("calibrations/camcalibs")?;
        if camcalibs.children.is_empty() {
            Ok(None)
        } else if camcalibs.children.len() > 1 {
            Err(Error::MultipleCameras)
        } else {
            let ref camera = camcalibs.children[0];
            Ok(Some(Camera {
                        fx: get_and_parse(camera, "internal_opencv/fx")?,
                        fy: get_and_parse(camera, "internal_opencv/fy")?,
                        cx: get_and_parse(camera, "internal_opencv/cx")?,
                        cy: get_and_parse(camera, "internal_opencv/cy")?,
                        k1: get_and_parse(camera, "internal_opencv/k1")?,
                        k2: get_and_parse(camera, "internal_opencv/k2")?,
                        k3: get_and_parse(camera, "internal_opencv/k3")?,
                        k4: get_and_parse(camera, "internal_opencv/k4")?,
                        p1: get_and_parse(camera, "internal_opencv/p1")?,
                        p2: get_and_parse(camera, "internal_opencv/p2")?,
                        nx: get_and_parse(camera, "intrinsic_opencv/nx")?,
                        ny: get_and_parse(camera, "intrinsic_opencv/ny")?,
                        dx: get_and_parse(camera, "intrinsic_opencv/dx")?,
                        dy: get_and_parse(camera, "intrinsic_opencv/dy")?,
                    }))
        }
    }

    /// Returns the scan position with the given name, or None.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// assert!(project.scan_position("SP01").unwrap().is_some());
    /// assert!(project.scan_position("SP03").unwrap().is_none());
    /// ```
    pub fn scan_position(&self, name: &str) -> Result<Option<ScanPosition>> {
        let scanpositions = self.get_element("scanpositions")?;
        Ok(scanpositions.children
               .iter()
               .find(|child| get_str(child, "name").map(|s| s == name).unwrap_or(false))
               .map(|_| ScanPosition {}))
    }

    fn get_projective3(&self, xpath: &str) -> Result<Projective3> {
        use utils;
        let s = self.get_str(xpath)?;
        utils::projective_from_str(s)
    }

    fn get_str(&self, xpath: &str) -> Result<&str> {
        get_str(&self.root, xpath)
    }

    fn get_element(&self, xpath: &str) -> Result<&Element> {
        get_element(&self.root, xpath)
    }
}

fn rsp_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
    use std::fs;

    let mut path = fs::canonicalize(path)?;
    if let Some(extension) = path.extension().map(|extension| {
                                                      extension.to_string_lossy().into_owned()
                                                  }) {
        match extension.as_str() {
            "RiSCAN" => {
                path.push(PROJECT_RSP);
                Ok(path)
            }
            "rsp" => Ok(path),
            _ => Err(Error::ProjectPath(path)),
        }
    } else {
        Err(Error::ProjectPath(path))
    }
}

fn get_and_parse<T>(element: &Element, xpath: &str) -> Result<T>
    where T: FromStr,
          Error: From<<T as FromStr>::Err>
{
    get_str(element, xpath).and_then(|s| s.parse().map_err(Error::from))
}

fn get_str<'a>(element: &'a Element, xpath: &str) -> Result<&'a str> {
    let element = get_element(element, xpath)?;
    if let Some(s) = element.text.as_ref() {
        Ok(s)
    } else {
        Err(Error::NoText(element.clone()))
    }
}

fn get_element<'a>(mut element: &'a Element, xpath: &str) -> Result<&'a Element> {
    for name in xpath.split('/') {
        if let Some(child) = element.get_child(name) {
            element = child;
        } else {
            return Err(Error::MissingChild(xpath.to_string()));
        }
    }
    Ok(element)
}

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

    fn xml_path() -> PathBuf {
        use std::fs;
        fs::canonicalize("data/project.RiSCAN/project.rsp").unwrap()
    }

    #[test]
    fn rsp_path_from_rsp_path() {
        let path = rsp_path("data/project.RiSCAN/project.rsp").unwrap();
        assert_eq!(xml_path(), path);
    }

    #[test]
    fn rsp_path_from_riscan_path() {
        let path = rsp_path("data/project.RiSCAN").unwrap();
        assert_eq!(xml_path(), path);
    }

    #[test]
    fn rsp_path_err() {
        assert!(rsp_path("data").is_err());
        assert!(rsp_path("Cargo.toml").is_err());
    }

    #[test]
    fn project() {
        use Camera;
        use nalgebra::Matrix4;

        let project = Project::from_path("data/project.RiSCAN").unwrap();
        let expected = Projective3::from_matrix_unchecked(Matrix4::new(0.99566497679815923,
                                                                       0.046111730526226816,
                                                                       -0.080777238659154112,
                                                                       -515632.66332186362,
                                                                       -0.093012117369304602,
                                                                       0.49361133154539053,
                                                                       -0.86469451217899213,
                                                                       -5519682.7927730317,
                                                                       0.,
                                                                       0.86845930340912512,
                                                                       0.49576046466225683,
                                                                       3143447.4201939853,
                                                                       0.,
                                                                       0.,
                                                                       0.,
                                                                       1.));
        let actual = project.pop().unwrap();
        assert_relative_eq!(expected.matrix(), actual.matrix());
        let camera = Camera::from_path("data/camera.cam").unwrap();
        assert_eq!(camera, project.camera().unwrap().unwrap());
        project.scan_position("SP01").unwrap().unwrap();
        project.scan_position("SP02").unwrap().unwrap();
        assert!(project.scan_position("SP03").unwrap().is_none());
    }

    #[test]
    fn notaproject_rsp() {
        assert!(Project::from_path("data/notaproject.rsp").is_err());
    }

    #[test]
    fn empty_rsp() {
        assert!(Project::from_path("data/empty.rsp").is_err());
    }

    #[test]
    fn two_cameras() {
        let project = Project::from_path("data/two-cameras.rsp").unwrap();
        assert!(project.camera().is_err());
    }

    #[test]
    fn extra_crap_in_doctype() {
        Project::from_path("data/extra-crap-in-doctype.rsp").unwrap();
    }
}
<commit_msg>Naming is hard<commit_after>use {Camera, Error, Projective3, Result, ScanPosition};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use xmltree::Element;

const PROJECT_RSP: &'static str = "project.rsp";

/// A RiSCAN Pro project.
///
/// This project isn't a one-to-one mapping to Riegl's XML structure. We've chosen to cut cornerns
/// in order to easily support *our* use case. Specifically:
///
/// - Only one or zero camera calibrations are supported, not more than one.
#[derive(Debug, PartialEq)]
pub struct Project {
    root: Element,
}

impl Project {
    /// Reads a project from a path.
    ///
    /// This path can either be the `.RiSCAN` directory, or the underlying `project.rsp` file.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project1 = Project::from_path("data/project.RiSCAN").unwrap();
    /// let project2 = Project::from_path("data/project.RiSCAN/project.rsp").unwrap();
    /// assert_eq!(project1, project2);
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Project> {
        use std::fs::File;

        let path = rsp_path(path)?;
        let mut file = File::open(path)?;
        let element = Element::parse(&mut file)?;
        if element.name != "project" {
            Err(Error::InvalidRspRoot(element.name))
        } else {
            Ok(Project { root: element })
        }
    }

    /// Returns this project's POP.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// let pop = project.pop();
    /// ```
    pub fn pop(&self) -> Result<Projective3> {
        projective3_from_xpath(&self.root, "pop/matrix")
    }

    /// Returns this project's camera calibration, if it exists.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// let camera = project.camera().unwrap();
    /// ```
    pub fn camera(&self) -> Result<Option<Camera>> {
        let camcalibs = xpath(&self.root, "calibrations/camcalibs")?;
        if camcalibs.children.is_empty() {
            Ok(None)
        } else if camcalibs.children.len() > 1 {
            Err(Error::MultipleCameras)
        } else {
            let ref camera = camcalibs.children[0];
            Ok(Some(Camera {
                        fx: parse_from_xpath(camera, "internal_opencv/fx")?,
                        fy: parse_from_xpath(camera, "internal_opencv/fy")?,
                        cx: parse_from_xpath(camera, "internal_opencv/cx")?,
                        cy: parse_from_xpath(camera, "internal_opencv/cy")?,
                        k1: parse_from_xpath(camera, "internal_opencv/k1")?,
                        k2: parse_from_xpath(camera, "internal_opencv/k2")?,
                        k3: parse_from_xpath(camera, "internal_opencv/k3")?,
                        k4: parse_from_xpath(camera, "internal_opencv/k4")?,
                        p1: parse_from_xpath(camera, "internal_opencv/p1")?,
                        p2: parse_from_xpath(camera, "internal_opencv/p2")?,
                        nx: parse_from_xpath(camera, "intrinsic_opencv/nx")?,
                        ny: parse_from_xpath(camera, "intrinsic_opencv/ny")?,
                        dx: parse_from_xpath(camera, "intrinsic_opencv/dx")?,
                        dy: parse_from_xpath(camera, "intrinsic_opencv/dy")?,
                    }))
        }
    }

    /// Returns the scan position with the given name, or None.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// assert!(project.scan_position("SP01").unwrap().is_some());
    /// assert!(project.scan_position("SP03").unwrap().is_none());
    /// ```
    pub fn scan_position(&self, name: &str) -> Result<Option<ScanPosition>> {
        let scanpositions = xpath(&self.root, "scanpositions")?;
        Ok(scanpositions.children
               .iter()
               .find(|child| str_from_xpath(child, "name").map(|s| s == name).unwrap_or(false))
               .map(|_| ScanPosition {}))
    }
}

fn rsp_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
    use std::fs;

    let mut path = fs::canonicalize(path)?;
    if let Some(extension) = path.extension().map(|extension| {
                                                      extension.to_string_lossy().into_owned()
                                                  }) {
        match extension.as_str() {
            "RiSCAN" => {
                path.push(PROJECT_RSP);
                Ok(path)
            }
            "rsp" => Ok(path),
            _ => Err(Error::ProjectPath(path)),
        }
    } else {
        Err(Error::ProjectPath(path))
    }
}

fn parse_from_xpath<T>(element: &Element, xpath: &str) -> Result<T>
    where T: FromStr,
          Error: From<<T as FromStr>::Err>
{
    str_from_xpath(element, xpath).and_then(|s| s.parse().map_err(Error::from))
}

fn projective3_from_xpath(element: &Element, xpath: &str) -> Result<Projective3> {
    use utils;
    let s = str_from_xpath(element, xpath)?;
    utils::projective_from_str(s)
}

fn str_from_xpath<'a>(element: &'a Element, s: &str) -> Result<&'a str> {
    let element = xpath(element, s)?;
    if let Some(s) = element.text.as_ref() {
        Ok(s)
    } else {
        Err(Error::NoText(element.clone()))
    }
}

fn xpath<'a>(mut element: &'a Element, xpath: &str) -> Result<&'a Element> {
    for name in xpath.split('/') {
        if let Some(child) = element.get_child(name) {
            element = child;
        } else {
            return Err(Error::MissingChild(xpath.to_string()));
        }
    }
    Ok(element)
}

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

    fn xml_path() -> PathBuf {
        use std::fs;
        fs::canonicalize("data/project.RiSCAN/project.rsp").unwrap()
    }

    #[test]
    fn rsp_path_from_rsp_path() {
        let path = rsp_path("data/project.RiSCAN/project.rsp").unwrap();
        assert_eq!(xml_path(), path);
    }

    #[test]
    fn rsp_path_from_riscan_path() {
        let path = rsp_path("data/project.RiSCAN").unwrap();
        assert_eq!(xml_path(), path);
    }

    #[test]
    fn rsp_path_err() {
        assert!(rsp_path("data").is_err());
        assert!(rsp_path("Cargo.toml").is_err());
    }

    #[test]
    fn project() {
        use Camera;
        use nalgebra::Matrix4;

        let project = Project::from_path("data/project.RiSCAN").unwrap();
        let expected = Projective3::from_matrix_unchecked(Matrix4::new(0.99566497679815923,
                                                                       0.046111730526226816,
                                                                       -0.080777238659154112,
                                                                       -515632.66332186362,
                                                                       -0.093012117369304602,
                                                                       0.49361133154539053,
                                                                       -0.86469451217899213,
                                                                       -5519682.7927730317,
                                                                       0.,
                                                                       0.86845930340912512,
                                                                       0.49576046466225683,
                                                                       3143447.4201939853,
                                                                       0.,
                                                                       0.,
                                                                       0.,
                                                                       1.));
        let actual = project.pop().unwrap();
        assert_relative_eq!(expected.matrix(), actual.matrix());
        let camera = Camera::from_path("data/camera.cam").unwrap();
        assert_eq!(camera, project.camera().unwrap().unwrap());
        project.scan_position("SP01").unwrap().unwrap();
        project.scan_position("SP02").unwrap().unwrap();
        assert!(project.scan_position("SP03").unwrap().is_none());
    }

    #[test]
    fn notaproject_rsp() {
        assert!(Project::from_path("data/notaproject.rsp").is_err());
    }

    #[test]
    fn empty_rsp() {
        assert!(Project::from_path("data/empty.rsp").is_err());
    }

    #[test]
    fn two_cameras() {
        let project = Project::from_path("data/two-cameras.rsp").unwrap();
        assert!(project.camera().is_err());
    }

    #[test]
    fn extra_crap_in_doctype() {
        Project::from_path("data/extra-crap-in-doctype.rsp").unwrap();
    }
}
<|endoftext|>


<commit_before>use crate::tokenizer;
use crate::{
    constants::*,
    token_collector::{Style, StyledToken, TokenCollector},
};
use diffus::{
    edit::{self, collection},
    Diffable,
};

/// Like format!(), but faster for our special case
fn format_simple_line(old_new: &str, plus_minus: char, contents: &str) -> String {
    let mut line = String::with_capacity(old_new.len() + 1 + contents.len() + NORMAL.len());
    line.push_str(old_new);
    line.push(plus_minus);
    line.push_str(contents);
    line.push_str(NORMAL);
    return line;
}

/// Format old and new lines in OLD and NEW colors.
///
/// No intra-line refinement.
///
/// Returns one old and one new line array.
#[must_use]
fn simple_format(old_text: &str, new_text: &str) -> (Vec<String>, Vec<String>) {
    let mut old_lines: Vec<String> = Vec::new();
    let mut new_lines: Vec<String> = Vec::new();

    for old_line in old_text.lines() {
        // Use a specialized line formatter since this code is in a hot path
        old_lines.push(format_simple_line(OLD, '-', old_line));
    }
    if (!old_text.is_empty()) && !old_text.ends_with('\n') {
        old_lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for add_line in new_text.lines() {
        // Use a specialized line formatter since this code is in a hot path
        new_lines.push(format_simple_line(NEW, '+', add_line));
    }
    if (!new_text.is_empty()) && !new_text.ends_with('\n') {
        new_lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return (old_lines, new_lines);
}

#[must_use]
fn concat(mut a: Vec<String>, mut b: Vec<String>) -> Vec<String> {
    let mut merged: Vec<String> = Vec::new();
    merged.append(&mut a);
    merged.append(&mut b);
    return merged;
}

/// Returns a vector of ANSI highlighted lines
#[must_use]
pub fn format(old_text: &str, new_text: &str) -> Vec<String> {
    match format_split(old_text, new_text) {
        None => {
            let (old_lines, new_lines) = simple_format(old_text, new_text);
            return concat(old_lines, new_lines);
        }

        Some((old_lines, new_lines)) => {
            return concat(old_lines, new_lines);
        }
    }
}

/// Returns two vectors of ANSI highlighted lines, the old lines and the new
/// lines.
///
/// A return value of None means you should try simple_format() instead.
#[must_use]
fn format_split(old_text: &str, new_text: &str) -> Option<(Vec<String>, Vec<String>)> {
    if old_text.is_empty() || new_text.is_empty() {
        return Some(simple_format(old_text, new_text));
    }

    // FIXME: LCS is O(m * n) complexity, consider returning None here if
    // len(old_text) * len(new_text) is too large.

    // Find diffs between adds and removals
    let mut old_collector = TokenCollector::create(StyledToken::new("-".to_string(), Style::Old));
    let mut new_collector = TokenCollector::create(StyledToken::new("+".to_string(), Style::New));

    // Tokenize adds and removes before diffing them
    let tokenized_old = tokenizer::tokenize(old_text);
    let tokenized_new = tokenizer::tokenize(new_text);

    let diff = tokenized_old.diff(&tokenized_new);
    match diff {
        edit::Edit::Copy(_) => {
            unimplemented!("Copy not implemented, help!");
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        collection::Edit::Copy(token) => {
                            old_collector.push(StyledToken::new(token.to_string(), Style::Old));
                            new_collector.push(StyledToken::new(token.to_string(), Style::New));
                        }
                        collection::Edit::Insert(token) => {
                            new_collector
                                .push(StyledToken::new(token.to_string(), Style::NewInverse));
                        }
                        collection::Edit::Remove(token) => {
                            old_collector
                                .push(StyledToken::new(token.to_string(), Style::OldInverse));
                        }
                        collection::Edit::Change(_) => {
                            unimplemented!("Edit/Change/Change not implemented, help!")
                        }
                    };
                })
                .for_each(drop);
        }
    }

    let highlighted_old_text = old_collector.render();
    let highlighted_new_text = new_collector.render();

    return Some(to_lines(&highlighted_old_text, &highlighted_new_text));
}

#[must_use]
fn to_lines(old: &str, new: &str) -> (Vec<String>, Vec<String>) {
    let mut old_lines: Vec<String> = Vec::new();
    for highlighted_old_line in old.lines() {
        old_lines.push(highlighted_old_line.to_string());
    }
    if (!old.is_empty()) && !old.ends_with('\n') {
        old_lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    let mut new_lines: Vec<String> = Vec::new();
    for highlighted_new_line in new.lines() {
        new_lines.push(highlighted_new_line.to_string());
    }
    if (!new.is_empty()) && !new.ends_with('\n') {
        new_lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return (old_lines, new_lines);
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    fn simple_format_merged(old_text: &str, new_text: &str) -> Vec<String> {
        let (old_lines, new_lines) = simple_format(old_text, new_text);

        return concat(old_lines, new_lines);
    }

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(
            simple_format_merged(&"".to_string(), &"".to_string()),
            empty
        );

        // Test adds-only
        assert_eq!(
            simple_format_merged(&"".to_string(), &"a\n".to_string()),
            ["".to_string() + NEW + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format_merged(&"".to_string(), &"a\nb\n".to_string()),
            [
                "".to_string() + NEW + "+a" + NORMAL,
                "".to_string() + NEW + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format_merged(&"a\n".to_string(), &"".to_string()),
            ["".to_string() + OLD + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format_merged(&"a\nb\n".to_string(), &"".to_string()),
            [
                "".to_string() + OLD + "-a" + NORMAL,
                "".to_string() + OLD + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        let result = format(
            &"<unchanged text between quotes>\n".to_string(),
            &"[unchanged text between quotes]\n".to_string(),
        );
        assert_eq!(
            result,
            [
                format!(
                    "{}-{}<{}unchanged text between quotes{}>{}",
                    OLD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
                format!(
                    "{}+{}[{}unchanged text between quotes{}]{}",
                    NEW, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }

    #[test]
    fn test_almost_empty_changes() {
        let result = format(&"x\n".to_string(), &"".to_string());
        assert_eq!(result, [format!("{}-x{}", OLD, NORMAL),]);

        let result = format(&"".to_string(), &"x\n".to_string());
        assert_eq!(result, [format!("{}+x{}", NEW, NORMAL),]);
    }
}
<commit_msg>Fix a crash<commit_after>use crate::tokenizer;
use crate::{
    constants::*,
    token_collector::{Style, StyledToken, TokenCollector},
};
use diffus::{
    edit::{self, collection},
    Diffable,
};

/// Like format!(), but faster for our special case
fn format_simple_line(old_new: &str, plus_minus: char, contents: &str) -> String {
    let mut line = String::with_capacity(old_new.len() + 1 + contents.len() + NORMAL.len());
    line.push_str(old_new);
    line.push(plus_minus);
    line.push_str(contents);
    line.push_str(NORMAL);
    return line;
}

/// Format old and new lines in OLD and NEW colors.
///
/// No intra-line refinement.
///
/// Returns one old and one new line array.
#[must_use]
fn simple_format(old_text: &str, new_text: &str) -> (Vec<String>, Vec<String>) {
    let mut old_lines: Vec<String> = Vec::new();
    let mut new_lines: Vec<String> = Vec::new();

    for old_line in old_text.lines() {
        // Use a specialized line formatter since this code is in a hot path
        old_lines.push(format_simple_line(OLD, '-', old_line));
    }
    if (!old_text.is_empty()) && !old_text.ends_with('\n') {
        old_lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for add_line in new_text.lines() {
        // Use a specialized line formatter since this code is in a hot path
        new_lines.push(format_simple_line(NEW, '+', add_line));
    }
    if (!new_text.is_empty()) && !new_text.ends_with('\n') {
        new_lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return (old_lines, new_lines);
}

#[must_use]
fn concat(mut a: Vec<String>, mut b: Vec<String>) -> Vec<String> {
    let mut merged: Vec<String> = Vec::new();
    merged.append(&mut a);
    merged.append(&mut b);
    return merged;
}

/// Returns a vector of ANSI highlighted lines
#[must_use]
pub fn format(old_text: &str, new_text: &str) -> Vec<String> {
    match format_split(old_text, new_text) {
        None => {
            let (old_lines, new_lines) = simple_format(old_text, new_text);
            return concat(old_lines, new_lines);
        }

        Some((old_lines, new_lines)) => {
            return concat(old_lines, new_lines);
        }
    }
}

/// Returns two vectors of ANSI highlighted lines, the old lines and the new
/// lines.
///
/// A return value of None means you should try simple_format() instead.
#[must_use]
fn format_split(old_text: &str, new_text: &str) -> Option<(Vec<String>, Vec<String>)> {
    if old_text.is_empty() || new_text.is_empty() {
        return Some(simple_format(old_text, new_text));
    }

    // FIXME: LCS is O(m * n) complexity, consider returning None here if
    // len(old_text) * len(new_text) is too large.

    // Find diffs between adds and removals
    let mut old_collector = TokenCollector::create(StyledToken::new("-".to_string(), Style::Old));
    let mut new_collector = TokenCollector::create(StyledToken::new("+".to_string(), Style::New));

    // Tokenize adds and removes before diffing them
    let tokenized_old = tokenizer::tokenize(old_text);
    let tokenized_new = tokenizer::tokenize(new_text);

    let diff = tokenized_old.diff(&tokenized_new);
    match diff {
        edit::Edit::Copy(tokens) => {
            for &token in tokens {
                // FIXME: "Copy" means that old and new are the same, why was
                // format_split() called on this non-difference?
                //
                // Get here using "git show 686f3d7ae | cargo run" with git 2.35.1
                old_collector.push(StyledToken::new(token.to_string(), Style::Old));
                new_collector.push(StyledToken::new(token.to_string(), Style::New));
            }
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        collection::Edit::Copy(token) => {
                            old_collector.push(StyledToken::new(token.to_string(), Style::Old));
                            new_collector.push(StyledToken::new(token.to_string(), Style::New));
                        }
                        collection::Edit::Insert(token) => {
                            new_collector
                                .push(StyledToken::new(token.to_string(), Style::NewInverse));
                        }
                        collection::Edit::Remove(token) => {
                            old_collector
                                .push(StyledToken::new(token.to_string(), Style::OldInverse));
                        }
                        collection::Edit::Change(_) => {
                            unimplemented!("Edit/Change/Change not implemented, help!")
                        }
                    };
                })
                .for_each(drop);
        }
    }

    let highlighted_old_text = old_collector.render();
    let highlighted_new_text = new_collector.render();

    return Some(to_lines(&highlighted_old_text, &highlighted_new_text));
}

#[must_use]
fn to_lines(old: &str, new: &str) -> (Vec<String>, Vec<String>) {
    let mut old_lines: Vec<String> = Vec::new();
    for highlighted_old_line in old.lines() {
        old_lines.push(highlighted_old_line.to_string());
    }
    if (!old.is_empty()) && !old.ends_with('\n') {
        old_lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    let mut new_lines: Vec<String> = Vec::new();
    for highlighted_new_line in new.lines() {
        new_lines.push(highlighted_new_line.to_string());
    }
    if (!new.is_empty()) && !new.ends_with('\n') {
        new_lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return (old_lines, new_lines);
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    fn simple_format_merged(old_text: &str, new_text: &str) -> Vec<String> {
        let (old_lines, new_lines) = simple_format(old_text, new_text);

        return concat(old_lines, new_lines);
    }

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(
            simple_format_merged(&"".to_string(), &"".to_string()),
            empty
        );

        // Test adds-only
        assert_eq!(
            simple_format_merged(&"".to_string(), &"a\n".to_string()),
            ["".to_string() + NEW + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format_merged(&"".to_string(), &"a\nb\n".to_string()),
            [
                "".to_string() + NEW + "+a" + NORMAL,
                "".to_string() + NEW + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format_merged(&"a\n".to_string(), &"".to_string()),
            ["".to_string() + OLD + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format_merged(&"a\nb\n".to_string(), &"".to_string()),
            [
                "".to_string() + OLD + "-a" + NORMAL,
                "".to_string() + OLD + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        let result = format(
            &"<unchanged text between quotes>\n".to_string(),
            &"[unchanged text between quotes]\n".to_string(),
        );
        assert_eq!(
            result,
            [
                format!(
                    "{}-{}<{}unchanged text between quotes{}>{}",
                    OLD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
                format!(
                    "{}+{}[{}unchanged text between quotes{}]{}",
                    NEW, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }

    #[test]
    fn test_almost_empty_changes() {
        let result = format(&"x\n".to_string(), &"".to_string());
        assert_eq!(result, [format!("{}-x{}", OLD, NORMAL),]);

        let result = format(&"".to_string(), &"x\n".to_string());
        assert_eq!(result, [format!("{}+x{}", NEW, NORMAL),]);
    }
}
<|endoftext|>


<commit_before>pub use Request = http::server::request::Request;
<commit_msg>(doc) Documented Request.<commit_after>//! An alias of the rust-http Request struct.

pub use Request = http::server::request::Request;
<|endoftext|>


<commit_before>use StunMethod;
use types::U12;

#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub enum Method {
    Reserved = 0x000,
    Binding = 0x001,
    ReservedWasSharedSecret = 0x002,
}
impl StunMethod for Method {
    fn from_u12(value: U12) -> Option<Self> {
        match value.as_u16() {
            0x000 => Some(Method::Reserved),
            0x001 => Some(Method::Binding),
            0x002 => Some(Method::ReservedWasSharedSecret),
            _ => None,
        }
    }
    fn as_u12(&self) -> U12 {
        U12::from_u8(*self as u8)
    }
}
<commit_msg>Remove old file<commit_after><|endoftext|>


<commit_before>use std::u64;
use std::io::{Read, Seek, SeekFrom};
use url::Url;
use hyper::header::ContentLength;
use hyper::client::Client;
use hyper::client::response::Response;

use std::io::Write;

use utils::*;
use rssource::*;

#[derive(Debug)]
pub struct HttpSrc {
    url: Option<Url>,
    client: Client,
    response: Option<Response>,
    position: u64,
    size: u64,
}

impl HttpSrc {
    fn new() -> HttpSrc {
        HttpSrc { url: None, client: Client::new(), response: None, position: 0, size: u64::MAX }
    }

    fn new_source() -> Box<Source> {
        Box::new(HttpSrc::new())
    }
    pub extern "C" fn new_ptr() -> *mut Box<Source> {
        let instance = Box::new(HttpSrc::new_source());
        return Box::into_raw(instance);
    }
}

impl Source for HttpSrc {
    fn set_uri(&mut self, uri_str: &Option<String>) -> bool {
        match *uri_str {
            None => {
                self.url = None;
                return true;
            },
            Some(ref uri_str) => {
                let uri_parsed = Url::parse(uri_str.as_str());
                match uri_parsed {
                    Ok(u) => {
                        if u.scheme() == "http" ||
                           u.scheme() == "https" {
                            self.url = Some(u);
                            return true;
                        } else {
                            self.url = None;
                            println_err!("Unsupported file URI '{}'", uri_str);
                            return false;
                        }
                    },
                    Err(err) => {
                        self.url = None;
                        println_err!("Failed to parse URI '{}': {}", uri_str, err);
                        return false;
                    }
                }
            }
        }
    }

    fn get_uri(&self) -> Option<String> {
        self.url.clone().map(|u| u.into_string())
    }

    fn is_seekable(&self) -> bool {
        false
    }

    fn get_size(&self) -> u64 {
        self.size
    }

    fn start(&mut self) -> bool {
        self.response = None;
        self.position = 0;
        self.size = u64::MAX;

        match self.url {
            None => return false,
            Some(ref url) => {
                match self.client.get(url.clone()).send() {
                    Ok(response) => {
                        if response.status.is_success() {
                            self.size = match response.headers.get::<ContentLength>() {
                                Some(&ContentLength(size)) => size,
                                _ => u64::MAX
                            };
                            self.response = Some(response);
                            return true;
                        } else {
                            println_err!("Failed to fetch {}: {}", url, response.status);
                            return false;
                        }
                    },
                    Err(err) => {
                        println_err!("Failed to fetch {}: {}", url, err.to_string());
                        return false;
                    }
                }
            },
        }
    }

    fn stop(&mut self) -> bool {
        self.position = 0;
        self.size = u64::MAX;
        match self.response {
            Some(ref mut response) => drop(response),
            None => ()
        }
        self.response = None;

        return true;
    }

    fn fill(&mut self, offset: u64, data: &mut [u8]) -> Result<usize, GstFlowReturn> {
        match self.response {
            None => return Err(GstFlowReturn::Error),
            Some(ref mut r) => {
                if self.position != offset {
                       println_err!("Failed to seek to {}", offset);
                       return Err(GstFlowReturn::Error);
                }

                match r.read(data) {
                    Ok(size) => {
                        if size == 0 {
                            return Err(GstFlowReturn::Eos);
                        }

                        self.position += size as u64;
                        return Ok(size)
                    },
                    Err(err) => {
                        println_err!("Failed to read at {}: {}", offset, err.to_string());
                        return Err(GstFlowReturn::Error);
                    },
                }
            },
        }
    }
}

<commit_msg>Implement seeking in HTTP source<commit_after>use std::u64;
use std::io::{Read, Seek, SeekFrom};
use url::Url;
use hyper::header::{ContentLength, ContentRange, ContentRangeSpec, Range, ByteRangeSpec, AcceptRanges, RangeUnit};
use hyper::client::Client;
use hyper::client::response::Response;

use std::io::Write;

use utils::*;
use rssource::*;

#[derive(Debug)]
pub struct HttpSrc {
    url: Option<Url>,
    client: Client,
    response: Option<Response>,
    seekable: bool,
    position: u64,
    size: u64,
    start: u64,
    stop: u64,
}

impl HttpSrc {
    fn new() -> HttpSrc {
        HttpSrc { url: None, client: Client::new(), response: None, seekable: false, position: 0, size: u64::MAX, start: 0, stop: u64::MAX }
    }

    fn new_source() -> Box<Source> {
        Box::new(HttpSrc::new())
    }
    pub extern "C" fn new_ptr() -> *mut Box<Source> {
        let instance = Box::new(HttpSrc::new_source());
        return Box::into_raw(instance);
    }

    pub fn do_request(&mut self, start: u64, stop: u64) -> bool {
        self.response = None;
        self.seekable = false;
        self.position = 0;
        self.size = u64::MAX;

        match self.url {
            None => return false,
            Some(ref url) => {
                let mut req = self.client.get(url.clone());

                if start != 0 || stop != u64::MAX {
                    req = if stop == u64::MAX {
                            req.header(Range::Bytes(vec![ByteRangeSpec::AllFrom(start)]))
                        } else {
                            req.header(Range::Bytes(vec![ByteRangeSpec::FromTo(start, stop)]))
                        };
                }

                match req.send() {
                    Ok(response) => {
                        if response.status.is_success() {
                            self.size = if let Some(&ContentLength(content_length)) = response.headers.get() {
                                content_length + start
                            } else {
                                u64::MAX
                            };
                            let accept_byte_ranges = if let Some(&AcceptRanges(ref ranges)) = response.headers.get() {
                                ranges.iter().any(|u| *u == RangeUnit::Bytes)
                            } else {
                                false
                            };

                            self.seekable = self.size != u64::MAX && accept_byte_ranges;

                            self.start = start;
                            self.stop = stop;

                            self.position = if let Some(&ContentRange(ContentRangeSpec::Bytes{range: Some((range_start, _)), ..})) = response.headers.get() {
                                range_start
                            } else {
                                start
                            };

                            if self.position != start {
                                println_err!("Failed to seek to {}: Got {}", start, self.position);
                                return false;
                            }

                            self.response = Some(response);

                            return true;
                        } else {
                            println_err!("Failed to fetch {}: {}", url, response.status);
                            return false;
                        }
                    },
                    Err(err) => {
                        println_err!("Failed to fetch {}: {}", url, err.to_string());
                        return false;
                    }
                }
            },
        }
    }
}

impl Source for HttpSrc {
    fn set_uri(&mut self, uri_str: &Option<String>) -> bool {
        if self.response.is_some() {
            println_err!("Can't set URI after starting");
            return false;
        }

        match *uri_str {
            None => {
                self.url = None;
                return true;
            },
            Some(ref uri_str) => {
                let uri_parsed = Url::parse(uri_str.as_str());
                match uri_parsed {
                    Ok(u) => {
                        if u.scheme() == "http" ||
                           u.scheme() == "https" {
                            self.url = Some(u);
                            return true;
                        } else {
                            self.url = None;
                            println_err!("Unsupported file URI '{}'", uri_str);
                            return false;
                        }
                    },
                    Err(err) => {
                        self.url = None;
                        println_err!("Failed to parse URI '{}': {}", uri_str, err);
                        return false;
                    }
                }
            }
        }
    }

    fn get_uri(&self) -> Option<String> {
        self.url.clone().map(|u| u.into_string())
    }

    fn is_seekable(&self) -> bool {
        self.seekable
    }

    fn get_size(&self) -> u64 {
        self.size
    }

    fn start(&mut self) -> bool {
        self.seekable = false;
        return self.do_request(0, u64::MAX);
    }

    fn stop(&mut self) -> bool {
        self.seekable = false;
        self.position = 0;
        self.size = u64::MAX;
        match self.response {
            Some(ref mut response) => drop(response),
            None => ()
        }
        self.response = None;

        return true;
    }

    fn do_seek(&mut self, start: u64, stop: u64) -> bool {
        return self.do_request(start, stop);
    }

    fn fill(&mut self, offset: u64, data: &mut [u8]) -> Result<usize, GstFlowReturn> {
        if self.position != offset || self.response.is_none() {
            let stop = self.stop;
            if !self.do_request(offset, stop) {
                println_err!("Failed to seek to {}", offset);
                return Err(GstFlowReturn::Error);
            }
        }

        match self.response {
            None => return Err(GstFlowReturn::Error),
            Some(ref mut r) => {
                match r.read(data) {
                    Ok(size) => {
                        if size == 0 {
                            return Err(GstFlowReturn::Eos);
                        }

                        self.position += size as u64;
                        return Ok(size)
                    },
                    Err(err) => {
                        println_err!("Failed to read at {}: {}", offset, err.to_string());
                        return Err(GstFlowReturn::Error);
                    },
                }
            },
        }
    }
}

<|endoftext|>


<commit_before>use core::prelude::*;
use core::default::Default;
use boxed::Box;
use fn_box::FnBox;
use collections::RingBuf;
use global::global;
use alloc::heap::allocate;

pub fn spawn<F, R>(f:F) -> Future<R> where F: FnOnce()->R, F:Send {
    global().scheduler.lock().spawn(f)
}

pub unsafe fn schedule(current_rsp: uint) -> uint {
    // park current thread
    let current = Thread::from_rsp(current_rsp);
    global().scheduler.lock().park(current);
    // schedule new thread
    loop {
        match global().scheduler.lock().schedule().unwrap_or_default() {
            Thread{state: ThreadState::Active{rsp}} => return rsp,
            Thread{state: ThreadState::New{function}} => {

                print!("creating thread...");

                let stack_bottom = allocate(4096*2, 4096);
                let stack_top = (stack_bottom as uint + 4096*2);

                print!("bottom {:x} top {:x}\n", stack_bottom as uint, stack_top as uint);
                panic!("done\n");

                asm!("
                    mov rdi, rsp;   // backup stack pointer
                    mov rsp, $2;    // load new stack pointer
                    push rdi;       // push our stack pointer to new stack
                    mov rdi, $1;    // load argument
                    call $0;        // call invocation fn on new stack
                    pop rsp;        // restore old stack
                    "
                    :: "r"(invoke), "r"(function), "r"(stack_top) : "rdi" : "intel");
            },
        }
    }
}

extern "C" fn invoke(function: Box<FnBox() + Send>) {
    function.call_once(())
}

pub type GlobalScheduler = Scheduler;
impl GlobalScheduler {
    pub fn new() -> GlobalScheduler {
        Scheduler::new()
    }
}

pub struct Future<T>;

pub struct Thread {
    state: ThreadState,
}

impl Thread {
    fn new<F>(f: F) -> Thread where F : FnOnce(), F: Send {
        Thread {
            state: ThreadState::New {
                function: box f,
            }
        }
    }

    pub fn from_rsp(rsp: uint) -> Thread {
        Thread {
            state: ThreadState::Active {
                rsp: rsp,
            }
        }
    }
}

impl Default for Thread {
    fn default() -> Thread {
        Thread::new(|| print!("."))
    }
}

enum ThreadState {
    New {
        function: Box<FnBox() + Send>,
    },
    Active {
        rsp: uint,
    }
}

pub struct Scheduler {
    threads: RingBuf<Thread>,
}

impl Scheduler {
    pub fn new() -> Scheduler {
        Scheduler{
            threads: RingBuf::new(),
        }
    }

    fn spawn<F, R>(&mut self, f:F) -> Future<R> where F: FnOnce()->R, F: Send {

        //let (tx, rx) = channel();

        self.threads.push_back(Thread::new(move |:| {
            /*tx.send_opt(*/ f();
        }));

        Future/*::from_receiver(rx)*/
    }

    fn park(&mut self, thread: Thread) {
        self.threads.push_back(thread)
    }

    unsafe fn schedule(&mut self) -> Option<Thread> {
        self.threads.pop_front()
    }
}<commit_msg>fix scheduler<commit_after>use core::prelude::*;
use core::default::Default;
use boxed::Box;
use fn_box::FnBox;
use collections::RingBuf;
use global::global;
use alloc::heap::allocate;

pub fn spawn<F, R>(f:F) -> Future<R> where F: FnOnce()->R, F:Send {
    global().scheduler.lock().spawn(f)
}

pub unsafe fn reschedule(current_rsp: uint) -> ! {

    // switch stack so we can park current thread
    const SCHEDULER_STACK_SIZE: uint = 4096;
    static SCHEDULER_STACK: [u8; SCHEDULER_STACK_SIZE] = [0; SCHEDULER_STACK_SIZE];

    let scheduler_stack_top = &SCHEDULER_STACK as *const [u8; 4096] as uint
        + SCHEDULER_STACK_SIZE;
    call_on_stack(inner, current_rsp, scheduler_stack_top);

    fn inner(current_rsp: uint) -> ! {
        extern {
            fn pop_registers_and_iret(rsp: uint) -> !;
        }

        // park current thread
        let current = Thread::from_rsp(current_rsp);
        global().scheduler.lock().park(current);

        // schedule new thread
        loop {
            print!("_");
            let new_thread = unsafe{global().scheduler.lock().schedule().unwrap_or_default()};
            match new_thread {
                Thread{state: ThreadState::Active{rsp}} => {
                    unsafe{pop_registers_and_iret(rsp)}
                },
                Thread{state: ThreadState::New{function}} => {
                    print!("new");
                    let new_stack_top = new_stack();
                    // TODO new stack
                    function.call_once(())
                },
            }
        }
    }

}

fn new_stack() -> uint {
    const NEW_STACK_SIZE: uint = 4096*2;
    let stack_bottom = unsafe{allocate(NEW_STACK_SIZE, 4096)};
    let stack_top = (stack_bottom as uint + NEW_STACK_SIZE);
    stack_top
}

fn call_on_stack<Arg>(function: fn(Arg) -> !, arg: Arg, stack_top: uint) -> ! {
    unsafe{asm!("call $0;" :: "r"(function), "{rdi}"(arg), "{rsp}"(stack_top) :: "intel")}
    panic!("diverging fn returned");
}

pub type GlobalScheduler = Scheduler;
impl GlobalScheduler {
    pub fn new() -> GlobalScheduler {
        Scheduler::new()
    }
}

pub struct Future<T>;

pub struct Thread {
    state: ThreadState,
}

impl Thread {
    fn new<F>(f: F) -> Thread where F : FnOnce(), F: Send {
        Thread {
            state: ThreadState::New {
                function: box f,
            }
        }
    }

    pub fn from_rsp(rsp: uint) -> Thread {
        Thread {
            state: ThreadState::Active {
                rsp: rsp,
            }
        }
    }
}

impl Default for Thread {
    fn default() -> Thread {
        Thread::new(|| print!("."))
    }
}

enum ThreadState {
    New {
        function: Box<FnBox() + Send>,
    },
    Active {
        rsp: uint,
    }
}

pub struct Scheduler {
    threads: RingBuf<Thread>,
}

impl Scheduler {
    pub fn new() -> Scheduler {
        Scheduler{
            threads: RingBuf::new(),
        }
    }

    fn spawn<F, R>(&mut self, f:F) -> Future<R> where F: FnOnce()->R, F: Send {

        //let (tx, rx) = channel();

        self.threads.push_back(Thread::new(move |:| {
            /*tx.send_opt(*/ f();
        }));

        Future/*::from_receiver(rx)*/
    }

    fn park(&mut self, thread: Thread) {
        self.threads.push_back(thread)
    }

    unsafe fn schedule(&mut self) -> Option<Thread> {
        self.threads.pop_front()
    }
}<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashMap};
use std::hash::Hash;
use std::io;

use byteorder::{ByteOrder, BigEndian, WriteBytesExt};

use error::NbtError;

pub trait NbtFmt {
    fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
       where W: io::Write;

    #[inline]
    fn to_nbt<W, S>(&self, dst: &mut W, name: S) -> Result<(), NbtError>
       where W: io::Write,
             S: AsRef<str>
    {
        try!(dst.write_u8(Self::tag()));
        try!(write_bare_string(dst, name.as_ref()));
        self.to_bare_nbt(dst)
    }

    #[inline] fn tag() -> u8 { 0x0a }
    #[inline] fn is_bare() -> bool { false }
}

pub fn close_nbt<W>(dst: &mut W) -> Result<(), NbtError>
    where W: io::Write {

    dst.write_u8(0x00).map_err(From::from)
}

pub fn to_writer<W, T>(dst: &mut W, obj: T) -> Result<(), NbtError>
    where W: io::Write,
          T: NbtFmt
{
    match T::is_bare() {
        // Refuse to blindly serialize types not wrapped in an NBT Compound.
        true  => { return Err(NbtError::NoRootCompound); },
        false => obj.to_nbt(dst, ""),
    }
}

macro_rules! nbtfmt_value {
  ($T:ty, $method:ident, $tag:expr) => (
    impl NbtFmt for $T {
        #[inline]
        fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
           where W: io::Write
        {
            $method(dst, *self)
        }

        #[inline] fn tag() -> u8 { $tag }
        #[inline] fn is_bare() -> bool { true }
    }
  )
}

macro_rules! nbtfmt_ptr {
  ($T:ty, $method:ident, $tag:expr) => (
    impl NbtFmt for $T {
        #[inline]
        fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
           where W: io::Write
        {
            $method(dst, self)
        }

        #[inline] fn tag() -> u8 { $tag }
        #[inline] fn is_bare() -> bool { true }
    }
  )
}

macro_rules! nbtfmt_slice {
  ($T:ty, $method:ident, $tag:expr) => (
    impl NbtFmt for $T {
        #[inline]
        fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
           where W: io::Write
        {
            $method(dst, &self[..])
        }

        #[inline] fn tag() -> u8 { $tag }
        #[inline] fn is_bare() -> bool { true }
    }
  )
}

nbtfmt_value!(i8, write_bare_byte, 0x01);
nbtfmt_value!(i16, write_bare_short, 0x02);
nbtfmt_value!(i32, write_bare_int, 0x03);
nbtfmt_value!(i64, write_bare_long, 0x04);
nbtfmt_value!(f32, write_bare_float, 0x05);
nbtfmt_value!(f64, write_bare_double, 0x06);
nbtfmt_ptr!(str, write_bare_string, 0x08);
nbtfmt_slice!(String, write_bare_string, 0x08);

// For now, to handle conflicting implementations, use slices to indicate when
// byte and int arrays should be preferred to lists.

nbtfmt_ptr!([i8], write_bare_byte_array, 0x07);
nbtfmt_ptr!([i32], write_bare_int_array, 0x0b);

// FIXME: Remove this workaround and enable some way of uncommenting the lines
// that follow.

// nbtfmt_slice!(Vec<i8>, write_bare_byte_array, 0x07);
// nbtfmt_slice!(Vec<i32>, write_bare_int_array, 0x0b);

// impl<T> NbtFmt for [T] where T: NbtFmt {
//  fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
//        where W: io::Write {

//          write_bare_list(dst, self.iter())
//  }
//     #[inline] fn tag() -> u8 { 0x09 }
//     #[inline] fn is_bare() -> bool { true }
// }

// This leaves Vec<T> alone for lists (which kind of makes sense).

impl<T> NbtFmt for Vec<T> where T: NbtFmt {
    #[inline]
    fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
       where W: io::Write
    {
           write_bare_list(dst, self.iter())
    }

    #[inline] fn tag() -> u8 { 0x09 }
    #[inline] fn is_bare() -> bool { true }
}

impl<S, T> NbtFmt for HashMap<S, T> where S: AsRef<str> + Hash + Eq, T: NbtFmt {
    #[inline]
    fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
       where W: io::Write
    {
        write_bare_compound(dst, self.iter())
    }

    #[inline] fn tag() -> u8 { 0x0a }
    #[inline] fn is_bare() -> bool { false }
}

impl<S, T> NbtFmt for BTreeMap<S, T> where S: AsRef<str>, T: NbtFmt {
    #[inline]
    fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
       where W: io::Write
    {
        write_bare_compound(dst, self.iter())
    }

    #[inline] fn tag() -> u8 { 0x0a }
    #[inline] fn is_bare() -> bool { false }
}

#[inline]
fn write_bare_byte<W>(dst: &mut W, value: i8) -> Result<(), NbtError>
   where W: io::Write {

    dst.write_i8(value).map_err(From::from)
}

#[inline]
fn write_bare_short<W>(dst: &mut W, value: i16) -> Result<(), NbtError>
   where W: io::Write {

    dst.write_i16::<BigEndian>(value).map_err(From::from)
}

#[inline]
fn write_bare_int<W>(dst: &mut W, value: i32) -> Result<(), NbtError>
   where W: io::Write {

    dst.write_i32::<BigEndian>(value).map_err(From::from)
}

#[inline]
fn write_bare_long<W>(dst: &mut W, value: i64) -> Result<(), NbtError>
   where W: io::Write {

    dst.write_i64::<BigEndian>(value).map_err(From::from)
}

#[inline]
fn write_bare_float<W>(dst: &mut W, value: f32) -> Result<(), NbtError>
   where W: io::Write {

    dst.write_f32::<BigEndian>(value).map_err(From::from)
}

#[inline]
fn write_bare_double<W>(dst: &mut W, value: f64) -> Result<(), NbtError>
   where W: io::Write {

    dst.write_f64::<BigEndian>(value).map_err(From::from)
}

#[inline]
fn write_bare_byte_array<W>(dst: &mut W, value: &[i8]) -> Result<(), NbtError>
   where W: io::Write {

    try!(dst.write_i32::<BigEndian>(value.len() as i32));
    for &v in value {
        try!(dst.write_i8(v));
    }
    Ok(())
}

#[inline]
fn write_bare_int_array<W>(dst: &mut W, value: &[i32]) -> Result<(), NbtError>
   where W: io::Write {

    try!(dst.write_i32::<BigEndian>(value.len() as i32));
    for &v in value {
        try!(dst.write_i32::<BigEndian>(v));
    }
    Ok(())
}

#[inline]
fn write_bare_string<W>(dst: &mut W, value: &str) -> Result<(), NbtError>
   where W: io::Write {

    try!(dst.write_u16::<BigEndian>(value.len() as u16));
    dst.write_all(value.as_bytes()).map_err(From::from)
}

#[inline]
fn write_bare_list<'a, W, I, T>(dst: &mut W, values: I) -> Result<(), NbtError>
   where W: io::Write,
         I: Iterator<Item=&'a T> + ExactSizeIterator,
         T: 'a + NbtFmt {

    // The list contents are prefixed by a byte tag for the type and the
    // length of the list (a big-endian i32).
    try!(dst.write_u8(T::tag()));
    try!(dst.write_i32::<BigEndian>(values.len() as i32));

    for ref value in values {
        // Note the use of bare values.
        try!(value.to_bare_nbt(dst));
    }

    Ok(())
}

#[inline]
fn write_bare_compound<'a, W, I, T, S>(dst: &mut W, values: I) -> Result<(), NbtError>
   where W: io::Write,
         I: Iterator<Item=(&'a S, &'a T)>,
         S: 'a + AsRef<str>,
         T: 'a + NbtFmt {

    for (key, ref value) in values {
        try!(value.to_nbt(dst, key.as_ref()));
    }

    // Write the marker for the end of the Compound.
    close_nbt(dst)
}

#[test]
fn serialize_basic_types() {
  struct TestStruct {
    name: String,
    health: i8,
    food: f32,
    emeralds: i16,
    timestamp: i32,
    ids: HashMap<String, i8>,
    data: Vec<i8>
  }

  impl NbtFmt for TestStruct {
    fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
           where W: io::Write {

            try!(self.name.to_nbt(dst, "name"));
            try!(self.health.to_nbt(dst, "health"));
            try!(self.food.to_nbt(dst, "food"));
            try!(self.emeralds.to_nbt(dst, "emeralds"));
            try!(self.timestamp.to_nbt(dst, "timestamp"));
            try!(self.ids.to_nbt(dst, "ids"));
            try!(self.data.to_nbt(dst, "data"));

            close_nbt(dst)
        }
  }

  let test = TestStruct {
    name: "Herobrine".to_string(),
    health: 100, food: 20.0, emeralds: 12345, timestamp: 1424778774,
    ids: HashMap::new(), data: vec![1, 2, 3]
  };

  let mut dst = Vec::new();
  to_writer(&mut dst, test).unwrap();

  let bytes = [
        0x0a,
            0x00, 0x00,
            0x08,
                0x00, 0x04,
                0x6e, 0x61, 0x6d, 0x65,
                0x00, 0x09,
                0x48, 0x65, 0x72, 0x6f, 0x62, 0x72, 0x69, 0x6e, 0x65,
            0x01,
                0x00, 0x06,
                0x68, 0x65, 0x61, 0x6c, 0x74, 0x68,
                0x64,
            0x05,
                0x00, 0x04,
                0x66, 0x6f, 0x6f, 0x64,
                0x41, 0xa0, 0x00, 0x00,
            0x02,
                0x00, 0x08,
                0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x64, 0x73,
                0x30, 0x39,
            0x03,
                0x00, 0x09,
                0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
                0x54, 0xec, 0x66, 0x16,
            0x0a,
                0x00, 0x03,
                0x69, 0x64, 0x73,
                // No content.
            0x00,
            0x09,
                0x00, 0x04,
                0x64, 0x61, 0x74, 0x61,
                0x01, // List type.
                0x00, 0x00, 0x00, 0x03, // Length.
                0x01, 0x02, 0x03, // Content.
        0x00
    ];

    assert_eq!(&bytes[..], &dst[..]);
}
<commit_msg>Adds documentation for the public API in `nbt::serialize`.<commit_after>//! Contains functions for serializing arbitrary objects to the Named Binary
//! Tag format.
//!
//! For working with existing serialization implementations, see `to_writer`.
//! For custom types, implement the `NbtFmt` trait.

use std::collections::{BTreeMap, HashMap};
use std::hash::Hash;
use std::io;

use byteorder::{ByteOrder, BigEndian, WriteBytesExt};

use error::NbtError;

/// A trait indicating that the type has a Named Binary Tag representation.
///
/// Keep in mind that not all Rust types (notably unsigned integers) have an
/// obvious NBT representation, and so structs that implement this trait may
/// have to convert them to one that does.
///
/// ## Serialization
///
/// To enable NBT serialization of a type, implement the `to_bare_nbt` method.
/// Many basic types (`i8`, `i16`, `String`, etc.) implement `NbtFmt`, enabling
/// the use of `to_nbt` when writing this implementation. The `close_nbt`
/// function is also provided to aid in implementing custom serialization,
/// given that types will likely advertise themselves as Compound-like. For
/// example:
///
/// ```rust
/// extern crate nbt;
///
/// use nbt::NbtError;
/// use nbt::serialize::{NbtFmt, to_writer, close_nbt};
///
/// struct MyMob {
///     name: String,
///     health: i8
/// }
///
/// impl NbtFmt for MyMob {
///     fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
///        where W: std::io::Write
///     {
///         try!(self.name.to_nbt(dst, "name"));
///         try!(self.health.to_nbt(dst, "health"));
///
///         close_nbt(dst)
///     }
/// }
///
/// fn main() {
///     let mut bytes = Vec::new();
///     let mob = MyMob { name: "Dr. Evil".to_string(), health: 240 };
///
///     to_writer(&mut bytes, mob).unwrap();
/// }
/// ```
///
pub trait NbtFmt {

    /// Convert this type to NBT format using the specified `io::Write`
    /// destination, but does not serialize its identifying NBT tag or name.
    fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
       where W: io::Write;

    /// Convert this type to NBT format using the specified `io::Write`
    /// destination, incuding its tag and a given name.
    #[inline]
    fn to_nbt<W, S>(&self, dst: &mut W, name: S) -> Result<(), NbtError>
       where W: io::Write,
             S: AsRef<str>
    {
        try!(dst.write_u8(Self::tag()));
        try!(write_bare_string(dst, name.as_ref()));
        self.to_bare_nbt(dst)
    }

    /// Indicates the NBT tag that this type corresponds to. Most custom types
    /// (usually structs) will advertise the default, `0x0a`, which is the
    /// default.
    #[inline] fn tag() -> u8 { 0x0a }

    /// Indicates whether this type is "bare", in that it must be wrapped in an
    /// NBT Compound before serialization. By default this is `false`, since
    /// most imeplementations will be Compound-like objects. Primitive NBT
    /// types (`i8`, `i16`, `String`, etc.) return `true`.
    #[inline] fn is_bare() -> bool { false }
}

/// A convenience function for closing NBT format objects.
///
/// This function writes a single `0x00` byte to the `io::Write` destination,
/// which in the NBT format indicates that an open Compound is now closed.
pub fn close_nbt<W>(dst: &mut W) -> Result<(), NbtError>
    where W: io::Write {

    dst.write_u8(0x00).map_err(From::from)
}

/// Serializes an object into NBT format at a given destination.
///
/// This function will try to ensure that the output is always a valid NBT
/// file, i.e. that it has a top-level Compound.
pub fn to_writer<W, T>(dst: &mut W, obj: T) -> Result<(), NbtError>
    where W: io::Write,
          T: NbtFmt
{
    match T::is_bare() {
        // Refuse to blindly serialize types not wrapped in an NBT Compound.
        true  => { return Err(NbtError::NoRootCompound); },
        false => obj.to_nbt(dst, ""),
    }
}

macro_rules! nbtfmt_value {
  ($T:ty, $method:ident, $tag:expr) => (
    impl NbtFmt for $T {
        #[inline]
        fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
           where W: io::Write
        {
            $method(dst, *self)
        }

        #[inline] fn tag() -> u8 { $tag }
        #[inline] fn is_bare() -> bool { true }
    }
  )
}

macro_rules! nbtfmt_ptr {
  ($T:ty, $method:ident, $tag:expr) => (
    impl NbtFmt for $T {
        #[inline]
        fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
           where W: io::Write
        {
            $method(dst, self)
        }

        #[inline] fn tag() -> u8 { $tag }
        #[inline] fn is_bare() -> bool { true }
    }
  )
}

macro_rules! nbtfmt_slice {
  ($T:ty, $method:ident, $tag:expr) => (
    impl NbtFmt for $T {
        #[inline]
        fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
           where W: io::Write
        {
            $method(dst, &self[..])
        }

        #[inline] fn tag() -> u8 { $tag }
        #[inline] fn is_bare() -> bool { true }
    }
  )
}

nbtfmt_value!(i8, write_bare_byte, 0x01);
nbtfmt_value!(i16, write_bare_short, 0x02);
nbtfmt_value!(i32, write_bare_int, 0x03);
nbtfmt_value!(i64, write_bare_long, 0x04);
nbtfmt_value!(f32, write_bare_float, 0x05);
nbtfmt_value!(f64, write_bare_double, 0x06);
nbtfmt_ptr!(str, write_bare_string, 0x08);
nbtfmt_slice!(String, write_bare_string, 0x08);

// For now, to handle conflicting implementations, use slices to indicate when
// byte and int arrays should be preferred to lists.

nbtfmt_ptr!([i8], write_bare_byte_array, 0x07);
nbtfmt_ptr!([i32], write_bare_int_array, 0x0b);

// FIXME: Remove this workaround and enable some way of uncommenting the lines
// that follow.

// nbtfmt_slice!(Vec<i8>, write_bare_byte_array, 0x07);
// nbtfmt_slice!(Vec<i32>, write_bare_int_array, 0x0b);

// impl<T> NbtFmt for [T] where T: NbtFmt {
//  fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
//        where W: io::Write {

//          write_bare_list(dst, self.iter())
//  }
//     #[inline] fn tag() -> u8 { 0x09 }
//     #[inline] fn is_bare() -> bool { true }
// }

// This leaves Vec<T> alone for lists (which kind of makes sense).

impl<T> NbtFmt for Vec<T> where T: NbtFmt {
    #[inline]
    fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
       where W: io::Write
    {
           write_bare_list(dst, self.iter())
    }

    #[inline] fn tag() -> u8 { 0x09 }
    #[inline] fn is_bare() -> bool { true }
}

impl<S, T> NbtFmt for HashMap<S, T> where S: AsRef<str> + Hash + Eq, T: NbtFmt {
    #[inline]
    fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
       where W: io::Write
    {
        write_bare_compound(dst, self.iter())
    }

    #[inline] fn tag() -> u8 { 0x0a }
    #[inline] fn is_bare() -> bool { false }
}

impl<S, T> NbtFmt for BTreeMap<S, T> where S: AsRef<str>, T: NbtFmt {
    #[inline]
    fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
       where W: io::Write
    {
        write_bare_compound(dst, self.iter())
    }

    #[inline] fn tag() -> u8 { 0x0a }
    #[inline] fn is_bare() -> bool { false }
}

#[inline]
fn write_bare_byte<W>(dst: &mut W, value: i8) -> Result<(), NbtError>
   where W: io::Write {

    dst.write_i8(value).map_err(From::from)
}

#[inline]
fn write_bare_short<W>(dst: &mut W, value: i16) -> Result<(), NbtError>
   where W: io::Write {

    dst.write_i16::<BigEndian>(value).map_err(From::from)
}

#[inline]
fn write_bare_int<W>(dst: &mut W, value: i32) -> Result<(), NbtError>
   where W: io::Write {

    dst.write_i32::<BigEndian>(value).map_err(From::from)
}

#[inline]
fn write_bare_long<W>(dst: &mut W, value: i64) -> Result<(), NbtError>
   where W: io::Write {

    dst.write_i64::<BigEndian>(value).map_err(From::from)
}

#[inline]
fn write_bare_float<W>(dst: &mut W, value: f32) -> Result<(), NbtError>
   where W: io::Write {

    dst.write_f32::<BigEndian>(value).map_err(From::from)
}

#[inline]
fn write_bare_double<W>(dst: &mut W, value: f64) -> Result<(), NbtError>
   where W: io::Write {

    dst.write_f64::<BigEndian>(value).map_err(From::from)
}

#[inline]
fn write_bare_byte_array<W>(dst: &mut W, value: &[i8]) -> Result<(), NbtError>
   where W: io::Write {

    try!(dst.write_i32::<BigEndian>(value.len() as i32));
    for &v in value {
        try!(dst.write_i8(v));
    }
    Ok(())
}

#[inline]
fn write_bare_int_array<W>(dst: &mut W, value: &[i32]) -> Result<(), NbtError>
   where W: io::Write {

    try!(dst.write_i32::<BigEndian>(value.len() as i32));
    for &v in value {
        try!(dst.write_i32::<BigEndian>(v));
    }
    Ok(())
}

#[inline]
fn write_bare_string<W>(dst: &mut W, value: &str) -> Result<(), NbtError>
   where W: io::Write {

    try!(dst.write_u16::<BigEndian>(value.len() as u16));
    dst.write_all(value.as_bytes()).map_err(From::from)
}

#[inline]
fn write_bare_list<'a, W, I, T>(dst: &mut W, values: I) -> Result<(), NbtError>
   where W: io::Write,
         I: Iterator<Item=&'a T> + ExactSizeIterator,
         T: 'a + NbtFmt {

    // The list contents are prefixed by a byte tag for the type and the
    // length of the list (a big-endian i32).
    try!(dst.write_u8(T::tag()));
    try!(dst.write_i32::<BigEndian>(values.len() as i32));

    for ref value in values {
        // Note the use of bare values.
        try!(value.to_bare_nbt(dst));
    }

    Ok(())
}

#[inline]
fn write_bare_compound<'a, W, I, T, S>(dst: &mut W, values: I) -> Result<(), NbtError>
   where W: io::Write,
         I: Iterator<Item=(&'a S, &'a T)>,
         S: 'a + AsRef<str>,
         T: 'a + NbtFmt {

    for (key, ref value) in values {
        try!(value.to_nbt(dst, key.as_ref()));
    }

    // Write the marker for the end of the Compound.
    close_nbt(dst)
}

#[test]
fn serialize_basic_types() {
  struct TestStruct {
    name: String,
    health: i8,
    food: f32,
    emeralds: i16,
    timestamp: i32,
    ids: HashMap<String, i8>,
    data: Vec<i8>
  }

  impl NbtFmt for TestStruct {
    fn to_bare_nbt<W>(&self, dst: &mut W) -> Result<(), NbtError>
           where W: io::Write {

            try!(self.name.to_nbt(dst, "name"));
            try!(self.health.to_nbt(dst, "health"));
            try!(self.food.to_nbt(dst, "food"));
            try!(self.emeralds.to_nbt(dst, "emeralds"));
            try!(self.timestamp.to_nbt(dst, "timestamp"));
            try!(self.ids.to_nbt(dst, "ids"));
            try!(self.data.to_nbt(dst, "data"));

            close_nbt(dst)
        }
  }

  let test = TestStruct {
    name: "Herobrine".to_string(),
    health: 100, food: 20.0, emeralds: 12345, timestamp: 1424778774,
    ids: HashMap::new(), data: vec![1, 2, 3]
  };

  let mut dst = Vec::new();
  to_writer(&mut dst, test).unwrap();

  let bytes = [
        0x0a,
            0x00, 0x00,
            0x08,
                0x00, 0x04,
                0x6e, 0x61, 0x6d, 0x65,
                0x00, 0x09,
                0x48, 0x65, 0x72, 0x6f, 0x62, 0x72, 0x69, 0x6e, 0x65,
            0x01,
                0x00, 0x06,
                0x68, 0x65, 0x61, 0x6c, 0x74, 0x68,
                0x64,
            0x05,
                0x00, 0x04,
                0x66, 0x6f, 0x6f, 0x64,
                0x41, 0xa0, 0x00, 0x00,
            0x02,
                0x00, 0x08,
                0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x64, 0x73,
                0x30, 0x39,
            0x03,
                0x00, 0x09,
                0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
                0x54, 0xec, 0x66, 0x16,
            0x0a,
                0x00, 0x03,
                0x69, 0x64, 0x73,
                // No content.
            0x00,
            0x09,
                0x00, 0x04,
                0x64, 0x61, 0x74, 0x61,
                0x01, // List type.
                0x00, 0x00, 0x00, 0x03, // Length.
                0x01, 0x02, 0x03, // Content.
        0x00
    ];

    assert_eq!(&bytes[..], &dst[..]);
}
<|endoftext|>


<commit_before>use reqwest::header::{self, HeaderMap, HeaderValue};
use url::form_urlencoded::Serializer;

use crate::errors::{SendgridError, SendgridResult};
use crate::mail::Mail;

static API_URL: &str = "https://api.sendgrid.com/api/mail.send.json?";

/// This is the struct that allows you to authenticate to the SendGrid API.
/// It contains the API key which allows you to send messages as well as a blocking request client.
#[derive(Clone, Debug)]
pub struct SGClient {
    api_key: String,
    #[cfg(feature = "async")]
    client: reqwest::Client,
    #[cfg(not(feature = "async"))]
    client: reqwest::blocking::Client,
}

// Given a form value and a key, generate the correct key.
fn make_form_key(form: &str, key: &str) -> String {
    let mut value = String::with_capacity(form.len() + key.len() + 2);
    value.push_str(form);
    value.push('[');
    value.push_str(key);
    value.push(']');

    value
}

// Use the URL form encoder to properly generate the body used in the mail send request.
fn make_post_body(mut mail_info: Mail) -> SendgridResult<String> {
    let body = String::new();
    let mut encoder = Serializer::new(body);

    for to in mail_info.to.iter() {
        encoder.append_pair("to[]", to.address);
        encoder.append_pair("toname[]", to.name);
    }

    for cc in mail_info.cc.iter() {
        encoder.append_pair("cc[]", &cc);
    }

    for bcc in mail_info.bcc.iter() {
        encoder.append_pair("bcc[]", &bcc);
    }

    for (attachment, contents) in &mail_info.attachments {
        encoder.append_pair(&make_form_key("files", attachment), &contents);
    }

    for (id, value) in &mail_info.content {
        encoder.append_pair(&make_form_key("content", id), &value);
    }

    encoder.append_pair("from", &mail_info.from);
    encoder.append_pair("subject", &mail_info.subject);
    encoder.append_pair("html", &mail_info.html);
    encoder.append_pair("text", &mail_info.text);
    encoder.append_pair("fromname", &mail_info.from_name);
    encoder.append_pair("replyto", &mail_info.reply_to);
    encoder.append_pair("date", &mail_info.date);
    encoder.append_pair("headers", &mail_info.make_header_string()?);
    encoder.append_pair("x-smtpapi", &mail_info.x_smtpapi);

    Ok(encoder.finish())
}

impl SGClient {
    /// Makes a new SendGrid cient with the specified API key. This will panic if you are using the
    /// default TLS backend and do not have a default TLS backend available. If you are using the
    /// RustTLS backend, this can never panic because RustTLS is statically linked.
    pub fn new<S: Into<String>>(key: S) -> SGClient {
        #[cfg(feature = "async")]
        use reqwest as rq;
        #[cfg(not(feature = "async"))]
        use reqwest::blocking as rq;

        let builder = rq::ClientBuilder::new();
        #[cfg(feature = "rustls")]
        builder.use_rustls_tls();
        let client = builder.build().unwrap();

        SGClient {
            api_key: key.into(),
            client,
        }
    }

    /// Sends a messages through the SendGrid API. It takes a Mail struct as an argument. It returns
    /// the string response from the API as JSON.
    ///
    /// ### Example
    ///
    /// ```rust,ignore
    /// use sendgrid::SendgridError;
    ///
    /// fn main() -> Result<(), SendgridError> {
    ///     let my_secret_key = std::env::var("SENDGRID_KEY").expect("need SENDGRID_KEY to test");
    ///     use sendgrid::{Mail, SGClient};
    ///
    ///     let mail = Mail::new()
    ///         .add_from("my-email@address.com")
    ///         .add_text("hi!")
    ///         .add_subject("Hello")
    ///         .add_to(("your-email@address.com", "Your Name").into());
    ///     let response = SGClient::new(my_secret_key)
    ///         .send(mail)?;
    ///     Ok(())
    /// }
    /// ```
    #[cfg(not(feature = "async"))]
    pub fn send(&self, mail_info: Mail) -> SendgridResult<String> {
        let post_body = make_post_body(mail_info)?;
        let res = self
            .client
            .post(API_URL)
            .headers(self.headers()?)
            .body(post_body)
            .send()?;

        if !res.status().is_success() {
            Err(SendgridError::RequestNotSuccessful(
                res.status(),
                res.text()?,
            ))
        } else {
            Ok(res.text()?)
        }
    }

    /// Sends a messages through the SendGrid API. It takes a Mail struct as an argument. It returns
    /// the string response from the API as JSON.
    ///
    /// ### Example
    ///
    /// ```rust,ignore
    /// use sendgrid::SendgridError;
    ///
    /// #[tokio::main]
    /// async fn main() -> Result<(), SendgridError> {
    ///     let my_secret_key = std::env::var("SENDGRID_KEY").expect("need SENDGRID_KEY to test");
    ///     use sendgrid::{Mail, SGClient};
    ///
    ///     let mail = Mail::new()
    ///         .add_from("my-email@address.com")
    ///         .add_text("hi!")
    ///         .add_subject("Hello")
    ///         .add_to(("your-email@address.com", "Your Name").into());
    ///     let response = SGClient::new(my_secret_key)
    ///         .send(mail)
    ///         .await?;
    ///     Ok(())
    /// }
    /// ```
    #[cfg(feature = "async")]
    pub async fn send(&self, mail_info: Mail<'_>) -> SendgridResult<String> {
        let post_body = make_post_body(mail_info)?;
        let res = self
            .client
            .post(API_URL)
            .headers(self.headers()?)
            .body(post_body)
            .send()
            .await?;

        if !res.status().is_success() {
            Err(SendgridError::RequestNotSuccessful(
                res.status(),
                res.text().await?,
            ))
        } else {
            Ok(res.text().await?)
        }
    }

    fn headers(&self) -> SendgridResult<HeaderMap> {
        let mut headers = HeaderMap::new();
        headers.insert(
            header::AUTHORIZATION,
            HeaderValue::from_str(&format!("Bearer {}", self.api_key.clone()))?,
        );
        headers.insert(
            header::CONTENT_TYPE,
            HeaderValue::from_static("application/x-www-form-urlencoded"),
        );
        headers.insert(header::USER_AGENT, HeaderValue::from_static("sendgrid-rs"));
        Ok(headers)
    }
}

#[test]
fn basic_message_body() {
    use crate::mail::Destination;

    let m = Mail::new()
        .add_to(Destination {
            address: "test@example.com",
            name: "Testy mcTestFace",
        })
        .add_from("me@example.com")
        .add_subject("Test")
        .add_text("It works");

    let body = make_post_body(m);
    let want = "to%5B%5D=test%40example.com&toname%5B%5D=Testy+mcTestFace&from=me%40example.com&subject=Test&\
                html=&text=It+works&fromname=&replyto=&date=&headers=%7B%7D&x-smtpapi=";
    assert_eq!(body.unwrap(), want);
}

#[test]
fn test_proper_key() {
    let want = "files[test.jpg]";
    let got = make_form_key("files", "test.jpg");
    assert_eq!(want, got);
}
<commit_msg>fix builder being moved<commit_after>use reqwest::header::{self, HeaderMap, HeaderValue};
use url::form_urlencoded::Serializer;

use crate::errors::{SendgridError, SendgridResult};
use crate::mail::Mail;

static API_URL: &str = "https://api.sendgrid.com/api/mail.send.json?";

/// This is the struct that allows you to authenticate to the SendGrid API.
/// It contains the API key which allows you to send messages as well as a blocking request client.
#[derive(Clone, Debug)]
pub struct SGClient {
    api_key: String,
    #[cfg(feature = "async")]
    client: reqwest::Client,
    #[cfg(not(feature = "async"))]
    client: reqwest::blocking::Client,
}

// Given a form value and a key, generate the correct key.
fn make_form_key(form: &str, key: &str) -> String {
    let mut value = String::with_capacity(form.len() + key.len() + 2);
    value.push_str(form);
    value.push('[');
    value.push_str(key);
    value.push(']');

    value
}

// Use the URL form encoder to properly generate the body used in the mail send request.
fn make_post_body(mut mail_info: Mail) -> SendgridResult<String> {
    let body = String::new();
    let mut encoder = Serializer::new(body);

    for to in mail_info.to.iter() {
        encoder.append_pair("to[]", to.address);
        encoder.append_pair("toname[]", to.name);
    }

    for cc in mail_info.cc.iter() {
        encoder.append_pair("cc[]", &cc);
    }

    for bcc in mail_info.bcc.iter() {
        encoder.append_pair("bcc[]", &bcc);
    }

    for (attachment, contents) in &mail_info.attachments {
        encoder.append_pair(&make_form_key("files", attachment), &contents);
    }

    for (id, value) in &mail_info.content {
        encoder.append_pair(&make_form_key("content", id), &value);
    }

    encoder.append_pair("from", &mail_info.from);
    encoder.append_pair("subject", &mail_info.subject);
    encoder.append_pair("html", &mail_info.html);
    encoder.append_pair("text", &mail_info.text);
    encoder.append_pair("fromname", &mail_info.from_name);
    encoder.append_pair("replyto", &mail_info.reply_to);
    encoder.append_pair("date", &mail_info.date);
    encoder.append_pair("headers", &mail_info.make_header_string()?);
    encoder.append_pair("x-smtpapi", &mail_info.x_smtpapi);

    Ok(encoder.finish())
}

impl SGClient {
    /// Makes a new SendGrid cient with the specified API key. This will panic if you are using the
    /// default TLS backend and do not have a default TLS backend available. If you are using the
    /// RustTLS backend, this can never panic because RustTLS is statically linked.
    pub fn new<S: Into<String>>(key: S) -> SGClient {
        #[cfg(feature = "async")]
        use reqwest as rq;
        #[cfg(not(feature = "async"))]
        use reqwest::blocking as rq;

        let builder = rq::ClientBuilder::new();
        #[cfg(feature = "rustls")]
        let builder = builder.use_rustls_tls();
        let client = builder.build().unwrap();

        SGClient {
            api_key: key.into(),
            client,
        }
    }

    /// Sends a messages through the SendGrid API. It takes a Mail struct as an argument. It returns
    /// the string response from the API as JSON.
    ///
    /// ### Example
    ///
    /// ```rust,ignore
    /// use sendgrid::SendgridError;
    ///
    /// fn main() -> Result<(), SendgridError> {
    ///     let my_secret_key = std::env::var("SENDGRID_KEY").expect("need SENDGRID_KEY to test");
    ///     use sendgrid::{Mail, SGClient};
    ///
    ///     let mail = Mail::new()
    ///         .add_from("my-email@address.com")
    ///         .add_text("hi!")
    ///         .add_subject("Hello")
    ///         .add_to(("your-email@address.com", "Your Name").into());
    ///     let response = SGClient::new(my_secret_key)
    ///         .send(mail)?;
    ///     Ok(())
    /// }
    /// ```
    #[cfg(not(feature = "async"))]
    pub fn send(&self, mail_info: Mail) -> SendgridResult<String> {
        let post_body = make_post_body(mail_info)?;
        let res = self
            .client
            .post(API_URL)
            .headers(self.headers()?)
            .body(post_body)
            .send()?;

        if !res.status().is_success() {
            Err(SendgridError::RequestNotSuccessful(
                res.status(),
                res.text()?,
            ))
        } else {
            Ok(res.text()?)
        }
    }

    /// Sends a messages through the SendGrid API. It takes a Mail struct as an argument. It returns
    /// the string response from the API as JSON.
    ///
    /// ### Example
    ///
    /// ```rust,ignore
    /// use sendgrid::SendgridError;
    ///
    /// #[tokio::main]
    /// async fn main() -> Result<(), SendgridError> {
    ///     let my_secret_key = std::env::var("SENDGRID_KEY").expect("need SENDGRID_KEY to test");
    ///     use sendgrid::{Mail, SGClient};
    ///
    ///     let mail = Mail::new()
    ///         .add_from("my-email@address.com")
    ///         .add_text("hi!")
    ///         .add_subject("Hello")
    ///         .add_to(("your-email@address.com", "Your Name").into());
    ///     let response = SGClient::new(my_secret_key)
    ///         .send(mail)
    ///         .await?;
    ///     Ok(())
    /// }
    /// ```
    #[cfg(feature = "async")]
    pub async fn send(&self, mail_info: Mail<'_>) -> SendgridResult<String> {
        let post_body = make_post_body(mail_info)?;
        let res = self
            .client
            .post(API_URL)
            .headers(self.headers()?)
            .body(post_body)
            .send()
            .await?;

        if !res.status().is_success() {
            Err(SendgridError::RequestNotSuccessful(
                res.status(),
                res.text().await?,
            ))
        } else {
            Ok(res.text().await?)
        }
    }

    fn headers(&self) -> SendgridResult<HeaderMap> {
        let mut headers = HeaderMap::new();
        headers.insert(
            header::AUTHORIZATION,
            HeaderValue::from_str(&format!("Bearer {}", self.api_key.clone()))?,
        );
        headers.insert(
            header::CONTENT_TYPE,
            HeaderValue::from_static("application/x-www-form-urlencoded"),
        );
        headers.insert(header::USER_AGENT, HeaderValue::from_static("sendgrid-rs"));
        Ok(headers)
    }
}

#[test]
fn basic_message_body() {
    use crate::mail::Destination;

    let m = Mail::new()
        .add_to(Destination {
            address: "test@example.com",
            name: "Testy mcTestFace",
        })
        .add_from("me@example.com")
        .add_subject("Test")
        .add_text("It works");

    let body = make_post_body(m);
    let want = "to%5B%5D=test%40example.com&toname%5B%5D=Testy+mcTestFace&from=me%40example.com&subject=Test&\
                html=&text=It+works&fromname=&replyto=&date=&headers=%7B%7D&x-smtpapi=";
    assert_eq!(body.unwrap(), want);
}

#[test]
fn test_proper_key() {
    let want = "files[test.jpg]";
    let got = make_form_key("files", "test.jpg");
    assert_eq!(want, got);
}
<|endoftext|>


<commit_before>#[cfg(target_family="unix")]
extern crate fuse;
extern crate time;
extern crate libc;
extern crate rustc_serialize;

use std::collections::BTreeMap;
use std::env;
use std::path::Path;
use libc::ENOENT;
use time::Timespec;
#[cfg(target_family="unix")]
use fuse::{FileAttr, FileType, Filesystem, Request, ReplyAttr, ReplyData, ReplyEntry,
           ReplyDirectory};
use rustc_serialize::json;

#[cfg(target_family="unix")]
struct JsonFilesystem {
    tree: json::Object,
    attrs: BTreeMap<u64, FileAttr>,
    inodes: BTreeMap<String, u64>,
}

#[cfg(target_family="unix")]
impl JsonFilesystem {
    fn new(tree: &json::Object) -> JsonFilesystem {
        let mut attrs = BTreeMap::new();
        let mut inodes = BTreeMap::new();
        let ts = time::now().to_timespec();
        let attr = FileAttr {
            ino: 1,
            size: 0,
            blocks: 0,
            atime: ts,
            mtime: ts,
            ctime: ts,
            crtime: ts,
            kind: FileType::Directory,
            perm: 0o755,
            nlink: 0,
            uid: 0,
            gid: 0,
            rdev: 0,
            flags: 0,
        };
        attrs.insert(1, attr);
        inodes.insert("/".to_string(), 1);
        for (i, (key, value)) in tree.iter().enumerate() {
            let attr = FileAttr {
                ino: i as u64 + 2,
                size: value.pretty().to_string().len() as u64,
                blocks: 0,
                atime: ts,
                mtime: ts,
                ctime: ts,
                crtime: ts,
                kind: FileType::RegularFile,
                perm: 0o644,
                nlink: 0,
                uid: 0,
                gid: 0,
                rdev: 0,
                flags: 0,
            };
            attrs.insert(attr.ino, attr);
            inodes.insert(key.clone(), attr.ino);
        }
        JsonFilesystem {
            tree: tree.clone(),
            attrs: attrs,
            inodes: inodes,
        }
    }
}

#[cfg(target_family="unix")]
impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(ino={})", ino);
        match self.attrs.get(&ino) {
            Some(attr) => {
                let ttl = Timespec::new(1, 0);
                reply.attr(&ttl, attr);
            }
            None => reply.error(ENOENT),
        };
    }

    fn lookup(&mut self, _req: &Request, parent: u64, name: &Path, reply: ReplyEntry) {
        println!("lookup(parent={}, name={})", parent, name.display());
        let inode = match self.inodes.get(name.to_str().unwrap()) {
            Some(inode) => inode,
            None => {
                reply.error(ENOENT);
                return;
            }
        };
        match self.attrs.get(inode) {
            Some(attr) => {
                let ttl = Timespec::new(1, 0);
                reply.entry(&ttl, attr, 0);
            }
            None => reply.error(ENOENT),
        };
    }

    fn read(&mut self,
            _req: &Request,
            ino: u64,
            fh: u64,
            offset: u64,
            size: u32,
            reply: ReplyData) {
        println!("read(ino={}, fh={}, offset={}, size={})",
                 ino,
                 fh,
                 offset,
                 size);
        for (key, &inode) in &self.inodes {
            if inode == ino {
                let value = &self.tree[key];
                reply.data(value.pretty().to_string().as_bytes());
                return;
            }
        }
        reply.error(ENOENT);
    }

    fn readdir(&mut self,
               _req: &Request,
               ino: u64,
               fh: u64,
               offset: u64,
               mut reply: ReplyDirectory) {
        println!("readdir(ino={}, fh={}, offset={})", ino, fh, offset);
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, &Path::new("."));
                reply.add(1, 1, FileType::Directory, &Path::new(".."));
                for (key, &inode) in &self.inodes {
                    if inode == 1 {
                        continue;
                    }
                    let offset = inode; // hack
                    println!("\tkey={}, inode={}, offset={}", key, inode, offset);
                    reply.add(inode, offset, FileType::RegularFile, &Path::new(key));
                }
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }
}

#[cfg(target_family="windows")]
fn main() {
    println!("24 days of Rust - fuse (days 15 & 16)");
    println!("This example does not work on Windows :(");
}

#[cfg(target_family="unix")]
fn main() {
    println!("24 days of Rust - fuse (days 15 & 16)");
    let data = json::Json::from_str("{\"foo\": \"bar\", \"answer\": 42}").unwrap();
    let tree = data.as_object().unwrap();
    let fs = JsonFilesystem::new(tree);
    let mountpoint = match env::args().nth(1) {
        Some(path) => path,
        None => {
            println!("Usage: {} <MOUNTPOINT>", env::args().nth(0).unwrap());
            return;
        }
    };
    fuse::mount(fs, &mountpoint, &[]);
}
<commit_msg>Add conditional compilation attributes in FUSE example.<commit_after>#[cfg(target_family="unix")]
extern crate fuse;
extern crate time;
extern crate libc;
extern crate rustc_serialize;

#[cfg(target_family="unix")]
use std::collections::BTreeMap;
#[cfg(target_family="unix")]
use std::env;
#[cfg(target_family="unix")]
use std::path::Path;
#[cfg(target_family="unix")]
use libc::ENOENT;
#[cfg(target_family="unix")]
use time::Timespec;
#[cfg(target_family="unix")]
use fuse::{FileAttr, FileType, Filesystem, Request, ReplyAttr, ReplyData, ReplyEntry,
           ReplyDirectory};
#[cfg(target_family="unix")]
use rustc_serialize::json;

#[cfg(target_family="unix")]
struct JsonFilesystem {
    tree: json::Object,
    attrs: BTreeMap<u64, FileAttr>,
    inodes: BTreeMap<String, u64>,
}

#[cfg(target_family="unix")]
impl JsonFilesystem {
    fn new(tree: &json::Object) -> JsonFilesystem {
        let mut attrs = BTreeMap::new();
        let mut inodes = BTreeMap::new();
        let ts = time::now().to_timespec();
        let attr = FileAttr {
            ino: 1,
            size: 0,
            blocks: 0,
            atime: ts,
            mtime: ts,
            ctime: ts,
            crtime: ts,
            kind: FileType::Directory,
            perm: 0o755,
            nlink: 0,
            uid: 0,
            gid: 0,
            rdev: 0,
            flags: 0,
        };
        attrs.insert(1, attr);
        inodes.insert("/".to_string(), 1);
        for (i, (key, value)) in tree.iter().enumerate() {
            let attr = FileAttr {
                ino: i as u64 + 2,
                size: value.pretty().to_string().len() as u64,
                blocks: 0,
                atime: ts,
                mtime: ts,
                ctime: ts,
                crtime: ts,
                kind: FileType::RegularFile,
                perm: 0o644,
                nlink: 0,
                uid: 0,
                gid: 0,
                rdev: 0,
                flags: 0,
            };
            attrs.insert(attr.ino, attr);
            inodes.insert(key.clone(), attr.ino);
        }
        JsonFilesystem {
            tree: tree.clone(),
            attrs: attrs,
            inodes: inodes,
        }
    }
}

#[cfg(target_family="unix")]
impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(ino={})", ino);
        match self.attrs.get(&ino) {
            Some(attr) => {
                let ttl = Timespec::new(1, 0);
                reply.attr(&ttl, attr);
            }
            None => reply.error(ENOENT),
        };
    }

    fn lookup(&mut self, _req: &Request, parent: u64, name: &Path, reply: ReplyEntry) {
        println!("lookup(parent={}, name={})", parent, name.display());
        let inode = match self.inodes.get(name.to_str().unwrap()) {
            Some(inode) => inode,
            None => {
                reply.error(ENOENT);
                return;
            }
        };
        match self.attrs.get(inode) {
            Some(attr) => {
                let ttl = Timespec::new(1, 0);
                reply.entry(&ttl, attr, 0);
            }
            None => reply.error(ENOENT),
        };
    }

    fn read(&mut self,
            _req: &Request,
            ino: u64,
            fh: u64,
            offset: u64,
            size: u32,
            reply: ReplyData) {
        println!("read(ino={}, fh={}, offset={}, size={})",
                 ino,
                 fh,
                 offset,
                 size);
        for (key, &inode) in &self.inodes {
            if inode == ino {
                let value = &self.tree[key];
                reply.data(value.pretty().to_string().as_bytes());
                return;
            }
        }
        reply.error(ENOENT);
    }

    fn readdir(&mut self,
               _req: &Request,
               ino: u64,
               fh: u64,
               offset: u64,
               mut reply: ReplyDirectory) {
        println!("readdir(ino={}, fh={}, offset={})", ino, fh, offset);
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, &Path::new("."));
                reply.add(1, 1, FileType::Directory, &Path::new(".."));
                for (key, &inode) in &self.inodes {
                    if inode == 1 {
                        continue;
                    }
                    let offset = inode; // hack
                    println!("\tkey={}, inode={}, offset={}", key, inode, offset);
                    reply.add(inode, offset, FileType::RegularFile, &Path::new(key));
                }
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }
}

#[cfg(target_family="windows")]
fn main() {
    println!("24 days of Rust - fuse (days 15 & 16)");
    println!("This example does not work on Windows :(");
}

#[cfg(target_family="unix")]
fn main() {
    println!("24 days of Rust - fuse (days 15 & 16)");
    let data = json::Json::from_str("{\"foo\": \"bar\", \"answer\": 42}").unwrap();
    let tree = data.as_object().unwrap();
    let fs = JsonFilesystem::new(tree);
    let mountpoint = match env::args().nth(1) {
        Some(path) => path,
        None => {
            println!("Usage: {} <MOUNTPOINT>", env::args().nth(0).unwrap());
            return;
        }
    };
    fuse::mount(fs, &mountpoint, &[]);
}
<|endoftext|>