<commit_before>
use {
    BackEnd,
    Borrowed,
    CanTransform,
    Clear,
    Color,
    Field,
    Fill,
    HasTransform,
    Matrix2d,
    RelativeRectangle,
    RoundRectangle,
    Value,
    View,
};
use vecmath::{
    identity,
    margin_round_rectangle,
    relative_round_rectangle,
};
use triangulation::{
    with_round_rectangle_tri_list_xy_f32_rgba_f32
};

/// A rectangle color context.
pub struct RoundRectangleColorContext<'a> {
    /// Base/original transformation.
    pub base: Field<'a, Matrix2d>,
    /// Current transformation.
    pub transform: Field<'a, Matrix2d>,
    /// Current rectangle.
    pub round_rect: Field<'a, RoundRectangle>,
    /// Current color.
    pub color: Field<'a, Color>,
}

impl<'a> HasTransform<'a, Matrix2d> for RoundRectangleColorContext<'a> {
    #[inline(always)]
    fn get_transform(&'a self) -> &'a Matrix2d {
        self.transform.get()
    }
}

impl<'a> CanTransform<'a, RoundRectangleColorContext<'a>, Matrix2d> for RoundRectangleColorContext<'a> {
    #[inline(always)]
    fn transform(&'a self, value: Matrix2d) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Value(value),
            round_rect: Borrowed(self.round_rect.get()),
            color: Borrowed(self.color.get()),
        }
    }
}

impl<'a> Clear for RoundRectangleColorContext<'a> {
    fn clear<B: BackEnd>(&self, back_end: &mut B) {
        if back_end.supports_clear_rgba() {
            let color = self.color.get();
            back_end.clear_rgba(color[0], color[1], color[2], color[3]);
        } else {
            unimplemented!();
        }
    }
}

impl<'a> RelativeRectangle<'a> for RoundRectangleColorContext<'a> {
    #[inline(always)]
    fn margin(&'a self, m: f64) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
            round_rect: Value(margin_round_rectangle(self.round_rect.get(), m)),
        }
    }

    #[inline(always)]
    fn rel(&'a self, x: f64, y: f64) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
            round_rect: Value(relative_round_rectangle(self.round_rect.get(), x, y)),
        }
    }
}

impl<'a> Fill<'a> for RoundRectangleColorContext<'a> {
    fn fill<B: BackEnd>(&'a self, back_end: &mut B) {
        if back_end.supports_tri_list_xy_f32_rgba_f32() {
            let round_rect = self.round_rect.get();
            let color = self.color.get();
            let color: [f32, ..4] = [color[0], color[1], color[2], color[3]];
            // Complete transparency does not need to be rendered.
            if color[3] == 0.0 { return; }
            // Turn on alpha blending if not completely opaque.
            let needs_alpha = color[3] != 1.0;
            if needs_alpha { back_end.enable_alpha_blend(); }
            with_round_rectangle_tri_list_xy_f32_rgba_f32(
                32,
                self.transform.get(),
                round_rect,
                color,
                |vertices, colors| {
                    back_end.tri_list_xy_f32_rgba_f32(vertices, colors)
                }
            );
            if needs_alpha { back_end.disable_alpha_blend(); }
        } else {
            unimplemented!();
        }
    }
}

impl<'a> View<'a> for RoundRectangleColorContext<'a> {
    #[inline(always)]
    fn view(&'a self) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.base.get()),
            round_rect: Borrowed(self.round_rect.get()),
            color: Borrowed(self.color.get()),
        }
    }

    #[inline(always)]
    fn reset(&'a self) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Value(identity()),
            round_rect: Borrowed(self.round_rect.get()),
            color: Borrowed(self.color.get()),
        }
    }

    #[inline(always)]
    fn store_view(&'a self) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.transform.get()),
            transform: Borrowed(self.transform.get()),
            round_rect: Borrowed(self.round_rect.get()),
            color: Borrowed(self.color.get()),
        }
    }
}

<commit_msg>Implemented `HasColor` and `CanColor` for `RoundRectangleColorContext`<commit_after>
use {
    BackEnd,
    Borrowed,
    CanColor,
    CanTransform,
    Clear,
    Color,
    Field,
    Fill,
    HasColor,
    HasTransform,
    Matrix2d,
    RelativeRectangle,
    RoundRectangle,
    Value,
    View,
};
use vecmath::{
    identity,
    margin_round_rectangle,
    relative_round_rectangle,
};
use triangulation::{
    with_round_rectangle_tri_list_xy_f32_rgba_f32
};

/// A rectangle color context.
pub struct RoundRectangleColorContext<'a> {
    /// Base/original transformation.
    pub base: Field<'a, Matrix2d>,
    /// Current transformation.
    pub transform: Field<'a, Matrix2d>,
    /// Current rectangle.
    pub round_rect: Field<'a, RoundRectangle>,
    /// Current color.
    pub color: Field<'a, Color>,
}

impl<'a> HasTransform<'a, Matrix2d> for RoundRectangleColorContext<'a> {
    #[inline(always)]
    fn get_transform(&'a self) -> &'a Matrix2d {
        self.transform.get()
    }
}

impl<'a> CanTransform<'a, RoundRectangleColorContext<'a>, Matrix2d> for RoundRectangleColorContext<'a> {
    #[inline(always)]
    fn transform(&'a self, value: Matrix2d) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Value(value),
            round_rect: Borrowed(self.round_rect.get()),
            color: Borrowed(self.color.get()),
        }
    }
}

impl<'a> HasColor<'a, Color> for RoundRectangleColorContext<'a> {
    #[inline(always)]
    fn get_color(&'a self) -> &'a Color {
        self.color.get()
    }
}

impl<'a> CanColor<'a, RoundRectangleColorContext<'a>, Color> for RoundRectangleColorContext<'a> {
    #[inline(always)]
    fn color(&'a self, value: Color) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Value(value),
            round_rect: Borrowed(self.round_rect.get()),
        }
    }
}

impl<'a> Clear for RoundRectangleColorContext<'a> {
    fn clear<B: BackEnd>(&self, back_end: &mut B) {
        if back_end.supports_clear_rgba() {
            let color = self.color.get();
            back_end.clear_rgba(color[0], color[1], color[2], color[3]);
        } else {
            unimplemented!();
        }
    }
}

impl<'a> RelativeRectangle<'a> for RoundRectangleColorContext<'a> {
    #[inline(always)]
    fn margin(&'a self, m: f64) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
            round_rect: Value(margin_round_rectangle(self.round_rect.get(), m)),
        }
    }

    #[inline(always)]
    fn rel(&'a self, x: f64, y: f64) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
            round_rect: Value(relative_round_rectangle(self.round_rect.get(), x, y)),
        }
    }
}

impl<'a> Fill<'a> for RoundRectangleColorContext<'a> {
    fn fill<B: BackEnd>(&'a self, back_end: &mut B) {
        if back_end.supports_tri_list_xy_f32_rgba_f32() {
            let round_rect = self.round_rect.get();
            let color = self.color.get();
            let color: [f32, ..4] = [color[0], color[1], color[2], color[3]];
            // Complete transparency does not need to be rendered.
            if color[3] == 0.0 { return; }
            // Turn on alpha blending if not completely opaque.
            let needs_alpha = color[3] != 1.0;
            if needs_alpha { back_end.enable_alpha_blend(); }
            with_round_rectangle_tri_list_xy_f32_rgba_f32(
                32,
                self.transform.get(),
                round_rect,
                color,
                |vertices, colors| {
                    back_end.tri_list_xy_f32_rgba_f32(vertices, colors)
                }
            );
            if needs_alpha { back_end.disable_alpha_blend(); }
        } else {
            unimplemented!();
        }
    }
}

impl<'a> View<'a> for RoundRectangleColorContext<'a> {
    #[inline(always)]
    fn view(&'a self) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.base.get()),
            round_rect: Borrowed(self.round_rect.get()),
            color: Borrowed(self.color.get()),
        }
    }

    #[inline(always)]
    fn reset(&'a self) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Value(identity()),
            round_rect: Borrowed(self.round_rect.get()),
            color: Borrowed(self.color.get()),
        }
    }

    #[inline(always)]
    fn store_view(&'a self) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.transform.get()),
            transform: Borrowed(self.transform.get()),
            round_rect: Borrowed(self.round_rect.get()),
            color: Borrowed(self.color.get()),
        }
    }
}

<|endoftext|>


<commit_before>//! # execution_plan
//!
//! Creates execution plan for a given task and makefile.
//!

#[cfg(test)]
#[path = "./execution_plan_test.rs"]
mod execution_plan_test;

use crate::environment;
use crate::logger;
use crate::profile;
use crate::runner::create_proxy_task;
use crate::types::{
    Config, CrateInfo, EnvValue, ExecutionPlan, ScriptValue, Step, Task, TaskIdentifier, Workspace,
};
use envmnt;
use glob::Pattern;
use indexmap::IndexMap;
use std::collections::HashSet;
use std::env;
use std::path;
use std::path::Path;
use std::vec::Vec;

/// Resolve aliases to different tasks, checking for cycles
fn get_task_name_recursive(config: &Config, name: &str, seen: &mut Vec<String>) -> Option<String> {
    seen.push(name.to_string());

    match config.tasks.get(name) {
        Some(task_config) => {
            let alias = task_config.get_alias();

            match alias {
                Some(ref alias) if seen.contains(alias) => {
                    let chain = seen.join(" -> ");
                    error!("Detected cycle while resolving alias {}: {}", &name, chain);
                    panic!("Detected cycle while resolving alias {}: {}", &name, chain);
                }
                Some(ref alias) => get_task_name_recursive(config, alias, seen),
                _ => Some(name.to_string()),
            }
        }
        None => None,
    }
}

/// Returns the actual task name to invoke as tasks may have aliases
fn get_task_name(config: &Config, name: &str) -> Option<String> {
    let mut seen = Vec::new();

    get_task_name_recursive(config, name, &mut seen)
}

pub(crate) fn get_normalized_task(config: &Config, name: &str, support_alias: bool) -> Task {
    match get_optional_normalized_task(config, name, support_alias) {
        Some(task) => task,
        None => {
            error!("Task {} not found", &name);
            panic!("Task {} not found", &name);
        }
    }
}

fn get_optional_normalized_task(config: &Config, name: &str, support_alias: bool) -> Option<Task> {
    let actual_task_name_option = if support_alias {
        get_task_name(config, name)
    } else {
        Some(name.to_string())
    };

    match actual_task_name_option {
        Some(actual_task_name) => match config.tasks.get(&actual_task_name) {
            Some(task_config) => {
                let mut clone_task = task_config.clone();
                let mut normalized_task = clone_task.get_normalized_task();

                normalized_task = match normalized_task.extend {
                    Some(ref extended_task_name) => {
                        let mut extended_task =
                            get_normalized_task(config, extended_task_name, support_alias);

                        extended_task.extend(&normalized_task);

                        extended_task
                    }
                    None => normalized_task,
                };

                Some(normalized_task)
            }
            None => None,
        },
        None => None,
    }
}

