<commit_before>use toml_edit;

#[derive(Debug, Hash, PartialEq, Eq, Clone)]
enum DependencySource {
    Version {
        version: Option<String>,
        path: Option<String>,
    },
    Git(String),
}

/// A dependency handled by Cargo
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub struct Dependency {
    /// The name of the dependency (as it is set in its `Cargo.toml` and known to crates.io)
    pub name: String,
    optional: bool,
    default_features: bool,
    source: DependencySource,
    /// If the dependency is renamed, this is the new name for the dependency
    /// as a string.  None if it is not renamed.
    rename: Option<String>,
}

impl Default for Dependency {
    fn default() -> Dependency {
        Dependency {
            name: "".into(),
            rename: None,
            optional: false,
            default_features: true,
            source: DependencySource::Version {
                version: None,
                path: None,
            },
        }
    }
}

impl Dependency {
    /// Create a new dependency with a name
    pub fn new(name: &str) -> Dependency {
        Dependency {
            name: name.into(),
            ..Dependency::default()
        }
    }

    /// Set dependency to a given version
    pub fn set_version(mut self, version: &str) -> Dependency {
        // versions might have semver metadata appended which we do not want to
        // store in the cargo toml files.  This would cause a warning upon compilation
        // ("version requirement […] includes semver metadata which will be ignored")
        let version = version.split('+').next().unwrap();
        let old_source = self.source;
        let old_path = match old_source {
            DependencySource::Version { path, .. } => path,
            _ => None,
        };
        self.source = DependencySource::Version {
            version: Some(version.into()),
            path: old_path,
        };
        self
    }

    /// Set dependency to a given repository
    pub fn set_git(mut self, repo: &str) -> Dependency {
        self.source = DependencySource::Git(repo.into());
        self
    }

    /// Set dependency to a given path
    pub fn set_path(mut self, path: &str) -> Dependency {
        let old_source = self.source;
        let old_version = match old_source {
            DependencySource::Version { version, .. } => version,
            _ => None,
        };
        self.source = DependencySource::Version {
            version: old_version,
            path: Some(path.into()),
        };
        self
    }

    /// Set whether the dependency is optional
    pub fn set_optional(mut self, opt: bool) -> Dependency {
        self.optional = opt;
        self
    }

    /// Set the value of default-features for the dependency
    pub fn set_default_features(mut self, default_features: bool) -> Dependency {
        self.default_features = default_features;
        self
    }

    /// Set the alias for the dependency
    pub fn set_rename(mut self, rename: &str) -> Dependency {
        self.rename = Some(rename.into());
        self
    }

    /// Get the dependency name as defined in the manifest,
    /// that is, either the alias (rename field if Some),
    /// or the official package name (name field).
    pub fn name_in_manifest(&self) -> &str {
        &self.rename().unwrap_or(&self.name)
    }

    /// Get version of dependency
    pub fn version(&self) -> Option<&str> {
        if let DependencySource::Version {
            version: Some(ref version),
            ..
        } = self.source
        {
            Some(version)
        } else {
            None
        }
    }

    /// Get the alias for the dependency (if any)
    pub fn rename(&self) -> Option<&str> {
        match &self.rename {
            Some(rename) => Some(&rename),
            None => None,
        }
    }

    /// Convert dependency to TOML
    ///
    /// Returns a tuple with the dependency's name and either the version as a `String`
    /// or the path/git repository as an `InlineTable`.
    /// (If the dependency is set as `optional` or `default-features` is set to `false`,
    /// an `InlineTable` is returned in any case.)
    pub fn to_toml(&self) -> (String, toml_edit::Item) {
        let data: toml_edit::Item =
            match (self.optional, self.default_features, self.source.clone()) {
                // Extra short when version flag only
                (
                    false,
                    true,
                    DependencySource::Version {
                        version: Some(v),
                        path: None,
                    },
                ) => toml_edit::value(v),
                // Other cases are represented as an inline table
                (optional, default_features, source) => {
                    let mut data = toml_edit::InlineTable::default();

                    match source {
                        DependencySource::Version { version, path } => {
                            if let Some(v) = version {
                                data.get_or_insert("version", v);
                            }
                            if let Some(p) = path {
                                data.get_or_insert("path", p);
                            }
                        }
                        DependencySource::Git(v) => {
                            data.get_or_insert("git", v);
                        }
                    }
                    if self.optional {
                        data.get_or_insert("optional", optional);
                    }
                    if !self.default_features {
                        data.get_or_insert("default-features", default_features);
                    }

                    data.fmt();
                    toml_edit::value(toml_edit::Value::InlineTable(data))
                }
            };

        (self.name.clone(), data)
    }
}
<commit_msg>handle renamed dep in Dependency::to_toml; add a bunch of unit tests<commit_after>use toml_edit;

#[derive(Debug, Hash, PartialEq, Eq, Clone)]
enum DependencySource {
    Version {
        version: Option<String>,
        path: Option<String>,
    },
    Git(String),
}

/// A dependency handled by Cargo
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub struct Dependency {
    /// The name of the dependency (as it is set in its `Cargo.toml` and known to crates.io)
    pub name: String,
    optional: bool,
    default_features: bool,
    source: DependencySource,
    /// If the dependency is renamed, this is the new name for the dependency
    /// as a string.  None if it is not renamed.
    rename: Option<String>,
}

impl Default for Dependency {
    fn default() -> Dependency {
        Dependency {
            name: "".into(),
            rename: None,
            optional: false,
            default_features: true,
            source: DependencySource::Version {
                version: None,
                path: None,
            },
        }
    }
}

impl Dependency {
    /// Create a new dependency with a name
    pub fn new(name: &str) -> Dependency {
        Dependency {
            name: name.into(),
            ..Dependency::default()
        }
    }

    /// Set dependency to a given version
    pub fn set_version(mut self, version: &str) -> Dependency {
        // versions might have semver metadata appended which we do not want to
        // store in the cargo toml files.  This would cause a warning upon compilation
        // ("version requirement […] includes semver metadata which will be ignored")
        let version = version.split('+').next().unwrap();
        let old_source = self.source;
        let old_path = match old_source {
            DependencySource::Version { path, .. } => path,
            _ => None,
        };
        self.source = DependencySource::Version {
            version: Some(version.into()),
            path: old_path,
        };
        self
    }

    /// Set dependency to a given repository
    pub fn set_git(mut self, repo: &str) -> Dependency {
        self.source = DependencySource::Git(repo.into());
        self
    }

    /// Set dependency to a given path
    pub fn set_path(mut self, path: &str) -> Dependency {
        let old_source = self.source;
        let old_version = match old_source {
            DependencySource::Version { version, .. } => version,
            _ => None,
        };
        self.source = DependencySource::Version {
            version: old_version,
            path: Some(path.into()),
        };
        self
    }

    /// Set whether the dependency is optional
    pub fn set_optional(mut self, opt: bool) -> Dependency {
        self.optional = opt;
        self
    }

    /// Set the value of default-features for the dependency
    pub fn set_default_features(mut self, default_features: bool) -> Dependency {
        self.default_features = default_features;
        self
    }

    /// Set the alias for the dependency
    pub fn set_rename(mut self, rename: &str) -> Dependency {
        self.rename = Some(rename.into());
        self
    }

    /// Get the dependency name as defined in the manifest,
    /// that is, either the alias (rename field if Some),
    /// or the official package name (name field).
    pub fn name_in_manifest(&self) -> &str {
        &self.rename().unwrap_or(&self.name)
    }

    /// Get version of dependency
    pub fn version(&self) -> Option<&str> {
        if let DependencySource::Version {
            version: Some(ref version),
            ..
        } = self.source
        {
            Some(version)
        } else {
            None
        }
    }

    /// Get the alias for the dependency (if any)
    pub fn rename(&self) -> Option<&str> {
        match &self.rename {
            Some(rename) => Some(&rename),
            None => None,
        }
    }

    /// Convert dependency to TOML
    ///
    /// Returns a tuple with the dependency's name and either the version as a `String`
    /// or the path/git repository as an `InlineTable`.
    /// (If the dependency is set as `optional` or `default-features` is set to `false`,
    /// an `InlineTable` is returned in any case.)
    pub fn to_toml(&self) -> (String, toml_edit::Item) {
        let data: toml_edit::Item = match (
            self.optional,
            self.default_features,
            self.source.clone(),
            self.rename.as_ref(),
        ) {
            // Extra short when version flag only
            (
                false,
                true,
                DependencySource::Version {
                    version: Some(v),
                    path: None,
                },
                None,
            ) => toml_edit::value(v),
            // Other cases are represented as an inline table
            (optional, default_features, source, rename) => {
                let mut data = toml_edit::InlineTable::default();

                match source {
                    DependencySource::Version { version, path } => {
                        if let Some(v) = version {
                            data.get_or_insert("version", v);
                        }
                        if let Some(p) = path {
                            data.get_or_insert("path", p);
                        }
                    }
                    DependencySource::Git(v) => {
                        data.get_or_insert("git", v);
                    }
                }
                if self.optional {
                    data.get_or_insert("optional", optional);
                }
                if !self.default_features {
                    data.get_or_insert("default-features", default_features);
                }
                if rename.is_some() {
                    data.get_or_insert("package", self.name.clone());
                }

                data.fmt();
                toml_edit::value(toml_edit::Value::InlineTable(data))
            }
        };

        (self.name_in_manifest().to_string(), data)
    }
}

#[cfg(test)]
mod tests {
    use crate::dependency::Dependency;

    #[test]
    fn to_toml_simple_dep() {
        let toml = Dependency::new("dep").to_toml();

        assert_eq!(toml.0, "dep".to_owned());
    }

    #[test]
    fn to_toml_simple_dep_with_version() {
        let toml = Dependency::new("dep").set_version("1.0").to_toml();

        assert_eq!(toml.0, "dep".to_owned());
        assert_eq!(toml.1.as_str(), Some("1.0"));
    }

