<commit_before>use std::env;
use std::fmt;
use std::io;
use std::path::Path;
use std::process::{Command, Output};
use std::str;

use anyhow::{bail, format_err, Error, Result};
use console::strip_ansi_codes;
use glob::{glob_with, MatchOptions};
// use serde::de::{Deserialize, Deserializer, Error as DeError};
use if_chain::if_chain;
use serde::de;

use crate::utils::releases::{get_xcode_release_name, infer_gradle_release_name};
use crate::utils::xcode::{InfoPlist, XcodeProjectInfo};

#[cfg(not(windows))]
static APPCENTER_BIN_PATH: &str = "appcenter";
#[cfg(not(windows))]
static APPCENTER_NPM_PATH: &str = "node_modules/.bin/appcenter";

#[cfg(windows)]
static APPCENTER_BIN_PATH: &str = "appcenter.cmd";
#[cfg(windows)]
static APPCENTER_NPM_PATH: &str = "node_modules/.bin/appcenter.cmd";

static APPCENTER_NOT_FOUND: &str = "AppCenter CLI not found

Install with `npm install -g appcenter-cli` and make sure it is on the PATH.";

#[derive(Debug)]
pub struct AppCenterPackage {
    pub label: String,
}

impl<'de> de::Deserialize<'de> for AppCenterPackage {
    fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        struct PackageVisitor;

        impl<'de> de::Visitor<'de> for PackageVisitor {
            type Value = AppCenterPackage;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("a deployment history entry")
            }

            fn visit_seq<S: de::SeqAccess<'de>>(
                self,
                mut seq: S,
            ) -> Result<AppCenterPackage, S::Error> {
                // Since we only need the package label, we can deserialize the JSON string very
                // efficiently by only looking at the first element.
                let label = seq
                    .next_element()?
                    .ok_or_else(|| de::Error::custom("missing package label"))?;

                // Drain the sequence, ignoring all other values.
                while seq.next_element::<de::IgnoredAny>()?.is_some() {}

                Ok(AppCenterPackage { label })
            }
        }

        deserializer.deserialize_seq(PackageVisitor)
    }
}

pub fn get_appcenter_error(output: &Output) -> Error {
    let message = str::from_utf8(&output.stdout).unwrap_or("Unknown AppCenter error");

    let stripped = strip_ansi_codes(message);
    let cause = if let Some(rest) = stripped.strip_prefix("Error: ") {
        rest
    } else {
        &stripped
    }
    .to_string();

    format_err!(cause)
}

pub fn get_appcenter_deployment_history(
    app: &str,
    deployment: &str,
) -> Result<Vec<AppCenterPackage>> {
    let appcenter_bin = if Path::new(APPCENTER_NPM_PATH).exists() {
        APPCENTER_NPM_PATH
    } else {
        APPCENTER_BIN_PATH
    };

    let output = Command::new(appcenter_bin)
        .arg("codepush")
        .arg("deployment")
        .arg("history")
        .arg(deployment)
        .arg("--app")
        .arg(app)
        .arg("--output")
        .arg("json")
        .output()
        .map_err(|e| match e.kind() {
            io::ErrorKind::NotFound => Error::msg(APPCENTER_NOT_FOUND),
            _ => Error::from(e).context("Failed to run AppCenter CLI"),
        })?;

    if output.status.success() {
        Ok(serde_json::from_slice(&output.stdout).unwrap_or_else(|_| {
            let format_err = format!("Command `{} codepush deployment history {} --app {} --output json` failed to produce a valid JSON output.", appcenter_bin, deployment, app);
            panic!("{}", format_err);
        }))
    } else {
        Err(get_appcenter_error(&output).context("Failed to load AppCenter deployment history"))
    }
}

pub fn get_appcenter_package(app: &str, deployment: &str) -> Result<AppCenterPackage> {
    let history = get_appcenter_deployment_history(app, deployment)?;
    if let Some(latest) = history.into_iter().last() {
        Ok(latest)
    } else {
        bail!("Could not find deployment {} for {}", deployment, app);
    }
}

pub fn get_react_native_appcenter_release(
    package: &AppCenterPackage,
    platform: &str,
    bundle_id_override: Option<&str>,
    version_name_override: Option<&str>,
    release_name_override: Option<&str>,
) -> Result<String> {
    let bundle_id_ovrr = bundle_id_override.unwrap_or("");
    let version_name_ovrr = version_name_override.unwrap_or("");
    let release_name_ovrr = release_name_override.unwrap_or("");

    if !release_name_ovrr.is_empty() {
        return Ok(release_name_ovrr.to_string());
    }

    if !bundle_id_ovrr.is_empty() && !version_name_ovrr.is_empty() {
        return Ok(format!(
            "{}@{}+codepush:{}",
            bundle_id_ovrr, version_name_ovrr, package.label
        ));
    }

    if platform == "ios" {
        if !cfg!(target_os = "macos") {
            bail!("AppCenter codepush releases for iOS require macOS if no bundle ID and version name are specified");
        }

        let mut opts = MatchOptions::new();
        opts.case_sensitive = false;

        for entry in (glob_with("ios/*.xcodeproj", opts)?).flatten() {
            let pi = XcodeProjectInfo::from_path(&entry)?;
            if let Some(ipl) = InfoPlist::from_project_info(&pi)? {
                if let Some(release_name) = get_xcode_release_name(Some(ipl))? {
                    let vec: Vec<&str> = release_name.split('@').collect();
                    let bundle_id = if bundle_id_ovrr.is_empty() {
                        vec[0]
                    } else {
                        bundle_id_ovrr
                    };
                    let version_name = if version_name_ovrr.is_empty() {
                        vec[1]
                    } else {
                        version_name_ovrr
                    };
                    return Ok(format!(
                        "{}@{}+codepush:{}",
                        bundle_id, version_name, package.label
                    ));
                }
            }
        }

        bail!("Could not find plist");
    } else if platform == "android" {
        if_chain! {
            if let Ok(here) = env::current_dir();
            if let Ok(android_folder) = here.join("android").metadata();
            if android_folder.is_dir();
            then {
                if let Some(release_name) = infer_gradle_release_name(Some(here.join("android")))? {
                    let vec: Vec<&str> = release_name.split('@').collect();
                    let bundle_id = if bundle_id_ovrr.is_empty() { vec[0] } else { bundle_id_ovrr };
                    let version_name = if version_name_ovrr.is_empty() { vec[1] } else { version_name_ovrr };
                    return Ok(format!("{}@{}+codepush:{}", bundle_id, version_name, package.label));
                } else {
                    bail!("Could not parse app id from build.gradle");
                }
            }
        }

        bail!("Could not find AndroidManifest.xml");
    }

    bail!("Unsupported platform '{}'", platform);
}
<commit_msg>ref: Log correct AppCenter error message<commit_after>use std::env;
use std::fmt;
use std::io;
use std::path::Path;
use std::process::{Command, Output};
use std::str;

use anyhow::{bail, format_err, Error, Result};
use console::strip_ansi_codes;
use glob::{glob_with, MatchOptions};
use if_chain::if_chain;
use serde::de;
use serde::Deserialize;

use crate::utils::releases::{get_xcode_release_name, infer_gradle_release_name};
use crate::utils::xcode::{InfoPlist, XcodeProjectInfo};

#[cfg(not(windows))]
static APPCENTER_BIN_PATH: &str = "appcenter";
#[cfg(not(windows))]
static APPCENTER_NPM_PATH: &str = "node_modules/.bin/appcenter";

#[cfg(windows)]
static APPCENTER_BIN_PATH: &str = "appcenter.cmd";
#[cfg(windows)]
static APPCENTER_NPM_PATH: &str = "node_modules/.bin/appcenter.cmd";

static APPCENTER_NOT_FOUND: &str = "AppCenter CLI not found

Install with `npm install -g appcenter-cli` and make sure it is on the PATH.";

#[derive(Debug)]
pub struct AppCenterPackage {
    pub label: String,
}

#[derive(Debug, Deserialize)]
pub struct AppCenterOutput {
    #[serde(rename = "errorMessage")]
    pub error_message: String,
}

impl<'de> de::Deserialize<'de> for AppCenterPackage {
    fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        struct PackageVisitor;

        impl<'de> de::Visitor<'de> for PackageVisitor {
            type Value = AppCenterPackage;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("a deployment history entry")
            }

            fn visit_seq<S: de::SeqAccess<'de>>(
                self,
                mut seq: S,
            ) -> Result<AppCenterPackage, S::Error> {
                // Since we only need the package label, we can deserialize the JSON string very
                // efficiently by only looking at the first element.
                let label = seq
                    .next_element()?
                    .ok_or_else(|| de::Error::custom("missing package label"))?;

                // Drain the sequence, ignoring all other values.
                while seq.next_element::<de::IgnoredAny>()?.is_some() {}

                Ok(AppCenterPackage { label })
            }
        }

        deserializer.deserialize_seq(PackageVisitor)
    }
}

// AppCenter CLI can throw errors in 2 different formats, based on the `--output` flag,
// and we want to handle them both (we call it with `--output json` ourselves).
//
// JSON: `{"succeeded":false,"errorCode":5,"errorMessage":"Command 'appcenter codepush deployment history' requires a logged in user. Use the 'appcenter login' command to log in."}`
// Text: `Error: Command 'appcenter codepush deployment history' requires a logged in user. Use the 'appcenter login' command to log in.`
//
// Also, starting version 2.10.8 (2022-01-10), it prints to `stderr`, where it used to use `stdout` before.
// ref: https://github.com/microsoft/appcenter-cli/commit/b3d6290afcb84affe6a4096893b1ea11d10ac3cf
pub fn get_appcenter_error(output: &Output) -> Error {
    let cause = serde_json::from_slice::<AppCenterOutput>(&output.stderr)
        .map(|o| o.error_message)
        .unwrap_or_else(|_| {
            str::from_utf8(&output.stderr)
                .map(|o| {
                    let stripped = strip_ansi_codes(o);
                    if let Some(rest) = stripped.strip_prefix("Error: ") {
                        rest.to_string()
                    } else {
                        stripped.to_string()
                    }
                })
                .unwrap_or_else(|_| "Unknown AppCenter error".to_string())
        });

    format_err!(cause)
}