fn get_workspace_members_config(members_config: String) -> HashSet<String> {
    let mut members = HashSet::new();

    let members_list: Vec<&str> = members_config.split(';').collect();

    for member in members_list.iter() {
        if member.len() > 0 {
            members.insert(member.to_string());
        }
    }

    members
}

fn is_workspace_member_found(member: &str, members_map: &HashSet<String>) -> bool {
    if members_map.contains(member) {
        true
    } else {
        // search for globs
        let mut found = false;

        for member_iter in members_map {
            if member_iter.contains("*") {
                found = match Pattern::new(member_iter) {
                    Ok(pattern) => pattern.matches(&member),
                    _ => false,
                };

                if found {
                    break;
                }
            }
        }

        found
    }
}

fn should_skip_workspace_member(member: &str, skipped_members: &HashSet<String>) -> bool {
    is_workspace_member_found(member, skipped_members)
}

fn should_include_workspace_member(member: &str, include_members: &HashSet<String>) -> bool {
    if include_members.is_empty() {
        true
    } else {
        is_workspace_member_found(member, include_members)
    }
}

fn update_member_path(member: &str) -> String {
    let os_separator = path::MAIN_SEPARATOR.to_string();

    //convert to OS path separators
    let mut member_path = str::replace(&member, "\\", &os_separator);
    member_path = str::replace(&member_path, "/", &os_separator);

    member_path
}

fn filter_workspace_members(members: &Vec<String>) -> Vec<String> {
    let skip_members_config = envmnt::get_or("CARGO_MAKE_WORKSPACE_SKIP_MEMBERS", "");
    let skip_members = get_workspace_members_config(skip_members_config);

    let include_members_config = envmnt::get_or("CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS", "");
    let include_members = get_workspace_members_config(include_members_config);

    let mut filtered_members = vec![];
    for member in members {
        if !should_skip_workspace_member(&member, &skip_members)
            && should_include_workspace_member(&member, &include_members)
        {
            filtered_members.push(member.to_string());
        } else {
            debug!("Skipping Member: {}.", &member);
        }
    }

    filtered_members
}

fn create_workspace_task(crate_info: CrateInfo, task: &str) -> Task {
    let set_workspace_emulation = crate_info.workspace.is_none()
        && envmnt::is("CARGO_MAKE_WORKSPACE_EMULATION")
        && !envmnt::exists("CARGO_MAKE_WORKSPACE_EMULATION_ROOT_DIRECTORY");
    if set_workspace_emulation {
        environment::search_and_set_workspace_cwd();
        let root_directory = envmnt::get_or_panic("CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY");
        envmnt::set(
            "CARGO_MAKE_WORKSPACE_EMULATION_ROOT_DIRECTORY",
            &root_directory,
        );
    }

    let members = if crate_info.workspace.is_some() {
        let workspace = crate_info.workspace.unwrap_or(Workspace::new());
        workspace.members.unwrap_or(vec![])
    } else {
        envmnt::get_list("CARGO_MAKE_CRATE_WORKSPACE_MEMBERS").unwrap_or(vec![])
    };

    let log_level = logger::get_log_level();

    let profile_name = if envmnt::is_or("CARGO_MAKE_USE_WORKSPACE_PROFILE", true) {
        profile::get()
    } else {
        profile::DEFAULT_PROFILE.to_string()
    };

    let filtered_members = filter_workspace_members(&members);

    let cargo_make_command = "cargo make";

    let mut script_lines = vec![];
    for member in &filtered_members {
        //convert to OS path separators
        let member_path = update_member_path(&member);

        let mut cd_line = if cfg!(windows) {
            "PUSHD ".to_string()
        } else {
            "cd ./".to_string()
        };
        cd_line.push_str(&member_path);
        script_lines.push(cd_line);

        //get member name
        let member_name = match Path::new(&member_path).file_name() {
            Some(name) => String::from(name.to_string_lossy()),
            None => member_path.clone(),
        };

        debug!("Adding Member: {} Path: {}", &member_name, &member_path);

        let mut make_line = cargo_make_command.to_string();
        make_line
            .push_str(" --disable-check-for-updates --allow-private --no-on-error --loglevel=");
        make_line.push_str(&log_level);
        make_line.push_str(" --env CARGO_MAKE_CRATE_CURRENT_WORKSPACE_MEMBER=");
        make_line.push_str(&member_name);
        make_line.push_str(" --profile ");
        make_line.push_str(&profile_name);
        make_line.push_str(" -- ");
        make_line.push_str(&task);

        if let Some(args) = envmnt::get_list("CARGO_MAKE_TASK_ARGS") {
            for arg in args {
                make_line.push_str(" ");
                make_line.push_str(&arg);
            }
        }

        script_lines.push(make_line);

        if cfg!(windows) {
            script_lines.push("if %errorlevel% neq 0 exit /b %errorlevel%".to_string());
            script_lines.push("POPD".to_string());
        } else {
            script_lines.push("cd -".to_string());
        };
    }

    //only if environment variable is set
    let task_env = if envmnt::is_or("CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE", false) {
        match env::var("CARGO_MAKE_MAKEFILE_PATH") {
            Ok(makefile) => {
                let mut env_map = IndexMap::new();
                env_map.insert(
                    "CARGO_MAKE_WORKSPACE_MAKEFILE".to_string(),
                    EnvValue::Value(makefile.to_string()),
                );

                Some(env_map)
            }
            _ => None,
        }
    } else {
        None
    };

    debug!("Workspace Task Script: {:#?}", &script_lines);

    let mut workspace_task = Task::new();
    workspace_task.script = Some(ScriptValue::Text(script_lines));
    workspace_task.env = task_env;

    workspace_task
}

fn is_workspace_flow(
    config: &Config,
    task: &str,
    disable_workspace: bool,
    crate_info: &CrateInfo,
    sub_flow: bool,
) -> bool {
    // determine if workspace flow is explicitly set and enabled in the requested task
    let (task_set_workspace, task_enable_workspace) =
        match get_optional_normalized_task(config, task, true) {
            Some(normalized_task) => match normalized_task.workspace {
                Some(enable_workspace) => (true, enable_workspace),
                None => (false, false),
            },
            None => (false, false),
        };

    // if project is not a workspace or if workspace is disabled via cli, return no workspace flow
    if disable_workspace
        || (sub_flow && !task_enable_workspace)
        || (crate_info.workspace.is_none() && !envmnt::is("CARGO_MAKE_WORKSPACE_EMULATION"))
        || envmnt::exists("CARGO_MAKE_CRATE_CURRENT_WORKSPACE_MEMBER")
    {
        false
    } else {
        // project is a workspace and wasn't disabled via cli, need to check requested task

        // use requested task's workspace flag if set
        if task_set_workspace {
            task_enable_workspace
        } else {
            // use configured default workspace flag if set
            config.config.default_to_workspace.unwrap_or(true)
        }
    }
}

/// Creates an execution plan for the given step based on existing execution plan data
fn create_for_step(
    config: &Config,
    task: &TaskIdentifier,
    steps: &mut Vec<Step>,
    task_names: &mut HashSet<String>,
    root: bool,
    allow_private: bool,
) {
    if let Some(path) = &task.path {
        // this is refering to a task in another file
        // so we create a proxy task to invoke it
        let proxy_name = format!("{}_proxy", task.name);
        let mut proxy_task = create_proxy_task(&proxy_name, true, false);

        proxy_task.command = Some("cargo".to_string());
        proxy_task.args = Some(vec!["make".to_string(), task.name.clone()]);
        proxy_task.cwd = Some(path.to_owned());

        steps.push(Step {
            name: task.to_string(),
            config: proxy_task,
        });
        task_names.insert(task.to_string());
        return;
    }

    let task_config = get_normalized_task(config, &task.name, true);

    debug!("Normalized Task: {} config: {:#?}", &task, &task_config);

    let is_private = match task_config.private {
        Some(value) => value,
        None => false,
    };

    if allow_private || !is_private {
        let add = !task_config.disabled.unwrap_or(false);

        if add {
            match task_config.dependencies {
                Some(ref dependencies) => {
                    for dependency in dependencies {
                        create_for_step(
                            &config,
                            &dependency.to_owned().into(),
                            steps,
                            task_names,
                            false,
                            true,
                        );
                    }
                }
                _ => debug!("No dependencies found for task: {}", &task),
            };

            if !task_names.contains(&task.name) {
                steps.push(Step {
                    name: task.to_string(),
                    config: task_config,
                });
                task_names.insert(task.to_string());
            } else if root {
                error!("Circular reference found for task: {}", &task);
            }
        }
    } else {
        error!("Task {} is private", &task);
        panic!("Task {} is private", &task);
    }
}

/// Creates the full execution plan
pub(crate) fn create(
    config: &Config,
    task: &str,
    disable_workspace: bool,
    allow_private: bool,
    sub_flow: bool,
) -> ExecutionPlan {
    let mut task_names = HashSet::new();
    let mut steps = Vec::new();

    if !sub_flow {
        match config.config.init_task {
            Some(ref task) => {
                let task_config = get_normalized_task(config, task, false);
                let add = !task_config.disabled.unwrap_or(false);

                if add {
                    steps.push(Step {
                        name: task.to_string(),
                        config: task_config,
                    });
                }
            }
            None => debug!("Init task not defined."),
        };
    }

    // load crate info and look for workspace info
    let crate_info = environment::crateinfo::load();

    let workspace_flow =
        is_workspace_flow(&config, &task, disable_workspace, &crate_info, sub_flow);

    if workspace_flow {
        let workspace_task = create_workspace_task(crate_info, task);

        steps.push(Step {
            name: "workspace".to_string(),
            config: workspace_task,
        });
    } else {
        create_for_step(
            &config,
            &TaskIdentifier::from_name(task),
            &mut steps,
            &mut task_names,
            true,
            allow_private,
        );
    }

    if !sub_flow {
        // always add end task even if already executed due to some depedency
        match config.config.end_task {
            Some(ref task) => {
                let task_config = get_normalized_task(config, task, false);
                let add = !task_config.disabled.unwrap_or(false);

                if add {
                    steps.push(Step {
                        name: task.to_string(),
                        config: task_config,
                    });
                }
            }
            None => debug!("End task not defined."),
        };
    }

    ExecutionPlan { steps }
}
<commit_msg>Remove unneeded overwrite on proxy_task<commit_after>//! # execution_plan
//!
//! Creates execution plan for a given task and makefile.
//!

#[cfg(test)]
#[path = "./execution_plan_test.rs"]
mod execution_plan_test;

use crate::environment;
use crate::logger;
use crate::profile;
use crate::runner::create_proxy_task;
use crate::types::{
    Config, CrateInfo, EnvValue, ExecutionPlan, ScriptValue, Step, Task, TaskIdentifier, Workspace,
};
use envmnt;
use glob::Pattern;
use indexmap::IndexMap;
use std::collections::HashSet;
use std::env;
use std::path;
use std::path::Path;
use std::vec::Vec;