    #[test]
    fn to_toml_optional_dep() {
        let toml = Dependency::new("dep").set_optional(true).to_toml();

        assert_eq!(toml.0, "dep".to_owned());
        assert!(toml.1.is_inline_table());

        let dep = toml.1.as_inline_table().unwrap();
        assert_eq!(dep.get("optional").unwrap().as_bool(), Some(true));
    }

    #[test]
    fn to_toml_dep_without_default_features() {
        let toml = Dependency::new("dep").set_default_features(false).to_toml();

        assert_eq!(toml.0, "dep".to_owned());
        assert!(toml.1.is_inline_table());

        let dep = toml.1.as_inline_table().unwrap();
        assert_eq!(dep.get("default-features").unwrap().as_bool(), Some(false));
    }

    #[test]
    fn to_toml_dep_with_path_source() {
        let toml = Dependency::new("dep").set_path("~/foo/bar").to_toml();

        assert_eq!(toml.0, "dep".to_owned());
        assert!(toml.1.is_inline_table());

        let dep = toml.1.as_inline_table().unwrap();
        assert_eq!(dep.get("path").unwrap().as_str(), Some("~/foo/bar"));
    }

    #[test]
    fn to_toml_dep_with_git_source() {
        let toml = Dependency::new("dep")
            .set_git("https://foor/bar.git")
            .to_toml();

        assert_eq!(toml.0, "dep".to_owned());
        assert!(toml.1.is_inline_table());

        let dep = toml.1.as_inline_table().unwrap();
        assert_eq!(
            dep.get("git").unwrap().as_str(),
            Some("https://foor/bar.git")
        );
    }

    #[test]
    fn to_toml_renamed_dep() {
        let toml = Dependency::new("dep").set_rename("d").to_toml();

        assert_eq!(toml.0, "d".to_owned());
        assert!(toml.1.is_inline_table());

        let dep = toml.1.as_inline_table().unwrap();
        assert_eq!(dep.get("package").unwrap().as_str(), Some("dep"));
    }

    #[test]
    fn to_toml_complex_dep() {
        let toml = Dependency::new("dep")
            .set_version("1.0")
            .set_default_features(false)
            .set_rename("d")
            .to_toml();

        assert_eq!(toml.0, "d".to_owned());
        assert!(toml.1.is_inline_table());

        let dep = toml.1.as_inline_table().unwrap();
        assert_eq!(dep.get("package").unwrap().as_str(), Some("dep"));
        assert_eq!(dep.get("version").unwrap().as_str(), Some("1.0"));
        assert_eq!(dep.get("default-features").unwrap().as_bool(), Some(false));
    }
}
<|endoftext|>


<commit_before>use std::process::exit;
use std::path::{Path, PathBuf};

use package::{
    PrebuiltPackage,
    download_package
};
use utils::find_cmd;

fn emscripten_package() -> Option< PrebuiltPackage > {
    let package =
        if cfg!( target_os = "linux" ) && cfg!( target_arch = "x86_64" ) {
            PrebuiltPackage {
                url: "https://github.com/koute/emscripten-build/releases/download/emscripten-1.37.27-1/emscripten-1.37.27-1-x86_64-unknown-linux-gnu.tgz",
                name: "emscripten",
                version: "1.37.27-1",
                arch: "x86_64-unknown-linux-gnu",
                hash: "43e653d26bfe95b010267538949e2d0cb23364571972042165d0258d55e8ca66",
                size: 136902444
            }
        } else if cfg!( target_os = "linux" ) && cfg!( target_arch = "x86" ) {
            PrebuiltPackage {
                url: "https://github.com/koute/emscripten-build/releases/download/emscripten-1.37.27-1/emscripten-1.37.27-1-i686-unknown-linux-gnu.tgz",
                name: "emscripten",
                version: "1.37.27-1",
                arch: "i686-unknown-linux-gnu",
                hash: "a3a1e4622f4509b903eaf76c3b1c7fc981f656185f3fcb7cd8a81718d0e11bb3",
                size: 144527242
            }
        } else {
            return None;
        };

    Some( package )
}

fn binaryen_package() -> Option< PrebuiltPackage > {
    let package =
        if cfg!( target_os = "linux" ) && cfg!( target_arch = "x86_64" ) {
            PrebuiltPackage {
                url: "https://github.com/koute/emscripten-build/releases/download/emscripten-1.37.27-1/binaryen-1.37.27-1-x86_64-unknown-linux-gnu.tgz",
                name: "binaryen",
                version: "1.37.27-1",
                arch: "x86_64-unknown-linux-gnu",
                hash: "aa46c2d3d6031481a88c45e072acb1c625fbc22aae8a5271fd70f5b879666c1a",
                size: 12625100
            }
        } else if cfg!( target_os = "linux" ) && cfg!( target_arch = "x86" ) {
            PrebuiltPackage {
                url: "https://github.com/koute/emscripten-build/releases/download/emscripten-1.37.27-1/binaryen-1.37.27-1-i686-unknown-linux-gnu.tgz",
                name: "binaryen",
                version: "1.37.27-1",
                arch: "i686-unknown-linux-gnu",
                hash: "2a3eff1a7bbb5f5e4bceb0da1ebd508c6458d1dfe7f511641668db4d96b98d8a",
                size: 12706642
            }
        } else {
            return None;
        };

    Some( package )
}

fn check_emscripten() {
    let possible_commands =
        if cfg!( windows ) {
            &[ "emcc.bat" ]
        } else {
            &[ "emcc" ]
        };

    if find_cmd( possible_commands ).is_some() {
        return;
    }

    eprintln!( "error: you don't have Emscripten installed!" );
    eprintln!( "" );

    if Path::new( "/usr/bin/pacman" ).exists() {
        eprintln!( "You can most likely install it like this:" );
        eprintln!( "  sudo pacman -S emscripten" );
    } else if Path::new( "/usr/bin/apt-get" ).exists() {
        eprintln!( "You can most likely install it like this:" );
        eprintln!( "  sudo apt-get install emscripten" );
    } else if cfg!( target_os = "linux" ) {
        eprintln!( "You can most likely find it in your distro's repositories." );
    } else if cfg!( target_os = "windows" ) {
        eprintln!( "Download and install emscripten from the official site: http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html" );
    }

    if cfg!( unix ) {
        if cfg!( target_os = "linux" ) {
            eprintln!( "If not you can install it manually like this:" );
        } else {
            eprintln!( "You can install it manually like this:" );
        }
        eprintln!( "  curl -O https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz" );
        eprintln!( "  tar -xzf emsdk-portable.tar.gz" );
        eprintln!( "  source emsdk_portable/emsdk_env.sh" );
        eprintln!( "  emsdk update" );
        eprintln!( "  emsdk install sdk-incoming-64bit" );
        eprintln!( "  emsdk activate sdk-incoming-64bit" );
    }

    exit( 101 );
}

pub struct Emscripten {
    pub binaryen_path: Option< PathBuf >,
    pub emscripten_path: PathBuf,
    pub emscripten_llvm_path: PathBuf
}

pub fn initialize_emscripten(
    use_system_emscripten: bool,
    targeting_webasm: bool
) -> Option< Emscripten > {

    if use_system_emscripten {
        check_emscripten();
        return None;
    }

    let emscripten_package = match emscripten_package() {
        Some( pkg ) => pkg,
        None => {
            check_emscripten();
            return None;
        }
    };

    let binaryen_package = if targeting_webasm {
        match binaryen_package() {
            Some( pkg ) => Some( pkg ),
            None => {
                check_emscripten();
                return None;
            }
        }
    } else {
        None
    };

    let emscripten_root = download_package( &emscripten_package );
    let emscripten_path = emscripten_root.join( "emscripten" );
    let emscripten_llvm_path = emscripten_root.join( "emscripten-fastcomp" );
    let binaryen_path = if let Some( binaryen_package ) = binaryen_package {
        let binaryen_root = download_package( &binaryen_package );
        Some( binaryen_root.join( "binaryen" ) )
    } else {
        None
    };

    Some( Emscripten {
        binaryen_path,
        emscripten_path,
        emscripten_llvm_path
    })
}
<commit_msg>Update to Emscripten 1.38.11<commit_after>use std::process::exit;
use std::path::{Path, PathBuf};

use package::{
    PrebuiltPackage,
    download_package
};
use utils::find_cmd;

fn emscripten_package() -> Option< PrebuiltPackage > {
    let package =
        if cfg!( target_os = "linux" ) && cfg!( target_arch = "x86_64" ) {
            PrebuiltPackage {
                url: "https://github.com/koute/emscripten-build/releases/download/emscripten-1.38.11-1/emscripten-1.38.11-1-x86_64-unknown-linux-gnu.tgz",
                name: "emscripten",
                version: "1.38.11-1",
                arch: "x86_64-unknown-linux-gnu",
                hash: "cc2727143297c37323c051e2d170fb3b31f43a38999954f812cdca1232555fd5",
                size: 211418062
            }
        } else if cfg!( target_os = "linux" ) && cfg!( target_arch = "x86" ) {
            PrebuiltPackage {
                url: "https://github.com/koute/emscripten-build/releases/download/emscripten-1.38.11-1/emscripten-1.38.11-1-i686-unknown-linux-gnu.tgz",
                name: "emscripten",
                version: "1.38.11-1",
                arch: "i686-unknown-linux-gnu",
                hash: "6be9f70fa79c096688aa630b8fe33df7e6f35716088c28c6fb058921c0ba927e",
                size: 223690133
            }
        } else {
            return None;
        };

    Some( package )
}

