<commit_before>use std::env;
use std::fs;
use std::io;
use std::path;

use sdl2;

use GameError;
use warn;
use conf;

/// Provides an interface to the user's filesystem.
///
/// This module provides access to files in specific places:
/// * The `resources/` subdirectory in the same directory as the program executable,
/// * The `resources.zip` file in the same directory as the program executable (eventually),
/// * The root folder of the game's `save` directory (eventually)
///
/// Files will be looked for in these places in order.
///
/// Right now files are read-only.  When we can write files, they will be written
/// to the game's save directory.
///
/// TODO: See SDL_GetBasePath and SDL_GetPrefPath!
#[derive(Debug)]
pub struct Filesystem {
    base_path: path::PathBuf,
    user_path: path::PathBuf,
    resource_path: path::PathBuf,
}

/// Represents a file, either in the filesystem, or in the resources zip file,
/// or whatever.
#[derive(Debug)]
pub enum File {
    FSFile(fs::File),
}

impl io::Read for File {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            File::FSFile(ref mut f) => f.read(buf),
        }
    }
}

impl Filesystem {
    pub fn new() -> Filesystem {
        // BUGGO: We should resolve errors here instead of unwrap.
        let root_path_string = sdl2::filesystem::base_path().unwrap();
        // BUGGO: We need to have the application ID in this path somehow,
        // except the best place to put it would be in the Conf object...
        // which is loaded using the Filesystem.  Hmmmm.
        // We probably need a Filesystem::config_file_path() function to
        // bootstrap the process.
        let pref_path_string = sdl2::filesystem::pref_path("ggez", "").ok().unwrap();

        let mut root_path = path::PathBuf::from(root_path_string);
        // Ditch the filename (if any)
        if let Some(_) = root_path.file_name() {
            root_path.pop();
        }

        let mut resource_path = root_path.clone();
        resource_path.push("resources");
        // TODO: This should also check for resources.zip
        if !resource_path.exists() || !resource_path.is_dir() {
            let message = String::from("'resources' directory not found!");
            let _ = warn(GameError::ResourceLoadError(message));
        }

        let user_path = path::PathBuf::from(pref_path_string);

        Filesystem {
            resource_path: resource_path,
            base_path: root_path,
            user_path: user_path
        }
    }

    pub fn open(&self, path: &path::Path) -> Result<File, GameError> {

        // Look in resource directory
        let pathbuf = try!(self.mongle_path(path));
        if pathbuf.is_file() {
            // BUGGO: Unwrap
            let f = fs::File::open(pathbuf).unwrap();
            return Ok(File::FSFile(f));
        }

        // TODO: Look in resources.zip

        // TODO: Look in save directory

        // Welp, can't find it.
        let errmessage = String::from(path.to_str().unwrap());
        Err(GameError::ResourceNotFound(errmessage))
    }

    /// Takes a relative path and returns an absolute PathBuf
    /// based in the Filesystem's root path.
    /// Sorry, can't think of a better name for this.
    fn mongle_path(&self, path: &path::Path) -> Result<path::PathBuf, GameError> {
        if !path.is_relative() {
            let err = GameError::ResourceNotFound(String::from(path.to_str().unwrap()));
            Err(err)
        } else {
            let pathbuf = self.resource_path.join(path);
            Ok(pathbuf)
        }
    }

    /// Check whether a file or directory exists.
    pub fn exists(&self, path: &path::Path) -> bool {
        match self.mongle_path(path) {
            Ok(p) => p.exists(),
            Err(_) => false,
        }
        // TODO: Look in resources.zip, save directory.
    }

    /// Check whether a path points at a file.
    pub fn is_file(&self, path: &path::Path) -> bool {
        match self.mongle_path(path) {
            Ok(p) => p.is_file(),
            Err(_) => false,
        }
        // TODO: Look in resources.zip, save directory.
    }

    /// Check wehther a path points at a directory.
    pub fn is_dir(&self, path: &path::Path) -> bool {
        match self.mongle_path(path) {
            Ok(p) => p.is_dir(),
            Err(_) => false,
        }
        // TODO: Look in resources.zip, save directory.
    }

    /// Return the full path to the directory containing the exe
    pub fn get_source_dir(&self) -> &path::Path {
        &self.base_path
    }

    /// Return the full path to the user directory
    pub fn get_user_dir(&self) -> &path::Path {
        &self.user_path
    }

    /// Returns the full path to the resource directory
    /// (even if it doesn't exist)
    pub fn get_resource_dir(&self) -> &path::Path {
        &self.resource_path
    }

    /// Returns an iterator over all files and directories in the directory.
    /// Lists the base directory if an empty path is given.
    pub fn read_dir(&self, path: &path::Path) -> io::Result<fs::ReadDir> {
        let dest = self.resource_path.join(path);
        dest.read_dir()
    }
}

fn get_dummy_fs_for_tests() -> Filesystem {
    let mut path = path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    path.push("resources");
    let f = Filesystem {
        resource_path: path.clone(),
        user_path: path.clone(),
        base_path: path.clone(),
    };
    f

}

#[test]
fn test_file_exists() {
    let f = get_dummy_fs_for_tests();

    let tile_file = path::Path::new("tile.png");
    assert!(f.exists(tile_file));
    assert!(f.is_file(tile_file));
}

#[test]
fn test_read_dir() {
    let f = get_dummy_fs_for_tests();

    let dir_contents_size = f.read_dir(path::Path::new("")).unwrap().count();
    assert!(dir_contents_size > 0);

}
<commit_msg>Silly refactoring of tests while I decide what to do next.<commit_after>use std::env;
use std::fs;
use std::io;
use std::path;

use sdl2;

use GameError;
use warn;
use conf;

/// Provides an interface to the user's filesystem.
///
/// This module provides access to files in specific places:
/// * The `resources/` subdirectory in the same directory as the program executable,
/// * The `resources.zip` file in the same directory as the program executable (eventually),
/// * The root folder of the game's `save` directory (eventually)
///
/// Files will be looked for in these places in order.
///
/// Right now files are read-only.  When we can write files, they will be written
/// to the game's save directory.
///
/// TODO: See SDL_GetBasePath and SDL_GetPrefPath!
#[derive(Debug)]
pub struct Filesystem {
    base_path: path::PathBuf,
    user_path: path::PathBuf,
    resource_path: path::PathBuf,
}

/// Represents a file, either in the filesystem, or in the resources zip file,
/// or whatever.
#[derive(Debug)]
pub enum File {
    FSFile(fs::File),
}

impl io::Read for File {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            File::FSFile(ref mut f) => f.read(buf),
        }
    }
}

impl Filesystem {
    pub fn new() -> Filesystem {
        // BUGGO: We should resolve errors here instead of unwrap.
        let root_path_string = sdl2::filesystem::base_path().unwrap();
        // BUGGO: We need to have the application ID in this path somehow,
        // except the best place to put it would be in the Conf object...
        // which is loaded using the Filesystem.  Hmmmm.
        // We probably need a Filesystem::config_file_path() function to
        // bootstrap the process.
        let pref_path_string = sdl2::filesystem::pref_path("ggez", "").ok().unwrap();

        let mut root_path = path::PathBuf::from(root_path_string);
        // Ditch the filename (if any)
        if let Some(_) = root_path.file_name() {
            root_path.pop();
        }

        let mut resource_path = root_path.clone();
        resource_path.push("resources");
        // TODO: This should also check for resources.zip
        if !resource_path.exists() || !resource_path.is_dir() {
            let message = String::from("'resources' directory not found!");
            let _ = warn(GameError::ResourceLoadError(message));
        }

        let user_path = path::PathBuf::from(pref_path_string);

        Filesystem {
            resource_path: resource_path,
            base_path: root_path,
            user_path: user_path
        }
    }

    pub fn open(&self, path: &path::Path) -> Result<File, GameError> {

        // Look in resource directory
        let pathbuf = try!(self.mongle_path(path));
        if pathbuf.is_file() {
            // BUGGO: Unwrap
            let f = fs::File::open(pathbuf).unwrap();
            return Ok(File::FSFile(f));
        }

        // TODO: Look in resources.zip

        // TODO: Look in save directory

        // Welp, can't find it.
        let errmessage = String::from(path.to_str().unwrap());
        Err(GameError::ResourceNotFound(errmessage))
    }

    /// Takes a relative path and returns an absolute PathBuf
    /// based in the Filesystem's root path.
    /// Sorry, can't think of a better name for this.
    fn mongle_path(&self, path: &path::Path) -> Result<path::PathBuf, GameError> {
        if !path.is_relative() {
            let err = GameError::ResourceNotFound(String::from(path.to_str().unwrap()));
            Err(err)
        } else {
            let pathbuf = self.resource_path.join(path);
            Ok(pathbuf)
        }
    }

    /// Check whether a file or directory exists.
    pub fn exists(&self, path: &path::Path) -> bool {
        match self.mongle_path(path) {
            Ok(p) => p.exists(),
            Err(_) => false,
        }
        // TODO: Look in resources.zip, save directory.
    }

    /// Check whether a path points at a file.
    pub fn is_file(&self, path: &path::Path) -> bool {
        match self.mongle_path(path) {
            Ok(p) => p.is_file(),
            Err(_) => false,
        }
        // TODO: Look in resources.zip, save directory.
    }

    /// Check wehther a path points at a directory.
    pub fn is_dir(&self, path: &path::Path) -> bool {
        match self.mongle_path(path) {
            Ok(p) => p.is_dir(),
            Err(_) => false,
        }
        // TODO: Look in resources.zip, save directory.
    }

    /// Return the full path to the directory containing the exe
    pub fn get_source_dir(&self) -> &path::Path {
        &self.base_path
    }

    /// Return the full path to the user directory
    pub fn get_user_dir(&self) -> &path::Path {
        &self.user_path
    }

    /// Returns the full path to the resource directory
    /// (even if it doesn't exist)
    pub fn get_resource_dir(&self) -> &path::Path {
        &self.resource_path
    }

    /// Returns an iterator over all files and directories in the directory.
    /// Lists the base directory if an empty path is given.
    pub fn read_dir(&self, path: &path::Path) -> io::Result<fs::ReadDir> {
        let dest = self.resource_path.join(path);
        dest.read_dir()
    }
}

mod tests {
    use filesystem::*;
    use std::path;