/// Resolve aliases to different tasks, checking for cycles
fn get_task_name_recursive(config: &Config, name: &str, seen: &mut Vec<String>) -> Option<String> {
    seen.push(name.to_string());

    match config.tasks.get(name) {
        Some(task_config) => {
            let alias = task_config.get_alias();

            match alias {
                Some(ref alias) if seen.contains(alias) => {
                    let chain = seen.join(" -> ");
                    error!("Detected cycle while resolving alias {}: {}", &name, chain);
                    panic!("Detected cycle while resolving alias {}: {}", &name, chain);
                }
                Some(ref alias) => get_task_name_recursive(config, alias, seen),
                _ => Some(name.to_string()),
            }
        }
        None => None,
    }
}

/// Returns the actual task name to invoke as tasks may have aliases
fn get_task_name(config: &Config, name: &str) -> Option<String> {
    let mut seen = Vec::new();

    get_task_name_recursive(config, name, &mut seen)
}

pub(crate) fn get_normalized_task(config: &Config, name: &str, support_alias: bool) -> Task {
    match get_optional_normalized_task(config, name, support_alias) {
        Some(task) => task,
        None => {
            error!("Task {} not found", &name);
            panic!("Task {} not found", &name);
        }
    }
}

fn get_optional_normalized_task(config: &Config, name: &str, support_alias: bool) -> Option<Task> {
    let actual_task_name_option = if support_alias {
        get_task_name(config, name)
    } else {
        Some(name.to_string())
    };

    match actual_task_name_option {
        Some(actual_task_name) => match config.tasks.get(&actual_task_name) {
            Some(task_config) => {
                let mut clone_task = task_config.clone();
                let mut normalized_task = clone_task.get_normalized_task();

                normalized_task = match normalized_task.extend {
                    Some(ref extended_task_name) => {
                        let mut extended_task =
                            get_normalized_task(config, extended_task_name, support_alias);

                        extended_task.extend(&normalized_task);

                        extended_task
                    }
                    None => normalized_task,
                };

                Some(normalized_task)
            }
            None => None,
        },
        None => None,
    }
}

fn get_workspace_members_config(members_config: String) -> HashSet<String> {
    let mut members = HashSet::new();

    let members_list: Vec<&str> = members_config.split(';').collect();

    for member in members_list.iter() {
        if member.len() > 0 {
            members.insert(member.to_string());
        }
    }

    members
}

fn is_workspace_member_found(member: &str, members_map: &HashSet<String>) -> bool {
    if members_map.contains(member) {
        true
    } else {
        // search for globs
        let mut found = false;

        for member_iter in members_map {
            if member_iter.contains("*") {
                found = match Pattern::new(member_iter) {
                    Ok(pattern) => pattern.matches(&member),
                    _ => false,
                };

                if found {
                    break;
                }
            }
        }

        found
    }
}

fn should_skip_workspace_member(member: &str, skipped_members: &HashSet<String>) -> bool {
    is_workspace_member_found(member, skipped_members)
}

fn should_include_workspace_member(member: &str, include_members: &HashSet<String>) -> bool {
    if include_members.is_empty() {
        true
    } else {
        is_workspace_member_found(member, include_members)
    }
}

fn update_member_path(member: &str) -> String {
    let os_separator = path::MAIN_SEPARATOR.to_string();

    //convert to OS path separators
    let mut member_path = str::replace(&member, "\\", &os_separator);
    member_path = str::replace(&member_path, "/", &os_separator);

    member_path
}

fn filter_workspace_members(members: &Vec<String>) -> Vec<String> {
    let skip_members_config = envmnt::get_or("CARGO_MAKE_WORKSPACE_SKIP_MEMBERS", "");
    let skip_members = get_workspace_members_config(skip_members_config);

    let include_members_config = envmnt::get_or("CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS", "");
    let include_members = get_workspace_members_config(include_members_config);

    let mut filtered_members = vec![];
    for member in members {
        if !should_skip_workspace_member(&member, &skip_members)
            && should_include_workspace_member(&member, &include_members)
        {
            filtered_members.push(member.to_string());
        } else {
            debug!("Skipping Member: {}.", &member);
        }
    }

    filtered_members
}

fn create_workspace_task(crate_info: CrateInfo, task: &str) -> Task {
    let set_workspace_emulation = crate_info.workspace.is_none()
        && envmnt::is("CARGO_MAKE_WORKSPACE_EMULATION")
        && !envmnt::exists("CARGO_MAKE_WORKSPACE_EMULATION_ROOT_DIRECTORY");
    if set_workspace_emulation {
        environment::search_and_set_workspace_cwd();
        let root_directory = envmnt::get_or_panic("CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY");
        envmnt::set(
            "CARGO_MAKE_WORKSPACE_EMULATION_ROOT_DIRECTORY",
            &root_directory,
        );
    }

    let members = if crate_info.workspace.is_some() {
        let workspace = crate_info.workspace.unwrap_or(Workspace::new());
        workspace.members.unwrap_or(vec![])
    } else {
        envmnt::get_list("CARGO_MAKE_CRATE_WORKSPACE_MEMBERS").unwrap_or(vec![])
    };

    let log_level = logger::get_log_level();

    let profile_name = if envmnt::is_or("CARGO_MAKE_USE_WORKSPACE_PROFILE", true) {
        profile::get()
    } else {
        profile::DEFAULT_PROFILE.to_string()
    };

    let filtered_members = filter_workspace_members(&members);

    let cargo_make_command = "cargo make";

    let mut script_lines = vec![];
    for member in &filtered_members {
        //convert to OS path separators
        let member_path = update_member_path(&member);

        let mut cd_line = if cfg!(windows) {
            "PUSHD ".to_string()
        } else {
            "cd ./".to_string()
        };
        cd_line.push_str(&member_path);
        script_lines.push(cd_line);

        //get member name
        let member_name = match Path::new(&member_path).file_name() {
            Some(name) => String::from(name.to_string_lossy()),
            None => member_path.clone(),
        };

        debug!("Adding Member: {} Path: {}", &member_name, &member_path);

        let mut make_line = cargo_make_command.to_string();
        make_line
            .push_str(" --disable-check-for-updates --allow-private --no-on-error --loglevel=");
        make_line.push_str(&log_level);
        make_line.push_str(" --env CARGO_MAKE_CRATE_CURRENT_WORKSPACE_MEMBER=");
        make_line.push_str(&member_name);
        make_line.push_str(" --profile ");
        make_line.push_str(&profile_name);
        make_line.push_str(" -- ");
        make_line.push_str(&task);

        if let Some(args) = envmnt::get_list("CARGO_MAKE_TASK_ARGS") {
            for arg in args {
                make_line.push_str(" ");
                make_line.push_str(&arg);
            }
        }

        script_lines.push(make_line);

        if cfg!(windows) {
            script_lines.push("if %errorlevel% neq 0 exit /b %errorlevel%".to_string());
            script_lines.push("POPD".to_string());
        } else {
            script_lines.push("cd -".to_string());
        };
    }

    //only if environment variable is set
    let task_env = if envmnt::is_or("CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE", false) {
        match env::var("CARGO_MAKE_MAKEFILE_PATH") {
            Ok(makefile) => {
                let mut env_map = IndexMap::new();
                env_map.insert(
                    "CARGO_MAKE_WORKSPACE_MAKEFILE".to_string(),
                    EnvValue::Value(makefile.to_string()),
                );

                Some(env_map)
            }
            _ => None,
        }
    } else {
        None
    };

    debug!("Workspace Task Script: {:#?}", &script_lines);

    let mut workspace_task = Task::new();
    workspace_task.script = Some(ScriptValue::Text(script_lines));
    workspace_task.env = task_env;

    workspace_task
}

fn is_workspace_flow(
    config: &Config,
    task: &str,
    disable_workspace: bool,
    crate_info: &CrateInfo,
    sub_flow: bool,
) -> bool {
    // determine if workspace flow is explicitly set and enabled in the requested task
    let (task_set_workspace, task_enable_workspace) =
        match get_optional_normalized_task(config, task, true) {
            Some(normalized_task) => match normalized_task.workspace {
                Some(enable_workspace) => (true, enable_workspace),
                None => (false, false),
            },
            None => (false, false),
        };

    // if project is not a workspace or if workspace is disabled via cli, return no workspace flow
    if disable_workspace
        || (sub_flow && !task_enable_workspace)
        || (crate_info.workspace.is_none() && !envmnt::is("CARGO_MAKE_WORKSPACE_EMULATION"))
        || envmnt::exists("CARGO_MAKE_CRATE_CURRENT_WORKSPACE_MEMBER")
    {
        false
    } else {
        // project is a workspace and wasn't disabled via cli, need to check requested task

        // use requested task's workspace flag if set
        if task_set_workspace {
            task_enable_workspace
        } else {
            // use configured default workspace flag if set
            config.config.default_to_workspace.unwrap_or(true)
        }
    }
}

/// Creates an execution plan for the given step based on existing execution plan data
fn create_for_step(
    config: &Config,
    task: &TaskIdentifier,
    steps: &mut Vec<Step>,
    task_names: &mut HashSet<String>,
    root: bool,
    allow_private: bool,
) {
    if let Some(path) = &task.path {
        // this is refering to a task in another file
        // so we create a proxy task to invoke it
        let proxy_name = format!("{}_proxy", task.name);

        let mut proxy_task = create_proxy_task(&proxy_name, true, false);
        proxy_task.cwd = Some(path.to_owned());

        steps.push(Step {
            name: task.to_string(),
            config: proxy_task,
        });
        task_names.insert(task.to_string());
        return;
    }

    let task_config = get_normalized_task(config, &task.name, true);

    debug!("Normalized Task: {} config: {:#?}", &task, &task_config);

    let is_private = match task_config.private {
        Some(value) => value,
        None => false,
    };

    if allow_private || !is_private {
        let add = !task_config.disabled.unwrap_or(false);

        if add {
            match task_config.dependencies {
                Some(ref dependencies) => {
                    for dependency in dependencies {
                        create_for_step(
                            &config,
                            &dependency.to_owned().into(),
                            steps,
                            task_names,
                            false,
                            true,
                        );
                    }
                }
                _ => debug!("No dependencies found for task: {}", &task),
            };

            if !task_names.contains(&task.name) {
                steps.push(Step {
                    name: task.to_string(),
                    config: task_config,
                });
                task_names.insert(task.to_string());
            } else if root {
                error!("Circular reference found for task: {}", &task);
            }
        }
    } else {
        error!("Task {} is private", &task);
        panic!("Task {} is private", &task);
    }
}