pub fn get_appcenter_deployment_history(
    app: &str,
    deployment: &str,
) -> Result<Vec<AppCenterPackage>> {
    let appcenter_bin = if Path::new(APPCENTER_NPM_PATH).exists() {
        APPCENTER_NPM_PATH
    } else {
        APPCENTER_BIN_PATH
    };

    let output = Command::new(appcenter_bin)
        .arg("codepush")
        .arg("deployment")
        .arg("history")
        .arg(deployment)
        .arg("--app")
        .arg(app)
        .arg("--output")
        .arg("json")
        .output()
        .map_err(|e| match e.kind() {
            io::ErrorKind::NotFound => Error::msg(APPCENTER_NOT_FOUND),
            _ => Error::from(e).context("Failed to run AppCenter CLI"),
        })?;

    if output.status.success() {
        Ok(serde_json::from_slice(&output.stdout).unwrap_or_else(|_| {
            let format_err = format!("Command `{} codepush deployment history {} --app {} --output json` failed to produce a valid JSON output.", appcenter_bin, deployment, app);
            panic!("{}", format_err);
        }))
    } else {
        Err(get_appcenter_error(&output).context("Failed to load AppCenter deployment history"))
    }
}

pub fn get_appcenter_package(app: &str, deployment: &str) -> Result<AppCenterPackage> {
    let history = get_appcenter_deployment_history(app, deployment)?;
    if let Some(latest) = history.into_iter().last() {
        Ok(latest)
    } else {
        bail!("Could not find deployment {} for {}", deployment, app);
    }
}

pub fn get_react_native_appcenter_release(
    package: &AppCenterPackage,
    platform: &str,
    bundle_id_override: Option<&str>,
    version_name_override: Option<&str>,
    release_name_override: Option<&str>,
) -> Result<String> {
    let bundle_id_ovrr = bundle_id_override.unwrap_or("");
    let version_name_ovrr = version_name_override.unwrap_or("");
    let release_name_ovrr = release_name_override.unwrap_or("");

    if !release_name_ovrr.is_empty() {
        return Ok(release_name_ovrr.to_string());
    }

    if !bundle_id_ovrr.is_empty() && !version_name_ovrr.is_empty() {
        return Ok(format!(
            "{}@{}+codepush:{}",
            bundle_id_ovrr, version_name_ovrr, package.label
        ));
    }

    if platform == "ios" {
        if !cfg!(target_os = "macos") {
            bail!("AppCenter codepush releases for iOS require macOS if no bundle ID and version name are specified");
        }

        let mut opts = MatchOptions::new();
        opts.case_sensitive = false;

        for entry in (glob_with("ios/*.xcodeproj", opts)?).flatten() {
            let pi = XcodeProjectInfo::from_path(&entry)?;
            if let Some(ipl) = InfoPlist::from_project_info(&pi)? {
                if let Some(release_name) = get_xcode_release_name(Some(ipl))? {
                    let vec: Vec<&str> = release_name.split('@').collect();
                    let bundle_id = if bundle_id_ovrr.is_empty() {
                        vec[0]
                    } else {
                        bundle_id_ovrr
                    };
                    let version_name = if version_name_ovrr.is_empty() {
                        vec[1]
                    } else {
                        version_name_ovrr
                    };
                    return Ok(format!(
                        "{}@{}+codepush:{}",
                        bundle_id, version_name, package.label
                    ));
                }
            }
        }

        bail!("Could not find plist");
    } else if platform == "android" {
        if_chain! {
            if let Ok(here) = env::current_dir();
            if let Ok(android_folder) = here.join("android").metadata();
            if android_folder.is_dir();
            then {
                if let Some(release_name) = infer_gradle_release_name(Some(here.join("android")))? {
                    let vec: Vec<&str> = release_name.split('@').collect();
                    let bundle_id = if bundle_id_ovrr.is_empty() { vec[0] } else { bundle_id_ovrr };
                    let version_name = if version_name_ovrr.is_empty() { vec[1] } else { version_name_ovrr };
                    return Ok(format!("{}@{}+codepush:{}", bundle_id, version_name, package.label));
                } else {
                    bail!("Could not parse app id from build.gradle");
                }
            }
        }

        bail!("Could not find AndroidManifest.xml");
    }

    bail!("Unsupported platform '{}'", platform);
}
<|endoftext|>


<commit_before>//! Crate documentation builder
//!
//! This module is extremely similar to cargo install operation, except it's building
//! documentation of a crate and not installing anything.

use std::path::{Path, PathBuf};
use std::env;
use std::sync::Arc;

use cargo::core::{SourceId, Dependency, Source, Package, Workspace};
use cargo::core::compiler::{DefaultExecutor, CompileMode, MessageFormat, BuildConfig, Executor};
use cargo::core::package::PackageSet;
use cargo::core::source::SourceMap;
use cargo::util::{CargoResult, Config, internal, Filesystem};
use cargo::sources::SourceConfigMap;
use cargo::ops::{self, Packages};

use utils::{get_current_versions, parse_rustc_version};
use error::Result;

use Metadata;

/// Builds documentation of a crate and version.
///
/// Crate will be built into current working directory/crate-version.
///
/// It will build latest version, if no version is given.
// idea is to make cargo to download
// and build a crate and its documentation
// instead of doing it manually like in the previous version of cratesfyi
pub fn build_doc(name: &str, vers: Option<&str>, target: Option<&str>) -> Result<Package> {
    let config = try!(Config::default());
    let source_id = try!(SourceId::crates_io(&config));

    let source_cfg_map = try!(SourceConfigMap::new(&config));
    let mut source = try!(source_cfg_map.load(&source_id));

    // update crates.io-index registry
    try!(source.update());

    let dep = try!(Dependency::parse_no_deprecated(name, vers, &source_id));
    let deps = try!(source.query_vec(&dep));
    let pkgid = try!(deps.iter().map(|p| p.package_id()).max()
                     // FIXME: This is probably not a rusty way to handle options and results
                     //        or maybe it is who knows...
                     .ok_or(internal("no package id available")));

    let mut source_map = SourceMap::new();
    source_map.insert(source);

    let pkg_set = try!(PackageSet::new(&[pkgid.clone()], source_map, &config));

    let pkg = try!(pkg_set.get_one(&pkgid)).clone();

    let current_dir = try!(env::current_dir());
    let target_dir = PathBuf::from(current_dir).join("cratesfyi");

    let metadata = Metadata::from_package(&pkg).map_err(|e| internal(e.to_string()))?;

    // This is only way to pass rustc_args to cargo.
    // CompileOptions::target_rustc_args is used only for the current crate,
    // and since docs.rs never runs rustc on the current crate, we assume rustc_args
    // will be used for the dependencies. That is why we are creating RUSTFLAGS environment
    // variable instead of using target_rustc_args.
    if let Some(rustc_args) = metadata.rustc_args {
        env::set_var("RUSTFLAGS", rustc_args.join(" "));
    }

    // since https://github.com/rust-lang/rust/pull/48511 we can pass --resource-suffix to
    // add correct version numbers to css and javascript files
    // TODO: we can add --extern-html-root-url too, thanks to
    // https://github.com/rust-lang/rust/pull/51384
    let mut rustdoc_args: Vec<String> =
        vec!["-Z".to_string(), "unstable-options".to_string(),
             "--resource-suffix".to_string(),
             format!("-{}", parse_rustc_version(get_current_versions()?.0)?)];
    if let Some(package_rustdoc_args) = metadata.rustdoc_args {
        rustdoc_args.append(&mut package_rustdoc_args.iter().map(|s| s.to_owned()).collect());
    }

    let mut build_config = try!(BuildConfig::new(&config,
                                                 None,
                                                 &target.map(|t| t.to_string()),
                                                 CompileMode::Doc { deps: false }));
    build_config.release = false;
    build_config.message_format = MessageFormat::Human;

    let opts = ops::CompileOptions {
        config: &config,
        build_config,
        features: metadata.features.unwrap_or(Vec::new()),
        all_features: metadata.all_features,
        no_default_features: metadata.no_default_features,
        spec: Packages::Packages(Vec::new()),
        filter: ops::CompileFilter::new(true,
                                        Vec::new(), false,
                                        Vec::new(), false,
                                        Vec::new(), false,
                                        Vec::new(), false,
                                        false),
        target_rustc_args: None,
        target_rustdoc_args: Some(rustdoc_args),
        local_rustdoc_args: None,
        export_dir: None,
    };

    let ws = try!(Workspace::ephemeral(pkg, &config, Some(Filesystem::new(target_dir)), false));
    let exec: Arc<Executor> = Arc::new(DefaultExecutor);
    let source = try!(source_cfg_map.load(&source_id));
    try!(ops::compile_ws(&ws, Some(source), &opts, &exec));

    Ok(try!(ws.current()).clone())
}

/// Downloads a crate and returns Cargo Package.
pub fn get_package(name: &str, vers: Option<&str>) -> CargoResult<Package> {
    debug!("Getting package with cargo");
    let config = try!(Config::default());
    let source_id = try!(SourceId::crates_io(&config));

    let source_map = try!(SourceConfigMap::new(&config));
    let mut source = try!(source_map.load(&source_id));

    try!(source.update());

    let dep = try!(Dependency::parse_no_deprecated(name, vers, &source_id));
    let deps = try!(source.query_vec(&dep));
    let pkgid = try!(deps.iter().map(|p| p.package_id()).max()
                     // FIXME: This is probably not a rusty way to handle options and results
                     //        or maybe it is who knows...
                     .ok_or(internal("no package id available")));

    let mut source_map = SourceMap::new();
    source_map.insert(source);

    let pkg_set = try!(PackageSet::new(&[pkgid.clone()], source_map, &config));

    let pkg = try!(pkg_set.get_one(&pkgid)).clone();

    Ok(pkg)
}