fn binaryen_package() -> Option< PrebuiltPackage > {
    let package =
        if cfg!( target_os = "linux" ) && cfg!( target_arch = "x86_64" ) {
            PrebuiltPackage {
                url: "https://github.com/koute/emscripten-build/releases/download/emscripten-1.38.11-1/binaryen-1.38.11-1-x86_64-unknown-linux-gnu.tgz",
                name: "binaryen",
                version: "1.38.11-1",
                arch: "x86_64-unknown-linux-gnu",
                hash: "1035effafac158fc1342a80a16cd800b4fd8418b6100b1e7cc4fd385cd13d801",
                size: 15048525
            }
        } else if cfg!( target_os = "linux" ) && cfg!( target_arch = "x86" ) {
            PrebuiltPackage {
                url: "https://github.com/koute/emscripten-build/releases/download/emscripten-1.38.11-1/binaryen-1.38.11-1-i686-unknown-linux-gnu.tgz",
                name: "binaryen",
                version: "1.38.11-1",
                arch: "i686-unknown-linux-gnu",
                hash: "6bca8f2378e86dfa3305bffc3dc231ace15f93904dab138692c8f8fb3814d337",
                size: 15042751
            }
        } else {
            return None;
        };

    Some( package )
}

fn check_emscripten() {
    let possible_commands =
        if cfg!( windows ) {
            &[ "emcc.bat" ]
        } else {
            &[ "emcc" ]
        };

    if find_cmd( possible_commands ).is_some() {
        return;
    }

    eprintln!( "error: you don't have Emscripten installed!" );
    eprintln!( "" );

    if Path::new( "/usr/bin/pacman" ).exists() {
        eprintln!( "You can most likely install it like this:" );
        eprintln!( "  sudo pacman -S emscripten" );
    } else if Path::new( "/usr/bin/apt-get" ).exists() {
        eprintln!( "You can most likely install it like this:" );
        eprintln!( "  sudo apt-get install emscripten" );
    } else if cfg!( target_os = "linux" ) {
        eprintln!( "You can most likely find it in your distro's repositories." );
    } else if cfg!( target_os = "windows" ) {
        eprintln!( "Download and install emscripten from the official site: http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html" );
    }

    if cfg!( unix ) {
        if cfg!( target_os = "linux" ) {
            eprintln!( "If not you can install it manually like this:" );
        } else {
            eprintln!( "You can install it manually like this:" );
        }
        eprintln!( "  curl -O https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz" );
        eprintln!( "  tar -xzf emsdk-portable.tar.gz" );
        eprintln!( "  source emsdk_portable/emsdk_env.sh" );
        eprintln!( "  emsdk update" );
        eprintln!( "  emsdk install sdk-incoming-64bit" );
        eprintln!( "  emsdk activate sdk-incoming-64bit" );
    }

    exit( 101 );
}

pub struct Emscripten {
    pub binaryen_path: Option< PathBuf >,
    pub emscripten_path: PathBuf,
    pub emscripten_llvm_path: PathBuf
}

pub fn initialize_emscripten(
    use_system_emscripten: bool,
    targeting_webasm: bool
) -> Option< Emscripten > {

    if use_system_emscripten {
        check_emscripten();
        return None;
    }

    let emscripten_package = match emscripten_package() {
        Some( pkg ) => pkg,
        None => {
            check_emscripten();
            return None;
        }
    };

    let binaryen_package = if targeting_webasm {
        match binaryen_package() {
            Some( pkg ) => Some( pkg ),
            None => {
                check_emscripten();
                return None;
            }
        }
    } else {
        None
    };

    let emscripten_root = download_package( &emscripten_package );
    let emscripten_path = emscripten_root.join( "emscripten" );
    let emscripten_llvm_path = emscripten_root.join( "emscripten-fastcomp" );
    let binaryen_path = if let Some( binaryen_package ) = binaryen_package {
        let binaryen_root = download_package( &binaryen_package );
        Some( binaryen_root.join( "binaryen" ) )
    } else {
        None
    };

    Some( Emscripten {
        binaryen_path,
        emscripten_path,
        emscripten_llvm_path
    })
}
<|endoftext|>


<commit_before>//! A module to handle `Event` enumerator

pub mod attributes;

use std::borrow::Cow;
use std::str::from_utf8;
use std::ops::Deref;
use encoding_rs::Encoding;
use std::io::BufRead;

use escape::{escape, unescape};
use self::attributes::{Attribute, Attributes};
use errors::Result;
use reader::Reader;

use memchr;

/// A struct to manage `Event::Start` events
///
/// Provides in particular an iterator over attributes
#[derive(Clone, Debug)]
pub struct BytesStart<'a> {
    /// content of the element, before any utf8 conversion
    buf: Cow<'a, [u8]>,
    /// end of the element name, the name starts at that the start of `buf`
    name_len: usize,
}

impl<'a> BytesStart<'a> {
    /// Creates a new `BytesStart` from the given name.
    #[inline]
    pub fn borrowed(content: &'a [u8], name_len: usize) -> BytesStart<'a> {
        BytesStart {
            buf: Cow::Borrowed(content),
            name_len: name_len,
        }
    }

    /// Creates a new `BytesStart` from the given name. Owns its content
    #[inline]
    pub fn owned(content: Vec<u8>, name_len: usize) -> BytesStart<'static> {
        BytesStart {
            buf: Cow::Owned(content),
            name_len: name_len,
        }
    }

    /// Converts the event into an Owned event
    pub fn into_owned(self) -> BytesStart<'static> {
        BytesStart {
            buf: Cow::Owned(self.buf.into_owned()),
            name_len: self.name_len,
        }
    }

    /// Consumes self and adds attributes to this element from an iterator
    /// over (key, value) tuples.
    /// Key and value can be anything that implements the AsRef<[u8]> trait,
    /// like byte slices and strings.
    pub fn with_attributes<'b, I>(mut self, attributes: I) -> Self
    where
        I: IntoIterator,
        I::Item: Into<Attribute<'b>>,
    {
        self.extend_attributes(attributes);
        self
    }

    /// name as &[u8] (without eventual attributes)
    pub fn name(&self) -> &[u8] {
        &self.buf[..self.name_len]
    }

    /// local name (excluding namespace) as &[u8] (without eventual attributes)
    /// returns the name() with any leading namespace removed (all content up to
    /// and including the first ':' character)
    #[inline]
    pub fn local_name(&self) -> &[u8] {
        let name = self.name();
        memchr::memchr(b':', name).map_or(name, |i| &name[i + 1..])
    }

    /// gets unescaped content
    ///
    /// Searches for '&' into content and try to escape the coded character if possible
    /// returns Malformed error with index within element if '&' is not followed by ';'
    pub fn unescaped(&self) -> Result<Cow<[u8]>> {
        unescape(&*self.buf)
    }

    /// gets attributes iterator
    pub fn attributes(&self) -> Attributes {
        Attributes::new(self, self.name_len)
    }

    /// extend the attributes of this element from an iterator over (key, value) tuples.
    /// Key and value can be anything that implements the AsRef<[u8]> trait,
    /// like byte slices and strings.
    pub fn extend_attributes<'b, I>(&mut self, attributes: I) -> &mut BytesStart<'a>
    where
        I: IntoIterator,
        I::Item: Into<Attribute<'b>>,
    {
        for attr in attributes {
            self.push_attribute(attr);
        }
        self
    }

    /// helper method to unescape then decode self using the reader encoding
    ///
    /// for performance reasons (could avoid allocating a `String`),
    /// it might be wiser to manually use
    /// 1. BytesStart::unescaped()
    /// 2. Reader::decode(...)
    pub fn unescape_and_decode<B: BufRead>(&self, reader: &Reader<B>) -> Result<String> {
        self.unescaped().map(|e| reader.decode(&*e).into_owned())
    }

    /// Adds an attribute to this element from the given key and value.
    /// Key and value can be anything that implements the AsRef<[u8]> trait,
    /// like byte slices and strings.
    pub fn push_attribute<'b, A: Into<Attribute<'b>>>(&mut self, attr: A) {
        let a = attr.into();
        let bytes = self.buf.to_mut();
        bytes.push(b' ');
        bytes.extend_from_slice(a.key);
        bytes.extend_from_slice(b"=\"");
        bytes.extend_from_slice(&*a.value);
        bytes.push(b'"');
    }
}

/// Wrapper around `BytesElement` to parse/write `XmlDecl`
///
/// Postpone element parsing only when needed.
///
/// [W3C XML 1.1 Prolog and Document Type Delcaration](http://w3.org/TR/xml11/#sec-prolog-dtd)
#[derive(Clone, Debug)]
pub struct BytesDecl<'a> {
    element: BytesStart<'a>,
}

impl<'a> BytesDecl<'a> {
    /// Creates a `BytesDecl` from a `BytesStart`
    pub fn from_start(start: BytesStart<'a>) -> BytesDecl<'a> {
        BytesDecl { element: start }
    }

    /// Gets xml version, including quotes (' or ")
    pub fn version(&self) -> Result<Cow<[u8]>> {
        match self.element.attributes().next() {
            Some(Err(e)) => Err(e),
            Some(
                Ok(Attribute {
                    key: b"version",
                    value: v,
                }),
            ) => Ok(v),
            Some(Ok(a)) => Err(
                format!(
                    "XmlDecl must start with 'version' attribute, found {:?}",
                    from_utf8(a.key)
                ).into(),
            ),
            None => Err(
                "XmlDecl must start with 'version' attribute, found none".into(),
            ),
        }
    }

    /// Gets xml encoding, including quotes (' or ")
    pub fn encoding(&self) -> Option<Result<Cow<[u8]>>> {
        for a in self.element.attributes() {
            match a {
                Err(e) => return Some(Err(e)),
                Ok(Attribute {
                    key: b"encoding",
                    value: v,
                }) => return Some(Ok(v)),
                _ => (),
            }
        }
        None
    }

    /// Gets xml standalone, including quotes (' or ")
    pub fn standalone(&self) -> Option<Result<Cow<[u8]>>> {
        for a in self.element.attributes() {
            match a {
                Err(e) => return Some(Err(e)),
                Ok(Attribute {
                    key: b"standalone",
                    value: v,
                }) => return Some(Ok(v)),
                _ => (),
            }
        }
        None
    }

    /// Constructs a new `XmlDecl` from the (mandatory) _version_ (should be `1.0` or `1.1`),
    /// the optional _encoding_ (e.g., `UTF-8`) and the optional _standalone_ (`yes` or `no`)
    /// attribute.
    ///
    /// Does not escape any of its inputs. Always uses double quotes to wrap the attribute values.
    /// The caller is responsible for escaping attribute values. Shouldn't usually be relevant since
    /// the double quote character is not allowed in any of the attribute values.
    pub fn new(
        version: &[u8],
        encoding: Option<&[u8]>,
        standalone: Option<&[u8]>,
    ) -> BytesDecl<'static> {
        // Compute length of the buffer based on supplied attributes
        // ' encoding=""'   => 12
        let encoding_attr_len = if let Some(xs) = encoding {
            12 + xs.len()
        } else {
            0
        };
        // ' standalone=""' => 14
        let standalone_attr_len = if let Some(xs) = standalone {
            14 + xs.len()
        } else {
            0
        };
        // 'xml version=""' => 14
        let mut buf = Vec::with_capacity(14 + encoding_attr_len + standalone_attr_len);

        buf.extend_from_slice(b"xml version=\"");
        buf.extend_from_slice(version);

        if let Some(encoding_val) = encoding {
            buf.extend_from_slice(b"\" encoding=\"");
            buf.extend_from_slice(encoding_val);
        }

        if let Some(standalone_val) = standalone {
            buf.extend_from_slice(b"\" standalone=\"");
            buf.extend_from_slice(standalone_val);
        }
        buf.push(b'"');

        BytesDecl {
            element: BytesStart::owned(buf, 3),
        }
    }