/// Creates the full execution plan
pub(crate) fn create(
    config: &Config,
    task: &str,
    disable_workspace: bool,
    allow_private: bool,
    sub_flow: bool,
) -> ExecutionPlan {
    let mut task_names = HashSet::new();
    let mut steps = Vec::new();

    if !sub_flow {
        match config.config.init_task {
            Some(ref task) => {
                let task_config = get_normalized_task(config, task, false);
                let add = !task_config.disabled.unwrap_or(false);

                if add {
                    steps.push(Step {
                        name: task.to_string(),
                        config: task_config,
                    });
                }
            }
            None => debug!("Init task not defined."),
        };
    }

    // load crate info and look for workspace info
    let crate_info = environment::crateinfo::load();

    let workspace_flow =
        is_workspace_flow(&config, &task, disable_workspace, &crate_info, sub_flow);

    if workspace_flow {
        let workspace_task = create_workspace_task(crate_info, task);

        steps.push(Step {
            name: "workspace".to_string(),
            config: workspace_task,
        });
    } else {
        create_for_step(
            &config,
            &TaskIdentifier::from_name(task),
            &mut steps,
            &mut task_names,
            true,
            allow_private,
        );
    }

    if !sub_flow {
        // always add end task even if already executed due to some depedency
        match config.config.end_task {
            Some(ref task) => {
                let task_config = get_normalized_task(config, task, false);
                let add = !task_config.disabled.unwrap_or(false);

                if add {
                    steps.push(Step {
                        name: task.to_string(),
                        config: task_config,
                    });
                }
            }
            None => debug!("End task not defined."),
        };
    }

    ExecutionPlan { steps }
}
<|endoftext|>


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

/*!

This modules provides bindings to the local event loop's TTY interface, using it
to have synchronous, but non-blocking versions of stdio. These handles can be
inspected for information about terminal dimensions or related information
about the stream or terminal that it is attached to.

# Example

```rust
use std::rt::io;

let mut out = io::stdout();
out.write(bytes!("Hello, world!"));
```

*/

use fmt;
use libc;
use option::{Option, Some, None};
use result::{Ok, Err};
use rt::io::buffered::{LineBufferedWriter, BufferedWriter};
use rt::rtio::{IoFactory, RtioTTY, RtioFileStream, with_local_io,
               CloseAsynchronously};
use super::{Reader, Writer, io_error, IoError, OtherIoError};

// And so begins the tale of acquiring a uv handle to a stdio stream on all
// platforms in all situations. Our story begins by splitting the world into two
// categories, windows and unix. Then one day the creators of unix said let
// there be redirection! And henceforth there was redirection away from the
// console for standard I/O streams.
//
// After this day, the world split into four factions:
//
// 1. Unix with stdout on a terminal.
// 2. Unix with stdout redirected.
// 3. Windows with stdout on a terminal.
// 4. Windows with stdout redirected.
//
// Many years passed, and then one day the nation of libuv decided to unify this
// world. After months of toiling, uv created three ideas: TTY, Pipe, File.
// These three ideas propagated throughout the lands and the four great factions
// decided to settle among them.
//
// The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
// doing so, they even enhanced themselves further then their Pipe/File
// brethren, becoming the dominant powers.
//
// The group of 4, however, decided to work independently. They abandoned the
// common TTY belief throughout, and even abandoned the fledgling Pipe belief.
// The members of the 4th faction decided to only align themselves with File.
//
// tl;dr; TTY works on everything but when windows stdout is redirected, in that
//        case pipe also doesn't work, but magically file does!
enum StdSource {
    TTY(~RtioTTY),
    File(~RtioFileStream),
}

#[fixed_stack_segment] #[inline(never)]
fn src<T>(fd: libc::c_int, readable: bool, f: &fn(StdSource) -> T) -> T {
    do with_local_io |io| {
        let fd = unsafe { libc::dup(fd) };
        match io.tty_open(fd, readable) {
            Ok(tty) => Some(f(TTY(tty))),
            Err(_) => {
                // It's not really that desirable if these handles are closed
                // synchronously, and because they're squirreled away in a task
                // structure the destructors will be run when the task is
                // attempted to get destroyed. This means that if we run a
                // synchronous destructor we'll attempt to do some scheduling
                // operations which will just result in sadness.
                Some(f(File(io.fs_from_raw_fd(fd, CloseAsynchronously))))
            }
        }
    }.unwrap()
}

/// Creates a new non-blocking handle to the stdin of the current process.
///
/// See `stdout()` for notes about this function.
#[fixed_stack_segment] #[inline(never)]
pub fn stdin() -> StdReader {
    do src(libc::STDIN_FILENO, true) |src| { StdReader { inner: src } }
}

/// Creates a new non-blocking handle to the stdout of the current process.
///
/// Note that this is a fairly expensive operation in that at least one memory
/// allocation is performed. Additionally, this must be called from a runtime
/// task context because the stream returned will be a non-blocking object using
/// the local scheduler to perform the I/O.
pub fn stdout() -> StdWriter {
    do src(libc::STDOUT_FILENO, false) |src| { StdWriter { inner: src } }
}

/// Creates a new non-blocking handle to the stderr of the current process.
///
/// See `stdout()` for notes about this function.
pub fn stderr() -> StdWriter {
    do src(libc::STDERR_FILENO, false) |src| { StdWriter { inner: src } }
}

// Helper to access the local task's stdout handle
//
// Note that this is not a safe function to expose because you can create an
// aliased pointer very easily:
//
//  do with_task_stdout |io1| {
//      do with_task_stdout |io2| {
//          // io1 aliases io2
//      }
//  }
fn with_task_stdout(f: &fn(&mut Writer)) {
    use rt::local::Local;
    use rt::task::Task;

    unsafe {
        // Logging may require scheduling operations, so we can't remove the
        // task from TLS right now, hence the unsafe borrow. Sad.
        let task: *mut Task = Local::unsafe_borrow();

        match (*task).stdout_handle {
            Some(ref mut handle) => f(*handle),
            None => {
                let handle = stdout();
                let mut handle = if handle.isatty() {
                    ~LineBufferedWriter::new(handle) as ~Writer
                } else {
                    // The default capacity is very large, 64k, but this is just
                    // a stdout stream, and possibly per task, so let's not make
                    // this too expensive.
                    ~BufferedWriter::with_capacity(4096, handle) as ~Writer
                };
                f(handle);
                (*task).stdout_handle = Some(handle);
            }
        }
    }
}

/// Flushes the local task's stdout handle.
///
/// By default, this stream is a buffering stream, flushing may be necessary to
/// ensure output is on the terminal screen. The buffering used is
/// line-buffering when stdout is attached to a terminal, and a fixed sized
/// buffer if it is not attached to a terminal.
///
/// Note that logging macros do not use this stream. Using the logging macros
/// will emit output to stderr, and while they are line buffered the log
/// messages are always terminated in a newline (no need to flush).
pub fn flush() {
    do with_task_stdout |io| {
        io.flush();
    }
}

/// Prints a string to the stdout of the current process. No newline is emitted
/// after the string is printed.
pub fn print(s: &str) {
    do with_task_stdout |io| {
        io.write(s.as_bytes());
    }
}

/// Prints a string as a line. to the stdout of the current process. A literal
/// `\n` character is printed to the console after the string.
pub fn println(s: &str) {
    do with_task_stdout |io| {
        io.write(s.as_bytes());
        io.write(['\n' as u8]);
    }
}

/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
/// with the `format_args!` macro.
pub fn print_args(fmt: &fmt::Arguments) {
    do with_task_stdout |io| {
        fmt::write(io, fmt);
    }
}

/// Similar to `println`, but takes a `fmt::Arguments` structure to be
/// compatible with the `format_args!` macro.
pub fn println_args(fmt: &fmt::Arguments) {
    do with_task_stdout |io| {
        fmt::writeln(io, fmt);
    }
}

/// Representation of a reader of a standard input stream
pub struct StdReader {
    priv inner: StdSource
}

impl Reader for StdReader {
    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
        let ret = match self.inner {
            TTY(ref mut tty) => tty.read(buf),
            File(ref mut file) => file.read(buf).map(|i| i as uint),
        };
        match ret {
            Ok(amt) => Some(amt as uint),
            Err(e) => {
                io_error::cond.raise(e);
                None
            }
        }
    }

    fn eof(&mut self) -> bool { false }
}

/// Representation of a writer to a standard output stream
pub struct StdWriter {
    priv inner: StdSource
}

impl StdWriter {
    /// Gets the size of this output window, if possible. This is typically used
    /// when the writer is attached to something like a terminal, this is used
    /// to fetch the dimensions of the terminal.
    ///
    /// If successful, returns Some((width, height)).
    ///
    /// # Failure
    ///
    /// This function will raise on the `io_error` condition if an error
    /// happens.
    pub fn winsize(&mut self) -> Option<(int, int)> {
        match self.inner {
            TTY(ref mut tty) => {
                match tty.get_winsize() {
                    Ok(p) => Some(p),
                    Err(e) => {
                        io_error::cond.raise(e);
                        None
                    }
                }
            }
            File(*) => {
                io_error::cond.raise(IoError {
                    kind: OtherIoError,
                    desc: "stream is not a tty",
                    detail: None,
                });
                None
            }
        }
    }

    /// Controls whether this output stream is a "raw stream" or simply a normal
    /// stream.
    ///
    /// # Failure
    ///
    /// This function will raise on the `io_error` condition if an error
    /// happens.
    pub fn set_raw(&mut self, raw: bool) {
        match self.inner {
            TTY(ref mut tty) => {
                match tty.set_raw(raw) {
                    Ok(()) => {},
                    Err(e) => io_error::cond.raise(e),
                }
            }
            File(*) => {
                io_error::cond.raise(IoError {
                    kind: OtherIoError,
                    desc: "stream is not a tty",
                    detail: None,
                });
            }
        }
    }

    /// Returns whether this tream is attached to a TTY instance or not.
    ///
    /// This is similar to libc's isatty() function
    pub fn isatty(&self) -> bool {
        match self.inner {
            TTY(ref tty) => tty.isatty(),
            File(*) => false,
        }
    }
}

impl Writer for StdWriter {
    fn write(&mut self, buf: &[u8]) {
        let ret = match self.inner {
            TTY(ref mut tty) => tty.write(buf),
            File(ref mut file) => file.write(buf),
        };
        match ret {
            Ok(()) => {}
            Err(e) => io_error::cond.raise(e)
        }
    }
}

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

    #[test]
    fn smoke() {
        // Just make sure we can acquire handles
        stdin();
        stdout();
        stderr();
    }
}
<commit_msg>auto merge of #10270 : alexcrichton/rust/no-super-buffer, r=brson<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

This modules provides bindings to the local event loop's TTY interface, using it
to have synchronous, but non-blocking versions of stdio. These handles can be
inspected for information about terminal dimensions or related information
about the stream or terminal that it is attached to.

# Example

```rust
use std::rt::io;

let mut out = io::stdout();
out.write(bytes!("Hello, world!"));
```

*/

use fmt;
use libc;
use option::{Option, Some, None};
use result::{Ok, Err};
use rt::io::buffered::LineBufferedWriter;
use rt::rtio::{IoFactory, RtioTTY, RtioFileStream, with_local_io,
               CloseAsynchronously};