/// Updates central crates-io.index repository
pub fn update_sources() -> CargoResult<()> {
    let config = try!(Config::default());
    let source_id = try!(SourceId::crates_io(&config));

    let source_map = try!(SourceConfigMap::new(&config));
    let mut source = try!(source_map.load(&source_id));

    source.update()
}

/// Gets source path of a downloaded package.
pub fn source_path(pkg: &Package) -> Option<&Path> {
    // parent of the manifest file is where source codes are stored
    pkg.manifest_path().parent()
}

#[cfg(test)]
mod test {
    use std::path::Path;
    use super::*;

    #[test]
    fn test_get_package() {
        let pkg = get_package("rand", None);
        assert!(pkg.is_ok());

        let pkg = pkg.unwrap();

        let manifest = pkg.manifest();
        assert_eq!(manifest.name().as_str(), "rand");
    }

    #[test]
    fn test_source_path() {
        let pkg = get_package("rand", None).unwrap();
        let source_path = source_path(&pkg).unwrap();
        assert!(source_path.is_dir());

        let cargo_toml_path = Path::new(source_path).join("Cargo.toml");
        assert!(cargo_toml_path.exists());
        assert!(cargo_toml_path.is_file());

        let src_path = Path::new(source_path).join("src");
        assert!(src_path.exists());
        assert!(src_path.is_dir());
    }
}
<commit_msg>force availability of nightly features in cargo<commit_after>//! Crate documentation builder
//!
//! This module is extremely similar to cargo install operation, except it's building
//! documentation of a crate and not installing anything.

use std::path::{Path, PathBuf};
use std::env;
use std::sync::Arc;

use cargo::core::{self, SourceId, Dependency, Source, Package, Workspace};
use cargo::core::compiler::{DefaultExecutor, CompileMode, MessageFormat, BuildConfig, Executor};
use cargo::core::package::PackageSet;
use cargo::core::source::SourceMap;
use cargo::util::{CargoResult, Config, internal, Filesystem};
use cargo::sources::SourceConfigMap;
use cargo::ops::{self, Packages};

use utils::{get_current_versions, parse_rustc_version};
use error::Result;

use Metadata;

/// Builds documentation of a crate and version.
///
/// Crate will be built into current working directory/crate-version.
///
/// It will build latest version, if no version is given.
// idea is to make cargo to download
// and build a crate and its documentation
// instead of doing it manually like in the previous version of cratesfyi
pub fn build_doc(name: &str, vers: Option<&str>, target: Option<&str>) -> Result<Package> {
    core::enable_nightly_features();
    let config = try!(Config::default());
    let source_id = try!(SourceId::crates_io(&config));

    let source_cfg_map = try!(SourceConfigMap::new(&config));
    let mut source = try!(source_cfg_map.load(&source_id));

    // update crates.io-index registry
    try!(source.update());

    let dep = try!(Dependency::parse_no_deprecated(name, vers, &source_id));
    let deps = try!(source.query_vec(&dep));
    let pkgid = try!(deps.iter().map(|p| p.package_id()).max()
                     // FIXME: This is probably not a rusty way to handle options and results
                     //        or maybe it is who knows...
                     .ok_or(internal("no package id available")));

    let mut source_map = SourceMap::new();
    source_map.insert(source);

    let pkg_set = try!(PackageSet::new(&[pkgid.clone()], source_map, &config));

    let pkg = try!(pkg_set.get_one(&pkgid)).clone();

    let current_dir = try!(env::current_dir());
    let target_dir = PathBuf::from(current_dir).join("cratesfyi");

    let metadata = Metadata::from_package(&pkg).map_err(|e| internal(e.to_string()))?;

    // This is only way to pass rustc_args to cargo.
    // CompileOptions::target_rustc_args is used only for the current crate,
    // and since docs.rs never runs rustc on the current crate, we assume rustc_args
    // will be used for the dependencies. That is why we are creating RUSTFLAGS environment
    // variable instead of using target_rustc_args.
    if let Some(rustc_args) = metadata.rustc_args {
        env::set_var("RUSTFLAGS", rustc_args.join(" "));
    }

    // since https://github.com/rust-lang/rust/pull/48511 we can pass --resource-suffix to
    // add correct version numbers to css and javascript files
    // TODO: we can add --extern-html-root-url too, thanks to
    // https://github.com/rust-lang/rust/pull/51384
    let mut rustdoc_args: Vec<String> =
        vec!["-Z".to_string(), "unstable-options".to_string(),
             "--resource-suffix".to_string(),
             format!("-{}", parse_rustc_version(get_current_versions()?.0)?)];
    if let Some(package_rustdoc_args) = metadata.rustdoc_args {
        rustdoc_args.append(&mut package_rustdoc_args.iter().map(|s| s.to_owned()).collect());
    }

    let mut build_config = try!(BuildConfig::new(&config,
                                                 None,
                                                 &target.map(|t| t.to_string()),
                                                 CompileMode::Doc { deps: false }));
    build_config.release = false;
    build_config.message_format = MessageFormat::Human;

    let opts = ops::CompileOptions {
        config: &config,
        build_config,
        features: metadata.features.unwrap_or(Vec::new()),
        all_features: metadata.all_features,
        no_default_features: metadata.no_default_features,
        spec: Packages::Packages(Vec::new()),
        filter: ops::CompileFilter::new(true,
                                        Vec::new(), false,
                                        Vec::new(), false,
                                        Vec::new(), false,
                                        Vec::new(), false,
                                        false),
        target_rustc_args: None,
        target_rustdoc_args: Some(rustdoc_args),
        local_rustdoc_args: None,
        export_dir: None,
    };

    let ws = try!(Workspace::ephemeral(pkg, &config, Some(Filesystem::new(target_dir)), false));
    let exec: Arc<Executor> = Arc::new(DefaultExecutor);
    let source = try!(source_cfg_map.load(&source_id));
    try!(ops::compile_ws(&ws, Some(source), &opts, &exec));

    Ok(try!(ws.current()).clone())
}

/// Downloads a crate and returns Cargo Package.
pub fn get_package(name: &str, vers: Option<&str>) -> CargoResult<Package> {
    core::enable_nightly_features();
    debug!("Getting package with cargo");
    let config = try!(Config::default());
    let source_id = try!(SourceId::crates_io(&config));

    let source_map = try!(SourceConfigMap::new(&config));
    let mut source = try!(source_map.load(&source_id));

    try!(source.update());

    let dep = try!(Dependency::parse_no_deprecated(name, vers, &source_id));
    let deps = try!(source.query_vec(&dep));
    let pkgid = try!(deps.iter().map(|p| p.package_id()).max()
                     // FIXME: This is probably not a rusty way to handle options and results
                     //        or maybe it is who knows...
                     .ok_or(internal("no package id available")));

    let mut source_map = SourceMap::new();
    source_map.insert(source);

    let pkg_set = try!(PackageSet::new(&[pkgid.clone()], source_map, &config));

    let pkg = try!(pkg_set.get_one(&pkgid)).clone();

    Ok(pkg)
}

/// Updates central crates-io.index repository
pub fn update_sources() -> CargoResult<()> {
    let config = try!(Config::default());
    let source_id = try!(SourceId::crates_io(&config));

    let source_map = try!(SourceConfigMap::new(&config));
    let mut source = try!(source_map.load(&source_id));

    source.update()
}

/// Gets source path of a downloaded package.
pub fn source_path(pkg: &Package) -> Option<&Path> {
    // parent of the manifest file is where source codes are stored
    pkg.manifest_path().parent()
}

#[cfg(test)]
mod test {
    use std::path::Path;
    use super::*;

    #[test]
    fn test_get_package() {
        let pkg = get_package("rand", None);
        assert!(pkg.is_ok());

        let pkg = pkg.unwrap();

        let manifest = pkg.manifest();
        assert_eq!(manifest.name().as_str(), "rand");
    }

    #[test]
    fn test_source_path() {
        let pkg = get_package("rand", None).unwrap();
        let source_path = source_path(&pkg).unwrap();
        assert!(source_path.is_dir());

        let cargo_toml_path = Path::new(source_path).join("Cargo.toml");
        assert!(cargo_toml_path.exists());
        assert!(cargo_toml_path.is_file());

        let src_path = Path::new(source_path).join("src");
        assert!(src_path.exists());
        assert!(src_path.is_dir());
    }
}
<|endoftext|>


<commit_before>use event::Event;
use top_level_event::TopLevelEvent;
use sass::rule::SassRule;
use sass::variable::SassVariable;
use std::collections::HashMap;

pub struct VariableMapper<I> {
    tokenizer: I,
    variables: HashMap<String, String>,
}

impl<I> VariableMapper<I> {
    pub fn new(tokenizer: I) -> VariableMapper<I> {
        VariableMapper {
            tokenizer: tokenizer,
            variables: HashMap::new(),
        }
    }

    fn replace_children_in_scope<'b>(&self, children: Vec<Event<'b>>, starting_variables: &HashMap<String, String>) -> Vec<Event<'b>> {
        let mut local_variables = starting_variables.clone();

        children.into_iter().filter_map(|c|
            match c {
                Event::Variable(SassVariable { name, value }) => {
                    let val = self.substitute_variables_using_locals(&value, &local_variables);
                    local_variables.insert((*name).to_string(), val);
                    None
                },
                Event::Property(name, value) => {
                    Some(Event::Property(name, self.substitute_variables_using_locals(&value, &local_variables).into()))
                },
                Event::ChildRule(rule) => {
                    Some(Event::ChildRule(SassRule {
                        children: self.replace_children_in_scope(rule.children, &local_variables), ..rule
                    }))
                },
                other => Some(other)
            }
        ).collect::<Vec<_>>()
    }

    fn substitute_variables_using_locals(&self, value: &str, local_variables: &HashMap<String, String>) -> String {
        value.split(' ').map(|value_part|
            match (*local_variables).get(value_part) {
                Some(v) => &v[..],
                None => value_part,
            }
        ).collect::<Vec<_>>().connect(" ")
    }
}