    /// Gets the decoder struct
    pub fn encoder(&self) -> Option<&'static Encoding> {
        self.encoding()
            .and_then(|e| e.ok())
            .and_then(|e| Encoding::for_label(&*e))
    }
}

/// A struct to manage `Event::End` events
#[derive(Clone, Debug)]
pub struct BytesEnd<'a> {
    name: Cow<'a, [u8]>,
}

impl<'a> BytesEnd<'a> {
    /// Creates a new `BytesEnd` borrowing a slice
    #[inline]
    pub fn borrowed(name: &'a [u8]) -> BytesEnd<'a> {
        BytesEnd {
            name: Cow::Borrowed(name),
        }
    }

    /// Creates a new `BytesEnd` owning its name
    #[inline]
    pub fn owned(name: Vec<u8>) -> BytesEnd<'static> {
        BytesEnd {
            name: Cow::Owned(name),
        }
    }

    /// Gets `BytesEnd` event name
    #[inline]
    pub fn name(&self) -> &[u8] {
        &*self.name
    }

    /// local name (excluding namespace) as &[u8] (without eventual attributes)
    /// returns the name() with any leading namespace removed (all content up to
    /// and including the first ':' character)
    #[inline]
    pub fn local_name(&self) -> &[u8] {
        if let Some(i) = self.name().iter().position(|b| *b == b':') {
            &self.name()[i + 1..]
        } else {
            self.name()
        }
    }
}

/// A struct to manage `Event::End` events
#[derive(Clone, Debug)]
pub struct BytesText<'a> {
    content: Cow<'a, [u8]>,
}

impl<'a> BytesText<'a> {
    /// Creates a new `BytesEnd` borrowing a slice
    #[inline]
    pub fn borrowed(content: &'a [u8]) -> BytesText<'a> {
        BytesText {
            content: Cow::Borrowed(content),
        }
    }

    /// Creates a new `BytesEnd` owning its name
    #[inline]
    pub fn owned(content: Vec<u8>) -> BytesText<'static> {
        BytesText {
            content: Cow::Owned(content),
        }
    }

    /// Creates a new `BytesEnd` from text
    #[inline]
    pub fn from_str<S: AsRef<str>>(text: S) -> BytesText<'static> {
        let bytes = escape(text.as_ref().as_bytes()).into_owned();
        BytesText {
            content: Cow::Owned(bytes),
        }
    }

    /// gets escaped content
    ///
    /// Searches for '&' into content and try to escape the coded character if possible
    /// returns Malformed error with index within element if '&' is not followed by ';'
    pub fn unescaped(&self) -> Result<Cow<[u8]>> {
        unescape(self)
    }

    /// helper method to unescape then decode self using the reader encoding
    ///
    /// for performance reasons (could avoid allocating a `String`),
    /// it might be wiser to manually use
    /// 1. BytesText::unescaped()
    /// 2. Reader::decode(...)
    pub fn unescape_and_decode<B: BufRead>(&self, reader: &Reader<B>) -> Result<String> {
        self.unescaped().map(|e| reader.decode(&*e).into_owned())
    }

    /// Gets escaped content
    ///
    /// Searches for any of `<, >, &, ', "` and xml escapes them.
    pub fn escaped(&self) -> Cow<[u8]> {
        self.content.clone()
    }
}

/// Event to interprete node as they are parsed
#[derive(Clone, Debug)]
pub enum Event<'a> {
    /// Start tag (with attributes) <...>
    Start(BytesStart<'a>),
    /// End tag </...>
    End(BytesEnd<'a>),
    /// Empty element tag (with attributes) <.../>
    Empty(BytesStart<'a>),
    /// Data between Start and End element
    Text(BytesText<'a>),
    /// Comment <!-- ... -->
    Comment(BytesText<'a>),
    /// CData <![CDATA[...]]>
    CData(BytesText<'a>),
    /// Xml declaration <?xml ...?>
    Decl(BytesDecl<'a>),
    /// Processing instruction <?...?>
    PI(BytesText<'a>),
    /// Doctype <!DOCTYPE...>
    DocType(BytesText<'a>),
    /// Eof of file event
    Eof,
}

impl<'a> Deref for BytesStart<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.buf
    }
}

impl<'a> Deref for BytesDecl<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.element
    }
}

impl<'a> Deref for BytesEnd<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.name
    }
}

impl<'a> Deref for BytesText<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.content
    }
}

impl<'a> Deref for Event<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        match *self {
            Event::Start(ref e) | Event::Empty(ref e) => &*e,
            Event::End(ref e) => &*e,
            Event::Text(ref e) => &*e,
            Event::Decl(ref e) => &*e,
            Event::PI(ref e) => &*e,
            Event::CData(ref e) => &*e,
            Event::Comment(ref e) => &*e,
            Event::DocType(ref e) => &*e,
            Event::Eof => &[],
        }
    }
}

impl<'a> AsRef<Event<'a>> for Event<'a> {
    fn as_ref(&self) -> &Event<'a> {
        self
    }
}

#[cfg(test)]
#[test]
fn local_name() {
    use std::str::from_utf8;
    let xml = r#"
        <foo:bus attr='bar'>foobusbar</foo:bus>
        <foo: attr='bar'>foobusbar</foo:>
        <:foo attr='bar'>foobusbar</:foo>
        <foo:bus:baz attr='bar'>foobusbar</foo:bus:baz>
        "#;
    let mut rdr = Reader::from_str(xml);
    let mut buf = Vec::new();
    let mut parsed_local_names = Vec::new();
    loop {
        match rdr.read_event(&mut buf).expect("unable to read xml event") {
            Event::Start(ref e) => parsed_local_names.push(
                from_utf8(e.local_name())
                    .expect("unable to build str from local_name")
                    .to_string(),
            ),
            Event::End(ref e) => parsed_local_names.push(
                from_utf8(e.local_name())
                    .expect("unable to build str from local_name")
                    .to_string(),
            ),
            Event::Eof => break,
            _ => {}
        }
    }
    assert_eq!(parsed_local_names[0], "bus".to_string());
    assert_eq!(parsed_local_names[1], "bus".to_string());
    assert_eq!(parsed_local_names[2], "".to_string());
    assert_eq!(parsed_local_names[3], "".to_string());
    assert_eq!(parsed_local_names[4], "foo".to_string());
    assert_eq!(parsed_local_names[5], "foo".to_string());
    assert_eq!(parsed_local_names[6], "bus:baz".to_string());
    assert_eq!(parsed_local_names[7], "bus:baz".to_string());
}
<commit_msg>proper names in docs<commit_after>//! A module to handle `Event` enumerator

pub mod attributes;

use std::borrow::Cow;
use std::str::from_utf8;
use std::ops::Deref;
use encoding_rs::Encoding;
use std::io::BufRead;

use escape::{escape, unescape};
use self::attributes::{Attribute, Attributes};
use errors::Result;
use reader::Reader;

use memchr;

/// A struct to manage `Event::Start` events
///
/// Provides in particular an iterator over attributes
#[derive(Clone, Debug)]
pub struct BytesStart<'a> {
    /// content of the element, before any utf8 conversion
    buf: Cow<'a, [u8]>,
    /// end of the element name, the name starts at that the start of `buf`
    name_len: usize,
}

impl<'a> BytesStart<'a> {
    /// Creates a new `BytesStart` from the given name.
    #[inline]
    pub fn borrowed(content: &'a [u8], name_len: usize) -> BytesStart<'a> {
        BytesStart {
            buf: Cow::Borrowed(content),
            name_len: name_len,
        }
    }

    /// Creates a new `BytesStart` from the given name. Owns its content
    #[inline]
    pub fn owned(content: Vec<u8>, name_len: usize) -> BytesStart<'static> {
        BytesStart {
            buf: Cow::Owned(content),
            name_len: name_len,
        }
    }