use super::{Reader, Writer, io_error, IoError, OtherIoError};

// And so begins the tale of acquiring a uv handle to a stdio stream on all
// platforms in all situations. Our story begins by splitting the world into two
// categories, windows and unix. Then one day the creators of unix said let
// there be redirection! And henceforth there was redirection away from the
// console for standard I/O streams.
//
// After this day, the world split into four factions:
//
// 1. Unix with stdout on a terminal.
// 2. Unix with stdout redirected.
// 3. Windows with stdout on a terminal.
// 4. Windows with stdout redirected.
//
// Many years passed, and then one day the nation of libuv decided to unify this
// world. After months of toiling, uv created three ideas: TTY, Pipe, File.
// These three ideas propagated throughout the lands and the four great factions
// decided to settle among them.
//
// The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
// doing so, they even enhanced themselves further then their Pipe/File
// brethren, becoming the dominant powers.
//
// The group of 4, however, decided to work independently. They abandoned the
// common TTY belief throughout, and even abandoned the fledgling Pipe belief.
// The members of the 4th faction decided to only align themselves with File.
//
// tl;dr; TTY works on everything but when windows stdout is redirected, in that
//        case pipe also doesn't work, but magically file does!
enum StdSource {
    TTY(~RtioTTY),
    File(~RtioFileStream),
}

#[fixed_stack_segment] #[inline(never)]
fn src<T>(fd: libc::c_int, readable: bool, f: &fn(StdSource) -> T) -> T {
    do with_local_io |io| {
        let fd = unsafe { libc::dup(fd) };
        match io.tty_open(fd, readable) {
            Ok(tty) => Some(f(TTY(tty))),
            Err(_) => {
                // It's not really that desirable if these handles are closed
                // synchronously, and because they're squirreled away in a task
                // structure the destructors will be run when the task is
                // attempted to get destroyed. This means that if we run a
                // synchronous destructor we'll attempt to do some scheduling
                // operations which will just result in sadness.
                Some(f(File(io.fs_from_raw_fd(fd, CloseAsynchronously))))
            }
        }
    }.unwrap()
}

/// Creates a new non-blocking handle to the stdin of the current process.
///
/// See `stdout()` for notes about this function.
#[fixed_stack_segment] #[inline(never)]
pub fn stdin() -> StdReader {
    do src(libc::STDIN_FILENO, true) |src| { StdReader { inner: src } }
}

/// Creates a new non-blocking handle to the stdout of the current process.
///
/// Note that this is a fairly expensive operation in that at least one memory
/// allocation is performed. Additionally, this must be called from a runtime
/// task context because the stream returned will be a non-blocking object using
/// the local scheduler to perform the I/O.
pub fn stdout() -> StdWriter {
    do src(libc::STDOUT_FILENO, false) |src| { StdWriter { inner: src } }
}

/// Creates a new non-blocking handle to the stderr of the current process.
///
/// See `stdout()` for notes about this function.
pub fn stderr() -> StdWriter {
    do src(libc::STDERR_FILENO, false) |src| { StdWriter { inner: src } }
}

// Helper to access the local task's stdout handle
//
// Note that this is not a safe function to expose because you can create an
// aliased pointer very easily:
//
//  do with_task_stdout |io1| {
//      do with_task_stdout |io2| {
//          // io1 aliases io2
//      }
//  }
fn with_task_stdout(f: &fn(&mut Writer)) {
    use rt::local::Local;
    use rt::task::Task;

    unsafe {
        // Logging may require scheduling operations, so we can't remove the
        // task from TLS right now, hence the unsafe borrow. Sad.
        let task: *mut Task = Local::unsafe_borrow();

        match (*task).stdout_handle {
            Some(ref mut handle) => f(*handle),
            None => {
                let handle = stdout();
                let mut handle = ~LineBufferedWriter::new(handle) as ~Writer;
                f(handle);
                (*task).stdout_handle = Some(handle);
            }
        }
    }
}

/// Flushes the local task's stdout handle.
///
/// By default, this stream is a line-buffering stream, so flushing may be
/// necessary to ensure that all output is printed to the screen (if there are
/// no newlines printed).
///
/// Note that logging macros do not use this stream. Using the logging macros
/// will emit output to stderr, and while they are line buffered the log
/// messages are always terminated in a newline (no need to flush).
pub fn flush() {
    do with_task_stdout |io| {
        io.flush();
    }
}

/// Prints a string to the stdout of the current process. No newline is emitted
/// after the string is printed.
pub fn print(s: &str) {
    do with_task_stdout |io| {
        io.write(s.as_bytes());
    }
}

/// Prints a string as a line. to the stdout of the current process. A literal
/// `\n` character is printed to the console after the string.
pub fn println(s: &str) {
    do with_task_stdout |io| {
        io.write(s.as_bytes());
        io.write(['\n' as u8]);
    }
}

/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
/// with the `format_args!` macro.
pub fn print_args(fmt: &fmt::Arguments) {
    do with_task_stdout |io| {
        fmt::write(io, fmt);
    }
}

/// Similar to `println`, but takes a `fmt::Arguments` structure to be
/// compatible with the `format_args!` macro.
pub fn println_args(fmt: &fmt::Arguments) {
    do with_task_stdout |io| {
        fmt::writeln(io, fmt);
    }
}

/// Representation of a reader of a standard input stream
pub struct StdReader {
    priv inner: StdSource
}

impl Reader for StdReader {
    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
        let ret = match self.inner {
            TTY(ref mut tty) => tty.read(buf),
            File(ref mut file) => file.read(buf).map(|i| i as uint),
        };
        match ret {
            Ok(amt) => Some(amt as uint),
            Err(e) => {
                io_error::cond.raise(e);
                None
            }
        }
    }

    fn eof(&mut self) -> bool { false }
}

/// Representation of a writer to a standard output stream
pub struct StdWriter {
    priv inner: StdSource
}

impl StdWriter {
    /// Gets the size of this output window, if possible. This is typically used
    /// when the writer is attached to something like a terminal, this is used
    /// to fetch the dimensions of the terminal.
    ///
    /// If successful, returns Some((width, height)).
    ///
    /// # Failure
    ///
    /// This function will raise on the `io_error` condition if an error
    /// happens.
    pub fn winsize(&mut self) -> Option<(int, int)> {
        match self.inner {
            TTY(ref mut tty) => {
                match tty.get_winsize() {
                    Ok(p) => Some(p),
                    Err(e) => {
                        io_error::cond.raise(e);
                        None
                    }
                }
            }
            File(*) => {
                io_error::cond.raise(IoError {
                    kind: OtherIoError,
                    desc: "stream is not a tty",
                    detail: None,
                });
                None
            }
        }
    }

    /// Controls whether this output stream is a "raw stream" or simply a normal
    /// stream.
    ///
    /// # Failure
    ///
    /// This function will raise on the `io_error` condition if an error
    /// happens.
    pub fn set_raw(&mut self, raw: bool) {
        match self.inner {
            TTY(ref mut tty) => {
                match tty.set_raw(raw) {
                    Ok(()) => {},
                    Err(e) => io_error::cond.raise(e),
                }
            }
            File(*) => {
                io_error::cond.raise(IoError {
                    kind: OtherIoError,
                    desc: "stream is not a tty",
                    detail: None,
                });
            }
        }
    }

    /// Returns whether this tream is attached to a TTY instance or not.
    ///
    /// This is similar to libc's isatty() function
    pub fn isatty(&self) -> bool {
        match self.inner {
            TTY(ref tty) => tty.isatty(),
            File(*) => false,
        }
    }
}

impl Writer for StdWriter {
    fn write(&mut self, buf: &[u8]) {
        let ret = match self.inner {
            TTY(ref mut tty) => tty.write(buf),
            File(ref mut file) => file.write(buf),
        };
        match ret {
            Ok(()) => {}
            Err(e) => io_error::cond.raise(e)
        }
    }
}

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

    #[test]
    fn smoke() {
        // Just make sure we can acquire handles
        stdin();
        stdout();
        stderr();
    }
}
<|endoftext|>


<commit_before>// ignore-windows: Concurrency on Windows is not supported yet.
// We are making scheduler assumptions here.
// compile-flags: -Zmiri-strict-provenance -Zmiri-preemption-rate=0

use std::sync::{Arc, Condvar, Mutex, RwLock};
use std::thread;

fn check_conditional_variables_notify_all() {
    let pair = Arc::new(((Mutex::new(())), Condvar::new()));

    // Spawn threads and block them on the conditional variable.
    let handles: Vec<_> = (0..5)
        .map(|_| {
            let pair2 = pair.clone();
            thread::spawn(move || {
                let (lock, cvar) = &*pair2;
                let guard = lock.lock().unwrap();
                // Block waiting on the conditional variable.
                let _ = cvar.wait(guard).unwrap();
            })
        })
        .inspect(|_| {
            // Ensure the other threads all run and block on the `wait`.
            thread::yield_now();
            thread::yield_now();
        })
        .collect();

    let (_, cvar) = &*pair;
    // Unblock all threads.
    cvar.notify_all();

    for handle in handles {
        handle.join().unwrap();
    }
}

fn check_rwlock_unlock_bug1() {
    // There was a bug where when un-read-locking an rwlock that still has other
    // readers waiting, we'd accidentally also let a writer in.
    // That caused an ICE.
    let l = Arc::new(RwLock::new(0));

    let r1 = l.read().unwrap();
    let r2 = l.read().unwrap();

    // Make a waiting writer.
    let l2 = l.clone();
    thread::spawn(move || {
        let mut w = l2.write().unwrap();
        *w += 1;
    });
    thread::yield_now();

    drop(r1);
    assert_eq!(*r2, 0);
    thread::yield_now();
    thread::yield_now();
    thread::yield_now();
    assert_eq!(*r2, 0);
    drop(r2);
}

fn check_rwlock_unlock_bug2() {
    // There was a bug where when un-read-locking an rwlock by letting the last reader leaver,
    // we'd forget to wake up a writer.
    // That meant the writer thread could never run again.
    let l = Arc::new(RwLock::new(0));

    let r = l.read().unwrap();

    // Make a waiting writer.
    let l2 = l.clone();
    let h = thread::spawn(move || {
        let _w = l2.write().unwrap();
    });
    thread::yield_now();

    drop(r);
    h.join().unwrap();
}

fn main() {
    check_conditional_variables_notify_all();
    check_rwlock_unlock_bug1();
    check_rwlock_unlock_bug2();
}
<commit_msg>Auto merge of #2276 - RalfJung:join, r=RalfJung<commit_after>// ignore-windows: Concurrency on Windows is not supported yet.
// We are making scheduler assumptions here.
// compile-flags: -Zmiri-strict-provenance -Zmiri-preemption-rate=0

use std::sync::{Arc, Condvar, Mutex, RwLock};
use std::thread;