impl<'a, I> Iterator for VariableMapper<I>
    where I: Iterator<Item = TopLevelEvent<'a>>
{
    type Item = TopLevelEvent<'a>;

    fn next(&mut self) -> Option<TopLevelEvent<'a>> {
        match self.tokenizer.next() {
            Some(TopLevelEvent::Variable(SassVariable { name, value })) => {
                let val = self.substitute_variables_using_locals(&value, &self.variables);
                self.variables.insert((*name).to_string(), val);
                self.next()
            },
            Some(TopLevelEvent::Rule(sass_rule)) => {
                Some(TopLevelEvent::Rule(SassRule {
                    children: self.replace_children_in_scope(sass_rule.children, &self.variables), ..sass_rule
                }))
            },
            other => other,
        }
    }
}
<commit_msg>Rename this function since the with_locals part doesn't make sense<commit_after>use event::Event;
use top_level_event::TopLevelEvent;
use sass::rule::SassRule;
use sass::variable::SassVariable;
use std::collections::HashMap;

pub struct VariableMapper<I> {
    tokenizer: I,
    variables: HashMap<String, String>,
}

impl<I> VariableMapper<I> {
    pub fn new(tokenizer: I) -> VariableMapper<I> {
        VariableMapper {
            tokenizer: tokenizer,
            variables: HashMap::new(),
        }
    }

    fn replace_children_in_scope<'b>(&self, children: Vec<Event<'b>>, starting_variables: &HashMap<String, String>) -> Vec<Event<'b>> {
        let mut local_variables = starting_variables.clone();

        children.into_iter().filter_map(|c|
            match c {
                Event::Variable(SassVariable { name, value }) => {
                    let val = self.substitute_variables(&value, &local_variables);
                    local_variables.insert((*name).to_string(), val);
                    None
                },
                Event::Property(name, value) => {
                    Some(Event::Property(name, self.substitute_variables(&value, &local_variables).into()))
                },
                Event::ChildRule(rule) => {
                    Some(Event::ChildRule(SassRule {
                        children: self.replace_children_in_scope(rule.children, &local_variables), ..rule
                    }))
                },
                other => Some(other)
            }
        ).collect::<Vec<_>>()
    }

    fn substitute_variables(&self, value: &str, local_variables: &HashMap<String, String>) -> String {
        value.split(' ').map(|value_part|
            match (*local_variables).get(value_part) {
                Some(v) => &v[..],
                None => value_part,
            }
        ).collect::<Vec<_>>().connect(" ")
    }
}

impl<'a, I> Iterator for VariableMapper<I>
    where I: Iterator<Item = TopLevelEvent<'a>>
{
    type Item = TopLevelEvent<'a>;

    fn next(&mut self) -> Option<TopLevelEvent<'a>> {
        match self.tokenizer.next() {
            Some(TopLevelEvent::Variable(SassVariable { name, value })) => {
                let val = self.substitute_variables(&value, &self.variables);
                self.variables.insert((*name).to_string(), val);
                self.next()
            },
            Some(TopLevelEvent::Rule(sass_rule)) => {
                Some(TopLevelEvent::Rule(SassRule {
                    children: self.replace_children_in_scope(sass_rule.children, &self.variables), ..sass_rule
                }))
            },
            other => other,
        }
    }
}
<|endoftext|>


<commit_before>use std::ffi::{OsStr, OsString};
use std::fmt;
use std::num::ParseIntError;

use glob;

use options::{flags, HelpString, VersionString};
use options::parser::{Arg, Flag, ParseError};

/// A list of legal choices for an argument-taking option
#[derive(PartialEq, Debug)]
pub struct Choices(&'static [&'static str]);

impl fmt::Display for Choices {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "choices: {}", self.0.join(", "))
    }
}

/// A **misfire** is a thing that can happen instead of listing files -- a
/// catch-all for anything outside the program’s normal execution.
#[derive(PartialEq, Debug)]
pub enum Misfire {

    /// The getopts crate didn’t like these Arguments.
    InvalidOptions(ParseError),

    /// The user supplied an illegal choice to an Argument.
    BadArgument(&'static Arg, OsString, Choices),

    /// The user asked for help. This isn’t strictly an error, which is why
    /// this enum isn’t named Error!
    Help(HelpString),

    /// The user wanted the version number.
    Version(VersionString),

    /// An option was given twice or more in strict mode.
    Duplicate(Flag, Flag),

    /// Two options were given that conflict with one another.
    Conflict(&'static Arg, &'static Arg),

    /// An option was given that does nothing when another one either is or
    /// isn't present.
    Useless(&'static Arg, bool, &'static Arg),

    /// An option was given that does nothing when either of two other options
    /// are not present.
    Useless2(&'static Arg, &'static Arg, &'static Arg),

    /// A very specific edge case where --tree can’t be used with --all twice.
    TreeAllAll,

    /// A numeric option was given that failed to be parsed as a number.
    FailedParse(ParseIntError),

    /// A glob ignore was given that failed to be parsed as a pattern.
    FailedGlobPattern(String),
}

impl Misfire {

    /// The OS return code this misfire should signify.
    pub fn is_error(&self) -> bool {
        match *self {
            Misfire::Help(_)    => false,
            Misfire::Version(_) => false,
            _                   => true,
        }
    }

    /// The Misfire that happens when an option gets given the wrong
    /// argument. This has to use one of the `getopts` failure
    /// variants--it’s meant to take just an option name, rather than an
    /// option *and* an argument, but it works just as well.
    pub fn bad_argument(option: &'static Arg, otherwise: &OsStr, legal: &'static [&'static str]) -> Misfire {
        Misfire::BadArgument(option, otherwise.to_os_string(), Choices(legal))
    }
}

impl From<glob::PatternError> for Misfire {
    fn from(error: glob::PatternError) -> Misfire {
        Misfire::FailedGlobPattern(error.to_string())
    }
}

impl fmt::Display for Misfire {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Misfire::*;

        match *self {
            BadArgument(ref a, ref b, ref c) => write!(f, "Option {} has no {:?} setting ({})", a, b, c),
            InvalidOptions(ref e)            => write!(f, "{}", e),
            Help(ref text)                   => write!(f, "{}", text),
            Version(ref version)             => write!(f, "{}", version),
            Conflict(ref a, ref b)           => write!(f, "Option {} conflicts with option {}", a, b),
            Duplicate(ref a, ref b)          => if a == b { write!(f, "Flag {} was given twice", a) }
                                                     else { write!(f, "Flag {} conflicts with flag {}", a, b) },
            Useless(ref a, false, ref b)     => write!(f, "Option {} is useless without option {}", a, b),
            Useless(ref a, true, ref b)      => write!(f, "Option {} is useless given option {}", a, b),
            Useless2(ref a, ref b1, ref b2)  => write!(f, "Option {} is useless without options {} or {}", a, b1, b2),
            TreeAllAll                       => write!(f, "Option --tree is useless given --all --all"),
            FailedParse(ref e)               => write!(f, "Failed to parse number: {}", e),
            FailedGlobPattern(ref e)         => write!(f, "Failed to parse glob pattern: {}", e),
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::ParseError::*;

        match *self {
            NeedsValue { ref flag }              => write!(f, "Flag {} needs a value", flag),
            ForbiddenValue { ref flag }          => write!(f, "Flag {} cannot take a value", flag),
            UnknownShortArgument { ref attempt } => write!(f, "Unknown argument -{}", *attempt as char),
            UnknownArgument { ref attempt }      => write!(f, "Unknown argument --{}", attempt.to_string_lossy()),
        }
    }
}

impl Misfire {
    pub fn suggestion(&self) -> Option<&'static str> {
        if let Misfire::BadArgument(ref time, ref r, ref _choices) = *self {
            if *time == &flags::TIME && r == "r" {
                return Some("To sort newest files first, try \"--sort modified\", or just \"-stime\"");
            }
        }

        None
    }
}
<commit_msg>Put misfire.rs in a nicer order<commit_after>use std::ffi::{OsStr, OsString};
use std::fmt;
use std::num::ParseIntError;

use glob;

use options::{flags, HelpString, VersionString};
use options::parser::{Arg, Flag, ParseError};

/// A **misfire** is a thing that can happen instead of listing files -- a
/// catch-all for anything outside the program’s normal execution.
#[derive(PartialEq, Debug)]
pub enum Misfire {

    /// The getopts crate didn’t like these Arguments.
    InvalidOptions(ParseError),

    /// The user supplied an illegal choice to an Argument.
    BadArgument(&'static Arg, OsString, Choices),

    /// The user asked for help. This isn’t strictly an error, which is why
    /// this enum isn’t named Error!
    Help(HelpString),

    /// The user wanted the version number.
    Version(VersionString),

    /// An option was given twice or more in strict mode.
    Duplicate(Flag, Flag),

    /// Two options were given that conflict with one another.
    Conflict(&'static Arg, &'static Arg),

    /// An option was given that does nothing when another one either is or
    /// isn't present.
    Useless(&'static Arg, bool, &'static Arg),

    /// An option was given that does nothing when either of two other options
    /// are not present.
    Useless2(&'static Arg, &'static Arg, &'static Arg),

    /// A very specific edge case where --tree can’t be used with --all twice.
    TreeAllAll,

    /// A numeric option was given that failed to be parsed as a number.
    FailedParse(ParseIntError),

    /// A glob ignore was given that failed to be parsed as a pattern.
    FailedGlobPattern(String),
}

impl Misfire {

    /// The OS return code this misfire should signify.
    pub fn is_error(&self) -> bool {
        match *self {
            Misfire::Help(_)    => false,
            Misfire::Version(_) => false,
            _                   => true,
        }
    }

    /// The Misfire that happens when an option gets given the wrong
    /// argument. This has to use one of the `getopts` failure
    /// variants--it’s meant to take just an option name, rather than an
    /// option *and* an argument, but it works just as well.
    pub fn bad_argument(option: &'static Arg, otherwise: &OsStr, legal: &'static [&'static str]) -> Misfire {
        Misfire::BadArgument(option, otherwise.to_os_string(), Choices(legal))
    }
}

impl From<glob::PatternError> for Misfire {
    fn from(error: glob::PatternError) -> Misfire {
        Misfire::FailedGlobPattern(error.to_string())
    }
}

impl fmt::Display for Misfire {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Misfire::*;

        match *self {
            BadArgument(ref a, ref b, ref c) => write!(f, "Option {} has no {:?} setting ({})", a, b, c),
            InvalidOptions(ref e)            => write!(f, "{}", e),
            Help(ref text)                   => write!(f, "{}", text),
            Version(ref version)             => write!(f, "{}", version),
            Conflict(ref a, ref b)           => write!(f, "Option {} conflicts with option {}", a, b),
            Duplicate(ref a, ref b)          => if a == b { write!(f, "Flag {} was given twice", a) }
                                                     else { write!(f, "Flag {} conflicts with flag {}", a, b) },
            Useless(ref a, false, ref b)     => write!(f, "Option {} is useless without option {}", a, b),
            Useless(ref a, true, ref b)      => write!(f, "Option {} is useless given option {}", a, b),
            Useless2(ref a, ref b1, ref b2)  => write!(f, "Option {} is useless without options {} or {}", a, b1, b2),
            TreeAllAll                       => write!(f, "Option --tree is useless given --all --all"),
            FailedParse(ref e)               => write!(f, "Failed to parse number: {}", e),
            FailedGlobPattern(ref e)         => write!(f, "Failed to parse glob pattern: {}", e),
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::ParseError::*;

        match *self {
            NeedsValue { ref flag }              => write!(f, "Flag {} needs a value", flag),
            ForbiddenValue { ref flag }          => write!(f, "Flag {} cannot take a value", flag),
            UnknownShortArgument { ref attempt } => write!(f, "Unknown argument -{}", *attempt as char),
            UnknownArgument { ref attempt }      => write!(f, "Unknown argument --{}", attempt.to_string_lossy()),
        }
    }
}

impl Misfire {
    pub fn suggestion(&self) -> Option<&'static str> {
        if let Misfire::BadArgument(ref time, ref r, ref _choices) = *self {
            if *time == &flags::TIME && r == "r" {
                return Some("To sort newest files first, try \"--sort modified\", or just \"-stime\"");
            }
        }

        None
    }
}

/// A list of legal choices for an argument-taking option.
#[derive(PartialEq, Debug)]
pub struct Choices(&'static [&'static str]);

impl fmt::Display for Choices {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "choices: {}", self.0.join(", "))
    }
}
<|endoftext|>


<commit_before>#![crate_id = "uusync#1.0.0"]
/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Alexander Fomin <xander.fomin@ya.ru>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