    /// Converts the event into an Owned event
    pub fn into_owned(self) -> BytesStart<'static> {
        BytesStart {
            buf: Cow::Owned(self.buf.into_owned()),
            name_len: self.name_len,
        }
    }

    /// Consumes self and adds attributes to this element from an iterator
    /// over (key, value) tuples.
    /// Key and value can be anything that implements the AsRef<[u8]> trait,
    /// like byte slices and strings.
    pub fn with_attributes<'b, I>(mut self, attributes: I) -> Self
    where
        I: IntoIterator,
        I::Item: Into<Attribute<'b>>,
    {
        self.extend_attributes(attributes);
        self
    }

    /// name as &[u8] (without eventual attributes)
    pub fn name(&self) -> &[u8] {
        &self.buf[..self.name_len]
    }

    /// local name (excluding namespace) as &[u8] (without eventual attributes)
    /// returns the name() with any leading namespace removed (all content up to
    /// and including the first ':' character)
    #[inline]
    pub fn local_name(&self) -> &[u8] {
        let name = self.name();
        memchr::memchr(b':', name).map_or(name, |i| &name[i + 1..])
    }

    /// gets unescaped content
    ///
    /// Searches for '&' into content and try to escape the coded character if possible
    /// returns Malformed error with index within element if '&' is not followed by ';'
    pub fn unescaped(&self) -> Result<Cow<[u8]>> {
        unescape(&*self.buf)
    }

    /// gets attributes iterator
    pub fn attributes(&self) -> Attributes {
        Attributes::new(self, self.name_len)
    }

    /// extend the attributes of this element from an iterator over (key, value) tuples.
    /// Key and value can be anything that implements the AsRef<[u8]> trait,
    /// like byte slices and strings.
    pub fn extend_attributes<'b, I>(&mut self, attributes: I) -> &mut BytesStart<'a>
    where
        I: IntoIterator,
        I::Item: Into<Attribute<'b>>,
    {
        for attr in attributes {
            self.push_attribute(attr);
        }
        self
    }

    /// helper method to unescape then decode self using the reader encoding
    ///
    /// for performance reasons (could avoid allocating a `String`),
    /// it might be wiser to manually use
    /// 1. BytesStart::unescaped()
    /// 2. Reader::decode(...)
    pub fn unescape_and_decode<B: BufRead>(&self, reader: &Reader<B>) -> Result<String> {
        self.unescaped().map(|e| reader.decode(&*e).into_owned())
    }

    /// Adds an attribute to this element from the given key and value.
    /// Key and value can be anything that implements the AsRef<[u8]> trait,
    /// like byte slices and strings.
    pub fn push_attribute<'b, A: Into<Attribute<'b>>>(&mut self, attr: A) {
        let a = attr.into();
        let bytes = self.buf.to_mut();
        bytes.push(b' ');
        bytes.extend_from_slice(a.key);
        bytes.extend_from_slice(b"=\"");
        bytes.extend_from_slice(&*a.value);
        bytes.push(b'"');
    }
}

/// Wrapper around `BytesElement` to parse/write `XmlDecl`
///
/// Postpone element parsing only when needed.
///
/// [W3C XML 1.1 Prolog and Document Type Delcaration](http://w3.org/TR/xml11/#sec-prolog-dtd)
#[derive(Clone, Debug)]
pub struct BytesDecl<'a> {
    element: BytesStart<'a>,
}

impl<'a> BytesDecl<'a> {
    /// Creates a `BytesDecl` from a `BytesStart`
    pub fn from_start(start: BytesStart<'a>) -> BytesDecl<'a> {
        BytesDecl { element: start }
    }

    /// Gets xml version, including quotes (' or ")
    pub fn version(&self) -> Result<Cow<[u8]>> {
        match self.element.attributes().next() {
            Some(Err(e)) => Err(e),
            Some(
                Ok(Attribute {
                    key: b"version",
                    value: v,
                }),
            ) => Ok(v),
            Some(Ok(a)) => Err(
                format!(
                    "XmlDecl must start with 'version' attribute, found {:?}",
                    from_utf8(a.key)
                ).into(),
            ),
            None => Err(
                "XmlDecl must start with 'version' attribute, found none".into(),
            ),
        }
    }

    /// Gets xml encoding, including quotes (' or ")
    pub fn encoding(&self) -> Option<Result<Cow<[u8]>>> {
        for a in self.element.attributes() {
            match a {
                Err(e) => return Some(Err(e)),
                Ok(Attribute {
                    key: b"encoding",
                    value: v,
                }) => return Some(Ok(v)),
                _ => (),
            }
        }
        None
    }

    /// Gets xml standalone, including quotes (' or ")
    pub fn standalone(&self) -> Option<Result<Cow<[u8]>>> {
        for a in self.element.attributes() {
            match a {
                Err(e) => return Some(Err(e)),
                Ok(Attribute {
                    key: b"standalone",
                    value: v,
                }) => return Some(Ok(v)),
                _ => (),
            }
        }
        None
    }

    /// Constructs a new `XmlDecl` from the (mandatory) _version_ (should be `1.0` or `1.1`),
    /// the optional _encoding_ (e.g., `UTF-8`) and the optional _standalone_ (`yes` or `no`)
    /// attribute.
    ///
    /// Does not escape any of its inputs. Always uses double quotes to wrap the attribute values.
    /// The caller is responsible for escaping attribute values. Shouldn't usually be relevant since
    /// the double quote character is not allowed in any of the attribute values.
    pub fn new(
        version: &[u8],
        encoding: Option<&[u8]>,
        standalone: Option<&[u8]>,
    ) -> BytesDecl<'static> {
        // Compute length of the buffer based on supplied attributes
        // ' encoding=""'   => 12
        let encoding_attr_len = if let Some(xs) = encoding {
            12 + xs.len()
        } else {
            0
        };
        // ' standalone=""' => 14
        let standalone_attr_len = if let Some(xs) = standalone {
            14 + xs.len()
        } else {
            0
        };
        // 'xml version=""' => 14
        let mut buf = Vec::with_capacity(14 + encoding_attr_len + standalone_attr_len);

        buf.extend_from_slice(b"xml version=\"");
        buf.extend_from_slice(version);

        if let Some(encoding_val) = encoding {
            buf.extend_from_slice(b"\" encoding=\"");
            buf.extend_from_slice(encoding_val);
        }

        if let Some(standalone_val) = standalone {
            buf.extend_from_slice(b"\" standalone=\"");
            buf.extend_from_slice(standalone_val);
        }
        buf.push(b'"');

        BytesDecl {
            element: BytesStart::owned(buf, 3),
        }
    }

    /// Gets the decoder struct
    pub fn encoder(&self) -> Option<&'static Encoding> {
        self.encoding()
            .and_then(|e| e.ok())
            .and_then(|e| Encoding::for_label(&*e))
    }
}

/// A struct to manage `Event::End` events
#[derive(Clone, Debug)]
pub struct BytesEnd<'a> {
    name: Cow<'a, [u8]>,
}

impl<'a> BytesEnd<'a> {
    /// Creates a new `BytesEnd` borrowing a slice
    #[inline]
    pub fn borrowed(name: &'a [u8]) -> BytesEnd<'a> {
        BytesEnd {
            name: Cow::Borrowed(name),
        }
    }

    /// Creates a new `BytesEnd` owning its name
    #[inline]
    pub fn owned(name: Vec<u8>) -> BytesEnd<'static> {
        BytesEnd {
            name: Cow::Owned(name),
        }
    }

    /// Gets `BytesEnd` event name
    #[inline]
    pub fn name(&self) -> &[u8] {
        &*self.name
    }

    /// local name (excluding namespace) as &[u8] (without eventual attributes)
    /// returns the name() with any leading namespace removed (all content up to
    /// and including the first ':' character)
    #[inline]
    pub fn local_name(&self) -> &[u8] {
        if let Some(i) = self.name().iter().position(|b| *b == b':') {
            &self.name()[i + 1..]
        } else {
            self.name()
        }
    }
}

/// A struct to manage `Event::End` events
#[derive(Clone, Debug)]
pub struct BytesText<'a> {
    content: Cow<'a, [u8]>,
}

impl<'a> BytesText<'a> {
    /// Creates a new `BytesText` borrowing a slice
    #[inline]
    pub fn borrowed(content: &'a [u8]) -> BytesText<'a> {
        BytesText {
            content: Cow::Borrowed(content),
        }
    }

    /// Creates a new `BytesText` owning its name
    #[inline]
    pub fn owned(content: Vec<u8>) -> BytesText<'static> {
        BytesText {
            content: Cow::Owned(content),
        }
    }

    /// Creates a new `BytesText` from text
    #[inline]
    pub fn from_str<S: AsRef<str>>(text: S) -> BytesText<'static> {
        let bytes = escape(text.as_ref().as_bytes()).into_owned();
        BytesText {
            content: Cow::Owned(bytes),
        }
    }

    /// gets escaped content
    ///
    /// Searches for '&' into content and try to escape the coded character if possible
    /// returns Malformed error with index within element if '&' is not followed by ';'
    pub fn unescaped(&self) -> Result<Cow<[u8]>> {
        unescape(self)
    }

    /// helper method to unescape then decode self using the reader encoding
    ///
    /// for performance reasons (could avoid allocating a `String`),
    /// it might be wiser to manually use
    /// 1. BytesText::unescaped()
    /// 2. Reader::decode(...)
    pub fn unescape_and_decode<B: BufRead>(&self, reader: &Reader<B>) -> Result<String> {
        self.unescaped().map(|e| reader.decode(&*e).into_owned())
    }

    /// Gets escaped content
    ///
    /// Searches for any of `<, >, &, ', "` and xml escapes them.
    pub fn escaped(&self) -> Cow<[u8]> {
        self.content.clone()
    }
}

/// Event to interprete node as they are parsed
#[derive(Clone, Debug)]
pub enum Event<'a> {
    /// Start tag (with attributes) <...>
    Start(BytesStart<'a>),
    /// End tag </...>
    End(BytesEnd<'a>),
    /// Empty element tag (with attributes) <.../>
    Empty(BytesStart<'a>),
    /// Data between Start and End element
    Text(BytesText<'a>),
    /// Comment <!-- ... -->
    Comment(BytesText<'a>),
    /// CData <![CDATA[...]]>
    CData(BytesText<'a>),
    /// Xml declaration <?xml ...?>
    Decl(BytesDecl<'a>),
    /// Processing instruction <?...?>
    PI(BytesText<'a>),
    /// Doctype <!DOCTYPE...>
    DocType(BytesText<'a>),
    /// Eof of file event
    Eof,
}

impl<'a> Deref for BytesStart<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.buf
    }
}

impl<'a> Deref for BytesDecl<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.element
    }
}

impl<'a> Deref for BytesEnd<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.name
    }
}

impl<'a> Deref for BytesText<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.content
    }
}