fn check_conditional_variables_notify_all() {
    let pair = Arc::new(((Mutex::new(())), Condvar::new()));

    // Spawn threads and block them on the conditional variable.
    let handles: Vec<_> = (0..5)
        .map(|_| {
            let pair2 = pair.clone();
            thread::spawn(move || {
                let (lock, cvar) = &*pair2;
                let guard = lock.lock().unwrap();
                // Block waiting on the conditional variable.
                let _ = cvar.wait(guard).unwrap();
            })
        })
        .inspect(|_| {
            // Ensure the other threads all run and block on the `wait`.
            thread::yield_now();
            thread::yield_now();
        })
        .collect();

    let (_, cvar) = &*pair;
    // Unblock all threads.
    cvar.notify_all();

    for handle in handles {
        handle.join().unwrap();
    }
}

fn check_rwlock_unlock_bug1() {
    // There was a bug where when un-read-locking an rwlock that still has other
    // readers waiting, we'd accidentally also let a writer in.
    // That caused an ICE.
    let l = Arc::new(RwLock::new(0));

    let r1 = l.read().unwrap();
    let r2 = l.read().unwrap();

    // Make a waiting writer.
    let l2 = l.clone();
    let t = thread::spawn(move || {
        let mut w = l2.write().unwrap();
        *w += 1;
    });
    thread::yield_now();

    drop(r1);
    assert_eq!(*r2, 0);
    thread::yield_now();
    thread::yield_now();
    thread::yield_now();
    assert_eq!(*r2, 0);
    drop(r2);
    t.join().unwrap();
}

fn check_rwlock_unlock_bug2() {
    // There was a bug where when un-read-locking an rwlock by letting the last reader leaver,
    // we'd forget to wake up a writer.
    // That meant the writer thread could never run again.
    let l = Arc::new(RwLock::new(0));

    let r = l.read().unwrap();

    // Make a waiting writer.
    let l2 = l.clone();
    let h = thread::spawn(move || {
        let _w = l2.write().unwrap();
    });
    thread::yield_now();

    drop(r);
    h.join().unwrap();
}

fn main() {
    check_conditional_variables_notify_all();
    check_rwlock_unlock_bug1();
    check_rwlock_unlock_bug2();
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{Factory, Result, Swarm, Version};
use anyhow::Context;
use rand::rngs::StdRng;
use std::{
    collections::HashMap,
    num::NonZeroUsize,
    path::{Path, PathBuf},
    sync::Arc,
};

mod cargo;
mod node;
mod swarm;
pub use node::LocalNode;
pub use swarm::{LocalSwarm, LocalSwarmBuilder, SwarmDirectory};

#[derive(Clone, Debug)]
pub struct LocalVersion {
    revision: String,
    bin: PathBuf,
    version: Version,
}

impl LocalVersion {
    fn bin(&self) -> &Path {
        &self.bin
    }

    fn version(&self) -> Version {
        self.version.clone()
    }
}

pub struct LocalFactory {
    versions: Arc<HashMap<Version, LocalVersion>>,
}

impl LocalFactory {
    pub fn new(versions: HashMap<Version, LocalVersion>) -> Self {
        Self {
            versions: Arc::new(versions),
        }
    }

    pub fn from_workspace() -> Result<Self> {
        let mut versions = HashMap::new();
        let new_version = cargo::get_diem_node_binary_from_worktree().map(|(revision, bin)| {
            let version = Version::new(usize::max_value(), revision.clone());
            LocalVersion {
                revision,
                bin,
                version,
            }
        })?;

        versions.insert(new_version.version.clone(), new_version);
        Ok(Self::new(versions))
    }

    pub fn from_revision(revision: &str) -> Result<Self> {
        let mut versions = HashMap::new();
        let new_version =
            cargo::get_diem_node_binary_at_revision(revision).map(|(revision, bin)| {
                let version = Version::new(usize::max_value(), revision.clone());
                LocalVersion {
                    revision,
                    bin,
                    version,
                }
            })?;

        versions.insert(new_version.version.clone(), new_version);
        Ok(Self::new(versions))
    }

    pub fn with_revision_and_workspace(revision: &str) -> Result<Self> {
        let workspace = cargo::get_diem_node_binary_from_worktree().map(|(revision, bin)| {
            let version = Version::new(usize::max_value(), revision.clone());
            LocalVersion {
                revision,
                bin,
                version,
            }
        })?;
        let revision =
            cargo::get_diem_node_binary_at_revision(revision).map(|(revision, bin)| {
                let version = Version::new(usize::min_value(), revision.clone());
                LocalVersion {
                    revision,
                    bin,
                    version,
                }
            })?;

        let mut versions = HashMap::new();
        versions.insert(workspace.version(), workspace);
        versions.insert(revision.version(), revision);
        Ok(Self::new(versions))
    }

    /// Create a LocalFactory with a diem-node version built at the tip of upstream/main and the
    /// current workspace, suitable for compatibility testing.
    pub fn with_upstream_and_workspace() -> Result<Self> {
        let upstream_main = cargo::git_get_upstream_remote().map(|r| format!("{}/main", r))?;
        Self::with_revision_and_workspace(&upstream_main)
    }

    /// Create a LocalFactory with a diem-node version built at merge-base of upstream/main and the
    /// current workspace, suitable for compatibility testing.
    pub fn with_upstream_merge_base_and_workspace() -> Result<Self> {
        let upstream_main = cargo::git_get_upstream_remote().map(|r| format!("{}/main", r))?;
        let merge_base = cargo::git_merge_base(upstream_main)?;
        Self::with_revision_and_workspace(&merge_base)
    }
}

impl Factory for LocalFactory {
    fn versions<'a>(&'a self) -> Box<dyn Iterator<Item = Version> + 'a> {
        Box::new(self.versions.keys().cloned())
    }

    fn launch_swarm(
        &self,
        rng: &mut StdRng,
        node_num: NonZeroUsize,
        version: &Version,
    ) -> Result<Box<dyn Swarm>> {
        let mut swarm = LocalSwarm::builder(self.versions.clone())
            .number_of_validators(node_num)
            .initial_version(version.clone())
            .build(rng)?;
        swarm
            .launch()
            .with_context(|| format!("Swarm logs can be found here: {}", swarm.logs_location()))?;

        // swarm.launch().map_err(|e| {
        //     swarm.logs_location()
        //     e
        // });
        // match swarm.launch() {
        //     Ok(()) => {}
        //     Err(e) => {
        //         swarm.
        //         return e;
        //     }
        // }

        Ok(Box::new(swarm))
    }
}
<commit_msg>forge: add swarm constructors to LocalFactory<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{Factory, Result, Swarm, Version};
use anyhow::Context;
use rand::rngs::StdRng;
use std::{
    collections::HashMap,
    num::NonZeroUsize,
    path::{Path, PathBuf},
    sync::Arc,
};

mod cargo;
mod node;
mod swarm;
pub use node::LocalNode;
pub use swarm::{LocalSwarm, LocalSwarmBuilder, SwarmDirectory};

#[derive(Clone, Debug)]
pub struct LocalVersion {
    revision: String,
    bin: PathBuf,
    version: Version,
}

impl LocalVersion {
    fn bin(&self) -> &Path {
        &self.bin
    }

    fn version(&self) -> Version {
        self.version.clone()
    }
}

pub struct LocalFactory {
    versions: Arc<HashMap<Version, LocalVersion>>,
}

impl LocalFactory {
    pub fn new(versions: HashMap<Version, LocalVersion>) -> Self {
        Self {
            versions: Arc::new(versions),
        }
    }

    pub fn from_workspace() -> Result<Self> {
        let mut versions = HashMap::new();
        let new_version = cargo::get_diem_node_binary_from_worktree().map(|(revision, bin)| {
            let version = Version::new(usize::max_value(), revision.clone());
            LocalVersion {
                revision,
                bin,
                version,
            }
        })?;

        versions.insert(new_version.version.clone(), new_version);
        Ok(Self::new(versions))
    }

    pub fn from_revision(revision: &str) -> Result<Self> {
        let mut versions = HashMap::new();
        let new_version =
            cargo::get_diem_node_binary_at_revision(revision).map(|(revision, bin)| {
                let version = Version::new(usize::max_value(), revision.clone());
                LocalVersion {
                    revision,
                    bin,
                    version,
                }
            })?;

        versions.insert(new_version.version.clone(), new_version);
        Ok(Self::new(versions))
    }

    pub fn with_revision_and_workspace(revision: &str) -> Result<Self> {
        let workspace = cargo::get_diem_node_binary_from_worktree().map(|(revision, bin)| {
            let version = Version::new(usize::max_value(), revision.clone());
            LocalVersion {
                revision,
                bin,
                version,
            }
        })?;
        let revision =
            cargo::get_diem_node_binary_at_revision(revision).map(|(revision, bin)| {
                let version = Version::new(usize::min_value(), revision.clone());
                LocalVersion {
                    revision,
                    bin,
                    version,
                }
            })?;

        let mut versions = HashMap::new();
        versions.insert(workspace.version(), workspace);
        versions.insert(revision.version(), revision);
        Ok(Self::new(versions))
    }

    /// Create a LocalFactory with a diem-node version built at the tip of upstream/main and the
    /// current workspace, suitable for compatibility testing.
    pub fn with_upstream_and_workspace() -> Result<Self> {
        let upstream_main = cargo::git_get_upstream_remote().map(|r| format!("{}/main", r))?;
        Self::with_revision_and_workspace(&upstream_main)
    }

    /// Create a LocalFactory with a diem-node version built at merge-base of upstream/main and the
    /// current workspace, suitable for compatibility testing.
    pub fn with_upstream_merge_base_and_workspace() -> Result<Self> {
        let upstream_main = cargo::git_get_upstream_remote().map(|r| format!("{}/main", r))?;
        let merge_base = cargo::git_merge_base(upstream_main)?;
        Self::with_revision_and_workspace(&merge_base)
    }

    pub fn new_swarm<R>(&self, rng: R, number_of_validators: NonZeroUsize) -> Result<LocalSwarm>
    where
        R: ::rand::RngCore + ::rand::CryptoRng,
    {
        let version = self.versions.keys().max().unwrap();
        self.new_swarm_with_version(rng, number_of_validators, version)
    }

    pub fn new_swarm_with_version<R>(
        &self,
        rng: R,
        number_of_validators: NonZeroUsize,
        version: &Version,
    ) -> Result<LocalSwarm>
    where
        R: ::rand::RngCore + ::rand::CryptoRng,
    {
        let mut swarm = LocalSwarm::builder(self.versions.clone())
            .number_of_validators(number_of_validators)
            .initial_version(version.clone())
            .build(rng)?;
        swarm
            .launch()
            .with_context(|| format!("Swarm logs can be found here: {}", swarm.logs_location()))?;

        Ok(swarm)
    }
}