 /* Last synced with: sync (GNU coreutils) 8.13 */

 #![feature(macro_rules)]

extern crate getopts;
extern crate libc;

use std::os;
use getopts::{optflag, getopts, usage};

#[path = "../common/util.rs"] mod util;

#[cfg(unix)]
mod platform {
    use super::libc;

    extern {
        fn sync() -> libc::c_void;
    }

    pub unsafe fn do_sync() -> int {
        sync();
        0
    }
}

#[cfg(windows)]
mod platform {
    pub use super::libc;
    use std::{mem, str};

    extern "system" {
        fn CreateFileA(lpFileName: *libc::c_char,
                      dwDesiredAccess: libc::uint32_t,
                      dwShareMode: libc::uint32_t,
                      lpSecurityAttributes: *libc::c_void, // *LPSECURITY_ATTRIBUTES
                      dwCreationDisposition: libc::uint32_t,
                      dwFlagsAndAttributes: libc::uint32_t,
                      hTemplateFile: *libc::c_void) -> *libc::c_void;
        fn GetDriveTypeA(lpRootPathName: *libc::c_char) -> libc::c_uint;
        fn GetLastError() -> libc::uint32_t;
        fn FindFirstVolumeA(lpszVolumeName: *libc::c_char,
                            cchBufferLength: libc::uint32_t) -> *libc::c_void;
        fn FindNextVolumeA(hFindVolume: *libc::c_void,
                           lpszVolumeName: *libc::c_char,
                           cchBufferLength: libc::uint32_t) -> libc::c_int;
        fn FindVolumeClose(hFindVolume: *libc::c_void) -> libc::c_int;
        fn FlushFileBuffers(hFile: *libc::c_void) -> libc::c_int;
    }

    #[allow(unused_unsafe)]
    unsafe fn flush_volume(name: &str) {
        name.to_c_str().with_ref(|name_buffer| {
            if 0x00000003 == GetDriveTypeA(name_buffer) { // DRIVE_FIXED
                let sliced_name = name.slice_to(name.len() - 1); // eliminate trailing backslash
                sliced_name.to_c_str().with_ref(|sliced_name_buffer| {
                    match CreateFileA(sliced_name_buffer,
                                      0xC0000000, // GENERIC_WRITE
                                      0x00000003, // FILE_SHARE_WRITE,
                                      0 as *libc::c_void,
                                      0x00000003, // OPEN_EXISTING
                                      0,
                                      0 as *libc::c_void) {
                        _x if _x == -1 as *libc::c_void => { // INVALID_HANDLE_VALUE
                            crash!(GetLastError(), "failed to create volume handle");
                        }
                        handle @ _ => {
                            if FlushFileBuffers(handle) == 0 {
                                crash!(GetLastError(), "failed to flush file buffer");
                            }
                        }
                    }
                });
            }
        });
    }

    #[allow(unused_unsafe)]
    unsafe fn find_first_volume() -> (String, *libc::c_void) {
        let name: [libc::c_char, ..260] = mem::uninitialized(); // MAX_PATH
        match FindFirstVolumeA(name.as_ptr(), name.len() as libc::uint32_t) {
            _x if _x == -1 as *libc::c_void => { // INVALID_HANDLE_VALUE
                crash!(GetLastError(), "failed to find first volume");
            }
            handle @ _ => {
                (str::raw::from_c_str(name.as_ptr()), handle)
            }
        }
    }

    #[allow(unused_unsafe)]
    unsafe fn find_all_volumes() -> Vec<String> {
        match find_first_volume() {
            (first_volume, next_volume_handle) => {
                let mut volumes = Vec::from_elem(1, first_volume);
                loop {
                    let name: [libc::c_char, ..260] = mem::uninitialized(); // MAX_PATH
                    match FindNextVolumeA(next_volume_handle, name.as_ptr(), name.len() as libc::uint32_t) {
                        0 => {
                            match GetLastError() {
                                0x12 => { // ERROR_NO_MORE_FILES
                                    FindVolumeClose(next_volume_handle); // ignore FindVolumeClose() failures
                                    break;
                                }
                                err @ _ => {
                                    crash!(err, "failed to find next volume");
                                }
                            }
                        }
                        _ => {
                            volumes.push(str::raw::from_c_str(name.as_ptr()));
                        }
                    }
                }
                volumes
            }
        }
    }

    pub unsafe fn do_sync() -> int {
        let volumes = find_all_volumes();
        for vol in volumes.iter() {
            flush_volume(vol.as_slice());
        }
        0
    }
}

static NAME: &'static str = "sync";

#[allow(dead_code)]
fn main () { os::set_exit_status(uumain(os::args())); }

pub fn uumain(args: Vec<String>) -> int {
    let program = args.get(0);

    let options = [
        optflag("h", "help", "display this help and exit"),
        optflag("V", "version", "output version information and exit")
    ];

    let matches = match getopts(args.tail(), options) {
        Ok(m) => { m }
        _ => { help(program.as_slice(), options); return 0 }
    };

    if matches.opt_present("h") {
        help(program.as_slice(), options);
        return 0
    }

    if matches.opt_present("V") {
        version();
        return 0
    }

    uusync();
    0
}

fn version() {
    println!("uusync (uutils) 1.0.0");
    println!("The MIT License");
    println!("");
    println!("Author -- Alexander Fomin.");
}

fn help(program: &str, options: &[getopts::OptGroup]) {
    println!("Usage: {:s} [OPTION]", program);
    print!("{:s}", usage("Force changed blocks to disk, update the super block.", options));
}

fn uusync() -> int {
    unsafe {
        platform::do_sync()
    }
}<commit_msg>Program name fixed<commit_after>#![crate_id = "uusync#1.0.0"]
/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Alexander Fomin <xander.fomin@ya.ru>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

 /* Last synced with: sync (GNU coreutils) 8.13 */

 #![feature(macro_rules)]

extern crate getopts;
extern crate libc;

use std::os;
use getopts::{optflag, getopts, usage};

#[path = "../common/util.rs"] mod util;

#[cfg(unix)]
mod platform {
    use super::libc;

    extern {
        fn sync() -> libc::c_void;
    }

    pub unsafe fn do_sync() -> int {
        sync();
        0
    }
}

#[cfg(windows)]
mod platform {
    pub use super::libc;
    use std::{mem, str};

    extern "system" {
        fn CreateFileA(lpFileName: *libc::c_char,
                      dwDesiredAccess: libc::uint32_t,
                      dwShareMode: libc::uint32_t,
                      lpSecurityAttributes: *libc::c_void, // *LPSECURITY_ATTRIBUTES
                      dwCreationDisposition: libc::uint32_t,
                      dwFlagsAndAttributes: libc::uint32_t,
                      hTemplateFile: *libc::c_void) -> *libc::c_void;
        fn GetDriveTypeA(lpRootPathName: *libc::c_char) -> libc::c_uint;
        fn GetLastError() -> libc::uint32_t;
        fn FindFirstVolumeA(lpszVolumeName: *libc::c_char,
                            cchBufferLength: libc::uint32_t) -> *libc::c_void;
        fn FindNextVolumeA(hFindVolume: *libc::c_void,
                           lpszVolumeName: *libc::c_char,
                           cchBufferLength: libc::uint32_t) -> libc::c_int;
        fn FindVolumeClose(hFindVolume: *libc::c_void) -> libc::c_int;
        fn FlushFileBuffers(hFile: *libc::c_void) -> libc::c_int;
    }