    fn get_dummy_fs_for_tests() -> Filesystem {
        let mut path = path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.push("resources");
        let f = Filesystem {
            resource_path: path.clone(),
            user_path: path.clone(),
            base_path: path.clone(),
        };
        f

    }

    #[test]
    fn test_file_exists() {
        let f = get_dummy_fs_for_tests();

        let tile_file = path::Path::new("tile.png");
        assert!(f.exists(tile_file));
        assert!(f.is_file(tile_file));
    }

    #[test]
    fn test_read_dir() {
        let f = get_dummy_fs_for_tests();

        let dir_contents_size = f.read_dir(path::Path::new("")).unwrap().count();
        assert!(dir_contents_size > 0);

    }
}

<|endoftext|>


<commit_before>//! Diagnostics rendering and fixits.
//!
//! Most of the diagnostics originate from the dark depth of the compiler, and
//! are originally expressed in term of IR. When we emit the diagnostic, we are
//! usually not in the position to decide how to best "render" it in terms of
//! user-authored source code. We are especially not in the position to offer
//! fixits, as the compiler completely lacks the infrastructure to edit the
//! source code.
//!
//! Instead, we "bubble up" raw, structured diagnostics until the `hir` crate,
//! where we "cook" them so that each diagnostic is formulated in terms of `hir`
//! types. Well, at least that's the aspiration, the "cooking" is somewhat
//! ad-hoc at the moment. Anyways, we get a bunch of ide-friendly diagnostic
//! structs from hir, and we want to render them to unified serializable
//! representation (span, level, message) here. If we can, we also provide
//! fixits. By the way, that's why we want to keep diagnostics structured
//! internally -- so that we have all the info to make fixes.
//!
//! We have one "handler" module per diagnostic code. Such a module contains
//! rendering, optional fixes and tests. It's OK if some low-level compiler
//! functionality ends up being tested via a diagnostic.
//!
//! There are also a couple of ad-hoc diagnostics implemented directly here, we
//! don't yet have a great pattern for how to do them properly.

mod handlers {
    // pub(crate) mod add_reference_here;
    pub(crate) mod break_outside_of_loop;
    pub(crate) mod inactive_code;
    pub(crate) mod incorrect_case;
    pub(crate) mod invalid_derive_target;
    pub(crate) mod macro_error;
    pub(crate) mod malformed_derive;
    pub(crate) mod mismatched_arg_count;
    pub(crate) mod missing_fields;
    pub(crate) mod missing_match_arms;
    // pub(crate) mod missing_ok_or_some_in_tail_expr;
    pub(crate) mod missing_unsafe;
    pub(crate) mod no_such_field;
    // pub(crate) mod remove_this_semicolon;
    pub(crate) mod replace_filter_map_next_with_find_map;
    pub(crate) mod unimplemented_builtin_macro;
    pub(crate) mod unresolved_extern_crate;
    pub(crate) mod unresolved_import;
    pub(crate) mod unresolved_macro_call;
    pub(crate) mod unresolved_module;
    pub(crate) mod unresolved_proc_macro;
    pub(crate) mod type_mismatch;

    // The handlers below are unusual, the implement the diagnostics as well.
    pub(crate) mod field_shorthand;
    pub(crate) mod useless_braces;
    pub(crate) mod unlinked_file;
}

#[cfg(test)]
mod tests;

use hir::{diagnostics::AnyDiagnostic, Semantics};
use ide_db::{
    assists::{Assist, AssistId, AssistKind, AssistResolveStrategy},
    base_db::{FileId, SourceDatabase},
    label::Label,
    source_change::SourceChange,
    RootDatabase,
};
use rustc_hash::FxHashSet;
use syntax::{ast::AstNode, TextRange};

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct DiagnosticCode(pub &'static str);

impl DiagnosticCode {
    pub fn as_str(&self) -> &str {
        self.0
    }
}

#[derive(Debug)]
pub struct Diagnostic {
    pub code: DiagnosticCode,
    pub message: String,
    pub range: TextRange,
    pub severity: Severity,
    pub unused: bool,
    pub experimental: bool,
    pub fixes: Option<Vec<Assist>>,
}

impl Diagnostic {
    fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic {
        let message = message.into();
        Diagnostic {
            code: DiagnosticCode(code),
            message,
            range,
            severity: Severity::Error,
            unused: false,
            experimental: false,
            fixes: None,
        }
    }

    fn experimental(mut self) -> Diagnostic {
        self.experimental = true;
        self
    }

    fn severity(mut self, severity: Severity) -> Diagnostic {
        self.severity = severity;
        self
    }

    fn with_fixes(mut self, fixes: Option<Vec<Assist>>) -> Diagnostic {
        self.fixes = fixes;
        self
    }

    fn with_unused(mut self, unused: bool) -> Diagnostic {
        self.unused = unused;
        self
    }
}

#[derive(Debug, Copy, Clone)]
pub enum Severity {
    Error,
    // We don't actually emit this one yet, but we should at some point.
    // Warning,
    WeakWarning,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ExprFillDefaultMode {
    Todo,
    Default,
}
impl Default for ExprFillDefaultMode {
    fn default() -> Self {
        Self::Todo
    }
}

#[derive(Default, Debug, Clone)]
pub struct DiagnosticsConfig {
    pub disable_experimental: bool,
    pub disabled: FxHashSet<String>,
    pub expr_fill_default: ExprFillDefaultMode,
}

struct DiagnosticsContext<'a> {
    config: &'a DiagnosticsConfig,
    sema: Semantics<'a, RootDatabase>,
    resolve: &'a AssistResolveStrategy,
}

pub fn diagnostics(
    db: &RootDatabase,
    config: &DiagnosticsConfig,
    resolve: &AssistResolveStrategy,
    file_id: FileId,
) -> Vec<Diagnostic> {
    let _p = profile::span("diagnostics");
    let sema = Semantics::new(db);
    let parse = db.parse(file_id);
    let mut res = Vec::new();

    // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
    res.extend(
        parse.errors().iter().take(128).map(|err| {
            Diagnostic::new("syntax-error", format!("Syntax Error: {}", err), err.range())
        }),
    );

    for node in parse.tree().syntax().descendants() {
        handlers::useless_braces::useless_braces(&mut res, file_id, &node);
        handlers::field_shorthand::field_shorthand(&mut res, file_id, &node);
    }

    let module = sema.to_module_def(file_id);

    let ctx = DiagnosticsContext { config, sema, resolve };
    if module.is_none() {
        handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id);
    }

    let mut diags = Vec::new();
    if let Some(m) = module {
        m.diagnostics(db, &mut diags)
    }

    for diag in diags {
        #[rustfmt::skip]
        let d = match diag {
            AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
            AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
            AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
            AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
            AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
            AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
            AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
            AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
            AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
            AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
            AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
            AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
            AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
            AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d),
            AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d),
            AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
            AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d),
            AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),

            AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
                Some(it) => it,
                None => continue,
            }
        };
        res.push(d)
    }

    res.retain(|d| {
        !ctx.config.disabled.contains(d.code.as_str())
            && !(ctx.config.disable_experimental && d.experimental)
    });

    res
}

fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist {
    let mut res = unresolved_fix(id, label, target);
    res.source_change = Some(source_change);
    res
}

fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
    assert!(!id.contains(' '));
    Assist {
        id: AssistId(id, AssistKind::QuickFix),
        label: Label::new(label.to_string()),
        group: None,
        target,
        source_change: None,
    }
}
<commit_msg>Clean up commented-out lines<commit_after>//! Diagnostics rendering and fixits.
//!
//! Most of the diagnostics originate from the dark depth of the compiler, and
//! are originally expressed in term of IR. When we emit the diagnostic, we are
//! usually not in the position to decide how to best "render" it in terms of
//! user-authored source code. We are especially not in the position to offer
//! fixits, as the compiler completely lacks the infrastructure to edit the
//! source code.
//!
//! Instead, we "bubble up" raw, structured diagnostics until the `hir` crate,
//! where we "cook" them so that each diagnostic is formulated in terms of `hir`
//! types. Well, at least that's the aspiration, the "cooking" is somewhat
//! ad-hoc at the moment. Anyways, we get a bunch of ide-friendly diagnostic
//! structs from hir, and we want to render them to unified serializable
//! representation (span, level, message) here. If we can, we also provide
//! fixits. By the way, that's why we want to keep diagnostics structured
//! internally -- so that we have all the info to make fixes.
//!
//! We have one "handler" module per diagnostic code. Such a module contains
//! rendering, optional fixes and tests. It's OK if some low-level compiler
//! functionality ends up being tested via a diagnostic.
//!
//! There are also a couple of ad-hoc diagnostics implemented directly here, we
//! don't yet have a great pattern for how to do them properly.

mod handlers {
    pub(crate) mod break_outside_of_loop;
    pub(crate) mod inactive_code;
    pub(crate) mod incorrect_case;
    pub(crate) mod invalid_derive_target;
    pub(crate) mod macro_error;
    pub(crate) mod malformed_derive;
    pub(crate) mod mismatched_arg_count;
    pub(crate) mod missing_fields;
    pub(crate) mod missing_match_arms;
    pub(crate) mod missing_unsafe;
    pub(crate) mod no_such_field;
    pub(crate) mod replace_filter_map_next_with_find_map;
    pub(crate) mod type_mismatch;
    pub(crate) mod unimplemented_builtin_macro;
    pub(crate) mod unresolved_extern_crate;
    pub(crate) mod unresolved_import;
    pub(crate) mod unresolved_macro_call;
    pub(crate) mod unresolved_module;
    pub(crate) mod unresolved_proc_macro;

    // The handlers below are unusual, the implement the diagnostics as well.
    pub(crate) mod field_shorthand;
    pub(crate) mod useless_braces;
    pub(crate) mod unlinked_file;
}

#[cfg(test)]
mod tests;