impl<'a> Deref for Event<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        match *self {
            Event::Start(ref e) | Event::Empty(ref e) => &*e,
            Event::End(ref e) => &*e,
            Event::Text(ref e) => &*e,
            Event::Decl(ref e) => &*e,
            Event::PI(ref e) => &*e,
            Event::CData(ref e) => &*e,
            Event::Comment(ref e) => &*e,
            Event::DocType(ref e) => &*e,
            Event::Eof => &[],
        }
    }
}

impl<'a> AsRef<Event<'a>> for Event<'a> {
    fn as_ref(&self) -> &Event<'a> {
        self
    }
}

#[cfg(test)]
#[test]
fn local_name() {
    use std::str::from_utf8;
    let xml = r#"
        <foo:bus attr='bar'>foobusbar</foo:bus>
        <foo: attr='bar'>foobusbar</foo:>
        <:foo attr='bar'>foobusbar</:foo>
        <foo:bus:baz attr='bar'>foobusbar</foo:bus:baz>
        "#;
    let mut rdr = Reader::from_str(xml);
    let mut buf = Vec::new();
    let mut parsed_local_names = Vec::new();
    loop {
        match rdr.read_event(&mut buf).expect("unable to read xml event") {
            Event::Start(ref e) => parsed_local_names.push(
                from_utf8(e.local_name())
                    .expect("unable to build str from local_name")
                    .to_string(),
            ),
            Event::End(ref e) => parsed_local_names.push(
                from_utf8(e.local_name())
                    .expect("unable to build str from local_name")
                    .to_string(),
            ),
            Event::Eof => break,
            _ => {}
        }
    }
    assert_eq!(parsed_local_names[0], "bus".to_string());
    assert_eq!(parsed_local_names[1], "bus".to_string());
    assert_eq!(parsed_local_names[2], "".to_string());
    assert_eq!(parsed_local_names[3], "".to_string());
    assert_eq!(parsed_local_names[4], "foo".to_string());
    assert_eq!(parsed_local_names[5], "foo".to_string());
    assert_eq!(parsed_local_names[6], "bus:baz".to_string());
    assert_eq!(parsed_local_names[7], "bus:baz".to_string());
}
<|endoftext|>


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

//! This module contains types and functions to support formatting specific line ranges.

use std::{cmp, iter, path, str};

use itertools::Itertools;
use multimap::MultiMap;
use serde_json as json;

use codemap::LineRange;

/// A range that is inclusive of both ends.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize)]
struct Range {
    pub lo: usize,
    pub hi: usize,
}

impl<'a> From<&'a LineRange> for Range {
    fn from(range: &'a LineRange) -> Range {
        Range::new(range.lo, range.hi)
    }
}

impl Range {
    fn new(lo: usize, hi: usize) -> Range {
        Range { lo: lo, hi: hi }
    }

    fn is_empty(self) -> bool {
        self.lo > self.hi
    }

    fn contains(self, other: Range) -> bool {
        if other.is_empty() {
            true
        } else {
            !self.is_empty() && self.lo <= other.lo && self.hi >= other.hi
        }
    }

    fn intersects(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            (self.lo <= other.hi && other.hi <= self.hi) ||
            (other.lo <= self.hi && self.hi <= other.hi)
        }
    }

    fn adjacent_to(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            self.hi + 1 == other.lo || other.hi + 1 == self.lo
        }
    }

    /// Returns a new `Range` with lines from `self` and `other` if they were adjacent or
    /// intersect; returns `None` otherwise.
    fn merge(self, other: Range) -> Option<Range> {
        if self.adjacent_to(other) || self.intersects(other) {
            Some(Range::new(cmp::min(self.lo, other.lo), cmp::max(self.hi, other.hi)))
        } else {
            None
        }
    }
}

/// A set of lines in files.
///
/// It is represented as a multimap keyed on file names, with values a collection of
/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all
/// lines in all files.
#[derive(Clone, Debug, Default)]
pub struct FileLines(Option<MultiMap<String, Range>>);

/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping,
/// and ordered by their start point.
fn normalize_ranges(map: &mut MultiMap<String, Range>) {
    for (_, ranges) in map.iter_all_mut() {
        ranges.sort_by_key(|x| x.lo);
        let merged = ranges
            .drain(..)
            .coalesce(|x, y| x.merge(y).ok_or((x, y)))
            .collect();
        *ranges = merged;
    }
}

impl FileLines {
    /// Creates a `FileLines` that contains all lines in all files.
    pub fn all() -> FileLines {
        FileLines(None)
    }

    /// Creates a `FileLines` from a `MultiMap`, ensuring that the invariants hold.
    fn from_multimap(map: MultiMap<String, Range>) -> FileLines {
        let mut map = map;
        normalize_ranges(&mut map);
        FileLines(Some(map))
    }

    /// Returns an iterator over the files contained in `self`.
    pub fn files(&self) -> Files {
        Files(self.0.as_ref().map(MultiMap::keys))
    }

    /// Returns true if `range` is fully contained in `self`.
    pub fn contains(&self, range: &LineRange) -> bool {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match canonicalize_path_string(range.file_name())
                  .and_then(|canonical| map.get_vec(&canonical).ok_or(())) {
            Ok(ranges) => ranges.iter().any(|r| r.contains(Range::from(range))),
            Err(_) => false,
        }
    }

    /// Returns true if any lines in `range` are in `self`.
    pub fn intersects(&self, range: &LineRange) -> bool {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match canonicalize_path_string(range.file_name())
                  .and_then(|canonical| map.get_vec(&canonical).ok_or(())) {
            Ok(ranges) => ranges.iter().any(|r| r.intersects(Range::from(range))),
            Err(_) => false,
        }
    }

    pub fn contains_line(&self, file_name: &str, line: usize) -> bool {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match canonicalize_path_string(file_name)
                  .and_then(|canonical| map.get_vec(&canonical).ok_or(())) {
            Ok(ranges) => ranges.iter().any(|r| r.lo <= line && r.hi >= line),
            Err(_) => false,
        }
    }

    pub fn intersects_range(&self, file_name: &str, lo: usize, hi: usize) -> bool {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match canonicalize_path_string(file_name)
                  .and_then(|canonical| map.get_vec(&canonical).ok_or(())) {
            Ok(ranges) => ranges.iter().any(|r| r.intersects(Range::new(lo, hi))),
            Err(_) => false,
        }
    }
}

/// FileLines files iterator.
pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, String, Vec<Range>>>);

impl<'a> iter::Iterator for Files<'a> {
    type Item = &'a String;

    fn next(&mut self) -> Option<&'a String> {
        self.0.as_mut().and_then(Iterator::next)
    }
}

fn canonicalize_path_string(s: &str) -> Result<String, ()> {
    if s == "stdin" {
        return Ok(s.to_string());
    }

    match path::PathBuf::from(s).canonicalize() {
        Ok(canonicalized) => canonicalized.to_str().map(|s| s.to_string()).ok_or(()),
        _ => Err(()),
    }
}

// This impl is needed for `Config::override_value` to work for use in tests.
impl str::FromStr for FileLines {
    type Err = String;

    fn from_str(s: &str) -> Result<FileLines, String> {
        let v: Vec<JsonSpan> = try!(json::from_str(s).map_err(|e| e.to_string()));
        let m = try!(v.into_iter().map(JsonSpan::into_tuple).collect());
        Ok(FileLines::from_multimap(m))
    }
}

// For JSON decoding.
#[derive(Clone, Debug, Deserialize)]
struct JsonSpan {
    file: String,
    range: (usize, usize),
}

impl JsonSpan {
    // To allow `collect()`ing into a `MultiMap`.
    fn into_tuple(self) -> Result<(String, Range), String> {
        let (lo, hi) = self.range;
        let canonical = try!(canonicalize_path_string(&self.file)
                                 .map_err(|_| format!("Can't canonicalize {}", &self.file)));
        Ok((canonical, Range::new(lo, hi)))
    }
}

// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation
// for `FileLines`, so it will just panic instead.
impl<'de> ::serde::de::Deserialize<'de> for FileLines {
    fn deserialize<D>(_: D) -> Result<Self, D::Error>
        where D: ::serde::de::Deserializer<'de>
    {
        panic!("FileLines cannot be deserialized from a project rustfmt.toml file: please \
                specify it via the `--file-lines` option instead");
    }
}

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

    #[test]
    fn test_range_intersects() {
        assert!(Range::new(1, 2).intersects(Range::new(1, 1)));
        assert!(Range::new(1, 2).intersects(Range::new(2, 2)));
        assert!(!Range::new(1, 2).intersects(Range::new(0, 0)));
        assert!(!Range::new(1, 2).intersects(Range::new(3, 10)));
        assert!(!Range::new(1, 3).intersects(Range::new(5, 5)));
    }

    #[test]
    fn test_range_adjacent_to() {
        assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1)));
        assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10)));
        assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5)));
    }

    #[test]
    fn test_range_contains() {
        assert!(Range::new(1, 2).contains(Range::new(1, 1)));
        assert!(Range::new(1, 2).contains(Range::new(2, 2)));
        assert!(!Range::new(1, 2).contains(Range::new(0, 0)));
        assert!(!Range::new(1, 2).contains(Range::new(3, 10)));
    }

    #[test]
    fn test_range_merge() {
        assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5)));
        assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(4, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(5, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(6, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 7).merge(Range::new(4, 5)));
    }
}
<commit_msg>Refactor file range checking<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module contains types and functions to support formatting specific line ranges.

use std::{cmp, iter, path, str};

use itertools::Itertools;
use multimap::MultiMap;
use serde_json as json;

use codemap::LineRange;

/// A range that is inclusive of both ends.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize)]
struct Range {
    pub lo: usize,
    pub hi: usize,
}

impl<'a> From<&'a LineRange> for Range {
    fn from(range: &'a LineRange) -> Range {
        Range::new(range.lo, range.hi)
    }
}

impl Range {
    fn new(lo: usize, hi: usize) -> Range {
        Range { lo: lo, hi: hi }
    }

    fn is_empty(self) -> bool {
        self.lo > self.hi
    }