    #[allow(unused_unsafe)]
    unsafe fn flush_volume(name: &str) {
        name.to_c_str().with_ref(|name_buffer| {
            if 0x00000003 == GetDriveTypeA(name_buffer) { // DRIVE_FIXED
                let sliced_name = name.slice_to(name.len() - 1); // eliminate trailing backslash
                sliced_name.to_c_str().with_ref(|sliced_name_buffer| {
                    match CreateFileA(sliced_name_buffer,
                                      0xC0000000, // GENERIC_WRITE
                                      0x00000003, // FILE_SHARE_WRITE,
                                      0 as *libc::c_void,
                                      0x00000003, // OPEN_EXISTING
                                      0,
                                      0 as *libc::c_void) {
                        _x if _x == -1 as *libc::c_void => { // INVALID_HANDLE_VALUE
                            crash!(GetLastError(), "failed to create volume handle");
                        }
                        handle @ _ => {
                            if FlushFileBuffers(handle) == 0 {
                                crash!(GetLastError(), "failed to flush file buffer");
                            }
                        }
                    }
                });
            }
        });
    }

    #[allow(unused_unsafe)]
    unsafe fn find_first_volume() -> (String, *libc::c_void) {
        let name: [libc::c_char, ..260] = mem::uninitialized(); // MAX_PATH
        match FindFirstVolumeA(name.as_ptr(), name.len() as libc::uint32_t) {
            _x if _x == -1 as *libc::c_void => { // INVALID_HANDLE_VALUE
                crash!(GetLastError(), "failed to find first volume");
            }
            handle @ _ => {
                (str::raw::from_c_str(name.as_ptr()), handle)
            }
        }
    }

    #[allow(unused_unsafe)]
    unsafe fn find_all_volumes() -> Vec<String> {
        match find_first_volume() {
            (first_volume, next_volume_handle) => {
                let mut volumes = Vec::from_elem(1, first_volume);
                loop {
                    let name: [libc::c_char, ..260] = mem::uninitialized(); // MAX_PATH
                    match FindNextVolumeA(next_volume_handle, name.as_ptr(), name.len() as libc::uint32_t) {
                        0 => {
                            match GetLastError() {
                                0x12 => { // ERROR_NO_MORE_FILES
                                    FindVolumeClose(next_volume_handle); // ignore FindVolumeClose() failures
                                    break;
                                }
                                err @ _ => {
                                    crash!(err, "failed to find next volume");
                                }
                            }
                        }
                        _ => {
                            volumes.push(str::raw::from_c_str(name.as_ptr()));
                        }
                    }
                }
                volumes
            }
        }
    }

    pub unsafe fn do_sync() -> int {
        let volumes = find_all_volumes();
        for vol in volumes.iter() {
            flush_volume(vol.as_slice());
        }
        0
    }
}

static NAME: &'static str = "sync";

#[allow(dead_code)]
fn main () { os::set_exit_status(uumain(os::args())); }

pub fn uumain(args: Vec<String>) -> int {
    let program = args.get(0);

    let options = [
        optflag("h", "help", "display this help and exit"),
        optflag("V", "version", "output version information and exit")
    ];

    let matches = match getopts(args.tail(), options) {
        Ok(m) => { m }
        _ => { help(program.as_slice(), options); return 0 }
    };

    if matches.opt_present("h") {
        help(program.as_slice(), options);
        return 0
    }

    if matches.opt_present("V") {
        version();
        return 0
    }

    uusync();
    0
}

fn version() {
    println!("sync (uutils) 1.0.0");
    println!("The MIT License");
    println!("");
    println!("Author -- Alexander Fomin.");
}

fn help(program: &str, options: &[getopts::OptGroup]) {
    println!("Usage: {:s} [OPTION]", program);
    print!("{:s}", usage("Force changed blocks to disk, update the super block.", options));
}

fn uusync() -> int {
    unsafe {
        platform::do_sync()
    }
}<|endoftext|>


<commit_before>// Copyright 2016 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[macro_use]
extern crate log;
extern crate chrono;
extern crate fern;

extern crate dirs;

extern crate xi_core_lib;
extern crate xi_rpc;

use std::collections::HashMap;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

use xi_core_lib::XiCore;
use xi_rpc::RpcLoop;

const XI_LOG_DIR: &str = "xi-core";
const XI_LOG_FILE: &str = "xi-core.log";

fn get_logging_directory_path<P: AsRef<Path>>(directory: P) -> Result<PathBuf, io::Error> {
    match dirs::data_local_dir() {
        Some(mut log_dir) => {
            log_dir.push(directory);
            Ok(log_dir)
        }
        None => Err(io::Error::new(
            io::ErrorKind::NotFound,
            "No standard logging directory known for this platform",
        )),
    }
}

fn setup_logging(logging_path_result: Option<PathBuf>) -> Result<(), fern::InitError> {
    let level_filter = match std::env::var("XI_LOG") {
        Ok(level) => match level.to_lowercase().as_ref() {
            "trace" => log::LevelFilter::Trace,
            "debug" => log::LevelFilter::Debug,
            _ => log::LevelFilter::Info,
        },
        // Default to info
        Err(_) => log::LevelFilter::Info,
    };

    let mut fern_dispatch = fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{}[{}][{}] {}",
                chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
                record.target(),
                record.level(),
                message,
            ))
        }).level(level_filter)
        .chain(io::stderr());

    if let Some(logging_file_path) = &logging_path_result {
        // Ensure the logging directory is created
        let parent_path = logging_file_path.parent().ok_or(io::Error::new(
            io::ErrorKind::InvalidInput,
            format!(
                "Unable to get the parent of the following Path: {}",
                logging_file_path.display(),
            ),
        ))?;
        fs::create_dir_all(parent_path)?;
        // Attach it to fern
        fern_dispatch = fern_dispatch.chain(fern::log_file(logging_file_path)?);
    };

    // Start fern
    fern_dispatch.apply()?;
    info!("Logging with fern is setup");

    // Log details of the logging_file_path result using fern/log
    // Either logging the path fern is outputting to or the error from obtaining the path
    match &logging_path_result {
        Some(logging_file_path) => info!(
            "Logging to the following file: {}",
            logging_file_path.display()
        ),
        None => warn!("There was no path supplied for the log file, falling back to stderr."),
    }
    Ok(())
}

fn generate_logging_path(logfile_config: LogfileConfig) -> Result<PathBuf, io::Error> {
    // Use the file name set in logfile_config or fallback to the default
    let logfile_file_name = match logfile_config.file {
        Some(file_name) => file_name,
        None => PathBuf::from(XI_LOG_FILE),
    };
    // Use the directory name set in logfile_config or fallback to the default
    let logfile_directory_name = match logfile_config.directory {
        Some(dir) => dir,
        None => PathBuf::from(XI_LOG_DIR),
    };

    let mut logging_directory_path = get_logging_directory_path(logfile_directory_name)?;

    // Add the file name & return the full path
    logging_directory_path.push(logfile_file_name);
    Ok(logging_directory_path)
}

fn get_flags() -> HashMap<String, Option<String>> {
    let mut flags: HashMap<String, Option<String>> = HashMap::new();

    let flag_prefix = "-";
    let mut args_itterator = std::env::args().peekable();
    while let Some(arg) = args_itterator.next() {
        if arg.starts_with(flag_prefix) {
            let key = arg.trim_left_matches(flag_prefix).to_string();

            // Check the next argument doesn't start with the flag prefix
            // map_or accounts for peek returning an Option
            let next_arg_not_a_flag: bool = args_itterator
                .peek()
                .map_or(false, |val| !val.starts_with(flag_prefix));
            if next_arg_not_a_flag {
                flags.insert(key, args_itterator.next());
            }
        }
    }
    flags
}

struct EnvFlagConfig {
    env_name: &'static str,
    flag_name: &'static str,
}

/// Extracts a value from the flags and the env.
///
/// In this order: `String` from the flags, then `String` from the env, then `None`
fn extract_env_or_flag(
    flags: &HashMap<String, Option<String>>,
    conf: EnvFlagConfig,
) -> Option<String> {
    flags
        .get(conf.flag_name)
        .cloned()
        .unwrap_or(std::env::var(conf.env_name).ok())
}

struct LogfileConfig {
    directory: Option<PathBuf>,
    file: Option<PathBuf>,
}

fn generate_logfile_config(flags: &HashMap<String, Option<String>>) -> LogfileConfig {
    // If the key is set, get the Option within
    let log_dir_env_flag = EnvFlagConfig {
        env_name: "XI_LOG_DIR",
        flag_name: "log-dir",
    };
    let log_file_env_flag = EnvFlagConfig {
        env_name: "XI_LOG_FILE",
        flag_name: "log-file",
    };
    let log_dir_flag_option = extract_env_or_flag(&flags, log_dir_env_flag).map(PathBuf::from);

    let log_file_flag_option = extract_env_or_flag(&flags, log_file_env_flag).map(PathBuf::from);

    LogfileConfig {
        directory: log_dir_flag_option,
        file: log_file_flag_option,
    }
}

fn main() {
    let mut state = XiCore::new();
    let stdin = io::stdin();
    let stdout = io::stdout();
    let mut rpc_looper = RpcLoop::new(stdout);

    let flags: HashMap<String, Option<String>> = get_flags();

    let logfile_config = generate_logfile_config(&flags);

    let logging_path_result: Result<PathBuf, io::Error> = generate_logging_path(logfile_config);
    let logging_path: Option<PathBuf> = match logging_path_result {
        Ok(val) => Some(val),
        Err(e) => {
            eprintln!(
                "[WARNING] Unable to successfully generate the logging path: {:?}",
                e
            );
            None
        }
    };
    if let Err(e) = setup_logging(logging_path) {
        eprintln!(
            "[ERROR] setup_logging returned error, logging disabled: {:?}",
            e
        );
    }

    match rpc_looper.mainloop(|| stdin.lock(), &mut state) {
        Ok(_) => (),
        Err(err) => error!("xi-core exited with error:\n{:?}", err),
    }
}
<commit_msg>Moved the eprint to a warn after fern is set up.<commit_after>// Copyright 2016 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[macro_use]
extern crate log;
extern crate chrono;
extern crate fern;