impl Factory for LocalFactory {
    fn versions<'a>(&'a self) -> Box<dyn Iterator<Item = Version> + 'a> {
        Box::new(self.versions.keys().cloned())
    }

    fn launch_swarm(
        &self,
        rng: &mut StdRng,
        node_num: NonZeroUsize,
        version: &Version,
    ) -> Result<Box<dyn Swarm>> {
        let swarm = self.new_swarm_with_version(rng, node_num, version)?;

        Ok(Box::new(swarm))
    }
}
<|endoftext|>


<commit_before>extern crate proc_macro;

use crate::proc_macro::TokenStream;
use quote::quote;
use syn;
use syn::DeriveInput;

#[proc_macro_derive(TestRandom)]
pub fn test_random_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();

    impl_test_random(&ast)
}

fn impl_test_random(ast: &DeriveInput) -> TokenStream {}
<commit_msg>added test_random_derive implementation (lighthouse-246)<commit_after>extern crate proc_macro;

use crate::proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(TestRandom)]
pub fn test_random_derive(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    let name = &ast.ident;

    let struct_data = match &ast.data {
        syn::Data::Struct(s) => s,
        _ => panic!("test_random_derive only supports structs."),
    };

    let field_names = get_named_field_idents_and_types(&struct_data);

    let output = quote! {
        impl<T: RngCore> TestRandom<T> for #name {
            fn random_for_test(rng: &mut T) -> Self {
               Self {
                    #(
                        #field_names: <_>::random_for_test(rng)
                    )*
               }
            }
        }
    };

    output.into()
}

fn get_named_field_idents_and_types(struct_data: &syn::DataStruct) -> Vec<(&syn::Ident)> {
    struct_data
        .fields
        .iter()
        .map(|f| match &f.ident {
            Some(ref ident) => ident,
            _ => panic!("test_random_derive only supports named struct fields."),
        })
        .collect()
}
<|endoftext|>


<commit_before>//! Diagnostic emitted for files that aren't part of any crate.

use hir::{
    db::DefDatabase,
    diagnostics::{Diagnostic, DiagnosticCode},
    InFile,
};
use ide_db::{
    base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt},
    source_change::SourceChange,
    RootDatabase,
};
use syntax::{
    ast::{self, ModuleItemOwner, NameOwner},
    AstNode, SyntaxNodePtr,
};
use text_edit::TextEdit;

use crate::Fix;

use super::fixes::DiagnosticWithFix;

#[derive(Debug)]
pub(crate) struct UnlinkedFile {
    pub(crate) file_id: FileId,
    pub(crate) node: SyntaxNodePtr,
}

impl Diagnostic for UnlinkedFile {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unlinked-file")
    }

    fn message(&self) -> String {
        "file not included in module tree".to_string()
    }

    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file_id.into(), self.node.clone())
    }

    fn as_any(&self) -> &(dyn std::any::Any + Send + 'static) {
        self
    }
}

impl DiagnosticWithFix for UnlinkedFile {
    fn fix(&self, sema: &hir::Semantics<RootDatabase>) -> Option<Fix> {
        // If there's an existing module that could add a `mod` item to include the unlinked file,
        // suggest that as a fix.

        let source_root = sema.db.source_root(sema.db.file_source_root(self.file_id));
        let our_path = source_root.path_for_file(&self.file_id)?;
        let module_name = our_path.name_and_extension()?.0;

        // Candidates to look for:
        // - `mod.rs` in the same folder
        //   - we also check `main.rs` and `lib.rs`
        // - `$dir.rs` in the parent folder, where `$dir` is the directory containing `self.file_id`
        let parent = our_path.parent()?;
        let mut paths =
            vec![parent.join("mod.rs")?, parent.join("main.rs")?, parent.join("lib.rs")?];

        // `submod/bla.rs` -> `submod.rs`
        if let Some(newmod) = (|| {
            let name = parent.name_and_extension()?.0;
            parent.parent()?.join(&format!("{}.rs", name))
        })() {
            paths.push(newmod);
        }

        for path in &paths {
            if let Some(parent_id) = source_root.file_for_path(path) {
                for krate in sema.db.relevant_crates(*parent_id).iter() {
                    let crate_def_map = sema.db.crate_def_map(*krate);
                    for (_, module) in crate_def_map.modules() {
                        if module.origin.is_inline() {
                            // We don't handle inline `mod parent {}`s, they use different paths.
                            continue;
                        }

                        if module.origin.file_id() == Some(*parent_id) {
                            return make_fix(sema.db, *parent_id, module_name, self.file_id);
                        }
                    }
                }
            }
        }

        None
    }
}

fn make_fix(
    db: &RootDatabase,
    parent_file_id: FileId,
    new_mod_name: &str,
    added_file_id: FileId,
) -> Option<Fix> {
    fn is_outline_mod(item: &ast::Item) -> bool {
        matches!(item, ast::Item::Module(m) if m.item_list().is_none())
    }

    let mod_decl = format!("mod {};", new_mod_name);
    let ast: ast::SourceFile = db.parse(parent_file_id).tree();

    let mut builder = TextEdit::builder();

    // If there's an existing `mod m;` statement matching the new one, don't emit a fix (it's
    // probably `#[cfg]`d out).
    for item in ast.items() {
        if let ast::Item::Module(m) = item {
            if let Some(name) = m.name() {
                if m.item_list().is_none() && name.to_string() == new_mod_name {
                    cov_mark::hit!(unlinked_file_skip_fix_when_mod_already_exists);
                    return None;
                }
            }
        }
    }

    // If there are existing `mod m;` items, append after them (after the first group of them, rather).
    match ast
        .items()
        .skip_while(|item| !is_outline_mod(item))
        .take_while(|item| is_outline_mod(item))
        .last()
    {
        Some(last) => {
            cov_mark::hit!(unlinked_file_append_to_existing_mods);
            builder.insert(last.syntax().text_range().end(), format!("\n{}", mod_decl));
        }
        None => {
            // Prepend before the first item in the file.
            match ast.items().next() {
                Some(item) => {
                    cov_mark::hit!(unlinked_file_prepend_before_first_item);
                    builder.insert(item.syntax().text_range().start(), format!("{}\n\n", mod_decl));
                }
                None => {
                    // No items in the file, so just append at the end.
                    cov_mark::hit!(unlinked_file_empty_file);
                    builder.insert(ast.syntax().text_range().end(), format!("{}\n", mod_decl));
                }
            }
        }
    }

    let edit = builder.finish();
    let trigger_range = db.parse(added_file_id).tree().syntax().text_range();
    Some(Fix::new(
        &format!("Insert `{}`", mod_decl),
        SourceChange::from_text_edit(parent_file_id, edit),
        trigger_range,
    ))
}
<commit_msg>Document unlinked-file diagnostic<commit_after>//! Diagnostic emitted for files that aren't part of any crate.

use hir::{
    db::DefDatabase,
    diagnostics::{Diagnostic, DiagnosticCode},
    InFile,
};
use ide_db::{
    base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt},
    source_change::SourceChange,
    RootDatabase,
};
use syntax::{
    ast::{self, ModuleItemOwner, NameOwner},
    AstNode, SyntaxNodePtr,
};
use text_edit::TextEdit;

use crate::Fix;

use super::fixes::DiagnosticWithFix;

// Diagnostic: unlinked-file
//
// This diagnostic is shown for files that are not included in any crate, or files that are part of
// crates rust-analyzer failed to discover. The file will not have IDE features available.
#[derive(Debug)]
pub(crate) struct UnlinkedFile {
    pub(crate) file_id: FileId,
    pub(crate) node: SyntaxNodePtr,
}

impl Diagnostic for UnlinkedFile {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unlinked-file")
    }

    fn message(&self) -> String {
        "file not included in module tree".to_string()
    }

    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file_id.into(), self.node.clone())
    }

    fn as_any(&self) -> &(dyn std::any::Any + Send + 'static) {
        self
    }
}

impl DiagnosticWithFix for UnlinkedFile {
    fn fix(&self, sema: &hir::Semantics<RootDatabase>) -> Option<Fix> {
        // If there's an existing module that could add a `mod` item to include the unlinked file,
        // suggest that as a fix.

        let source_root = sema.db.source_root(sema.db.file_source_root(self.file_id));
        let our_path = source_root.path_for_file(&self.file_id)?;
        let module_name = our_path.name_and_extension()?.0;

        // Candidates to look for:
        // - `mod.rs` in the same folder
        //   - we also check `main.rs` and `lib.rs`
        // - `$dir.rs` in the parent folder, where `$dir` is the directory containing `self.file_id`
        let parent = our_path.parent()?;
        let mut paths =
            vec![parent.join("mod.rs")?, parent.join("main.rs")?, parent.join("lib.rs")?];

        // `submod/bla.rs` -> `submod.rs`
        if let Some(newmod) = (|| {
            let name = parent.name_and_extension()?.0;
            parent.parent()?.join(&format!("{}.rs", name))
        })() {
            paths.push(newmod);
        }

        for path in &paths {
            if let Some(parent_id) = source_root.file_for_path(path) {
                for krate in sema.db.relevant_crates(*parent_id).iter() {
                    let crate_def_map = sema.db.crate_def_map(*krate);
                    for (_, module) in crate_def_map.modules() {
                        if module.origin.is_inline() {
                            // We don't handle inline `mod parent {}`s, they use different paths.
                            continue;
                        }

                        if module.origin.file_id() == Some(*parent_id) {
                            return make_fix(sema.db, *parent_id, module_name, self.file_id);
                        }
                    }
                }
            }
        }

        None
    }
}

fn make_fix(
    db: &RootDatabase,
    parent_file_id: FileId,
    new_mod_name: &str,
    added_file_id: FileId,
) -> Option<Fix> {
    fn is_outline_mod(item: &ast::Item) -> bool {
        matches!(item, ast::Item::Module(m) if m.item_list().is_none())
    }

    let mod_decl = format!("mod {};", new_mod_name);
    let ast: ast::SourceFile = db.parse(parent_file_id).tree();

    let mut builder = TextEdit::builder();

    // If there's an existing `mod m;` statement matching the new one, don't emit a fix (it's
    // probably `#[cfg]`d out).
    for item in ast.items() {
        if let ast::Item::Module(m) = item {
            if let Some(name) = m.name() {
                if m.item_list().is_none() && name.to_string() == new_mod_name {
                    cov_mark::hit!(unlinked_file_skip_fix_when_mod_already_exists);
                    return None;
                }
            }
        }
    }

    // If there are existing `mod m;` items, append after them (after the first group of them, rather).
    match ast
        .items()
        .skip_while(|item| !is_outline_mod(item))
        .take_while(|item| is_outline_mod(item))
        .last()
    {
        Some(last) => {
            cov_mark::hit!(unlinked_file_append_to_existing_mods);
            builder.insert(last.syntax().text_range().end(), format!("\n{}", mod_decl));
        }
        None => {
            // Prepend before the first item in the file.
            match ast.items().next() {
                Some(item) => {
                    cov_mark::hit!(unlinked_file_prepend_before_first_item);
                    builder.insert(item.syntax().text_range().start(), format!("{}\n\n", mod_decl));
                }
                None => {
                    // No items in the file, so just append at the end.
                    cov_mark::hit!(unlinked_file_empty_file);
                    builder.insert(ast.syntax().text_range().end(), format!("{}\n", mod_decl));
                }
            }
        }
    }

    let edit = builder.finish();
    let trigger_range = db.parse(added_file_id).tree().syntax().text_range();
    Some(Fix::new(
        &format!("Insert `{}`", mod_decl),
        SourceChange::from_text_edit(parent_file_id, edit),
        trigger_range,
    ))
}
<|endoftext|>