    fn contains(self, other: Range) -> bool {
        if other.is_empty() {
            true
        } else {
            !self.is_empty() && self.lo <= other.lo && self.hi >= other.hi
        }
    }

    fn intersects(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            (self.lo <= other.hi && other.hi <= self.hi) ||
            (other.lo <= self.hi && self.hi <= other.hi)
        }
    }

    fn adjacent_to(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            self.hi + 1 == other.lo || other.hi + 1 == self.lo
        }
    }

    /// Returns a new `Range` with lines from `self` and `other` if they were adjacent or
    /// intersect; returns `None` otherwise.
    fn merge(self, other: Range) -> Option<Range> {
        if self.adjacent_to(other) || self.intersects(other) {
            Some(Range::new(cmp::min(self.lo, other.lo), cmp::max(self.hi, other.hi)))
        } else {
            None
        }
    }
}

/// A set of lines in files.
///
/// It is represented as a multimap keyed on file names, with values a collection of
/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all
/// lines in all files.
#[derive(Clone, Debug, Default)]
pub struct FileLines(Option<MultiMap<String, Range>>);

/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping,
/// and ordered by their start point.
fn normalize_ranges(map: &mut MultiMap<String, Range>) {
    for (_, ranges) in map.iter_all_mut() {
        ranges.sort_by_key(|x| x.lo);
        let merged = ranges
            .drain(..)
            .coalesce(|x, y| x.merge(y).ok_or((x, y)))
            .collect();
        *ranges = merged;
    }
}

impl FileLines {
    /// Creates a `FileLines` that contains all lines in all files.
    pub fn all() -> FileLines {
        FileLines(None)
    }

    /// Creates a `FileLines` from a `MultiMap`, ensuring that the invariants hold.
    fn from_multimap(map: MultiMap<String, Range>) -> FileLines {
        let mut map = map;
        normalize_ranges(&mut map);
        FileLines(Some(map))
    }

    /// Returns an iterator over the files contained in `self`.
    pub fn files(&self) -> Files {
        Files(self.0.as_ref().map(MultiMap::keys))
    }

    /// Returns true if `self` includes all lines in all files. Otherwise runs `f` on all ranges in
    /// the designated file (if any) and returns true if `f` ever does.
    fn file_range_matches<F>(&self, file_name: &str, f: F) -> bool
        where F: FnMut(&Range) -> bool
    {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match canonicalize_path_string(file_name).and_then(|file| map.get_vec(&file).ok_or(())) {
            Ok(ranges) => ranges.iter().any(f),
            Err(_) => false,
        }
    }

    /// Returns true if `range` is fully contained in `self`.
    pub fn contains(&self, range: &LineRange) -> bool {
        self.file_range_matches(range.file_name(), |r| r.contains(Range::from(range)))
    }

    /// Returns true if any lines in `range` are in `self`.
    pub fn intersects(&self, range: &LineRange) -> bool {
        self.file_range_matches(range.file_name(), |r| r.intersects(Range::from(range)))
    }

    /// Returns true if `line` from `file_name` is in `self`.
    pub fn contains_line(&self, file_name: &str, line: usize) -> bool {
        self.file_range_matches(file_name, |r| r.lo <= line && r.hi >= line)
    }

    /// Returns true if any of the lines between `lo` and `hi` from `file_name` are in `self`.
    pub fn intersects_range(&self, file_name: &str, lo: usize, hi: usize) -> bool {
        self.file_range_matches(file_name, |r| r.intersects(Range::new(lo, hi)))
    }
}

/// FileLines files iterator.
pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, String, Vec<Range>>>);

impl<'a> iter::Iterator for Files<'a> {
    type Item = &'a String;

    fn next(&mut self) -> Option<&'a String> {
        self.0.as_mut().and_then(Iterator::next)
    }
}

fn canonicalize_path_string(s: &str) -> Result<String, ()> {
    if s == "stdin" {
        return Ok(s.to_string());
    }

    match path::PathBuf::from(s).canonicalize() {
        Ok(canonicalized) => canonicalized.to_str().map(|s| s.to_string()).ok_or(()),
        _ => Err(()),
    }
}

// This impl is needed for `Config::override_value` to work for use in tests.
impl str::FromStr for FileLines {
    type Err = String;

    fn from_str(s: &str) -> Result<FileLines, String> {
        let v: Vec<JsonSpan> = try!(json::from_str(s).map_err(|e| e.to_string()));
        let m = try!(v.into_iter().map(JsonSpan::into_tuple).collect());
        Ok(FileLines::from_multimap(m))
    }
}

// For JSON decoding.
#[derive(Clone, Debug, Deserialize)]
struct JsonSpan {
    file: String,
    range: (usize, usize),
}

impl JsonSpan {
    // To allow `collect()`ing into a `MultiMap`.
    fn into_tuple(self) -> Result<(String, Range), String> {
        let (lo, hi) = self.range;
        let canonical = try!(canonicalize_path_string(&self.file)
                                 .map_err(|_| format!("Can't canonicalize {}", &self.file)));
        Ok((canonical, Range::new(lo, hi)))
    }
}

// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation
// for `FileLines`, so it will just panic instead.
impl<'de> ::serde::de::Deserialize<'de> for FileLines {
    fn deserialize<D>(_: D) -> Result<Self, D::Error>
        where D: ::serde::de::Deserializer<'de>
    {
        panic!("FileLines cannot be deserialized from a project rustfmt.toml file: please \
                specify it via the `--file-lines` option instead");
    }
}

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

    #[test]
    fn test_range_intersects() {
        assert!(Range::new(1, 2).intersects(Range::new(1, 1)));
        assert!(Range::new(1, 2).intersects(Range::new(2, 2)));
        assert!(!Range::new(1, 2).intersects(Range::new(0, 0)));
        assert!(!Range::new(1, 2).intersects(Range::new(3, 10)));
        assert!(!Range::new(1, 3).intersects(Range::new(5, 5)));
    }

    #[test]
    fn test_range_adjacent_to() {
        assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1)));
        assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10)));
        assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5)));
    }

    #[test]
    fn test_range_contains() {
        assert!(Range::new(1, 2).contains(Range::new(1, 1)));
        assert!(Range::new(1, 2).contains(Range::new(2, 2)));
        assert!(!Range::new(1, 2).contains(Range::new(0, 0)));
        assert!(!Range::new(1, 2).contains(Range::new(3, 10)));
    }

    #[test]
    fn test_range_merge() {
        assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5)));
        assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(4, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(5, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(6, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 7).merge(Range::new(4, 5)));
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::str::{self, FromStr};
use std::io::{Error as IOError, ErrorKind as IOErrorKind, Read};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::result::Result;
use std::sync::Mutex;
use std::error::Error;

use regex::Regex;

use futures::{self, Future};
use hyper::header::{ContentType, Location};
use hyper::mime;

use hyper::{self, Error as HyperError, Method, Request, Response, StatusCode, Uri};
use hyper::server::{Http, NewService, Server, Service};
use hyper_staticfile::Static;

use tokio_core::reactor::Handle;

use super::*;

lazy_static! {
    pub static ref APP_ROUTE: Regex = Regex::new(r"app/(?P<app>[a-zA-Z0-9_]+)(?P<path>/*(.*))").unwrap();
    pub static ref APP_RESOURCE_ROUTE: Regex = Regex::new(r"resources/app/(?P<app>[a-zA-Z0-9_]+)(?P<path>/*(.*))").unwrap();
}

#[derive(Debug)]
pub struct DefaultServiceFactory {
    render_service_factory: TemplateRenderServiceFactory,
    resource_service_factory: TemplateResourceServiceFactory,
    handle: Handle,
}

impl DefaultServiceFactory {
    pub fn new(
        render_service_factory: TemplateRenderServiceFactory,
        resource_service_factory: TemplateResourceServiceFactory,
        handle: Handle,
    ) -> Self {
        DefaultServiceFactory {
            render_service_factory: render_service_factory,
            resource_service_factory: resource_service_factory,
            handle: handle,
        }
    }
}

impl NewService for DefaultServiceFactory {
    type Request = <Self::Instance as Service>::Request;
    type Response = <Self::Instance as Service>::Response;
    type Error = <Self::Instance as Service>::Error;
    type Instance = DefaultService;

    fn new_service(&self) -> Result<Self::Instance, io::Error> {
        let render_service = self.render_service_factory.create();
        let resource_service = self.resource_service_factory.create();

        Ok(DefaultService {
            render_service: render_service,
            resource_service: resource_service,
            handle: self.handle.clone(),
        })
    }
}

#[derive(Debug)]
pub struct DefaultService {
    render_service: TemplateRenderService,
    resource_service: TemplateResourceService,
    handle: Handle,
}

impl Service for DefaultService {
    type Request = Request;
    type Response = Response;
    type Error = HyperError;
    type Future = Box<Future<Item = Response, Error = Self::Error>>;

    fn call(&self, req: Self::Request) -> Self::Future {
        let original_path = req.path().to_owned();
        let trimmed_path = req.path().trim_left_matches('/').to_owned();

        // let default_app_str = default_app.to_string_lossy().to_string();
        // let default_app_trimmed = default_app_str.trim_left_matches('/').to_owned();
        // let default_app_trailing = format!("{}/", default_app_trimmed);
        let default_workspace_str = format!("/app/{}/preview-1bcx1", &*DEFAULT_APP);

        // Redirect to default app
        if (trimmed_path == "") {
            let response = Response::new()
                .with_status(StatusCode::Found)
                .with_header(Location::new(format!("/app/{}/", &*DEFAULT_APP)));

            return Box::new(future::ok(response));
        };

        eprintln!("Request uri: {:?}", req.uri());

        let host = req.headers().get_raw("host").unwrap().one().unwrap();
        let host = str::from_utf8(&host).unwrap();

        let mut proto = "http";
        if let Some(forwarded_proto) = req.headers().get_raw("x-forwarded-proto").and_then(|s| s.one()).and_then(|s| str::from_utf8(s).ok()) {
            if forwarded_proto.to_lowercase() == "https" {
                proto = "https";
            };
        };

        let base_url = format!("{}://{}/", proto, host);

        if let Some(captures) = APP_RESOURCE_ROUTE.captures(&trimmed_path) {
            let app_name = captures.name("app").unwrap().as_str().to_owned();

            // let (scheme, authority) = (req.uri().scheme().unwrap(), req.uri().authority().unwrap());
            // let base_url = format!("{}://{}/app/{}/", scheme, authority, app_name);
            let base_url = format!("{}app/{}/", base_url, app_name);

            eprintln!("Base uri: {:?}", base_url);

            let path = captures.name("path").map(|m| m.as_str()).unwrap_or_default();
            let path = if path == "" { "/" } else { path }.to_owned();
            let trimmed_resource_path = path.trim_left_matches('/').to_owned();

            // Handle resource file case
            let app_resource_path = &*APP_DIR.join(&app_name).join(&trimmed_resource_path);

            // Serve resource
            if app_resource_path.is_file() {
                let resource_path = format!("/{}/{}", app_name, trimmed_resource_path);
                let resource_req = Request::new(Method::Get, FromStr::from_str(&resource_path).unwrap());
                let response = self.resource_service
                    .call(&base_url, &app_name, resource_req);
                return Box::new(response);
            };
        }

        if let Some(captures) = APP_ROUTE.captures(&trimmed_path) {
            // let captures: Vec<_> = captures.into_iter().collect();
            let app_name = captures.name("app").unwrap().as_str().to_owned();

            // let (scheme, authority) = (req.uri().scheme().unwrap(), req.uri().authority().unwrap());
            // let base_url = format!("{}://{}/app/{}/", scheme, authority, app_name);
            let base_url = format!("{}resources/app/{}/", base_url, app_name);

            eprintln!("Base uri: {:?}", base_url);

            let path = captures.name("path").map(|m| m.as_str()).unwrap_or_default();
            let path = if path == "" { "/" } else { path }.to_owned();
            let trimmed_path_in_app = path.trim_left_matches('/').to_owned();

            eprintln!("[default service] requested path [{:?}] in app {}", path, app_name);
            let trimmed_relative_path = path.trim_left_matches('/').to_owned();

            // Handle resource file case
            let app_resource_path = &*APP_DIR.join(&app_name).join(&trimmed_path_in_app);

            // Serve resource
            if app_resource_path.is_file() {
                let resource_path = format!("/{}/{}", app_name, trimmed_path_in_app);
                let resource_req = Request::new(Method::Get, FromStr::from_str(&resource_path).unwrap());
                let response = self.resource_service
                    .call(&base_url, &app_name, resource_req);
                return Box::new(response);
            };

            // Render route
            // let template_path = if path == "/" { "/app.ism".to_owned() } else { path }.to_owned();
            let isymtope_req = Request::new(Method::Get, FromStr::from_str(&path).unwrap());
            let response = self.render_service
                .call(&base_url, &app_name, isymtope_req);
            return Box::new(response);
        };

        // Return not found error
        let response = hyper::Response::new()
            .with_status(StatusCode::NotFound)
            .with_body("Resource not found");
        Box::new(future::ok(response))
    }
}
<commit_msg>Print forwarded proto<commit_after>use std::env;
use std::str::{self, FromStr};
use std::io::{Error as IOError, ErrorKind as IOErrorKind, Read};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::result::Result;
use std::sync::Mutex;
use std::error::Error;

use regex::Regex;

use futures::{self, Future};
use hyper::header::{ContentType, Location};
use hyper::mime;

use hyper::{self, Error as HyperError, Method, Request, Response, StatusCode, Uri};
use hyper::server::{Http, NewService, Server, Service};
use hyper_staticfile::Static;

use tokio_core::reactor::Handle;

use super::*;

lazy_static! {
    pub static ref APP_ROUTE: Regex = Regex::new(r"app/(?P<app>[a-zA-Z0-9_]+)(?P<path>/*(.*))").unwrap();
    pub static ref APP_RESOURCE_ROUTE: Regex = Regex::new(r"resources/app/(?P<app>[a-zA-Z0-9_]+)(?P<path>/*(.*))").unwrap();
}

#[derive(Debug)]
pub struct DefaultServiceFactory {
    render_service_factory: TemplateRenderServiceFactory,
    resource_service_factory: TemplateResourceServiceFactory,
    handle: Handle,
}

impl DefaultServiceFactory {
    pub fn new(
        render_service_factory: TemplateRenderServiceFactory,
        resource_service_factory: TemplateResourceServiceFactory,
        handle: Handle,
    ) -> Self {
        DefaultServiceFactory {
            render_service_factory: render_service_factory,
            resource_service_factory: resource_service_factory,
            handle: handle,
        }
    }
}

impl NewService for DefaultServiceFactory {
    type Request = <Self::Instance as Service>::Request;
    type Response = <Self::Instance as Service>::Response;
    type Error = <Self::Instance as Service>::Error;
    type Instance = DefaultService;

    fn new_service(&self) -> Result<Self::Instance, io::Error> {
        let render_service = self.render_service_factory.create();
        let resource_service = self.resource_service_factory.create();

        Ok(DefaultService {
            render_service: render_service,
            resource_service: resource_service,
            handle: self.handle.clone(),
        })
    }
}

#[derive(Debug)]
pub struct DefaultService {
    render_service: TemplateRenderService,
    resource_service: TemplateResourceService,
    handle: Handle,
}

impl Service for DefaultService {
    type Request = Request;
    type Response = Response;
    type Error = HyperError;
    type Future = Box<Future<Item = Response, Error = Self::Error>>;

    fn call(&self, req: Self::Request) -> Self::Future {
        let original_path = req.path().to_owned();
        let trimmed_path = req.path().trim_left_matches('/').to_owned();

        // let default_app_str = default_app.to_string_lossy().to_string();
        // let default_app_trimmed = default_app_str.trim_left_matches('/').to_owned();
        // let default_app_trailing = format!("{}/", default_app_trimmed);
        let default_workspace_str = format!("/app/{}/preview-1bcx1", &*DEFAULT_APP);

        // Redirect to default app
        if (trimmed_path == "") {
            let response = Response::new()
                .with_status(StatusCode::Found)
                .with_header(Location::new(format!("/app/{}/", &*DEFAULT_APP)));

            return Box::new(future::ok(response));
        };

        eprintln!("Request uri: {:?}", req.uri());

        let host = req.headers().get_raw("host").unwrap().one().unwrap();
        let host = str::from_utf8(&host).unwrap();

        let forwarded_proto = req.headers().get_raw("x-forwarded-proto").and_then(|s| s.one()).and_then(|s| str::from_utf8(s).ok());
        eprintln!("Forwarded proto: {:?}", forwarded_proto);

        let mut proto = "http";
        if let Some(forwarded_proto) = forwarded_proto {
            if forwarded_proto.to_lowercase() == "https" {
                proto = "https";
            };
        };

        let base_url = format!("{}://{}/", proto, host);

        if let Some(captures) = APP_RESOURCE_ROUTE.captures(&trimmed_path) {
            let app_name = captures.name("app").unwrap().as_str().to_owned();

            // let (scheme, authority) = (req.uri().scheme().unwrap(), req.uri().authority().unwrap());
            // let base_url = format!("{}://{}/app/{}/", scheme, authority, app_name);
            let base_url = format!("{}app/{}/", base_url, app_name);

            eprintln!("Base uri: {:?}", base_url);

            let path = captures.name("path").map(|m| m.as_str()).unwrap_or_default();
            let path = if path == "" { "/" } else { path }.to_owned();
            let trimmed_resource_path = path.trim_left_matches('/').to_owned();

            // Handle resource file case
            let app_resource_path = &*APP_DIR.join(&app_name).join(&trimmed_resource_path);

            // Serve resource
            if app_resource_path.is_file() {
                let resource_path = format!("/{}/{}", app_name, trimmed_resource_path);
                let resource_req = Request::new(Method::Get, FromStr::from_str(&resource_path).unwrap());
                let response = self.resource_service
                    .call(&base_url, &app_name, resource_req);
                return Box::new(response);
            };
        }

        if let Some(captures) = APP_ROUTE.captures(&trimmed_path) {
            // let captures: Vec<_> = captures.into_iter().collect();
            let app_name = captures.name("app").unwrap().as_str().to_owned();

            // let (scheme, authority) = (req.uri().scheme().unwrap(), req.uri().authority().unwrap());
            // let base_url = format!("{}://{}/app/{}/", scheme, authority, app_name);
            let base_url = format!("{}resources/app/{}/", base_url, app_name);

            eprintln!("Base uri: {:?}", base_url);

            let path = captures.name("path").map(|m| m.as_str()).unwrap_or_default();
            let path = if path == "" { "/" } else { path }.to_owned();
            let trimmed_path_in_app = path.trim_left_matches('/').to_owned();

            eprintln!("[default service] requested path [{:?}] in app {}", path, app_name);
            let trimmed_relative_path = path.trim_left_matches('/').to_owned();

            // Handle resource file case
            let app_resource_path = &*APP_DIR.join(&app_name).join(&trimmed_path_in_app);

            // Serve resource
            if app_resource_path.is_file() {
                let resource_path = format!("/{}/{}", app_name, trimmed_path_in_app);
                let resource_req = Request::new(Method::Get, FromStr::from_str(&resource_path).unwrap());
                let response = self.resource_service
                    .call(&base_url, &app_name, resource_req);
                return Box::new(response);
            };

            // Render route
            // let template_path = if path == "/" { "/app.ism".to_owned() } else { path }.to_owned();
            let isymtope_req = Request::new(Method::Get, FromStr::from_str(&path).unwrap());
            let response = self.render_service
                .call(&base_url, &app_name, isymtope_req);
            return Box::new(response);
        };

        // Return not found error
        let response = hyper::Response::new()
            .with_status(StatusCode::NotFound)
            .with_body("Resource not found");
        Box::new(future::ok(response))
    }
}
<|endoftext|>