extern crate dirs;

extern crate xi_core_lib;
extern crate xi_rpc;

use std::collections::HashMap;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

use xi_core_lib::XiCore;
use xi_rpc::RpcLoop;

const XI_LOG_DIR: &str = "xi-core";
const XI_LOG_FILE: &str = "xi-core.log";

fn get_logging_directory_path<P: AsRef<Path>>(directory: P) -> Result<PathBuf, io::Error> {
    match dirs::data_local_dir() {
        Some(mut log_dir) => {
            log_dir.push(directory);
            Ok(log_dir)
        }
        None => Err(io::Error::new(
            io::ErrorKind::NotFound,
            "No standard logging directory known for this platform",
        )),
    }
}

fn setup_logging(logging_path_result: Option<PathBuf>) -> Result<(), fern::InitError> {
    let level_filter = match std::env::var("XI_LOG") {
        Ok(level) => match level.to_lowercase().as_ref() {
            "trace" => log::LevelFilter::Trace,
            "debug" => log::LevelFilter::Debug,
            _ => log::LevelFilter::Info,
        },
        // Default to info
        Err(_) => log::LevelFilter::Info,
    };

    let mut fern_dispatch = fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{}[{}][{}] {}",
                chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
                record.target(),
                record.level(),
                message,
            ))
        }).level(level_filter)
        .chain(io::stderr());

    if let Some(logging_file_path) = &logging_path_result {
        // Ensure the logging directory is created
        let parent_path = logging_file_path.parent().ok_or(io::Error::new(
            io::ErrorKind::InvalidInput,
            format!(
                "Unable to get the parent of the following Path: {}",
                logging_file_path.display(),
            ),
        ))?;
        fs::create_dir_all(parent_path)?;
        // Attach it to fern
        fern_dispatch = fern_dispatch.chain(fern::log_file(logging_file_path)?);
    };

    // Start fern
    fern_dispatch.apply()?;
    info!("Logging with fern is setup");

    // Log details of the logging_file_path result using fern/log
    // Either logging the path fern is outputting to or the error from obtaining the path
    match &logging_path_result {
        Some(logging_file_path) => info!(
            "Logging to the following file: {}",
            logging_file_path.display()
        ),
        None => warn!("There was no path supplied for the log file, falling back to stderr."),
    }
    Ok(())
}

fn generate_logging_path(logfile_config: LogfileConfig) -> Result<PathBuf, io::Error> {
    // Use the file name set in logfile_config or fallback to the default
    let logfile_file_name = match logfile_config.file {
        Some(file_name) => file_name,
        None => PathBuf::from(XI_LOG_FILE),
    };
    // Use the directory name set in logfile_config or fallback to the default
    let logfile_directory_name = match logfile_config.directory {
        Some(dir) => dir,
        None => PathBuf::from(XI_LOG_DIR),
    };

    let mut logging_directory_path = get_logging_directory_path(logfile_directory_name)?;

    // Add the file name & return the full path
    logging_directory_path.push(logfile_file_name);
    Ok(logging_directory_path)
}

fn get_flags() -> HashMap<String, Option<String>> {
    let mut flags: HashMap<String, Option<String>> = HashMap::new();

    let flag_prefix = "-";
    let mut args_itterator = std::env::args().peekable();
    while let Some(arg) = args_itterator.next() {
        if arg.starts_with(flag_prefix) {
            let key = arg.trim_left_matches(flag_prefix).to_string();

            // Check the next argument doesn't start with the flag prefix
            // map_or accounts for peek returning an Option
            let next_arg_not_a_flag: bool = args_itterator
                .peek()
                .map_or(false, |val| !val.starts_with(flag_prefix));
            if next_arg_not_a_flag {
                flags.insert(key, args_itterator.next());
            }
        }
    }
    flags
}

struct EnvFlagConfig {
    env_name: &'static str,
    flag_name: &'static str,
}

/// Extracts a value from the flags and the env.
///
/// In this order: `String` from the flags, then `String` from the env, then `None`
fn extract_env_or_flag(
    flags: &HashMap<String, Option<String>>,
    conf: EnvFlagConfig,
) -> Option<String> {
    flags
        .get(conf.flag_name)
        .cloned()
        .unwrap_or(std::env::var(conf.env_name).ok())
}

struct LogfileConfig {
    directory: Option<PathBuf>,
    file: Option<PathBuf>,
}

fn generate_logfile_config(flags: &HashMap<String, Option<String>>) -> LogfileConfig {
    // If the key is set, get the Option within
    let log_dir_env_flag = EnvFlagConfig {
        env_name: "XI_LOG_DIR",
        flag_name: "log-dir",
    };
    let log_file_env_flag = EnvFlagConfig {
        env_name: "XI_LOG_FILE",
        flag_name: "log-file",
    };
    let log_dir_flag_option = extract_env_or_flag(&flags, log_dir_env_flag).map(PathBuf::from);

    let log_file_flag_option = extract_env_or_flag(&flags, log_file_env_flag).map(PathBuf::from);

    LogfileConfig {
        directory: log_dir_flag_option,
        file: log_file_flag_option,
    }
}

fn main() {
    let mut state = XiCore::new();
    let stdin = io::stdin();
    let stdout = io::stdout();
    let mut rpc_looper = RpcLoop::new(stdout);

    let flags: HashMap<String, Option<String>> = get_flags();

    let logfile_config = generate_logfile_config(&flags);

    let logging_path_result: Result<PathBuf, io::Error> = generate_logging_path(logfile_config);

    let mut logging_path: Option<PathBuf> = None;
    let mut logging_error: Option<io::Error> = None;
    match logging_path_result {
        Ok(val) => logging_path = Some(val),
        Err(e) => logging_error = Some(e),
    };
    if let Err(e) = setup_logging(logging_path) {
        eprintln!(
            "[ERROR] setup_logging returned error, logging disabled: {:?}",
            e
        );
    }
    if let Some(e) = logging_error {
        warn!("Unable to successfully generate the logging path: {:?}", e)
    }

    match rpc_looper.mainloop(|| stdin.lock(), &mut state) {
        Ok(_) => (),
        Err(err) => error!("xi-core exited with error:\n{:?}", err),
    }
}
<|endoftext|>


<commit_before>extern crate fuse;
extern crate libc;
extern crate time;
extern crate argparse;

use std::string::String;
use std::fmt;
use std::env;
use std::path::Path;
use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;

use libc::{ENOSYS,c_int,ENOENT};
use time::Timespec;
use argparse::{ArgumentParser, StoreTrue, Store};
use fuse::{FileType, FileAttr, Filesystem, Request, ReplyData, ReplyEntry, ReplyAttr, ReplyEmpty, ReplyDirectory};

const DEBUG : bool = true;
// FIXME I hate macros. Basically the same as println! but with an IF!!!!!
macro_rules! log {
    ($fmt:expr) => (if DEBUG {println!(concat!($fmt, "\n"))});
    ($fmt:expr, $($arg:tt)*) => (if DEBUG {println!(concat!($fmt, "\n"), $($arg)*)});
}

// FIXME these are ugly as fuck and it seems like a BAD DECISION to have these kind of macros in rust.
// FIXME in any case, clean up our usage of these later.
// from: http://stackoverflow.com/questions/28392008/more-concise-hashmap-initialization
// auto-converts to String
macro_rules! hashmap {
    ($( $key: expr => $val: expr ),*) => {{
         let mut map = ::std::collections::HashMap::new();
         $( map.insert($key.to_string(), $val); )*
         map
    }}
}
// similar to above, jesus christ this is terrible
macro_rules! hashset {
    ($( $x: expr ),* ) => {{
            let mut set = HashSet::new();
            $( set.insert($x.to_string()); )*
            set
    }}
}

const TTL: Timespec = Timespec { sec: 1, nsec: 0 };                 // 1 second

const CREATE_TIME: Timespec = Timespec { sec: 1381237736, nsec: 0 };    // 2013-10-08 08:56

const HELLO_DIR_ATTR: FileAttr = FileAttr {
    ino: 1,
    size: 0,
    blocks: 0,
    atime: CREATE_TIME,
    mtime: CREATE_TIME,
    ctime: CREATE_TIME,
    crtime: CREATE_TIME,
    kind: FileType::Directory,
    perm: 0o755,
    nlink: 2,
    uid: 501,
    gid: 20,
    rdev: 0,
    flags: 0,
};

const HELLO_TXT_CONTENT: &'static str = "Hello World!\n";

const HELLO_TXT_ATTR: FileAttr = FileAttr {
    ino: 2,
    size: 13,
    blocks: 1,
    atime: CREATE_TIME,
    mtime: CREATE_TIME,
    ctime: CREATE_TIME,
    crtime: CREATE_TIME,
    kind: FileType::RegularFile,
    perm: 0o644,
    nlink: 1,
    uid: 501,
    gid: 20,
    rdev: 0,
    flags: 0,
};

struct UrchinFSEntry {
    display_name: String,
    destination: String,
    metadata: HashMap<String, HashSet<String>>
}

struct UrchinFS {
    entries: Vec<UrchinFSEntry>
}

impl Filesystem for UrchinFS {
    fn lookup (&mut self, _req: &Request, parent: u64, name: &Path, reply: ReplyEntry) {
        if parent == 1 && name.to_str() == Some("hello.txt") {
            reply.entry(&TTL, &HELLO_TXT_ATTR, 0);
        } else {
            reply.error(ENOENT);
        }
    }

    fn getattr (&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        match ino {
            1 => reply.attr(&TTL, &HELLO_DIR_ATTR),
            2 => reply.attr(&TTL, &HELLO_TXT_ATTR),
            _ => reply.error(ENOENT),
        }
    }