<commit_before>//! The thread-local state.

use std::mem;
use std::cell::RefCell;
use {global, hazard};
use garbage::Garbage;

thread_local! {
    /// The state of this thread.
    static STATE: RefCell<State> = RefCell::new(State::default());
}

/// Add new garbage to be deleted.
///
/// This garbage is pushed to a thread-local queue. When enough garbage is accumulated in the
/// thread, it is exported to the global state.
pub fn add_garbage(garbage: Garbage) {
    STATE.with(|s| s.borrow_mut().add_garbage(garbage));
}

/// Get a blocked hazard.
///
/// If possible, this will simply pop one of the thread-local cache of hazards. Otherwise, one must
/// be registered in the global state.
pub fn get_hazard() -> hazard::Writer {
    STATE.with(|s| s.borrow_mut().get_hazard())
}

/// Free a hazard.
///
/// This frees a hazard to the thread-local cache of hazards.
pub fn free_hazard(hazard: hazard::Writer) {
    STATE.with(|s| s.borrow_mut().free_hazard(hazard));
}

/// Export the garbage of this thread to the global state.
///
/// This is useful for propagating accumulated garbage such that it can be destroyed by the next
/// garbage collection.
pub fn export_garbage() {
    STATE.with(|s| s.borrow_mut().export_garbage());
}

/// A thread-local state.
#[derive(Default)]
struct State {
    /// The cached garbage waiting to be exported to the global state.
    garbage: Vec<Garbage>,
    /// The cache of currently available hazards.
    ///
    /// We maintain this cache to avoid the performance hit of creating new hazards.
    ///
    /// The hazards in this vector are not necessarily in state "free". Only when a sufficient
    /// amount of available hazards has accumulated, they will be set to free. This means that we
    /// don't have to reset the state of a hazard after usage, giving a quite significant speed-up.
    available_hazards: Vec<hazard::Writer>,
    /// The hazards in the cache before this index are free.
    ///
    /// This number keeps track what hazards in `self.available_hazard` are set to state "free".
    /// Before this index, every hazard must be set to "free".
    ///
    /// It is useful for nowing when to free the hazards to allow garbage collection.
    available_hazards_free_before: usize,
}

impl State {
    /// Get the number of hazards in the cache which are not in state "free".
    fn non_free_hazards(&self) -> usize {
        self.available_hazards.len() - self.available_hazards_free_before
    }

    /// See `get_hazard()`.
    fn get_hazard(&mut self) -> hazard::Writer {
        // Check if there is hazards in the cache.
        if let Some(hazard) = self.available_hazards.pop() {
            // There is; we don't need to create a new hazard.

            // Since the hazard popped from the cache is not blocked, we must block the hazard to
            // satisfy the requrements of this function.
            hazard.block();
            hazard
        } else {
            // There is not; we must create a new hazard.
            global::create_hazard()
        }
    }

    /// See `free_hazard()`.
    fn free_hazard(&mut self, hazard: hazard::Writer) {
        /// The maximal amount of hazards before cleaning up.
        ///
        /// With "cleaning up" we mean setting the state of the hazards to "free" in order to allow
        /// garbage collection of the object it is currently protecting.
        const MAX_NON_FREE_HAZARDS: usize = 128;

        // Push the given hazard to the cache.
        self.available_hazards.push(hazard);

        // Check if we exceeded the limit.
        if self.non_free_hazards() > MAX_NON_FREE_HAZARDS {
            // We did; we must now set the non-free hazards to "free".
            for i in &self.available_hazards[self.available_hazards_free_before..] {
                i.set(hazard::State::Free);
            }

            // Update the counter such that we mark the new hazards set to "free".
            self.available_hazards_free_before = self.available_hazards.len();
        }
    }

    /// See `add_garbage()`.
    fn add_garbage(&mut self, garbage: Garbage) {
        /// The maximal amount of garbage before exportation to the global state.
        const MAX_GARBAGE: usize = 64;

        // Push the garbage to the cache of garbage.
        self.garbage.push(garbage);

        // Export the garbage if it exceeds the limit.
        // TODO: use memory instead of items as a metric.
        if self.garbage.len() > MAX_GARBAGE {
            self.export_garbage();
        }
    }

    /// See `export_garbage()`.
    fn export_garbage(&mut self) {
        // Clear the vector and export the garbage.
        global::export_garbage(mem::replace(&mut self.garbage, Vec::new()));
    }
}

impl Drop for State {
    fn drop(&mut self) {
        // The thread is exitting, thus we must export the garbage to the global state to avoid
        // memory leaks.
        self.export_garbage();

        // Due to RAII, the hazards will set themself to "dead", and thus eventually be deallocated
        // by the garbo.
    }
}
<commit_msg>Ensure that the hazards are set to `dead` state when the thread local cache goes out (in `concurrent`).<commit_after>//! The thread-local state.

use std::mem;
use std::cell::RefCell;
use {global, hazard};
use garbage::Garbage;

thread_local! {
    /// The state of this thread.
    static STATE: RefCell<State> = RefCell::new(State::default());
}

/// Add new garbage to be deleted.
///
/// This garbage is pushed to a thread-local queue. When enough garbage is accumulated in the
/// thread, it is exported to the global state.
pub fn add_garbage(garbage: Garbage) {
    STATE.with(|s| s.borrow_mut().add_garbage(garbage));
}

/// Get a blocked hazard.
///
/// If possible, this will simply pop one of the thread-local cache of hazards. Otherwise, one must
/// be registered in the global state.
pub fn get_hazard() -> hazard::Writer {
    STATE.with(|s| s.borrow_mut().get_hazard())
}

/// Free a hazard.
///
/// This frees a hazard to the thread-local cache of hazards.
pub fn free_hazard(hazard: hazard::Writer) {
    STATE.with(|s| s.borrow_mut().free_hazard(hazard));
}

/// Export the garbage of this thread to the global state.
///
/// This is useful for propagating accumulated garbage such that it can be destroyed by the next
/// garbage collection.
pub fn export_garbage() {
    STATE.with(|s| s.borrow_mut().export_garbage());
}

/// A thread-local state.
#[derive(Default)]
struct State {
    /// The cached garbage waiting to be exported to the global state.
    garbage: Vec<Garbage>,
    /// The cache of currently available hazards.
    ///
    /// We maintain this cache to avoid the performance hit of creating new hazards.
    ///
    /// The hazards in this vector are not necessarily in state "free". Only when a sufficient
    /// amount of available hazards has accumulated, they will be set to free. This means that we
    /// don't have to reset the state of a hazard after usage, giving a quite significant speed-up.
    available_hazards: Vec<hazard::Writer>,
    /// The hazards in the cache before this index are free.
    ///
    /// This number keeps track what hazards in `self.available_hazard` are set to state "free".
    /// Before this index, every hazard must be set to "free".
    ///
    /// It is useful for nowing when to free the hazards to allow garbage collection.
    available_hazards_free_before: usize,
}

impl State {
    /// Get the number of hazards in the cache which are not in state "free".
    fn non_free_hazards(&self) -> usize {
        self.available_hazards.len() - self.available_hazards_free_before
    }

    /// See `get_hazard()`.
    fn get_hazard(&mut self) -> hazard::Writer {
        // Check if there is hazards in the cache.
        if let Some(hazard) = self.available_hazards.pop() {
            // There is; we don't need to create a new hazard.

            // Since the hazard popped from the cache is not blocked, we must block the hazard to
            // satisfy the requrements of this function.
            hazard.block();
            hazard
        } else {
            // There is not; we must create a new hazard.
            global::create_hazard()
        }
    }

    /// See `free_hazard()`.
    fn free_hazard(&mut self, hazard: hazard::Writer) {
        /// The maximal amount of hazards before cleaning up.
        ///
        /// With "cleaning up" we mean setting the state of the hazards to "free" in order to allow
        /// garbage collection of the object it is currently protecting.
        const MAX_NON_FREE_HAZARDS: usize = 128;

        // Push the given hazard to the cache.
        self.available_hazards.push(hazard);

        // Check if we exceeded the limit.
        if self.non_free_hazards() > MAX_NON_FREE_HAZARDS {
            // We did; we must now set the non-free hazards to "free".
            for i in &self.available_hazards[self.available_hazards_free_before..] {
                i.set(hazard::State::Free);
            }

            // Update the counter such that we mark the new hazards set to "free".
            self.available_hazards_free_before = self.available_hazards.len();
        }
    }

    /// See `add_garbage()`.
    fn add_garbage(&mut self, garbage: Garbage) {
        /// The maximal amount of garbage before exportation to the global state.
        const MAX_GARBAGE: usize = 64;

        // Push the garbage to the cache of garbage.
        self.garbage.push(garbage);

        // Export the garbage if it exceeds the limit.
        // TODO: use memory instead of items as a metric.
        if self.garbage.len() > MAX_GARBAGE {
            self.export_garbage();
        }
    }

    /// See `export_garbage()`.
    fn export_garbage(&mut self) {
        // Clear the vector and export the garbage.
        global::export_garbage(mem::replace(&mut self.garbage, Vec::new()));
    }
}

impl Drop for State {
    fn drop(&mut self) {
        // The thread is exitting, thus we must export the garbage to the global state to avoid
        // memory leaks.
        self.export_garbage();

        // Clear every hazard to "dead" state.
        for hazard in self.available_hazards.drain(..) {
            hazard.kill();
        }
    }
}
<|endoftext|>


<commit_before>pub use self::{
    material_icons_font_ttf::*,
    material_icons_font::*,
    material_icons_round_font::*,
    material_icons_sharp_font::*,
    material_icons_twotone_font::*,
};

// ttf mappings
pub mod material_icons_font_ttf;

// woff2 mappings
pub mod material_icons_font;
pub mod material_icons_round_font;
pub mod material_icons_sharp_font;
pub mod material_icons_twotone_font;
<commit_msg>theme: vector_graphics: update woff2 usage material_icons_baseline_font<commit_after>pub use self::{
    material_icons_font_ttf::*,
    material_icons_baseline_font::*,
    material_icons_round_font::*,
    material_icons_sharp_font::*,
    material_icons_twotone_font::*,
};

// ttf mappings
pub mod material_icons_font_ttf;

// woff2 mappings
pub mod material_icons_baseline_font;
pub mod material_icons_round_font;
pub mod material_icons_sharp_font;
pub mod material_icons_twotone_font;
<|endoftext|>