use hir::{diagnostics::AnyDiagnostic, Semantics};
use ide_db::{
    assists::{Assist, AssistId, AssistKind, AssistResolveStrategy},
    base_db::{FileId, SourceDatabase},
    label::Label,
    source_change::SourceChange,
    RootDatabase,
};
use rustc_hash::FxHashSet;
use syntax::{ast::AstNode, TextRange};

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct DiagnosticCode(pub &'static str);

impl DiagnosticCode {
    pub fn as_str(&self) -> &str {
        self.0
    }
}

#[derive(Debug)]
pub struct Diagnostic {
    pub code: DiagnosticCode,
    pub message: String,
    pub range: TextRange,
    pub severity: Severity,
    pub unused: bool,
    pub experimental: bool,
    pub fixes: Option<Vec<Assist>>,
}

impl Diagnostic {
    fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic {
        let message = message.into();
        Diagnostic {
            code: DiagnosticCode(code),
            message,
            range,
            severity: Severity::Error,
            unused: false,
            experimental: false,
            fixes: None,
        }
    }

    fn experimental(mut self) -> Diagnostic {
        self.experimental = true;
        self
    }

    fn severity(mut self, severity: Severity) -> Diagnostic {
        self.severity = severity;
        self
    }

    fn with_fixes(mut self, fixes: Option<Vec<Assist>>) -> Diagnostic {
        self.fixes = fixes;
        self
    }

    fn with_unused(mut self, unused: bool) -> Diagnostic {
        self.unused = unused;
        self
    }
}

#[derive(Debug, Copy, Clone)]
pub enum Severity {
    Error,
    // We don't actually emit this one yet, but we should at some point.
    // Warning,
    WeakWarning,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ExprFillDefaultMode {
    Todo,
    Default,
}
impl Default for ExprFillDefaultMode {
    fn default() -> Self {
        Self::Todo
    }
}

#[derive(Default, Debug, Clone)]
pub struct DiagnosticsConfig {
    pub disable_experimental: bool,
    pub disabled: FxHashSet<String>,
    pub expr_fill_default: ExprFillDefaultMode,
}

struct DiagnosticsContext<'a> {
    config: &'a DiagnosticsConfig,
    sema: Semantics<'a, RootDatabase>,
    resolve: &'a AssistResolveStrategy,
}

pub fn diagnostics(
    db: &RootDatabase,
    config: &DiagnosticsConfig,
    resolve: &AssistResolveStrategy,
    file_id: FileId,
) -> Vec<Diagnostic> {
    let _p = profile::span("diagnostics");
    let sema = Semantics::new(db);
    let parse = db.parse(file_id);
    let mut res = Vec::new();

    // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
    res.extend(
        parse.errors().iter().take(128).map(|err| {
            Diagnostic::new("syntax-error", format!("Syntax Error: {}", err), err.range())
        }),
    );

    for node in parse.tree().syntax().descendants() {
        handlers::useless_braces::useless_braces(&mut res, file_id, &node);
        handlers::field_shorthand::field_shorthand(&mut res, file_id, &node);
    }

    let module = sema.to_module_def(file_id);

    let ctx = DiagnosticsContext { config, sema, resolve };
    if module.is_none() {
        handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id);
    }

    let mut diags = Vec::new();
    if let Some(m) = module {
        m.diagnostics(db, &mut diags)
    }

    for diag in diags {
        #[rustfmt::skip]
        let d = match diag {
            AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
            AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
            AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
            AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
            AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
            AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
            AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
            AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
            AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
            AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
            AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
            AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
            AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
            AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d),
            AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d),
            AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
            AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d),
            AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),

            AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
                Some(it) => it,
                None => continue,
            }
        };
        res.push(d)
    }

    res.retain(|d| {
        !ctx.config.disabled.contains(d.code.as_str())
            && !(ctx.config.disable_experimental && d.experimental)
    });

    res
}

fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist {
    let mut res = unresolved_fix(id, label, target);
    res.source_change = Some(source_change);
    res
}

fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
    assert!(!id.contains(' '));
    Assist {
        id: AssistId(id, AssistKind::QuickFix),
        label: Label::new(label.to_string()),
        group: None,
        target,
        source_change: None,
    }
}
<|endoftext|>


<commit_before>extern crate byteorder;

use std;
use std::io::Write;
use self::byteorder::NativeEndian;
use self::byteorder::WriteBytesExt;

/// Represents a value in the scalar set.
/// The values must be able to store booking information
/// for managing the contents of the set. These requirements
/// are captured in this trait.
pub trait Value
{
    /// Gets the index of the bucket for this value.
    fn get_bucket_index(
        &self,
        buckets: &Self,
    ) -> usize;

    /// Gets a zero.
    fn zero() -> Self;

    /// Gets the integer value as a size.
    fn as_index( &self ) -> usize;

    /// Converts the number of buckets into the value type.i
    fn from_bucket_count( bucket_count: &usize ) -> Self;

    /// Converts the number of set members into the value type.
    fn from_member_count( member_count: usize ) -> Self;

    /// Converts the given index into the value type.
    fn from_index( index: usize ) -> Self;

    /// Decrements the value by one.
    fn decrement( &self ) -> Self;

    /// Provides total ordering for elements.
    fn cmp(
        &self,
        other: &Self,
    ) -> std::cmp::Ordering;

    /// Serializes the value.
    fn serialize(
        &self,
        &mut Write,
    ) -> Result<(), std::io::Error>;
}

/// Implements Value trait for i32
impl Value for i32
{
    /// Gets the index of the bucket for this value.
    fn get_bucket_index(
        &self,
        buckets: &i32,
    ) -> usize
    {
        return ( self.clone() as usize ) % ( buckets.clone() as usize );
    }

    /// Gets a zero.
    fn zero() -> i32
    {
        return 0;
    }

    /// Gets the integer value as a size.
    fn as_index( &self ) -> usize
    {
        return self.clone() as usize;
    }

    /// Converts the specified value to a value that can be stored in the container.
    fn from_bucket_count( bucket_count: &usize ) -> i32
    {
        return bucket_count.clone() as i32;
    }

    /// Converts the number of set members into the value type.
    fn from_member_count( member_count: usize ) -> i32
    {
        return member_count as i32;
    }

    /// Converts the given value to index.
    fn from_index( index: usize ) -> i32
    {
        return index as i32;
    }

    /// Decrements the value by one.
    fn decrement( &self ) -> i32
    {
        return self - 1;
    }

    /// Provides total ordering for elements.
    fn cmp(
        &self,
        other: &i32,
    ) -> std::cmp::Ordering
    {
        std::cmp::Ord::cmp( self, other )
    }

    /// Serializes the value.
    fn serialize(
        &self,
        writer: &mut Write,
    ) -> Result<(), std::io::Error>
    {
        writer.write_i32::<NativeEndian>( *self )?;
        Ok( () )
    }
}

/// Implements Value trait for f32
/// * The largest permissible value is 2^24.
/// * Mainly implemented to provide an easy to use interface for sending the structure to GPU.
#[cfg(any(feature = "floats", test))]
impl Value for f32
{
    /// Gets the index of the bucket for this value.
    fn get_bucket_index(
        &self,
        buckets: &f32,
    ) -> usize
    {
        return ( self.clone() as usize ) % ( buckets.clone() as usize );
    }

    /// Gets a zero.
    fn zero() -> f32
    {
        return 0.0;
    }

    /// Gets the integer value as a size.
    fn as_index( &self ) -> usize
    {
        return self.clone() as usize;
    }

    /// Converts the specified value to a value that can be stored in the container.
    fn from_bucket_count( bucket_count: &usize ) -> f32
    {
        return bucket_count.clone() as f32;
    }

    /// Converts the number of set members into the value type.
    fn from_member_count( member_count: usize ) -> f32
    {
        return member_count as f32;
    }

    /// Converts the given value to index.
    fn from_index( index: usize ) -> f32
    {
        return index as f32;
    }

    /// Decrements the value by one.
    fn decrement( &self ) -> f32
    {
        return self - 1.0;
    }

    /// Provides total ordering for Value elements.
    fn cmp(
        &self,
        other: &f32,
    ) -> std::cmp::Ordering
    {
        let me = self.clone() as i32;
        let other = other.clone() as i32;
        std::cmp::Ord::cmp( &me, &other )
    }

    /// Serializes the value.
    fn serialize(
        &self,
        writer: &mut Write,
    ) -> Result<(), std::io::Error>
    {
        writer.write_f32::<NativeEndian>( *self )?;
        Ok( () )
    }
}

/// The index or position in the array that holds the number of members in the set.
const SIZE_INDEX: usize = 1;

/// The index or position in the array that holds the index to the first value of the first bucket.
const FIRST_BUCKET_INDEX: usize = 2;