    fn read (&mut self, _req: &Request, ino: u64, _fh: u64, offset: u64, _size: u32, reply: ReplyData) {
        if ino == 2 {
            reply.data(&HELLO_TXT_CONTENT.as_bytes()[offset as usize..]);
        } else {
            reply.error(ENOENT);
        }
    }

    fn readdir (&mut self, _req: &Request, ino: u64, _fh: u64, offset: u64, mut reply: ReplyDirectory) {
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, ".");
                reply.add(1, 1, FileType::Directory, "..");
                reply.add(2, 2, FileType::RegularFile, "hello.txt");
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }

    fn access (&mut self, _req: &Request, _ino: u64, _mask: u32, reply: ReplyEmpty) {
        reply.error(ENOSYS);
    }
}

fn main () {
    let mut mountpoint = String::new();
    let mut optstr = String::new();
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("faceted-search FUSE filesystem");
        ap.refer(&mut optstr).add_option(&["-o", "--options"], Store, "comma-separated mount options");
        ap.refer(&mut mountpoint).add_argument("mountpoint", Store, "the mountpoint").required();
        ap.parse_args_or_exit();
    }

// -o plugin=foo,plugin=bar,source=/home/kellen/films/,glob=*.json,format=json,watch=true,type=directory,formatter=generic
// FIXME dynamically load plugins for globbing/formatting see:
// http://stackoverflow.com/questions/22461457/does-rust-have-a-dlopen-equivalent

    // FIXME seems weird to have to have two variable refs
    let vec;
    let mut options : &[&OsStr] = &[];
    if optstr.trim() != "" {
        vec = optstr.trim().split(",").map(|s| OsStr::new(s)).collect::<Vec<_>>();
        options = &vec;
    }

    if DEBUG {
        if options.len() > 0 {
            println!("using options:");
            for s in options {
                match s.to_str() {
                    Some(y) => {println!("\t{}", y);}
                    None => {}
                }
            }
        }
    }

    // FIMXE do this better
    let easter = UrchinFSEntry {
        display_name: "Easter Parade (1948, color)".to_string(),
        destination: "/home/kellen/test".to_string(),
        metadata: hashmap!["year" => hashset!["1948"], "color" => hashset!["color"]]
    };
    let city = UrchinFSEntry {
        display_name: "The Naked City (1948, bw)".to_string(),
        destination: "/home/kellen/test".to_string(),
        metadata: hashmap!["year" => hashset!["1948"], "color" => hashset!["black-and-white"]]
    };
    let vanish = UrchinFSEntry {
        display_name: "The Lady Vanishes (1938, bw)".to_string(),
        destination: "/home/kellen/test".to_string(),
        metadata: hashmap!["year" => hashset!["1938"], "color" => hashset!["black-and-white"]]
    };
    let kiss = UrchinFSEntry {
        display_name: "Kiss Me Deadly (1955, bw)".to_string(),
        destination: "/home/kellen/test".to_string(),
        metadata: hashmap!["year" => hashset!["1955"], "color" => hashset!["black-and-white"]]
    };

    fuse::mount(UrchinFS {entries: vec![easter, city, vanish, kiss]}, &mountpoint, options);
}
<commit_msg>Add error macro<commit_after>extern crate fuse;
extern crate libc;
extern crate time;
extern crate argparse;

use std::string::String;
use std::fmt;
use std::env;
use std::path::Path;
use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;

use libc::{ENOSYS,c_int,ENOENT};
use time::Timespec;
use argparse::{ArgumentParser, StoreTrue, Store};
use fuse::{FileType, FileAttr, Filesystem, Request, ReplyData, ReplyEntry, ReplyAttr, ReplyEmpty, ReplyDirectory};

const DEBUG : bool = true;
// FIXME I hate macros. Basically the same as println! but with an IF!!!!!
macro_rules! log {
    ($fmt:expr) => (if DEBUG {println!(concat!($fmt, "\n"))});
    ($fmt:expr, $($arg:tt)*) => (if DEBUG {println!(concat!($fmt, "\n"), $($arg)*)});
}
// FIMXE this should output to stderr or to syslog
macro_rules! error {
    ($fmt:expr) => (if DEBUG {println!(concat!($fmt, "\n"))});
    ($fmt:expr, $($arg:tt)*) => (if DEBUG {println!(concat!($fmt, "\n"), $($arg)*)});
}

// FIXME these are ugly as fuck and it seems like a BAD DECISION to have these kind of macros in rust.
// FIXME in any case, clean up our usage of these later.
// from: http://stackoverflow.com/questions/28392008/more-concise-hashmap-initialization
// auto-converts to String
macro_rules! hashmap {
    ($( $key: expr => $val: expr ),*) => {{
         let mut map = ::std::collections::HashMap::new();
         $( map.insert($key.to_string(), $val); )*
         map
    }}
}
// similar to above, jesus christ this is terrible
macro_rules! hashset {
    ($( $x: expr ),* ) => {{
            let mut set = HashSet::new();
            $( set.insert($x.to_string()); )*
            set
    }}
}

const TTL: Timespec = Timespec { sec: 1, nsec: 0 };                 // 1 second

const CREATE_TIME: Timespec = Timespec { sec: 1381237736, nsec: 0 };    // 2013-10-08 08:56

const HELLO_DIR_ATTR: FileAttr = FileAttr {
    ino: 1,
    size: 0,
    blocks: 0,
    atime: CREATE_TIME,
    mtime: CREATE_TIME,
    ctime: CREATE_TIME,
    crtime: CREATE_TIME,
    kind: FileType::Directory,
    perm: 0o755,
    nlink: 2,
    uid: 501,
    gid: 20,
    rdev: 0,
    flags: 0,
};

const HELLO_TXT_CONTENT: &'static str = "Hello World!\n";

const HELLO_TXT_ATTR: FileAttr = FileAttr {
    ino: 2,
    size: 13,
    blocks: 1,
    atime: CREATE_TIME,
    mtime: CREATE_TIME,
    ctime: CREATE_TIME,
    crtime: CREATE_TIME,
    kind: FileType::RegularFile,
    perm: 0o644,
    nlink: 1,
    uid: 501,
    gid: 20,
    rdev: 0,
    flags: 0,
};

struct UrchinFSEntry {
    display_name: String,
    destination: String,
    metadata: HashMap<String, HashSet<String>>
}

struct UrchinFS {
    entries: Vec<UrchinFSEntry>
}

impl Filesystem for UrchinFS {
    fn lookup (&mut self, _req: &Request, parent: u64, name: &Path, reply: ReplyEntry) {
        if parent == 1 && name.to_str() == Some("hello.txt") {
            reply.entry(&TTL, &HELLO_TXT_ATTR, 0);
        } else {
            reply.error(ENOENT);
        }
    }

    fn getattr (&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        match ino {
            1 => reply.attr(&TTL, &HELLO_DIR_ATTR),
            2 => reply.attr(&TTL, &HELLO_TXT_ATTR),
            _ => reply.error(ENOENT),
        }
    }

    fn read (&mut self, _req: &Request, ino: u64, _fh: u64, offset: u64, _size: u32, reply: ReplyData) {
        if ino == 2 {
            reply.data(&HELLO_TXT_CONTENT.as_bytes()[offset as usize..]);
        } else {
            reply.error(ENOENT);
        }
    }

    fn readdir (&mut self, _req: &Request, ino: u64, _fh: u64, offset: u64, mut reply: ReplyDirectory) {
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, ".");
                reply.add(1, 1, FileType::Directory, "..");
                reply.add(2, 2, FileType::RegularFile, "hello.txt");
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }

    fn access (&mut self, _req: &Request, _ino: u64, _mask: u32, reply: ReplyEmpty) {
        reply.error(ENOSYS);
    }
}

fn main () {
    let mut mountpoint = String::new();
    let mut optstr = String::new();
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("faceted-search FUSE filesystem");
        ap.refer(&mut optstr).add_option(&["-o", "--options"], Store, "comma-separated mount options");
        ap.refer(&mut mountpoint).add_argument("mountpoint", Store, "the mountpoint").required();
        ap.parse_args_or_exit();
    }

// -o plugin=foo,plugin=bar,source=/home/kellen/films/,glob=*.json,format=json,watch=true,type=directory,formatter=generic
// FIXME dynamically load plugins for globbing/formatting see:
// http://stackoverflow.com/questions/22461457/does-rust-have-a-dlopen-equivalent

    // FIXME seems weird to have to have two variable refs
    let vec;
    let mut options : &[&OsStr] = &[];
    if optstr.trim() != "" {
        vec = optstr.trim().split(",").map(|s| OsStr::new(s)).collect::<Vec<_>>();
        options = &vec;
    }

    if DEBUG {
        if options.len() > 0 {
            println!("using options:");
            for s in options {
                match s.to_str() {
                    Some(y) => {println!("\t{}", y);}
                    None => {}
                }
            }
        }
    }

    // FIMXE do this better
    let easter = UrchinFSEntry {
        display_name: "Easter Parade (1948, color)".to_string(),
        destination: "/home/kellen/test".to_string(),
        metadata: hashmap!["year" => hashset!["1948"], "color" => hashset!["color"]]
    };
    let city = UrchinFSEntry {
        display_name: "The Naked City (1948, bw)".to_string(),
        destination: "/home/kellen/test".to_string(),
        metadata: hashmap!["year" => hashset!["1948"], "color" => hashset!["black-and-white"]]
    };
    let vanish = UrchinFSEntry {
        display_name: "The Lady Vanishes (1938, bw)".to_string(),
        destination: "/home/kellen/test".to_string(),
        metadata: hashmap!["year" => hashset!["1938"], "color" => hashset!["black-and-white"]]
    };
    let kiss = UrchinFSEntry {
        display_name: "Kiss Me Deadly (1955, bw)".to_string(),
        destination: "/home/kellen/test".to_string(),
        metadata: hashmap!["year" => hashset!["1955"], "color" => hashset!["black-and-white"]]
    };

    fuse::mount(UrchinFS {entries: vec![easter, city, vanish, kiss]}, &mountpoint, options);
}
<|endoftext|>