/// Defines how the data is stored in the scalar set.
enum Storage<'a, TA, TB>
where
    TB: 'a,
{
    /// The data is stored as a vector.
    Vector
    { data: Vec<TA> },

    /// The data is stored in a slice. Not owned by the scalar set.
    Slice
    { data: &'a [TB] },
}

/// Scalar set cabable of storing values with Value trait.
pub struct RoScalarSet<'a, T>
where
    T: std::clone::Clone + Value + 'a,
{
    _storage: Storage<'a, T, T>,
}

impl<'a, T> Clone for RoScalarSet<'a, T>
where
    T: std::clone::Clone + Value + 'a,
{
    /// Returns a copy of the set.
    fn clone( &self ) -> Self
    {
        RoScalarSet::new( self.borrow_values() )
    }

    /// Copies the content from the given set.alloc
    fn clone_from(
        &mut self,
        source: &Self,
    )
    {
        let storage = RoScalarSet::create_storage( source.borrow_values() );
        self._storage = storage;
    }
}

impl<'a, T> RoScalarSet<'a, T>
where
    T: std::clone::Clone + Value + 'a,
{
    /// Returns a new integer hash set holding the specified values.
    ///
    /// # ArgumentRoScalarSets
    ///
    /// * 'values' Holds the values stored in the hash set.
    pub fn new<'b, 'c>( values: &'c [T] ) -> RoScalarSet<'b, T>
    {
        let storage = RoScalarSet::create_storage( values );
        return RoScalarSet { _storage: storage };
    }

    /// Attaches an integer hash set to a slice holding the values.
    ///
    /// # ArgumentRoScalarSets
    ///
    /// * 'values' Slice that holds the values stored in the hash set.
    pub fn attach<'b>( buffer: &'b [T] ) -> Result<( RoScalarSet<'b, T>, &'b [T] ), &str>
    {
        // Determine the length of this scalar set.
        if buffer.len() < 4
        {
            return Err( "The buffer is to small" );
        }
        let buckets = buffer[0].as_index();
        let values = buffer[SIZE_INDEX].as_index();
        let total_size = FIRST_BUCKET_INDEX + ( buckets + 1 ) + values;

        // Attach.
        let ( set, remainder ) = buffer.split_at( total_size );
        let storage: Storage<T, T> = Storage::Slice { data: set };
        return Ok( ( RoScalarSet { _storage: storage }, remainder ) );
    }

    /// Checks if any the sets have anything in common.
    pub fn any(
        &self,
        other: &RoScalarSet<T>,
    ) -> bool
    {
        // Determine the smaller set.
        let small;
        let large;
        if other.size() < self.size()
        {
            small = other;
            large = self;
        }
        else
        {
            small = self;
            large = other;
        }

        // For better performance we iterate the smaller set.
        for s in small.borrow_values()
        {
            if large.contains( s )
            {
                return true;
            }
        }

        // Values was not found.
        return false;
    }

    /// Checks whether the given value exists in the set or not.
    pub fn contains(
        &self,
        value: &T,
    ) -> bool
    {
        // Get the bucket associated with the value.
        let bucket = self.get_bucket( &value );

        // Locate the value.
        match bucket.binary_search_by( |probe| probe.cmp( &value ) )
        {
            Ok( _ ) => return true,
            Err( _ ) => return false,
        }
    }

    /// Gets the number of members in the set.
    pub fn size( &self ) -> usize
    {
        let storage = self.borrow_storage();
        return storage[SIZE_INDEX].as_index();
    }

    /// Gets the number of buckets.
    pub fn bucket_count( &self ) -> usize
    {
        let storage = self.borrow_storage();
        return storage[0].as_index();
    }

    /// Serializes the scalar set into the given writer.
    pub fn serialize<W>(
        &self,
        writer: &mut W,
    ) -> Result<(), std::io::Error>
    where
        W: Write,
    {
        // Write our intern
        let storage = self.borrow_storage();
        for v in storage
        {
            v.serialize( writer )?;
        }
        Ok( () )
    }

    /// Gets a read-only slice containing the values of a bucket that holdes the given value.
    fn get_bucket(
        &self,
        value: &T,
    ) -> &[T]
    {
        // Determine the bucket.
        let storage = self.borrow_storage();
        let bucket_id: usize = value.get_bucket_index( &storage[0] );
        let bucket_index = bucket_id + FIRST_BUCKET_INDEX;
        let begin: usize = storage[bucket_index].as_index();
        let end: usize = storage[( bucket_index + 1 )].as_index();
        let ( _, remainder ) = storage.split_at( begin );
        let ( bucket, _ ) = remainder.split_at( end - begin );
        return bucket;
    }

    /// Borrows the values of the set.
    fn borrow_values( &'a self ) -> &'a [T]
    {
        // Split the value part from the storage.
        let start = FIRST_BUCKET_INDEX + self.bucket_count() + 1;
        let ( _, values ) = self.borrow_storage().split_at( start );
        return values;
    }

    /// Borrows the storage for accessing the values.
    fn borrow_storage( &'a self ) -> &'a [T]
    {
        let s: &'a [T] = match &self._storage
        {
            &Storage::Vector { ref data } => data.as_slice(),
            &Storage::Slice { ref data } => data,
        };
        return s;
    }

    /// Creates a new storage buffer from the given values.
    fn create_storage( values: &[T] ) -> Storage<'a, T, T>
    {
        // Determine the number of buckets. We introduce a 5% overhead.
        let bucket_count: usize = ( values.len() as f64 * 0.05 ).ceil() as usize;
        let mut storage: Vec<T> = vec![T::zero(); FIRST_BUCKET_INDEX + bucket_count + 1 + values.len()];

        // Store the number of buckets.
        let buckets = T::from_bucket_count( &bucket_count );
        storage[0] = buckets.clone();

        // Store the number of members.
        storage[SIZE_INDEX] = T::from_member_count( values.len() );

        // Count the values required for each bucket.
        let mut values_in_buckets: Vec<i32> = vec![0; bucket_count];
        for v in values
        {
            let bucket = v.get_bucket_index( &buckets );
            values_in_buckets[bucket] += 1;
        }

        // Set bucket pointers to point the end of each bucket.
        // This way we can use the bucket pointers to track the next empty position when
        // distributing the values into the buckets.
        let first_bucket: usize = FIRST_BUCKET_INDEX;
        let data_start: usize = FIRST_BUCKET_INDEX + bucket_count + 1;
        let mut previous_bucket_end = data_start;
        for b in 0..bucket_count
        {
            // The end of the previous bucket is the start of the previous bucket.
            let index = previous_bucket_end + values_in_buckets[b].as_index();
            storage[b + first_bucket] = T::from_index( index );
            previous_bucket_end = index;
        }

        // Fix the end of the last bucket.
        storage[first_bucket + bucket_count] = T::from_index( storage.len() );

        // Put values into buckets.
        for v in values
        {
            // Determine bucket for the value.
            let bucket_id = v.get_bucket_index( &buckets );

            // Make room for the new value.
            storage[bucket_id + first_bucket] = storage[bucket_id + first_bucket].decrement();
            let value_index: usize = storage[bucket_id + first_bucket].as_index();

            // And finally store the value.
            storage[value_index] = v.clone();
        }

        // Sort each bucket to enable binary search.
        for b in 0..bucket_count
        {
            // Determine the location of the bucket.
            let begin: usize = storage[b + first_bucket].as_index();
            let end: usize = storage[b + first_bucket + 1].as_index();
            if end < begin
            {
                panic!( "Invalid bucket: {}", b );
            }

            // Get a splice that represents the bucket and sort it.
            let ( _, remainder ) = storage.split_at_mut( begin );
            let ( bucket, _ ) = remainder.split_at_mut( end - begin );
            bucket.sort_by( |a, b| a.cmp( b ) );
        }

        let storage: Storage<'a, T, T> = Storage::Vector { data: storage };
        storage
    }
}
<commit_msg>Removed a redundant temporary variable<commit_after>extern crate byteorder;

use std;
use std::io::Write;
use self::byteorder::NativeEndian;
use self::byteorder::WriteBytesExt;

/// Represents a value in the scalar set.
/// The values must be able to store booking information
/// for managing the contents of the set. These requirements
/// are captured in this trait.
pub trait Value
{
    /// Gets the index of the bucket for this value.
    fn get_bucket_index(
        &self,
        buckets: &Self,
    ) -> usize;

    /// Gets a zero.
    fn zero() -> Self;

    /// Gets the integer value as a size.
    fn as_index( &self ) -> usize;

    /// Converts the number of buckets into the value type.i
    fn from_bucket_count( bucket_count: &usize ) -> Self;

    /// Converts the number of set members into the value type.
    fn from_member_count( member_count: usize ) -> Self;

    /// Converts the given index into the value type.
    fn from_index( index: usize ) -> Self;

    /// Decrements the value by one.
    fn decrement( &self ) -> Self;

    /// Provides total ordering for elements.
    fn cmp(
        &self,
        other: &Self,
    ) -> std::cmp::Ordering;

    /// Serializes the value.
    fn serialize(
        &self,
        &mut Write,
    ) -> Result<(), std::io::Error>;
}

/// Implements Value trait for i32
impl Value for i32
{
    /// Gets the index of the bucket for this value.
    fn get_bucket_index(
        &self,
        buckets: &i32,
    ) -> usize
    {
        return ( self.clone() as usize ) % ( buckets.clone() as usize );
    }

    /// Gets a zero.
    fn zero() -> i32
    {
        return 0;
    }

    /// Gets the integer value as a size.
    fn as_index( &self ) -> usize
    {
        return self.clone() as usize;
    }

    /// Converts the specified value to a value that can be stored in the container.
    fn from_bucket_count( bucket_count: &usize ) -> i32
    {
        return bucket_count.clone() as i32;
    }

    /// Converts the number of set members into the value type.
    fn from_member_count( member_count: usize ) -> i32
    {
        return member_count as i32;
    }

    /// Converts the given value to index.
    fn from_index( index: usize ) -> i32
    {
        return index as i32;
    }

    /// Decrements the value by one.
    fn decrement( &self ) -> i32
    {
        return self - 1;
    }

    /// Provides total ordering for elements.
    fn cmp(
        &self,
        other: &i32,
    ) -> std::cmp::Ordering
    {
        std::cmp::Ord::cmp( self, other )
    }

    /// Serializes the value.
    fn serialize(
        &self,
        writer: &mut Write,
    ) -> Result<(), std::io::Error>
    {
        writer.write_i32::<NativeEndian>( *self )?;
        Ok( () )
    }
}

/// Implements Value trait for f32
/// * The largest permissible value is 2^24.
/// * Mainly implemented to provide an easy to use interface for sending the structure to GPU.
#[cfg(any(feature = "floats", test))]
impl Value for f32
{
    /// Gets the index of the bucket for this value.
    fn get_bucket_index(
        &self,
        buckets: &f32,
    ) -> usize
    {
        return ( self.clone() as usize ) % ( buckets.clone() as usize );
    }

    /// Gets a zero.
    fn zero() -> f32
    {
        return 0.0;
    }

    /// Gets the integer value as a size.
    fn as_index( &self ) -> usize
    {
        return self.clone() as usize;
    }

    /// Converts the specified value to a value that can be stored in the container.
    fn from_bucket_count( bucket_count: &usize ) -> f32
    {
        return bucket_count.clone() as f32;
    }

    /// Converts the number of set members into the value type.
    fn from_member_count( member_count: usize ) -> f32
    {
        return member_count as f32;
    }

    /// Converts the given value to index.
    fn from_index( index: usize ) -> f32
    {
        return index as f32;
    }

    /// Decrements the value by one.
    fn decrement( &self ) -> f32
    {
        return self - 1.0;
    }

    /// Provides total ordering for Value elements.
    fn cmp(
        &self,
        other: &f32,
    ) -> std::cmp::Ordering
    {
        let me = self.clone() as i32;
        let other = other.clone() as i32;
        std::cmp::Ord::cmp( &me, &other )
    }

    /// Serializes the value.
    fn serialize(
        &self,
        writer: &mut Write,
    ) -> Result<(), std::io::Error>
    {
        writer.write_f32::<NativeEndian>( *self )?;
        Ok( () )
    }
}

/// The index or position in the array that holds the number of members in the set.
const SIZE_INDEX: usize = 1;

/// The index or position in the array that holds the index to the first value of the first bucket.
const FIRST_BUCKET_INDEX: usize = 2;

/// Defines how the data is stored in the scalar set.
enum Storage<'a, TA, TB>
where
    TB: 'a,
{
    /// The data is stored as a vector.
    Vector
    { data: Vec<TA> },

    /// The data is stored in a slice. Not owned by the scalar set.
    Slice
    { data: &'a [TB] },
}

/// Scalar set cabable of storing values with Value trait.
pub struct RoScalarSet<'a, T>
where
    T: std::clone::Clone + Value + 'a,
{
    _storage: Storage<'a, T, T>,
}

impl<'a, T> Clone for RoScalarSet<'a, T>
where
    T: std::clone::Clone + Value + 'a,
{
    /// Returns a copy of the set.
    fn clone( &self ) -> Self
    {
        RoScalarSet::new( self.borrow_values() )
    }

    /// Copies the content from the given set.alloc
    fn clone_from(
        &mut self,
        source: &Self,
    )
    {
        let storage = RoScalarSet::create_storage( source.borrow_values() );
        self._storage = storage;
    }
}

impl<'a, T> RoScalarSet<'a, T>
where
    T: std::clone::Clone + Value + 'a,
{
    /// Returns a new integer hash set holding the specified values.
    ///
    /// # ArgumentRoScalarSets
    ///
    /// * 'values' Holds the values stored in the hash set.
    pub fn new<'b, 'c>( values: &'c [T] ) -> RoScalarSet<'b, T>
    {
        let storage = RoScalarSet::create_storage( values );
        return RoScalarSet { _storage: storage };
    }

    /// Attaches an integer hash set to a slice holding the values.
    ///
    /// # ArgumentRoScalarSets
    ///
    /// * 'values' Slice that holds the values stored in the hash set.
    pub fn attach<'b>( buffer: &'b [T] ) -> Result<( RoScalarSet<'b, T>, &'b [T] ), &str>
    {
        // Determine the length of this scalar set.
        if buffer.len() < 4
        {
            return Err( "The buffer is to small" );
        }
        let buckets = buffer[0].as_index();
        let values = buffer[SIZE_INDEX].as_index();
        let total_size = FIRST_BUCKET_INDEX + ( buckets + 1 ) + values;

        // Attach.
        let ( set, remainder ) = buffer.split_at( total_size );
        let storage: Storage<T, T> = Storage::Slice { data: set };
        return Ok( ( RoScalarSet { _storage: storage }, remainder ) );
    }

    /// Checks if any the sets have anything in common.
    pub fn any(
        &self,
        other: &RoScalarSet<T>,
    ) -> bool
    {
        // Determine the smaller set.
        let small;
        let large;
        if other.size() < self.size()
        {
            small = other;
            large = self;
        }
        else
        {
            small = self;
            large = other;
        }

        // For better performance we iterate the smaller set.
        for s in small.borrow_values()
        {
            if large.contains( s )
            {
                return true;
            }
        }

        // Values was not found.
        return false;
    }

    /// Checks whether the given value exists in the set or not.
    pub fn contains(
        &self,
        value: &T,
    ) -> bool
    {
        // Get the bucket associated with the value.
        let bucket = self.get_bucket( &value );

        // Locate the value.
        match bucket.binary_search_by( |probe| probe.cmp( &value ) )
        {
            Ok( _ ) => return true,
            Err( _ ) => return false,
        }
    }

    /// Gets the number of members in the set.
    pub fn size( &self ) -> usize
    {
        let storage = self.borrow_storage();
        return storage[SIZE_INDEX].as_index();
    }

    /// Gets the number of buckets.
    pub fn bucket_count( &self ) -> usize
    {
        let storage = self.borrow_storage();
        return storage[0].as_index();
    }

    /// Serializes the scalar set into the given writer.
    pub fn serialize<W>(
        &self,
        writer: &mut W,
    ) -> Result<(), std::io::Error>
    where
        W: Write,
    {
        // Write our intern
        let storage = self.borrow_storage();
        for v in storage
        {
            v.serialize( writer )?;
        }
        Ok( () )
    }

    /// Gets a read-only slice containing the values of a bucket that holdes the given value.
    fn get_bucket(
        &self,
        value: &T,
    ) -> &[T]
    {
        // Determine the bucket.
        let storage = self.borrow_storage();
        let bucket_id: usize = value.get_bucket_index( &storage[0] );
        let bucket_index = bucket_id + FIRST_BUCKET_INDEX;
        let begin: usize = storage[bucket_index].as_index();
        let end: usize = storage[( bucket_index + 1 )].as_index();
        let ( _, remainder ) = storage.split_at( begin );
        let ( bucket, _ ) = remainder.split_at( end - begin );
        return bucket;
    }

    /// Borrows the values of the set.
    fn borrow_values( &'a self ) -> &'a [T]
    {
        // Split the value part from the storage.
        let start = FIRST_BUCKET_INDEX + self.bucket_count() + 1;
        let ( _, values ) = self.borrow_storage().split_at( start );
        return values;
    }

    /// Borrows the storage for accessing the values.
    fn borrow_storage( &'a self ) -> &'a [T]
    {
        let s: &'a [T] = match &self._storage
        {
            &Storage::Vector { ref data } => data.as_slice(),
            &Storage::Slice { ref data } => data,
        };
        return s;
    }

    /// Creates a new storage buffer from the given values.
    fn create_storage( values: &[T] ) -> Storage<'a, T, T>
    {
        // Determine the number of buckets. We introduce a 5% overhead.
        let bucket_count: usize = ( values.len() as f64 * 0.05 ).ceil() as usize;
        let mut storage: Vec<T> = vec![T::zero(); FIRST_BUCKET_INDEX + bucket_count + 1 + values.len() ];

        // Store the number of buckets.
        let buckets = T::from_bucket_count( &bucket_count );
        storage[0] = buckets.clone();

        // Store the number of members.
        storage[SIZE_INDEX] = T::from_member_count( values.len() );

        // Count the values required for each bucket.
        let mut values_in_buckets: Vec<i32> = vec![0; bucket_count];
        for v in values
        {
            let bucket = v.get_bucket_index( &buckets );
            values_in_buckets[bucket] += 1;
        }

        // Set bucket pointers to point the end of each bucket.
        // This way we can use the bucket pointers to track the next empty position when
        // distributing the values into the buckets.
        let first_bucket: usize = FIRST_BUCKET_INDEX;
        let data_start: usize = FIRST_BUCKET_INDEX + bucket_count + 1;
        let mut previous_bucket_end = data_start;
        for b in 0..bucket_count
        {
            // The end of the previous bucket is the start of the previous bucket.
            let index = previous_bucket_end + values_in_buckets[b].as_index();
            storage[b + first_bucket] = T::from_index( index );
            previous_bucket_end = index;
        }

        // Fix the end of the last bucket.
        storage[first_bucket + bucket_count] = T::from_index( storage.len() );

        // Put values into buckets.
        for v in values
        {
            // Determine bucket for the value.
            let bucket_id = v.get_bucket_index( &buckets );

            // Make room for the new value.
            storage[bucket_id + first_bucket] = storage[bucket_id + first_bucket].decrement();
            let value_index: usize = storage[bucket_id + first_bucket].as_index();

            // And finally store the value.
            storage[value_index] = v.clone();
        }

        // Sort each bucket to enable binary search.
        for b in 0..bucket_count
        {
            // Determine the location of the bucket.
            let begin: usize = storage[b + first_bucket].as_index();
            let end: usize = storage[b + first_bucket + 1].as_index();
            if end < begin
            {
                panic!( "Invalid bucket: {}", b );
            }

            // Get a splice that represents the bucket and sort it.
            let ( _, remainder ) = storage.split_at_mut( begin );
            let ( bucket, _ ) = remainder.split_at_mut( end - begin );
            bucket.sort_by( |a, b| a.cmp( b ) );
        }

        Storage::Vector { data: storage }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// 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.

use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

use command::studio::enter::ARTIFACT_PATH_ENVVAR;
use common::ui::UI;
use feat;
use hcore::crypto::default_cache_key_path;
use hcore::env as henv;
use hcore::fs::{find_command, CACHE_ARTIFACT_PATH, CACHE_KEY_PATH};
use hcore::os::process;

use error::{Error, Result};
use VERSION;

const DOCKER_CMD: &'static str = "docker";
const DOCKER_CMD_ENVVAR: &'static str = "HAB_DOCKER_BINARY";
const DOCKER_IMAGE: &'static str = "habitat-docker-registry.bintray.io/studio";
const DOCKER_WINDOWS_IMAGE: &'static str = "habitat-docker-registry.bintray.io/win-studio";
const DOCKER_IMAGE_ENVVAR: &'static str = "HAB_DOCKER_STUDIO_IMAGE";
const DOCKER_OPTS_ENVVAR: &'static str = "HAB_DOCKER_OPTS";
const DOCKER_SOCKET: &'static str = "/var/run/docker.sock";
const HAB_STUDIO_SECRET: &'static str = "HAB_STUDIO_SECRET_";

pub fn start_docker_studio(_ui: &mut UI, mut args: Vec<OsString>) -> Result<()> {
    let docker_cmd = find_docker_cmd()?;

    if is_image_present(&docker_cmd) {
        debug!("Found Studio Docker image locally.");
    } else {
        debug!("Failed to find Studio Docker image locally.");
        pull_image(&docker_cmd)?;
    }

    let mnt_prefix = match is_serving_windows_containers(&docker_cmd) {
        true => "c:",
        false => "",
    };
    let mut volumes = vec![
        format!(
            "{}:{}{}",
            env::current_dir().unwrap().to_string_lossy(),
            mnt_prefix,
            "/src"
        ),
        format!(
            "{}:{}/{}",
            default_cache_key_path(None).to_string_lossy(),
            mnt_prefix,
            CACHE_KEY_PATH
        ),
    ];
    if let Ok(cache_artifact_path) = henv::var(ARTIFACT_PATH_ENVVAR) {
        volumes.push(format!(
            "{}:{}/{}",
            cache_artifact_path, mnt_prefix, CACHE_ARTIFACT_PATH
        ));
    }
    if !is_serving_windows_containers(&docker_cmd)
        && (Path::new(DOCKER_SOCKET).exists() || cfg!(target_os = "windows"))
    {
        volumes.push(format!("{}:{}", DOCKER_SOCKET, DOCKER_SOCKET));
    }

    let mut env_vars = vec![
        String::from("DEBUG"),
        String::from("HAB_AUTH_TOKEN"),
        String::from("HAB_BLDR_URL"),
        String::from("HAB_BLDR_CHANNEL"),
        String::from("HAB_ORIGIN"),
        String::from("HAB_ORIGIN_KEYS"),
        String::from("HAB_STUDIO_BACKLINE_PKG"),
        String::from("HAB_STUDIO_NOSTUDIORC"),
        String::from("HAB_STUDIO_SUP"),
        String::from("HAB_UPDATE_STRATEGY_FREQUENCY_MS"),
        String::from("http_proxy"),
        String::from("https_proxy"),
        String::from("RUST_LOG"),
    ];

    for (key, _) in env::vars() {
        if key.starts_with(HAB_STUDIO_SECRET) {
            env_vars.push(key);
        }
    }

    // We need to strip out the -D if it exists to avoid
    // it getting passed to the sup on entering the studio
    let to_cull = OsString::from("-D");
    if let Some(index) = args.iter().position(|x| *x == to_cull) {
        args.remove(index);
    }

    // Windows containers do not use filesystem sharing for
    // local mounts
    if !is_serving_windows_containers(&docker_cmd) {
        check_mounts(&docker_cmd, volumes.iter())?;
    }
    run_container(docker_cmd, args, volumes.iter(), env_vars.iter())
}

fn find_docker_cmd() -> Result<PathBuf> {
    let docker_cmd = henv::var(DOCKER_CMD_ENVVAR).unwrap_or(DOCKER_CMD.to_string());

    match find_command(&docker_cmd) {
        Some(docker_abs_path) => Ok(docker_abs_path),
        None => Err(Error::ExecCommandNotFound(docker_cmd.into())),
    }
}

fn is_image_present(docker_cmd: &Path) -> bool {
    let mut cmd = Command::new(docker_cmd);
    cmd.arg("images")
        .arg(&image_identifier(docker_cmd))
        .arg("-q");
    debug!("Running command: {:?}", cmd);
    let result = cmd.output().expect("Docker command failed to spawn");

    !String::from_utf8_lossy(&result.stdout).as_ref().is_empty()
}

fn is_serving_windows_containers(docker_cmd: &Path) -> bool {
    let mut cmd = Command::new(docker_cmd);
    cmd.arg("version").arg("--format='{{.Server.Os}}'");
    debug!("Running command: {:?}", cmd);
    let result = cmd.output().expect("Docker command failed to spawn");
    String::from_utf8_lossy(&result.stdout).contains("windows")
}

fn pull_image(docker_cmd: &Path) -> Result<()> {
    let image = image_identifier(docker_cmd);
    let mut cmd = Command::new(docker_cmd);
    cmd.arg("pull")
        .arg(&image)
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit());
    debug!("Running command: {:?}", cmd);
    let result = cmd
        .spawn()
        .expect("Docker command failed to spawn")
        .wait_with_output()
        .expect("Failed to wait on child process");

    if result.status.success() {
        debug!("Docker image '{}' is present locally.", &image);
    } else {
        debug!(
            "Pulling Docker image '{}' failed with exit code: {:?}",
            &image, result.status
        );

        let err_output = String::from_utf8_lossy(&result.stderr);

        if err_output.contains("image") && err_output.contains("not found") {
            return Err(Error::DockerImageNotFound(
                image_identifier(docker_cmd).to_string(),
            ));
        } else if err_output.contains("Cannot connect to the Docker daemon") {
            return Err(Error::DockerDaemonDown);
        } else {
            return Err(Error::DockerNetworkDown(
                image_identifier(docker_cmd).to_string(),
            ));
        }
    }

    Ok(())
}

/// Checks whether or not the volume mounts are working.
///
/// We need to ensure that filesystem sharing has been enabled, otherwise the user will be
/// greeted with a horrible error message that's difficult to make sense of. To mitigate this,
/// we check the studio version. This will cause Docker to go through the mounting steps, so we
/// can watch stderr for failure, but has the advantage of not requiring a TTY.
fn check_mounts<I, S>(docker_cmd: &Path, volumes: I) -> Result<()>
where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,
{
    let mut cmd_args: Vec<OsString> = vec!["run".into(), "--rm".into()];
    for vol in volumes {
        cmd_args.push("--volume".into());
        cmd_args.push(vol.as_ref().into());
    }

    if !feat::is_enabled(feat::RootlessStudio) {
        cmd_args.push("--privileged".into());
    }
    cmd_args.push(image_identifier(docker_cmd).into());
    cmd_args.push("-V".into());
    let version_output = Command::new(docker_cmd)
        .args(&cmd_args)
        .output()
        .expect("docker failed to start");

    let stderr = String::from_utf8(version_output.stderr).unwrap();
    if !stderr.is_empty()
        && (stderr.as_str().contains("Mounts denied")
            || stderr.as_str().contains("drive is not shared"))
    {
        return Err(Error::DockerFileSharingNotEnabled);
    }
    Ok(())
}

fn run_container<I, J, S, T>(
    docker_cmd: PathBuf,
    args: Vec<OsString>,
    volumes: I,
    env_vars: J,
) -> Result<()>
where
    I: IntoIterator<Item = S>,
    J: IntoIterator<Item = T>,
    S: AsRef<OsStr>,
    T: AsRef<str>,
{
    let mut cmd_args: Vec<OsString> = vec!["run".into(), "--rm".into()];
    if !is_serving_windows_containers(&docker_cmd) {
        cmd_args.push("--privileged".into());
    }
    match args.first().map(|f| f.to_str().unwrap_or_default()) {
        Some("build") => {}
        _ => {
            cmd_args.push("--tty".into());
            cmd_args.push("--interactive".into());
        }
    }
    if let Ok(opts) = henv::var(DOCKER_OPTS_ENVVAR) {
        let opts = opts.split(" ")
                .map(|v| v.into())
                // Ensure we're not passing something like `--tty` again here.
                .filter(|v| !cmd_args.contains(v))
                .collect::<Vec<_>>();
        if !opts.is_empty() {
            debug!(
                "Adding extra Docker options from {} = {:?}",
                DOCKER_OPTS_ENVVAR, opts
            );
            cmd_args.extend_from_slice(opts.as_slice());
        }
    }
    for var in env_vars {
        if let Ok(val) = henv::var(var.as_ref()) {
            debug!("Setting container env var: {:?}='{}'", var.as_ref(), val);
            cmd_args.push("--env".into());
            cmd_args.push(format!("{}={}", var.as_ref(), val).into());
        }
    }
    for vol in volumes {
        cmd_args.push("--volume".into());
        cmd_args.push(vol.as_ref().into());
    }
    cmd_args.push(image_identifier(&docker_cmd).into());
    cmd_args.extend_from_slice(args.as_slice());
    if is_serving_windows_containers(&docker_cmd) {
        cmd_args.push("-w".into());
        cmd_args.push("-n".into());
        cmd_args.push("-o".into());
        cmd_args.push("c:/".into());
    }
    unset_proxy_env_vars();
    Ok(process::become_command(docker_cmd, cmd_args)?)
}

fn unset_proxy_env_vars() {
    for var in vec!["http_proxy", "https_proxy"] {
        if let Ok(_) = henv::var(var) {
            debug!("Unsetting process environment variable '{}'", var);
            env::remove_var(var);
        }
    }
}

/// Returns the Docker Studio image with tag for the desired version which corresponds to the
/// same version (minus release) as this program.
fn image_identifier(docker_cmd: &Path) -> String {
    let version: Vec<&str> = VERSION.split("/").collect();
    let img = match is_serving_windows_containers(docker_cmd) {
        true => DOCKER_WINDOWS_IMAGE,
        false => DOCKER_IMAGE,
    };
    henv::var(DOCKER_IMAGE_ENVVAR).unwrap_or(format!("{}:{}", img, version[0]))
}

#[cfg(test)]
#[cfg(target_os = "linux")]
mod tests {
    use super::{find_docker_cmd, image_identifier, DOCKER_IMAGE};
    use VERSION;

    #[test]
    fn retrieve_image_identifier() {
        let docker_cmd = find_docker_cmd().unwrap();
        assert_eq!(
            image_identifier(&docker_cmd),
            format!("{}:{}", DOCKER_IMAGE, VERSION)
        );
    }
}
<commit_msg>Make docker studio behave like chroot studio<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// 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.

use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

use command::studio::enter::ARTIFACT_PATH_ENVVAR;
use common::ui::UI;
use feat;
use hcore::crypto::default_cache_key_path;
use hcore::env as henv;
use hcore::fs::{find_command, CACHE_ARTIFACT_PATH, CACHE_KEY_PATH};
use hcore::os::process;

use error::{Error, Result};
use VERSION;

const DOCKER_CMD: &'static str = "docker";
const DOCKER_CMD_ENVVAR: &'static str = "HAB_DOCKER_BINARY";
const DOCKER_IMAGE: &'static str = "habitat-docker-registry.bintray.io/studio";
const DOCKER_WINDOWS_IMAGE: &'static str = "habitat-docker-registry.bintray.io/win-studio";
const DOCKER_IMAGE_ENVVAR: &'static str = "HAB_DOCKER_STUDIO_IMAGE";
const DOCKER_OPTS_ENVVAR: &'static str = "HAB_DOCKER_OPTS";
const DOCKER_SOCKET: &'static str = "/var/run/docker.sock";
const HAB_STUDIO_SECRET: &'static str = "HAB_STUDIO_SECRET_";

pub fn start_docker_studio(_ui: &mut UI, mut args: Vec<OsString>) -> Result<()> {
    let docker_cmd = find_docker_cmd()?;
    // We need to strip out the -D if it exists to avoid
    // it getting passed to the sup on entering the studio
    let to_cull = OsString::from("-D");
    if let Some(index) = args.iter().position(|x| *x == to_cull) {
        args.remove(index);
    }
    if args[0] == OsString::from("rm") {
        return rm_container(&docker_cmd);
    }
    return enter(&docker_cmd, &args);
}

fn enter(docker_cmd: &Path, args: &Vec<OsString>) -> Result<()> {
    if is_image_present(&docker_cmd) {
        debug!("Found Studio Docker image locally.");
    } else {
        debug!("Failed to find Studio Docker image locally.");
        pull_image(&docker_cmd)?;
    }

    let mnt_prefix = match is_serving_windows_containers(&docker_cmd) {
        true => "c:",
        false => "",
    };
    let mut volumes = vec![
        format!(
            "{}:{}{}",
            env::current_dir().unwrap().to_string_lossy(),
            mnt_prefix,
            "/src"
        ),
        format!(
            "{}:{}/{}",
            default_cache_key_path(None).to_string_lossy(),
            mnt_prefix,
            CACHE_KEY_PATH
        ),
    ];
    if let Ok(cache_artifact_path) = henv::var(ARTIFACT_PATH_ENVVAR) {
        volumes.push(format!(
            "{}:{}/{}",
            cache_artifact_path, mnt_prefix, CACHE_ARTIFACT_PATH
        ));
    }
    if !is_serving_windows_containers(&docker_cmd)
        && (Path::new(DOCKER_SOCKET).exists() || cfg!(target_os = "windows"))
    {
        volumes.push(format!("{}:{}", DOCKER_SOCKET, DOCKER_SOCKET));
    }

    let mut env_vars = vec![
        String::from("DEBUG"),
        String::from("HAB_AUTH_TOKEN"),
        String::from("HAB_BLDR_URL"),
        String::from("HAB_BLDR_CHANNEL"),
        String::from("HAB_ORIGIN"),
        String::from("HAB_ORIGIN_KEYS"),
        String::from("HAB_STUDIO_BACKLINE_PKG"),
        String::from("HAB_STUDIO_NOSTUDIORC"),
        String::from("HAB_STUDIO_SUP"),
        String::from("HAB_UPDATE_STRATEGY_FREQUENCY_MS"),
        String::from("http_proxy"),
        String::from("https_proxy"),
        String::from("RUST_LOG"),
    ];

    for (key, _) in env::vars() {
        if key.starts_with(HAB_STUDIO_SECRET) {
            env_vars.push(key);
        }
    }

    // Windows containers do not use filesystem sharing for
    // local mounts
    if !is_serving_windows_containers(&docker_cmd) {
        check_mounts(&docker_cmd, volumes.iter())?;
    }
    if container_exists(&docker_cmd) {
        return start_container(&docker_cmd);
    }
    run_container(&docker_cmd, args.to_vec(), volumes.iter(), env_vars.iter())
}

fn find_docker_cmd() -> Result<PathBuf> {
    let docker_cmd = henv::var(DOCKER_CMD_ENVVAR).unwrap_or(DOCKER_CMD.to_string());

    match find_command(&docker_cmd) {
        Some(docker_abs_path) => Ok(docker_abs_path),
        None => Err(Error::ExecCommandNotFound(docker_cmd.into())),
    }
}

fn is_image_present(docker_cmd: &Path) -> bool {
    let mut cmd = Command::new(docker_cmd);
    cmd.arg("images")
        .arg(&image_identifier(docker_cmd))
        .arg("-q");
    debug!("Running command: {:?}", cmd);
    let result = cmd.output().expect("Docker command failed to spawn");

    !String::from_utf8_lossy(&result.stdout).as_ref().is_empty()
}

fn is_serving_windows_containers(docker_cmd: &Path) -> bool {
    let mut cmd = Command::new(docker_cmd);
    cmd.arg("version").arg("--format='{{.Server.Os}}'");
    debug!("Running command: {:?}", cmd);
    let result = cmd.output().expect("Docker command failed to spawn");
    String::from_utf8_lossy(&result.stdout).contains("windows")
}

fn pull_image(docker_cmd: &Path) -> Result<()> {
    let image = image_identifier(docker_cmd);
    let mut cmd = Command::new(docker_cmd);
    cmd.arg("pull")
        .arg(&image)
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit());
    debug!("Running command: {:?}", cmd);
    let result = cmd
        .spawn()
        .expect("Docker command failed to spawn")
        .wait_with_output()
        .expect("Failed to wait on child process");

    if result.status.success() {
        debug!("Docker image '{}' is present locally.", &image);
    } else {
        debug!(
            "Pulling Docker image '{}' failed with exit code: {:?}",
            &image, result.status
        );

        let err_output = String::from_utf8_lossy(&result.stderr);

        if err_output.contains("image") && err_output.contains("not found") {
            return Err(Error::DockerImageNotFound(
                image_identifier(docker_cmd).to_string(),
            ));
        } else if err_output.contains("Cannot connect to the Docker daemon") {
            return Err(Error::DockerDaemonDown);
        } else {
            return Err(Error::DockerNetworkDown(
                image_identifier(docker_cmd).to_string(),
            ));
        }
    }

    Ok(())
}

/// Checks whether or not the volume mounts are working.
///
/// We need to ensure that filesystem sharing has been enabled, otherwise the user will be
/// greeted with a horrible error message that's difficult to make sense of. To mitigate this,
/// we check the studio version. This will cause Docker to go through the mounting steps, so we
/// can watch stderr for failure, but has the advantage of not requiring a TTY.
fn check_mounts<I, S>(docker_cmd: &Path, volumes: I) -> Result<()>
where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,
{
    let mut cmd_args: Vec<OsString> = vec!["run".into(), "--rm".into()];
    for vol in volumes {
        cmd_args.push("--volume".into());
        cmd_args.push(vol.as_ref().into());
    }

    if !feat::is_enabled(feat::RootlessStudio) {
        cmd_args.push("--privileged".into());
    }
    cmd_args.push(image_identifier(docker_cmd).into());
    cmd_args.push("-V".into());
    let version_output = Command::new(docker_cmd)
        .args(&cmd_args)
        .output()
        .expect("docker failed to start");

    let stderr = String::from_utf8(version_output.stderr).unwrap();
    if !stderr.is_empty()
        && (stderr.as_str().contains("Mounts denied")
            || stderr.as_str().contains("drive is not shared"))
    {
        return Err(Error::DockerFileSharingNotEnabled);
    }
    Ok(())
}

fn container_exists(docker_cmd: &Path) -> bool {
    let name = path_to_name().expect("Unable to load name from path");
    let cmd_args: Vec<OsString> = vec!["container".into(), "inspect".into(), name.into()];
    let output = Command::new(docker_cmd)
        .args(&cmd_args)
        .output()
        .expect("docker failed to start");
    if output.status.success() {
        return true;
    }
    return false;
}

#[cfg(not(target_os = "windows"))]
fn path_to_name() -> Result<String> {
    let mut cwd = env::current_dir()?;
    cwd = cwd.strip_prefix("/")?.to_path_buf();
    let pathstr = cwd.to_str().expect("Path to be parseable");
    return Ok(pathstr.replace("/", "--"));
}

#[cfg(target_os = "windows")]
fn path_to_name() -> Result<String> {
    let cwd = env::current_dir()?;
    let pathstr = cwd.to_str().expect("Path to be parseable");
    let stripped_path: String = pathstr.chars().skip(3).take(pathstr.len() - 3).collect();
    return Ok(stripped_path.replace("\\", "--"));
}

fn rm_container(docker_cmd: &Path) -> Result<()> {
    let mut cmd_args: Vec<OsString> = vec!["rm".into()];
    let name = path_to_name()?;
    cmd_args.push(name.into());
    Ok(process::become_command(docker_cmd.to_path_buf(), cmd_args)?)
}

fn run_container<I, J, S, T>(
    docker_cmd: &Path,
    args: Vec<OsString>,
    volumes: I,
    env_vars: J,
) -> Result<()>
where
    I: IntoIterator<Item = S>,
    J: IntoIterator<Item = T>,
    S: AsRef<OsStr>,
    T: AsRef<str>,
{
    let mut cmd_args: Vec<OsString> = vec!["run".into()];
    if !is_serving_windows_containers(&docker_cmd) {
        cmd_args.push("--privileged".into());
    }
    let name = path_to_name()?;
    cmd_args.push("--name".into());
    cmd_args.push(name.into());
    match args.first().map(|f| f.to_str().unwrap_or_default()) {
        Some("build") => {}
        _ => {
            cmd_args.push("--tty".into());
            cmd_args.push("--interactive".into());
        }
    }
    if let Ok(opts) = henv::var(DOCKER_OPTS_ENVVAR) {
        let opts = opts.split(" ")
                .map(|v| v.into())
                // Ensure we're not passing something like `--tty` again here.
                .filter(|v| !cmd_args.contains(v))
                .collect::<Vec<_>>();
        if !opts.is_empty() {
            debug!(
                "Adding extra Docker options from {} = {:?}",
                DOCKER_OPTS_ENVVAR, opts
            );
            cmd_args.extend_from_slice(opts.as_slice());
        }
    }
    for var in env_vars {
        if let Ok(val) = henv::var(var.as_ref()) {
            debug!("Setting container env var: {:?}='{}'", var.as_ref(), val);
            cmd_args.push("--env".into());
            cmd_args.push(format!("{}={}", var.as_ref(), val).into());
        }
    }
    for vol in volumes {
        cmd_args.push("--volume".into());
        cmd_args.push(vol.as_ref().into());
    }
    cmd_args.push(image_identifier(&docker_cmd).into());
    cmd_args.extend_from_slice(args.as_slice());
    if is_serving_windows_containers(&docker_cmd) {
        cmd_args.push("-w".into());
        cmd_args.push("-n".into());
        cmd_args.push("-o".into());
        cmd_args.push("c:/".into());
    }
    unset_proxy_env_vars();
    Ok(process::become_command(docker_cmd.to_path_buf(), cmd_args)?)
}

fn start_container(docker_cmd: &Path) -> Result<()> {
    let mut cmd_args: Vec<OsString> = vec!["start".into()];
    cmd_args.push("--interactive".into());
    let name = path_to_name()?;
    cmd_args.push(name.into());
    Ok(process::become_command(docker_cmd.to_path_buf(), cmd_args)?)
}

fn unset_proxy_env_vars() {
    for var in vec!["http_proxy", "https_proxy"] {
        if let Ok(_) = henv::var(var) {
            debug!("Unsetting process environment variable '{}'", var);
            env::remove_var(var);
        }
    }
}

/// Returns the Docker Studio image with tag for the desired version which corresponds to the
/// same version (minus release) as this program.
fn image_identifier(docker_cmd: &Path) -> String {
    let version: Vec<&str> = VERSION.split("/").collect();
    let img = match is_serving_windows_containers(docker_cmd) {
        true => DOCKER_WINDOWS_IMAGE,
        false => DOCKER_IMAGE,
    };
    henv::var(DOCKER_IMAGE_ENVVAR).unwrap_or(format!("{}:{}", img, version[0]))
}

#[cfg(test)]
#[cfg(target_os = "linux")]
mod tests {
    use super::{find_docker_cmd, image_identifier, DOCKER_IMAGE};
    use VERSION;

    #[test]
    fn retrieve_image_identifier() {
        let docker_cmd = find_docker_cmd().unwrap();
        assert_eq!(
            image_identifier(&docker_cmd),
            format!("{}:{}", DOCKER_IMAGE, VERSION)
        );
    }
}
<|endoftext|>


<commit_before>//extern crate serde_json;
extern crate rustc_serialize;

//use std::fmt;
use std::collections::HashMap;

//use self::serde_json::{StreamDeserializer, Value};
use self::rustc_serialize::json::{JsonEvent, Parser, Stack, StackElement};
use key_builder::{KeyBuilder, SegmentType};

//struct PrintableStack(Stack);
//pub struct Stack {
//    stack: Vec<InternalStackElement>,
//    str_buffer: Vec<u8>,
//}

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

// Good example of using rustc_serialize: https://github.com/ajroetker/beautician/blob/master/src/lib.rs
// Callback based JSON streaming parser: https://github.com/gyscos/json-streamer.rs
// Another parser pased on rustc_serializ: https://github.com/isagalaev/ijson-rust/blob/master/src/test.rs#L11

struct word_info {
    //offset in the text field where the stemmed text starts
    stemmed_offset: usize,

    // the suffix of the stemmed text. When applied over stemmed, the original
    // text is returned.
    suffix_text: String,

    // the start of the suffixText
    suffix_offset: usize,
}

type array_offsets = Vec<usize>;
type array_offsets_to_word_info = HashMap<array_offsets, Vec<word_info>>;
type word_path_info_map = HashMap<String, array_offsets_to_word_info>;

struct Shredder<'a> {
    json: &'a str,
    docseq: u64,
    map: word_path_info_map,
    path_array_offsets: Vec<usize>,
}

impl<'a> Shredder<'a> {
    fn add_entries(&mut self, kb: &mut KeyBuilder, text: String) {
        // TODO vmx 2016-09-29: Do the stemming. For now we just split at whitespaces
        let words = text.split(" ");
        for word in words {
            // TODO vmx 2016-09-29: Set the `path_array_offset` properly
            let path_array_offset = array_offsets_to_word_info::new();
            println!("word: {}", word);
            kb.push_word(word.to_string());
            kb.push_doc_seq(self.docseq);
            self.map.insert(kb.key(), path_array_offset);
            kb.pop_doc_seq();
            kb.pop_word();
        }
    }

    fn inc_top_array_offset(&mut self, kb: &KeyBuilder) {
        // we encounter a new element. if we are a child element of an array
        // increment the offset. If we aren't (we are the root value or a map
        // value) we don't increment
        if let Some(SegmentType::Array) = kb.last_pushed_segment_type() {
            if let Some(last) = self.path_array_offsets.last_mut() {
                *last += 1;
            }
        }
    }
}

// TODO vmx 2009-09-29: Make it a metho of `Shredder`
fn shred() {
    //let json = r#"{"hello": {"my": "world!"}, "anumber": 2}"#;
    let json = r#"{"A":[{"B":"B2VMX","C":"C2"},{"B": "b1","C":"C2"}]}"#;
    let docseq = 123;
    println!("{}", json);
    let mut shredder = Shredder{
        json: json,
        docseq: docseq,
        map: word_path_info_map::new(),
        path_array_offsets: Vec::new(),
    };

    let mut kb = KeyBuilder::new();
    let mut parser = Parser::new(json.chars());
    loop {
        let token = match parser.next() {
            Some(tt) => tt,
            None => break
        };

        println!("token: {:?}: {:?}", token, parser.stack().top());
        match parser.stack().top() {
            Some(StackElement::Key(key)) => {
                println!("key: {}", key);
                match token {
                    JsonEvent::ObjectStart => {
                        shredder.inc_top_array_offset(&kb);
                        kb.push_object_key(key.to_string());
                    },
                    JsonEvent::ObjectEnd => {
                        kb.pop_object_key();
                    },
                    JsonEvent::ArrayStart => {
                        shredder.inc_top_array_offset(&kb);
                        kb.push_object_key(key.to_string());
                        kb.push_array();
                    },
                    JsonEvent::ArrayEnd => {
                        kb.pop_array();
                    },
                    JsonEvent::StringValue(value) => {
                        shredder.inc_top_array_offset(&kb);
                        // TODO vmx 2016-09-22:  AddEntries()
                        kb.push_object_key(key.to_string());
                        println!("about to add entries: keybuilder: {}", kb.key());
                        shredder.add_entries(&mut kb, value);
                        kb.pop_object_key();
                    },
                    _ => {},
                }
            },
            _ => {},
        }
        println!("keybuilder: {}", kb.key());
        //match token {
        //    JsonEvent::ObjectStart => {
        //        // push a dummy value
        //        kb.push_object_key("".to_string());
        //    },
        //    //JsonString:
        //    //    //let value = parser.next();
        //    //    //let key = parser.stack().top().unwrap();
        //    //    //let key = match parser.stack().top().unwrap() {
        //    //    match parser.stack().top() {
        //    //        Some(StackElement::Index(_)) => { println!("index!") },
        //    //        Some(StackElement::Key(key)) => { println!("Key: {}!", key) },
        //    //        None => { println!("none") },
        //    //    };
        //    //    //println!("key-value: {:?} {:?}", key, value);
        //    //    //kb.push_object_key(key);
        //    //},
        //    _ => {}
        //}

    }
    println!("keybuilder: {}", kb.key());
    println!("shredder: keys:");
    for key in shredder.map.keys() {
        println!("  {}", key);
    }
    //let mut parsed: StreamDeserializer<Value, _> = StreamDeserializer::new(
    //    json.as_bytes().iter().map(|byte| Ok(*byte)));
    //println!("{:?}", parsed.next().unwrap().next().unwrap());
    //    //let stream = "{\"x\":40}\n{\"x\":".to_string();
    ////let mut parsed: StreamDeserializer<Value, _> = StreamDeserializer::new(
    ////    stream.as_bytes().iter().map(|byte| Ok(*byte))
    ////);
}

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

    #[test]
    fn test_shred() {
        super::shred();
    }
}
<commit_msg>Make the Shredder a bit nicer<commit_after>extern crate rustc_serialize;

use std::collections::HashMap;

use self::rustc_serialize::json::{JsonEvent, Parser, StackElement};
use key_builder::{KeyBuilder, SegmentType};

// Good example of using rustc_serialize: https://github.com/ajroetker/beautician/blob/master/src/lib.rs
// Callback based JSON streaming parser: https://github.com/gyscos/json-streamer.rs
// Another parser pased on rustc_serializ: https://github.com/isagalaev/ijson-rust/blob/master/src/test.rs#L11

struct WordInfo {
    //offset in the text field where the stemmed text starts
    stemmed_offset: usize,

    // the suffix of the stemmed text. When applied over stemmed, the original
    // text is returned.
    suffix_text: String,

    // the start of the suffixText
    suffix_offset: usize,
}

type ArrayOffsets = Vec<usize>;
type ArrayOffsetsToWordInfo = HashMap<ArrayOffsets, Vec<WordInfo>>;
type WordPathInfoMap = HashMap<String, ArrayOffsetsToWordInfo>;

struct Shredder {
    keybuilder: KeyBuilder,
    map: WordPathInfoMap,
    path_array_offsets: Vec<usize>,
}

impl Shredder {
    fn new() -> Shredder {
        Shredder{
            keybuilder: KeyBuilder::new(),
            map: WordPathInfoMap::new(),
            path_array_offsets: Vec::new(),
        }
    }
    fn add_entries(&mut self, text: String, docseq: u64) {
        // TODO vmx 2016-09-29: Do the stemming. For now we just split at whitespaces
        let words = text.split(" ");
        for word in words {
            // TODO vmx 2016-09-29: Set the `path_array_offset` properly
            let path_array_offset = ArrayOffsetsToWordInfo::new();
            println!("word: {}", word);
            self.keybuilder.push_word(word.to_string());
            self.keybuilder.push_doc_seq(docseq);
            self.map.insert(self.keybuilder.key(), path_array_offset);
            self.keybuilder.pop_doc_seq();
            self.keybuilder.pop_word();
        }
    }

    fn inc_top_array_offset(&mut self) {
        // we encounter a new element. if we are a child element of an array
        // increment the offset. If we aren't (we are the root value or a map
        // value) we don't increment
        if let Some(SegmentType::Array) = self.keybuilder.last_pushed_segment_type() {
            if let Some(last) = self.path_array_offsets.last_mut() {
                *last += 1;
            }
        }
    }

    fn shred(&mut self, json: &str, docseq: u64) {
        println!("{}", json);
        let mut parser = Parser::new(json.chars());
        loop {
            let token = match parser.next() {
                Some(tt) => tt,
                None => break
            };

            println!("token: {:?}: {:?}", token, parser.stack().top());
            match parser.stack().top() {
                Some(StackElement::Key(key)) => {
                    println!("key: {}", key);
                    match token {
                        JsonEvent::ObjectStart => {
                            self.inc_top_array_offset();
                            self.keybuilder.push_object_key(key.to_string());
                        },
                        JsonEvent::ObjectEnd => {
                            self.keybuilder.pop_object_key();
                        },
                        JsonEvent::ArrayStart => {
                            self.inc_top_array_offset();
                            self.keybuilder.push_object_key(key.to_string());
                            self.keybuilder.push_array();
                        },
                        JsonEvent::ArrayEnd => {
                            self.keybuilder.pop_array();
                        },
                        JsonEvent::StringValue(value) => {
                            self.inc_top_array_offset();
                            // TODO vmx 2016-09-22:  AddEntries()
                            self.keybuilder.push_object_key(key.to_string());
                            println!("about to add entries: keybuilder: {}", self.keybuilder.key());
                            self.add_entries(value, docseq);
                            self.keybuilder.pop_object_key();
                        },
                        _ => {},
                    }
                },
                _ => {},
            }
            println!("keybuilder: {}", self.keybuilder.key());
        }
        println!("keybuilder: {}", self.keybuilder.key());
        println!("shredder: keys:");
        for key in self.map.keys() {
            println!("  {}", key);
        }
    }
}

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

    #[test]
    fn test_shred() {
        let mut shredder = super::Shredder::new();
        //let json = r#"{"hello": {"my": "world!"}, "anumber": 2}"#;
        let json = r#"{"A":[{"B":"B2VMX","C":"C2"},{"B": "b1","C":"C2"}]}"#;
        let docseq = 123;
        shredder.shred(json, docseq);
    }
}
<|endoftext|>