<commit_before>//! Project loading & configuration updates
use std::{mem, sync::Arc};

use flycheck::{FlycheckConfig, FlycheckHandle};
use ide::Change;
use ide_db::base_db::{CrateGraph, SourceRoot, VfsPath};
use project_model::{BuildDataCollector, BuildDataResult, ProcMacroClient, ProjectWorkspace};
use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};

use crate::{
    config::{Config, FilesWatcher, LinkedProject},
    global_state::{GlobalState, Status},
    lsp_ext,
    main_loop::Task,
};
use lsp_ext::StatusParams;

#[derive(Debug)]
pub(crate) enum ProjectWorkspaceProgress {
    Begin,
    Report(String),
    End(Vec<anyhow::Result<ProjectWorkspace>>),
}

#[derive(Debug)]
pub(crate) enum BuildDataProgress {
    Begin,
    Report(String),
    End(anyhow::Result<BuildDataResult>),
}

impl GlobalState {
    pub(crate) fn update_configuration(&mut self, config: Config) {
        let _p = profile::span("GlobalState::update_configuration");
        let old_config = mem::replace(&mut self.config, Arc::new(config));
        if self.config.lru_capacity() != old_config.lru_capacity() {
            self.analysis_host.update_lru_capacity(self.config.lru_capacity());
        }
        if self.config.linked_projects() != old_config.linked_projects() {
            self.fetch_workspaces_request()
        } else if self.config.flycheck() != old_config.flycheck() {
            self.reload_flycheck();
        }
    }
    pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) {
        if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
            return;
        }
        match self.status {
            Status::Loading | Status::NeedsReload => return,
            Status::Ready { .. } | Status::Invalid => (),
        }
        log::info!(
            "Reloading workspace because of the following changes: {}",
            itertools::join(
                changes
                    .iter()
                    .filter(|(path, kind)| is_interesting(path, *kind))
                    .map(|(path, kind)| format!("{}/{:?}", path.display(), kind)),
                ", "
            )
        );
        if self.config.cargo_autoreload() {
            self.fetch_workspaces_request();
        } else {
            self.transition(Status::NeedsReload);
        }

        fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
            const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
            const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];

            if path.ends_with("Cargo.toml") || path.ends_with("Cargo.lock") {
                return true;
            }
            if change_kind == ChangeKind::Modify {
                return false;
            }
            if path.extension().unwrap_or_default() != "rs" {
                return false;
            }
            if IMPLICIT_TARGET_FILES.iter().any(|it| path.ends_with(it)) {
                return true;
            }
            let parent = match path.parent() {
                Some(it) => it,
                None => return false,
            };
            if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.ends_with(it)) {
                return true;
            }
            if path.ends_with("main.rs") {
                let grand_parent = match parent.parent() {
                    Some(it) => it,
                    None => return false,
                };
                if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.ends_with(it)) {
                    return true;
                }
            }
            false
        }
    }
    pub(crate) fn transition(&mut self, new_status: Status) {
        self.status = new_status;
        if self.config.status_notification() {
            let lsp_status = match new_status {
                Status::Loading => lsp_ext::Status::Loading,
                Status::Ready { partial: true } => lsp_ext::Status::ReadyPartial,
                Status::Ready { partial: false } => lsp_ext::Status::Ready,
                Status::Invalid => lsp_ext::Status::Invalid,
                Status::NeedsReload => lsp_ext::Status::NeedsReload,
            };
            self.send_notification::<lsp_ext::StatusNotification>(StatusParams {
                status: lsp_status,
            });
        }
    }

    pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) {
        self.fetch_build_data_queue.request_op(build_data_collector);
    }

    pub(crate) fn fetch_build_data_if_needed(&mut self) {
        let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() {
            Some(it) => it,
            None => return,
        };
        self.task_pool.handle.spawn_with_sender(move |sender| {
            sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();

            let progress = {
                let sender = sender.clone();
                move |msg| {
                    sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap()
                }
            };
            let res = build_data_collector.collect(&progress);
            sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap();
        });
    }
    pub(crate) fn fetch_build_data_completed(
        &mut self,
        build_data: anyhow::Result<BuildDataResult>,
    ) {
        self.fetch_build_data_queue.op_completed(Some(build_data))
    }

    pub(crate) fn fetch_workspaces_request(&mut self) {
        self.fetch_workspaces_queue.request_op(())
    }
    pub(crate) fn fetch_workspaces_if_needed(&mut self) {
        if self.fetch_workspaces_queue.should_start_op().is_none() {
            return;
        }
        log::info!("will fetch workspaces");

        self.task_pool.handle.spawn_with_sender({
            let linked_projects = self.config.linked_projects();
            let cargo_config = self.config.cargo();

            move |sender| {
                let progress = {
                    let sender = sender.clone();
                    move |msg| {
                        sender
                            .send(Task::FetchWorkspace(ProjectWorkspaceProgress::Report(msg)))
                            .unwrap()
                    }
                };

                sender.send(Task::FetchWorkspace(ProjectWorkspaceProgress::Begin)).unwrap();

                let workspaces = linked_projects
                    .iter()
                    .map(|project| match project {
                        LinkedProject::ProjectManifest(manifest) => {
                            project_model::ProjectWorkspace::load(
                                manifest.clone(),
                                &cargo_config,
                                &progress,
                            )
                        }
                        LinkedProject::InlineJsonProject(it) => {
                            project_model::ProjectWorkspace::load_inline(
                                it.clone(),
                                cargo_config.target.as_deref(),
                            )
                        }
                    })
                    .collect::<Vec<_>>();

                log::info!("did fetch workspaces {:?}", workspaces);
                sender
                    .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(workspaces)))
                    .unwrap();
            }
        });
    }
    pub(crate) fn fetch_workspaces_completed(
        &mut self,
        workspaces: Vec<anyhow::Result<ProjectWorkspace>>,
    ) {
        self.fetch_workspaces_queue.op_completed(workspaces)
    }

    pub(crate) fn switch_workspaces(&mut self) {
        let _p = profile::span("GlobalState::switch_workspaces");
        let workspaces = self.fetch_workspaces_queue.last_op_result();
        log::info!("will switch workspaces: {:?}", workspaces);

        let mut error_message = None;
        let workspaces = workspaces
            .iter()
            .filter_map(|res| match res {
                Ok(it) => Some(it.clone()),
                Err(err) => {
                    log::error!("failed to load workspace: {:#}", err);
                    let message = error_message.get_or_insert_with(String::new);
                    stdx::format_to!(
                        message,
                        "rust-analyzer failed to load workspace: {:#}\n",
                        err
                    );
                    None
                }
            })
            .collect::<Vec<_>>();

        let workspace_build_data = match self.fetch_build_data_queue.last_op_result() {
            Some(Ok(it)) => Some(it.clone()),
            None => None,
            Some(Err(err)) => {
                log::error!("failed to fetch build data: {:#}", err);
                let message = error_message.get_or_insert_with(String::new);
                stdx::format_to!(message, "rust-analyzer failed to fetch build data: {:#}\n", err);
                None
            }
        };

        if let Some(error_message) = error_message {
            self.show_message(lsp_types::MessageType::Error, error_message);
            if !self.workspaces.is_empty() {
                return;
            }
        }

        if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data {
            return;
        }

        if let FilesWatcher::Client = self.config.files().watcher {
            if self.config.did_change_watched_files_dynamic_registration() {
                let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
                    watchers: workspaces
                        .iter()
                        .flat_map(|it| it.to_roots(workspace_build_data.as_ref()))
                        .filter(|it| it.is_member)
                        .flat_map(|root| {
                            root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display()))
                        })
                        .map(|glob_pattern| lsp_types::FileSystemWatcher {
                            glob_pattern,
                            kind: None,
                        })
                        .collect(),
                };
                let registration = lsp_types::Registration {
                    id: "workspace/didChangeWatchedFiles".to_string(),
                    method: "workspace/didChangeWatchedFiles".to_string(),
                    register_options: Some(serde_json::to_value(registration_options).unwrap()),
                };
                self.send_request::<lsp_types::request::RegisterCapability>(
                    lsp_types::RegistrationParams { registrations: vec![registration] },
                    |_, _| (),
                );
            }
        }

        let mut change = Change::new();

        let files_config = self.config.files();
        let project_folders =
            ProjectFolders::new(&workspaces, &files_config.exclude, workspace_build_data.as_ref());

        if self.proc_macro_client.is_none() {
            self.proc_macro_client = match self.config.proc_macro_srv() {
                None => None,
                Some((path, args)) => match ProcMacroClient::extern_process(path.clone(), args) {
                    Ok(it) => Some(it),
                    Err(err) => {
                        log::error!(
                            "Failed to run proc_macro_srv from path {}, error: {:?}",
                            path.display(),
                            err
                        );
                        None
                    }
                },
            };
        }

        let watch = match files_config.watcher {
            FilesWatcher::Client => vec![],
            FilesWatcher::Notify => project_folders.watch,
        };
        self.vfs_config_version += 1;
        self.loader.handle.set_config(vfs::loader::Config {
            load: project_folders.load,
            watch,
            version: self.vfs_config_version,
        });

        // Create crate graph from all the workspaces
        let crate_graph = {
            let mut crate_graph = CrateGraph::default();
            let vfs = &mut self.vfs.write().0;
            let loader = &mut self.loader;
            let mem_docs = &self.mem_docs;
            let mut load = |path: &AbsPath| {
                let vfs_path = vfs::VfsPath::from(path.to_path_buf());
                if !mem_docs.contains_key(&vfs_path) {
                    let contents = loader.handle.load_sync(path);
                    vfs.set_file_contents(vfs_path.clone(), contents);
                }
                let res = vfs.file_id(&vfs_path);
                if res.is_none() {
                    log::warn!("failed to load {}", path.display())
                }
                res
            };
            for ws in workspaces.iter() {
                crate_graph.extend(ws.to_crate_graph(
                    workspace_build_data.as_ref(),
                    self.proc_macro_client.as_ref(),
                    &mut load,
                ));
            }

            crate_graph
        };
        change.set_crate_graph(crate_graph);

        self.source_root_config = project_folders.source_root_config;
        self.workspaces = Arc::new(workspaces);
        self.workspace_build_data = workspace_build_data;

        self.analysis_host.apply_change(change);
        self.process_changes();
        self.reload_flycheck();
        log::info!("did switch workspaces");
    }

    fn reload_flycheck(&mut self) {
        let _p = profile::span("GlobalState::reload_flycheck");
        let config = match self.config.flycheck() {
            Some(it) => it,
            None => {
                self.flycheck = Vec::new();
                return;
            }
        };

        let sender = self.flycheck_sender.clone();
        self.flycheck = self
            .workspaces
            .iter()
            .enumerate()
            .filter_map(|(id, w)| match w {
                ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
                ProjectWorkspace::Json { project, .. } => {
                    // Enable flychecks for json projects if a custom flycheck command was supplied
                    // in the workspace configuration.
                    match config {
                        FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
                        _ => None,
                    }
                }
            })
            .map(|(id, root)| {
                let sender = sender.clone();
                FlycheckHandle::spawn(
                    id,
                    Box::new(move |msg| sender.send(msg).unwrap()),
                    config.clone(),
                    root.to_path_buf().into(),
                )
            })
            .collect();
    }
}

#[derive(Default)]
pub(crate) struct ProjectFolders {
    pub(crate) load: Vec<vfs::loader::Entry>,
    pub(crate) watch: Vec<usize>,
    pub(crate) source_root_config: SourceRootConfig,
}

impl ProjectFolders {
    pub(crate) fn new(
        workspaces: &[ProjectWorkspace],
        global_excludes: &[AbsPathBuf],
        build_data: Option<&BuildDataResult>,
    ) -> ProjectFolders {
        let mut res = ProjectFolders::default();
        let mut fsc = FileSetConfig::builder();
        let mut local_filesets = vec![];

        for root in workspaces.iter().flat_map(|it| it.to_roots(build_data)) {
            let file_set_roots: Vec<VfsPath> =
                root.include.iter().cloned().map(VfsPath::from).collect();

            let entry = {
                let mut dirs = vfs::loader::Directories::default();
                dirs.extensions.push("rs".into());
                dirs.include.extend(root.include);
                dirs.exclude.extend(root.exclude);
                for excl in global_excludes {
                    if dirs.include.iter().any(|incl| incl.starts_with(excl)) {
                        dirs.exclude.push(excl.clone());
                    }
                }

                vfs::loader::Entry::Directories(dirs)
            };

            if root.is_member {
                res.watch.push(res.load.len());
            }
            res.load.push(entry);

            if root.is_member {
                local_filesets.push(fsc.len());
            }
            fsc.add_file_set(file_set_roots)
        }

        let fsc = fsc.build();
        res.source_root_config = SourceRootConfig { fsc, local_filesets };

        res
    }
}

#[derive(Default, Debug)]
pub(crate) struct SourceRootConfig {
    pub(crate) fsc: FileSetConfig,
    pub(crate) local_filesets: Vec<usize>,
}

impl SourceRootConfig {
    pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> {
        let _p = profile::span("SourceRootConfig::partition");
        self.fsc
            .partition(vfs)
            .into_iter()
            .enumerate()
            .map(|(idx, file_set)| {
                let is_local = self.local_filesets.contains(&idx);
                if is_local {
                    SourceRoot::new_local(file_set)
                } else {
                    SourceRoot::new_library(file_set)
                }
            })
            .collect()
    }
}
<commit_msg>Improve readability<commit_after>//! Project loading & configuration updates
use std::{mem, sync::Arc};

use flycheck::{FlycheckConfig, FlycheckHandle};
use ide::Change;
use ide_db::base_db::{CrateGraph, SourceRoot, VfsPath};
use project_model::{BuildDataCollector, BuildDataResult, ProcMacroClient, ProjectWorkspace};
use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};

use crate::{
    config::{Config, FilesWatcher, LinkedProject},
    global_state::{GlobalState, Status},
    lsp_ext,
    main_loop::Task,
};
use lsp_ext::StatusParams;

#[derive(Debug)]
pub(crate) enum ProjectWorkspaceProgress {
    Begin,
    Report(String),
    End(Vec<anyhow::Result<ProjectWorkspace>>),
}

#[derive(Debug)]
pub(crate) enum BuildDataProgress {
    Begin,
    Report(String),
    End(anyhow::Result<BuildDataResult>),
}

impl GlobalState {
    pub(crate) fn update_configuration(&mut self, config: Config) {
        let _p = profile::span("GlobalState::update_configuration");
        let old_config = mem::replace(&mut self.config, Arc::new(config));
        if self.config.lru_capacity() != old_config.lru_capacity() {
            self.analysis_host.update_lru_capacity(self.config.lru_capacity());
        }
        if self.config.linked_projects() != old_config.linked_projects() {
            self.fetch_workspaces_request()
        } else if self.config.flycheck() != old_config.flycheck() {
            self.reload_flycheck();
        }
    }
    pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) {
        if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
            return;
        }
        match self.status {
            Status::Loading | Status::NeedsReload => return,
            Status::Ready { .. } | Status::Invalid => (),
        }
        log::info!(
            "Reloading workspace because of the following changes: {}",
            itertools::join(
                changes
                    .iter()
                    .filter(|(path, kind)| is_interesting(path, *kind))
                    .map(|(path, kind)| format!("{}/{:?}", path.display(), kind)),
                ", "
            )
        );
        if self.config.cargo_autoreload() {
            self.fetch_workspaces_request();
        } else {
            self.transition(Status::NeedsReload);
        }

        fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
            const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
            const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];

            if path.ends_with("Cargo.toml") || path.ends_with("Cargo.lock") {
                return true;
            }
            if change_kind == ChangeKind::Modify {
                return false;
            }
            if path.extension().unwrap_or_default() != "rs" {
                return false;
            }
            if IMPLICIT_TARGET_FILES.iter().any(|it| path.ends_with(it)) {
                return true;
            }
            let parent = match path.parent() {
                Some(it) => it,
                None => return false,
            };
            if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.ends_with(it)) {
                return true;
            }
            if path.ends_with("main.rs") {
                let grand_parent = match parent.parent() {
                    Some(it) => it,
                    None => return false,
                };
                if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.ends_with(it)) {
                    return true;
                }
            }
            false
        }
    }
    pub(crate) fn transition(&mut self, new_status: Status) {
        self.status = new_status;
        if self.config.status_notification() {
            let lsp_status = match new_status {
                Status::Loading => lsp_ext::Status::Loading,
                Status::Ready { partial: true } => lsp_ext::Status::ReadyPartial,
                Status::Ready { partial: false } => lsp_ext::Status::Ready,
                Status::Invalid => lsp_ext::Status::Invalid,
                Status::NeedsReload => lsp_ext::Status::NeedsReload,
            };
            self.send_notification::<lsp_ext::StatusNotification>(StatusParams {
                status: lsp_status,
            });
        }
    }

    pub(crate) fn fetch_workspaces_request(&mut self) {
        self.fetch_workspaces_queue.request_op(())
    }
    pub(crate) fn fetch_workspaces_if_needed(&mut self) {
        if self.fetch_workspaces_queue.should_start_op().is_none() {
            return;
        }
        log::info!("will fetch workspaces");

        self.task_pool.handle.spawn_with_sender({
            let linked_projects = self.config.linked_projects();
            let cargo_config = self.config.cargo();

            move |sender| {
                let progress = {
                    let sender = sender.clone();
                    move |msg| {
                        sender
                            .send(Task::FetchWorkspace(ProjectWorkspaceProgress::Report(msg)))
                            .unwrap()
                    }
                };

                sender.send(Task::FetchWorkspace(ProjectWorkspaceProgress::Begin)).unwrap();

                let workspaces = linked_projects
                    .iter()
                    .map(|project| match project {
                        LinkedProject::ProjectManifest(manifest) => {
                            project_model::ProjectWorkspace::load(
                                manifest.clone(),
                                &cargo_config,
                                &progress,
                            )
                        }
                        LinkedProject::InlineJsonProject(it) => {
                            project_model::ProjectWorkspace::load_inline(
                                it.clone(),
                                cargo_config.target.as_deref(),
                            )
                        }
                    })
                    .collect::<Vec<_>>();

                log::info!("did fetch workspaces {:?}", workspaces);
                sender
                    .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(workspaces)))
                    .unwrap();
            }
        });
    }
    pub(crate) fn fetch_workspaces_completed(
        &mut self,
        workspaces: Vec<anyhow::Result<ProjectWorkspace>>,
    ) {
        self.fetch_workspaces_queue.op_completed(workspaces)
    }

    pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) {
        self.fetch_build_data_queue.request_op(build_data_collector);
    }
    pub(crate) fn fetch_build_data_if_needed(&mut self) {
        let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() {
            Some(it) => it,
            None => return,
        };
        self.task_pool.handle.spawn_with_sender(move |sender| {
            sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();

            let progress = {
                let sender = sender.clone();
                move |msg| {
                    sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap()
                }
            };
            let res = build_data_collector.collect(&progress);
            sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap();
        });
    }
    pub(crate) fn fetch_build_data_completed(
        &mut self,
        build_data: anyhow::Result<BuildDataResult>,
    ) {
        self.fetch_build_data_queue.op_completed(Some(build_data))
    }

    pub(crate) fn switch_workspaces(&mut self) {
        let _p = profile::span("GlobalState::switch_workspaces");
        let workspaces = self.fetch_workspaces_queue.last_op_result();
        log::info!("will switch workspaces: {:?}", workspaces);

        let mut error_message = None;
        let workspaces = workspaces
            .iter()
            .filter_map(|res| match res {
                Ok(it) => Some(it.clone()),
                Err(err) => {
                    log::error!("failed to load workspace: {:#}", err);
                    let message = error_message.get_or_insert_with(String::new);
                    stdx::format_to!(
                        message,
                        "rust-analyzer failed to load workspace: {:#}\n",
                        err
                    );
                    None
                }
            })
            .collect::<Vec<_>>();

        let workspace_build_data = match self.fetch_build_data_queue.last_op_result() {
            Some(Ok(it)) => Some(it.clone()),
            None => None,
            Some(Err(err)) => {
                log::error!("failed to fetch build data: {:#}", err);
                let message = error_message.get_or_insert_with(String::new);
                stdx::format_to!(message, "rust-analyzer failed to fetch build data: {:#}\n", err);
                None
            }
        };

        if let Some(error_message) = error_message {
            self.show_message(lsp_types::MessageType::Error, error_message);
            if !self.workspaces.is_empty() {
                return;
            }
        }

        if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data {
            return;
        }

        if let FilesWatcher::Client = self.config.files().watcher {
            if self.config.did_change_watched_files_dynamic_registration() {
                let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
                    watchers: workspaces
                        .iter()
                        .flat_map(|it| it.to_roots(workspace_build_data.as_ref()))
                        .filter(|it| it.is_member)
                        .flat_map(|root| {
                            root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display()))
                        })
                        .map(|glob_pattern| lsp_types::FileSystemWatcher {
                            glob_pattern,
                            kind: None,
                        })
                        .collect(),
                };
                let registration = lsp_types::Registration {
                    id: "workspace/didChangeWatchedFiles".to_string(),
                    method: "workspace/didChangeWatchedFiles".to_string(),
                    register_options: Some(serde_json::to_value(registration_options).unwrap()),
                };
                self.send_request::<lsp_types::request::RegisterCapability>(
                    lsp_types::RegistrationParams { registrations: vec![registration] },
                    |_, _| (),
                );
            }
        }

        let mut change = Change::new();

        let files_config = self.config.files();
        let project_folders =
            ProjectFolders::new(&workspaces, &files_config.exclude, workspace_build_data.as_ref());

        if self.proc_macro_client.is_none() {
            self.proc_macro_client = match self.config.proc_macro_srv() {
                None => None,
                Some((path, args)) => match ProcMacroClient::extern_process(path.clone(), args) {
                    Ok(it) => Some(it),
                    Err(err) => {
                        log::error!(
                            "Failed to run proc_macro_srv from path {}, error: {:?}",
                            path.display(),
                            err
                        );
                        None
                    }
                },
            };
        }

        let watch = match files_config.watcher {
            FilesWatcher::Client => vec![],
            FilesWatcher::Notify => project_folders.watch,
        };
        self.vfs_config_version += 1;
        self.loader.handle.set_config(vfs::loader::Config {
            load: project_folders.load,
            watch,
            version: self.vfs_config_version,
        });

        // Create crate graph from all the workspaces
        let crate_graph = {
            let mut crate_graph = CrateGraph::default();
            let vfs = &mut self.vfs.write().0;
            let loader = &mut self.loader;
            let mem_docs = &self.mem_docs;
            let mut load = |path: &AbsPath| {
                let vfs_path = vfs::VfsPath::from(path.to_path_buf());
                if !mem_docs.contains_key(&vfs_path) {
                    let contents = loader.handle.load_sync(path);
                    vfs.set_file_contents(vfs_path.clone(), contents);
                }
                let res = vfs.file_id(&vfs_path);
                if res.is_none() {
                    log::warn!("failed to load {}", path.display())
                }
                res
            };
            for ws in workspaces.iter() {
                crate_graph.extend(ws.to_crate_graph(
                    workspace_build_data.as_ref(),
                    self.proc_macro_client.as_ref(),
                    &mut load,
                ));
            }

            crate_graph
        };
        change.set_crate_graph(crate_graph);

        self.source_root_config = project_folders.source_root_config;
        self.workspaces = Arc::new(workspaces);
        self.workspace_build_data = workspace_build_data;

        self.analysis_host.apply_change(change);
        self.process_changes();
        self.reload_flycheck();
        log::info!("did switch workspaces");
    }

    fn reload_flycheck(&mut self) {
        let _p = profile::span("GlobalState::reload_flycheck");
        let config = match self.config.flycheck() {
            Some(it) => it,
            None => {
                self.flycheck = Vec::new();
                return;
            }
        };

        let sender = self.flycheck_sender.clone();
        self.flycheck = self
            .workspaces
            .iter()
            .enumerate()
            .filter_map(|(id, w)| match w {
                ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
                ProjectWorkspace::Json { project, .. } => {
                    // Enable flychecks for json projects if a custom flycheck command was supplied
                    // in the workspace configuration.
                    match config {
                        FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
                        _ => None,
                    }
                }
            })
            .map(|(id, root)| {
                let sender = sender.clone();
                FlycheckHandle::spawn(
                    id,
                    Box::new(move |msg| sender.send(msg).unwrap()),
                    config.clone(),
                    root.to_path_buf().into(),
                )
            })
            .collect();
    }
}

#[derive(Default)]
pub(crate) struct ProjectFolders {
    pub(crate) load: Vec<vfs::loader::Entry>,
    pub(crate) watch: Vec<usize>,
    pub(crate) source_root_config: SourceRootConfig,
}

impl ProjectFolders {
    pub(crate) fn new(
        workspaces: &[ProjectWorkspace],
        global_excludes: &[AbsPathBuf],
        build_data: Option<&BuildDataResult>,
    ) -> ProjectFolders {
        let mut res = ProjectFolders::default();
        let mut fsc = FileSetConfig::builder();
        let mut local_filesets = vec![];

        for root in workspaces.iter().flat_map(|it| it.to_roots(build_data)) {
            let file_set_roots: Vec<VfsPath> =
                root.include.iter().cloned().map(VfsPath::from).collect();

            let entry = {
                let mut dirs = vfs::loader::Directories::default();
                dirs.extensions.push("rs".into());
                dirs.include.extend(root.include);
                dirs.exclude.extend(root.exclude);
                for excl in global_excludes {
                    if dirs.include.iter().any(|incl| incl.starts_with(excl)) {
                        dirs.exclude.push(excl.clone());
                    }
                }

                vfs::loader::Entry::Directories(dirs)
            };

            if root.is_member {
                res.watch.push(res.load.len());
            }
            res.load.push(entry);

            if root.is_member {
                local_filesets.push(fsc.len());
            }
            fsc.add_file_set(file_set_roots)
        }

        let fsc = fsc.build();
        res.source_root_config = SourceRootConfig { fsc, local_filesets };

        res
    }
}

#[derive(Default, Debug)]
pub(crate) struct SourceRootConfig {
    pub(crate) fsc: FileSetConfig,
    pub(crate) local_filesets: Vec<usize>,
}

impl SourceRootConfig {
    pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> {
        let _p = profile::span("SourceRootConfig::partition");
        self.fsc
            .partition(vfs)
            .into_iter()
            .enumerate()
            .map(|(idx, file_set)| {
                let is_local = self.local_filesets.contains(&idx);
                if is_local {
                    SourceRoot::new_local(file_set)
                } else {
                    SourceRoot::new_library(file_set)
                }
            })
            .collect()
    }
}
<|endoftext|>


<commit_before>use crate::prelude::*;

static RANGE_MIN: f64 = 0.0;
static RANGE_MAX: f64 = 1.0;
static ID_INDICATOR: &'static str = "PGBAR_INDICATOR";

#[derive(Default, AsAny)]
struct BarState {
    indicator: Entity,
}

impl State for BarState {
    fn init(&mut self, registry: &mut Registry, ctx: &mut Context) {
        self.indicator = ctx
            .entity_of_child(ID_INDICATOR)
            .expect("BarState.init(): Child could not be found!");
        self.update(registry, ctx);
    }

    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        let val = ctx.widget().clone_or_default::<f64>("val");
        let max_width = ctx.widget().get::<Rectangle>("bounds").width();
        let new_width = calculate_width(val, max_width);

        ctx.get_widget(self.indicator)
            .get_mut::<Constraint>("constraint")
            .set_width(new_width);
    }
}

fn calculate_width(curren_progress: f64, max_width: f64) -> f64 {
    if curren_progress == RANGE_MIN {
        return 0.01;
    } else {
        if curren_progress == RANGE_MAX {
            return max_width * 0.99;
        } else if curren_progress > RANGE_MIN && curren_progress < RANGE_MAX {
            return max_width * curren_progress;
        } else {
            return max_width * 0.99;
        }
    }
}

widget!(
    /// The `ProgressBar` widget is used to indicating a finite progress
    /// (e.g. copying a file, downloading a video from the internet).
    /// A progress is visually represented as a horizontal bar which grows when the progress advances.
    /// The ProgressBar expects values between 0.0 and 1.0, whereas 0.0 means 0%, and 1.0 means 100%.
    /// Any value outside of this range cosidered as 100%.
    ///
    /// This example creates a ProgressBar with default values:
    /// ```rust
    /// ProgressBar::new().build(ctx)
    /// ```
    ///
    /// The next example creates a ProgressBar initialized with 25% progress:
    /// ```rust
    /// ProgressBar::new().val(0.25).build(ctx)
    /// ```
    ///
    /// The progress can be controlled by changing the value of the `val` property.
    /// (this code assumes that you have a children with id "pgbar")
    /// ```rust
    /// ctx.child("pgbar").set::<f64>("val", amount);
    /// ```
    ProgressBar<BarState> {
        /// Sets or shares the background color property
        background: Brush,
        /// Sets or shares the border color property
        border_brush: Brush,
        /// Sets or shares the border radius property
        border_radius: f64,
        /// Sets or shares the border width property
        border_width: Thickness,
        /// Sets or shares the padding property
        padding: Thickness,
        /// Sets or shares the current progress property
        val: f64
    }
);

impl Template for ProgressBar {
    fn template(self, _: Entity, ctx: &mut BuildContext) -> Self {
        self.name("ProgressBar")
            .background("#000000")
            .border_brush("#BABABA")
            .border_radius(4.0)
            .border_width(1.0)
            .element("progress_bar")
            .height(34.0)
            .padding((2.0, 4.0, 2.0, 4.0))
            .child(
                Container::new()
                    .id(ID_INDICATOR)
                    .element("progress_bar_indicator")
                    .background("#EFD035")
                    .height(24.0)
                    .v_align("start")
                    .border_radius(1.0)
                    .width(0.0)
                    .build(ctx),
            )
            .val(0.0)
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }

    fn layout(&self) -> Box<dyn Layout> {
        Box::new(PaddingLayout::new())
    }
}

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

    #[test]
    fn test_calculate_width() {
        assert_eq!(0.01, calculate_width(0.0, 100.0));
        assert_eq!(50.0, calculate_width(0.5, 100.0));
        assert_eq!(99.0, calculate_width(1.0, 100.0));
        assert_eq!(99.0, calculate_width(1.23, 100.0));
        assert_eq!(99.0, calculate_width(-1.23, 100.0));
    }
}
<commit_msg>indicator v_align centered, default width is 100<commit_after>use crate::prelude::*;

static RANGE_MIN: f64 = 0.0;
static RANGE_MAX: f64 = 1.0;
static ID_INDICATOR: &'static str = "PGBAR_INDICATOR";

#[derive(Default, AsAny)]
struct BarState {
    indicator: Entity,
}

impl State for BarState {
    fn init(&mut self, registry: &mut Registry, ctx: &mut Context) {
        self.indicator = ctx
            .entity_of_child(ID_INDICATOR)
            .expect("BarState.init(): Child could not be found!");
        self.update(registry, ctx);
    }

    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        let val = ctx.widget().clone_or_default::<f64>("val");
        let max_width = ctx.widget().get::<Rectangle>("bounds").width();
        let new_width = calculate_width(val, max_width);

        ctx.get_widget(self.indicator)
            .get_mut::<Constraint>("constraint")
            .set_width(new_width);
    }
}

fn calculate_width(curren_progress: f64, max_width: f64) -> f64 {
    if curren_progress == RANGE_MIN {
        return 0.01;
    } else {
        if curren_progress == RANGE_MAX {
            return max_width * 0.99;
        } else if curren_progress > RANGE_MIN && curren_progress < RANGE_MAX {
            return max_width * curren_progress;
        } else {
            return max_width * 0.99;
        }
    }
}

widget!(
    /// The `ProgressBar` widget is used to indicating a finite progress
    /// (e.g. copying a file, downloading a video from the internet).
    /// A progress is visually represented as a horizontal bar which grows when the progress advances.
    /// The ProgressBar expects values between 0.0 and 1.0, whereas 0.0 means 0%, and 1.0 means 100%.
    /// Any value outside of this range considered as 100%.
    ///
    /// This example creates a ProgressBar with default values:
    /// ```rust
    /// ProgressBar::new().build(ctx)
    /// ```
    ///
    /// The next example creates a ProgressBar initialized with 25% progress:
    /// ```rust
    /// ProgressBar::new().val(0.25).build(ctx)
    /// ```
    ///
    /// The progress can be controlled by changing the value of the `val` property.
    /// (this code assumes that you have a children with id "pgbar")
    /// ```rust
    /// ctx.child("pgbar").set::<f64>("val", amount);
    /// ```
    ProgressBar<BarState> {
        /// Sets or shares the background color property
        background: Brush,
        /// Sets or shares the border color property
        border_brush: Brush,
        /// Sets or shares the border radius property
        border_radius: f64,
        /// Sets or shares the border width property
        border_width: Thickness,
        /// Sets or shares the padding property
        padding: Thickness,
        /// Sets or shares the current progress property
        val: f64
    }
);

impl Template for ProgressBar {
    fn template(self, _: Entity, ctx: &mut BuildContext) -> Self {
        self.name("ProgressBar")
            .background("#000000")
            .border_brush("#BABABA")
            .border_radius(4.0)
            .border_width(1.0)
            .element("progress_bar")
            .height(34.0)
            .padding((2.0, 4.0, 2.0, 4.0))
            .child(
                Container::new()
                    .id(ID_INDICATOR)
                    .element("progress_bar_indicator")
                    .background("#EFD035")
                    .height(24.0)
                    .v_align("center")
                    .border_radius(1.0)
                    .width(0.0)
                    .build(ctx),
            )
            .width(100.0)
            .val(0.0)
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }

    fn layout(&self) -> Box<dyn Layout> {
        Box::new(PaddingLayout::new())
    }
}

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

    #[test]
    fn test_calculate_width() {
        assert_eq!(0.01, calculate_width(0.0, 100.0));
        assert_eq!(50.0, calculate_width(0.5, 100.0));
        assert_eq!(99.0, calculate_width(1.0, 100.0));
        assert_eq!(99.0, calculate_width(1.23, 100.0));
        assert_eq!(99.0, calculate_width(-1.23, 100.0));
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::sync::{Arc, Condvar, Mutex};
use std::time::Duration;
use std::{fmt, time::Instant};

/// A thread parking primitive.
///
/// Conceptually, each `Parker` has an associated token which is initially not present:
///
/// * The [`park`] method blocks the current thread unless or until the token is available, at
///   which point it automatically consumes the token.
///
/// * The [`park_timeout`] and [`park_deadline`] methods work the same as [`park`], but block for
///   a specified maximum time.
///
/// * The [`unpark`] method atomically makes the token available if it wasn't already. Because the
///   token is initially absent, [`unpark`] followed by [`park`] will result in the second call
///   returning immediately.
///
/// In other words, each `Parker` acts a bit like a spinlock that can be locked and unlocked using
/// [`park`] and [`unpark`].
///
/// # Examples
///
/// ```
/// use std::thread;
/// use std::time::Duration;
/// use crossbeam_utils::sync::Parker;
///
/// let p = Parker::new();
/// let u = p.unparker().clone();
///
/// // Make the token available.
/// u.unpark();
/// // Wakes up immediately and consumes the token.
/// p.park();
///
/// thread::spawn(move || {
///     thread::sleep(Duration::from_millis(500));
///     u.unpark();
/// });
///
/// // Wakes up when `u.unpark()` provides the token.
/// p.park();
/// ```
///
/// [`park`]: Parker::park
/// [`park_timeout`]: Parker::park_timeout
/// [`park_deadline`]: Parker::park_deadline
/// [`unpark`]: Unparker::unpark
pub struct Parker {
    unparker: Unparker,
    _marker: PhantomData<*const ()>,
}

unsafe impl Send for Parker {}

impl Default for Parker {
    fn default() -> Self {
        Self {
            unparker: Unparker {
                inner: Arc::new(Inner {
                    state: AtomicUsize::new(EMPTY),
                    lock: Mutex::new(()),
                    cvar: Condvar::new(),
                }),
            },
            _marker: PhantomData,
        }
    }
}

impl Parker {
    /// Creates a new `Parker`.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// ```
    ///
    pub fn new() -> Parker {
        Self::default()
    }

    /// Blocks the current thread until the token is made available.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// let u = p.unparker().clone();
    ///
    /// // Make the token available.
    /// u.unpark();
    ///
    /// // Wakes up immediately and consumes the token.
    /// p.park();
    /// ```
    pub fn park(&self) {
        self.unparker.inner.park(None);
    }

    /// Blocks the current thread until the token is made available, but only for a limited time.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    ///
    /// // Waits for the token to become available, but will not wait longer than 500 ms.
    /// p.park_timeout(Duration::from_millis(500));
    /// ```
    pub fn park_timeout(&self, timeout: Duration) -> UnparkReason {
        self.park_deadline(Instant::now() + timeout)
    }

    /// Blocks the current thread until the token is made available, or until a certain deadline.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::{Duration, Instant};
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// let deadline = Instant::now() + Duration::from_millis(500);
    ///
    /// // Waits for the token to become available, but will not wait longer than 500 ms.
    /// p.park_deadline(deadline);
    /// ```
    pub fn park_deadline(&self, deadline: Instant) -> UnparkReason {
        self.unparker.inner.park(Some(deadline))
    }

    /// Returns a reference to an associated [`Unparker`].
    ///
    /// The returned [`Unparker`] doesn't have to be used by reference - it can also be cloned.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// let u = p.unparker().clone();
    ///
    /// // Make the token available.
    /// u.unpark();
    /// // Wakes up immediately and consumes the token.
    /// p.park();
    /// ```
    ///
    /// [`park`]: Parker::park
    /// [`park_timeout`]: Parker::park_timeout
    pub fn unparker(&self) -> &Unparker {
        &self.unparker
    }

    /// Converts a `Parker` into a raw pointer.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// let raw = Parker::into_raw(p);
    /// ```
    pub fn into_raw(this: Parker) -> *const () {
        Unparker::into_raw(this.unparker)
    }

    /// Converts a raw pointer into a `Parker`.
    ///
    /// # Safety
    ///
    /// This method is safe to use only with pointers returned by [`Parker::into_raw`].
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// let raw = Parker::into_raw(p);
    /// let p = unsafe { Parker::from_raw(raw) };
    /// ```
    pub unsafe fn from_raw(ptr: *const ()) -> Parker {
        Parker {
            unparker: Unparker::from_raw(ptr),
            _marker: PhantomData,
        }
    }
}

impl fmt::Debug for Parker {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.pad("Parker { .. }")
    }
}

/// Unparks a thread parked by the associated [`Parker`].
pub struct Unparker {
    inner: Arc<Inner>,
}

unsafe impl Send for Unparker {}
unsafe impl Sync for Unparker {}

impl Unparker {
    /// Atomically makes the token available if it is not already.
    ///
    /// This method will wake up the thread blocked on [`park`] or [`park_timeout`], if there is
    /// any.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::thread;
    /// use std::time::Duration;
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// let u = p.unparker().clone();
    ///
    /// thread::spawn(move || {
    ///     thread::sleep(Duration::from_millis(500));
    ///     u.unpark();
    /// });
    ///
    /// // Wakes up when `u.unpark()` provides the token.
    /// p.park();
    /// ```
    ///
    /// [`park`]: Parker::park
    /// [`park_timeout`]: Parker::park_timeout
    pub fn unpark(&self) {
        self.inner.unpark()
    }

    /// Converts an `Unparker` into a raw pointer.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::{Parker, Unparker};
    ///
    /// let p = Parker::new();
    /// let u = p.unparker().clone();
    /// let raw = Unparker::into_raw(u);
    /// ```
    pub fn into_raw(this: Unparker) -> *const () {
        Arc::into_raw(this.inner) as *const ()
    }

    /// Converts a raw pointer into an `Unparker`.
    ///
    /// # Safety
    ///
    /// This method is safe to use only with pointers returned by [`Unparker::into_raw`].
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::{Parker, Unparker};
    ///
    /// let p = Parker::new();
    /// let u = p.unparker().clone();
    ///
    /// let raw = Unparker::into_raw(u);
    /// let u = unsafe { Unparker::from_raw(raw) };
    /// ```
    pub unsafe fn from_raw(ptr: *const ()) -> Unparker {
        Unparker {
            inner: Arc::from_raw(ptr as *const Inner),
        }
    }
}

impl fmt::Debug for Unparker {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.pad("Unparker { .. }")
    }
}

impl Clone for Unparker {
    fn clone(&self) -> Unparker {
        Unparker {
            inner: self.inner.clone(),
        }
    }
}

/// An enum that reports whether a `Parker::park_timeout` or
/// `Parker::park_deadline` returned because another thread called `unpark` or
/// because of a timeout.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnparkReason {
    /// The park method returned due to a call to `unpark`.
    Unparked,

    /// The park method returned due to a timeout.
    Timeout,
}

const EMPTY: usize = 0;
const PARKED: usize = 1;
const NOTIFIED: usize = 2;

struct Inner {
    state: AtomicUsize,
    lock: Mutex<()>,
    cvar: Condvar,
}

impl Inner {
    fn park(&self, deadline: Option<Instant>) -> UnparkReason {
        // If we were previously notified then we consume this notification and return quickly.
        if self
            .state
            .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst)
            .is_ok()
        {
            return UnparkReason::Unparked;
        }

        // If the timeout is zero, then there is no need to actually block.
        if let Some(deadline) = deadline {
            if deadline <= Instant::now() {
                return UnparkReason::Timeout;
            }
        }

        // Otherwise we need to coordinate going to sleep.
        let mut m = self.lock.lock().unwrap();

        match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
            Ok(_) => {}
            // Consume this notification to avoid spurious wakeups in the next park.
            Err(NOTIFIED) => {
                // We must read `state` here, even though we know it will be `NOTIFIED`. This is
                // because `unpark` may have been called again since we read `NOTIFIED` in the
                // `compare_exchange` above. We must perform an acquire operation that synchronizes
                // with that `unpark` to observe any writes it made before the call to `unpark`. To
                // do that we must read from the write it made to `state`.
                let old = self.state.swap(EMPTY, SeqCst);
                assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
                return UnparkReason::Unparked;
            }
            Err(n) => panic!("inconsistent park_timeout state: {}", n),
        }

        loop {
            // Block the current thread on the conditional variable.
            m = match deadline {
                None => self.cvar.wait(m).unwrap(),
                Some(deadline) => match deadline.checked_duration_since(Instant::now()) {
                    // We could check for a timeout here, but in the case that a timeout and an
                    // unpark arrive simultaneously, we prefer to report the former.
                    Some(duration) if duration > Duration::from_secs(0) => {
                        self.cvar.wait_timeout(m, duration).unwrap().0
                    }

                    // We've timed out; swap out the state back to empty on our way out
                    _ => match self.state.swap(EMPTY, SeqCst) {
                        NOTIFIED => return UnparkReason::Unparked, // got a notification
                        PARKED => return UnparkReason::Timeout,    // no notification
                        n => panic!("inconsistent park_timeout state: {}", n),
                    },
                },
            };

            if self
                .state
                .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst)
                .is_ok()
            {
                // got a notification
                return UnparkReason::Unparked;
            }

            // Spurious wakeup, go back to sleep. Alternatively, if we timed out, it will be caught
            // in the branch above, when we discover the deadline is in the past
        }
    }

    pub fn unpark(&self) {
        // To ensure the unparked thread will observe any writes we made before this call, we must
        // perform a release operation that `park` can synchronize with. To do that we must write
        // `NOTIFIED` even if `state` is already `NOTIFIED`. That is why this must be a swap rather
        // than a compare-and-swap that returns if it reads `NOTIFIED` on failure.
        match self.state.swap(NOTIFIED, SeqCst) {
            EMPTY => return,    // no one was waiting
            NOTIFIED => return, // already unparked
            PARKED => {}        // gotta go wake someone up
            _ => panic!("inconsistent state in unpark"),
        }

        // There is a period between when the parked thread sets `state` to `PARKED` (or last
        // checked `state` in the case of a spurious wakeup) and when it actually waits on `cvar`.
        // If we were to notify during this period it would be ignored and then when the parked
        // thread went to sleep it would never wake up. Fortunately, it has `lock` locked at this
        // stage so we can acquire `lock` to wait until it is ready to receive the notification.
        //
        // Releasing `lock` before the call to `notify_one` means that when the parked thread wakes
        // it doesn't get woken only to have to wait for us to release `lock`.
        drop(self.lock.lock().unwrap());
        self.cvar.notify_one();
    }
}
<commit_msg>Fixed weird import order<commit_after>use std::fmt;
use std::marker::PhantomData;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::sync::{Arc, Condvar, Mutex};
use std::time::{Duration, Instant};

/// A thread parking primitive.
///
/// Conceptually, each `Parker` has an associated token which is initially not present:
///
/// * The [`park`] method blocks the current thread unless or until the token is available, at
///   which point it automatically consumes the token.
///
/// * The [`park_timeout`] and [`park_deadline`] methods work the same as [`park`], but block for
///   a specified maximum time.
///
/// * The [`unpark`] method atomically makes the token available if it wasn't already. Because the
///   token is initially absent, [`unpark`] followed by [`park`] will result in the second call
///   returning immediately.
///
/// In other words, each `Parker` acts a bit like a spinlock that can be locked and unlocked using
/// [`park`] and [`unpark`].
///
/// # Examples
///
/// ```
/// use std::thread;
/// use std::time::Duration;
/// use crossbeam_utils::sync::Parker;
///
/// let p = Parker::new();
/// let u = p.unparker().clone();
///
/// // Make the token available.
/// u.unpark();
/// // Wakes up immediately and consumes the token.
/// p.park();
///
/// thread::spawn(move || {
///     thread::sleep(Duration::from_millis(500));
///     u.unpark();
/// });
///
/// // Wakes up when `u.unpark()` provides the token.
/// p.park();
/// ```
///
/// [`park`]: Parker::park
/// [`park_timeout`]: Parker::park_timeout
/// [`park_deadline`]: Parker::park_deadline
/// [`unpark`]: Unparker::unpark
pub struct Parker {
    unparker: Unparker,
    _marker: PhantomData<*const ()>,
}

unsafe impl Send for Parker {}

impl Default for Parker {
    fn default() -> Self {
        Self {
            unparker: Unparker {
                inner: Arc::new(Inner {
                    state: AtomicUsize::new(EMPTY),
                    lock: Mutex::new(()),
                    cvar: Condvar::new(),
                }),
            },
            _marker: PhantomData,
        }
    }
}

impl Parker {
    /// Creates a new `Parker`.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// ```
    ///
    pub fn new() -> Parker {
        Self::default()
    }

    /// Blocks the current thread until the token is made available.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// let u = p.unparker().clone();
    ///
    /// // Make the token available.
    /// u.unpark();
    ///
    /// // Wakes up immediately and consumes the token.
    /// p.park();
    /// ```
    pub fn park(&self) {
        self.unparker.inner.park(None);
    }

    /// Blocks the current thread until the token is made available, but only for a limited time.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    ///
    /// // Waits for the token to become available, but will not wait longer than 500 ms.
    /// p.park_timeout(Duration::from_millis(500));
    /// ```
    pub fn park_timeout(&self, timeout: Duration) -> UnparkReason {
        self.park_deadline(Instant::now() + timeout)
    }

    /// Blocks the current thread until the token is made available, or until a certain deadline.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::{Duration, Instant};
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// let deadline = Instant::now() + Duration::from_millis(500);
    ///
    /// // Waits for the token to become available, but will not wait longer than 500 ms.
    /// p.park_deadline(deadline);
    /// ```
    pub fn park_deadline(&self, deadline: Instant) -> UnparkReason {
        self.unparker.inner.park(Some(deadline))
    }

    /// Returns a reference to an associated [`Unparker`].
    ///
    /// The returned [`Unparker`] doesn't have to be used by reference - it can also be cloned.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// let u = p.unparker().clone();
    ///
    /// // Make the token available.
    /// u.unpark();
    /// // Wakes up immediately and consumes the token.
    /// p.park();
    /// ```
    ///
    /// [`park`]: Parker::park
    /// [`park_timeout`]: Parker::park_timeout
    pub fn unparker(&self) -> &Unparker {
        &self.unparker
    }

    /// Converts a `Parker` into a raw pointer.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// let raw = Parker::into_raw(p);
    /// ```
    pub fn into_raw(this: Parker) -> *const () {
        Unparker::into_raw(this.unparker)
    }

    /// Converts a raw pointer into a `Parker`.
    ///
    /// # Safety
    ///
    /// This method is safe to use only with pointers returned by [`Parker::into_raw`].
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// let raw = Parker::into_raw(p);
    /// let p = unsafe { Parker::from_raw(raw) };
    /// ```
    pub unsafe fn from_raw(ptr: *const ()) -> Parker {
        Parker {
            unparker: Unparker::from_raw(ptr),
            _marker: PhantomData,
        }
    }
}

impl fmt::Debug for Parker {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.pad("Parker { .. }")
    }
}

/// Unparks a thread parked by the associated [`Parker`].
pub struct Unparker {
    inner: Arc<Inner>,
}

unsafe impl Send for Unparker {}
unsafe impl Sync for Unparker {}

impl Unparker {
    /// Atomically makes the token available if it is not already.
    ///
    /// This method will wake up the thread blocked on [`park`] or [`park_timeout`], if there is
    /// any.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::thread;
    /// use std::time::Duration;
    /// use crossbeam_utils::sync::Parker;
    ///
    /// let p = Parker::new();
    /// let u = p.unparker().clone();
    ///
    /// thread::spawn(move || {
    ///     thread::sleep(Duration::from_millis(500));
    ///     u.unpark();
    /// });
    ///
    /// // Wakes up when `u.unpark()` provides the token.
    /// p.park();
    /// ```
    ///
    /// [`park`]: Parker::park
    /// [`park_timeout`]: Parker::park_timeout
    pub fn unpark(&self) {
        self.inner.unpark()
    }

    /// Converts an `Unparker` into a raw pointer.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::{Parker, Unparker};
    ///
    /// let p = Parker::new();
    /// let u = p.unparker().clone();
    /// let raw = Unparker::into_raw(u);
    /// ```
    pub fn into_raw(this: Unparker) -> *const () {
        Arc::into_raw(this.inner) as *const ()
    }

    /// Converts a raw pointer into an `Unparker`.
    ///
    /// # Safety
    ///
    /// This method is safe to use only with pointers returned by [`Unparker::into_raw`].
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_utils::sync::{Parker, Unparker};
    ///
    /// let p = Parker::new();
    /// let u = p.unparker().clone();
    ///
    /// let raw = Unparker::into_raw(u);
    /// let u = unsafe { Unparker::from_raw(raw) };
    /// ```
    pub unsafe fn from_raw(ptr: *const ()) -> Unparker {
        Unparker {
            inner: Arc::from_raw(ptr as *const Inner),
        }
    }
}

impl fmt::Debug for Unparker {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.pad("Unparker { .. }")
    }
}

impl Clone for Unparker {
    fn clone(&self) -> Unparker {
        Unparker {
            inner: self.inner.clone(),
        }
    }
}

/// An enum that reports whether a `Parker::park_timeout` or
/// `Parker::park_deadline` returned because another thread called `unpark` or
/// because of a timeout.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnparkReason {
    /// The park method returned due to a call to `unpark`.
    Unparked,

    /// The park method returned due to a timeout.
    Timeout,
}

const EMPTY: usize = 0;
const PARKED: usize = 1;
const NOTIFIED: usize = 2;

struct Inner {
    state: AtomicUsize,
    lock: Mutex<()>,
    cvar: Condvar,
}

impl Inner {
    fn park(&self, deadline: Option<Instant>) -> UnparkReason {
        // If we were previously notified then we consume this notification and return quickly.
        if self
            .state
            .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst)
            .is_ok()
        {
            return UnparkReason::Unparked;
        }

        // If the timeout is zero, then there is no need to actually block.
        if let Some(deadline) = deadline {
            if deadline <= Instant::now() {
                return UnparkReason::Timeout;
            }
        }

        // Otherwise we need to coordinate going to sleep.
        let mut m = self.lock.lock().unwrap();

        match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
            Ok(_) => {}
            // Consume this notification to avoid spurious wakeups in the next park.
            Err(NOTIFIED) => {
                // We must read `state` here, even though we know it will be `NOTIFIED`. This is
                // because `unpark` may have been called again since we read `NOTIFIED` in the
                // `compare_exchange` above. We must perform an acquire operation that synchronizes
                // with that `unpark` to observe any writes it made before the call to `unpark`. To
                // do that we must read from the write it made to `state`.
                let old = self.state.swap(EMPTY, SeqCst);
                assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
                return UnparkReason::Unparked;
            }
            Err(n) => panic!("inconsistent park_timeout state: {}", n),
        }

        loop {
            // Block the current thread on the conditional variable.
            m = match deadline {
                None => self.cvar.wait(m).unwrap(),
                Some(deadline) => match deadline.checked_duration_since(Instant::now()) {
                    // We could check for a timeout here, but in the case that a timeout and an
                    // unpark arrive simultaneously, we prefer to report the former.
                    Some(duration) if duration > Duration::from_secs(0) => {
                        self.cvar.wait_timeout(m, duration).unwrap().0
                    }

                    // We've timed out; swap out the state back to empty on our way out
                    _ => match self.state.swap(EMPTY, SeqCst) {
                        NOTIFIED => return UnparkReason::Unparked, // got a notification
                        PARKED => return UnparkReason::Timeout,    // no notification
                        n => panic!("inconsistent park_timeout state: {}", n),
                    },
                },
            };

            if self
                .state
                .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst)
                .is_ok()
            {
                // got a notification
                return UnparkReason::Unparked;
            }

            // Spurious wakeup, go back to sleep. Alternatively, if we timed out, it will be caught
            // in the branch above, when we discover the deadline is in the past
        }
    }

    pub fn unpark(&self) {
        // To ensure the unparked thread will observe any writes we made before this call, we must
        // perform a release operation that `park` can synchronize with. To do that we must write
        // `NOTIFIED` even if `state` is already `NOTIFIED`. That is why this must be a swap rather
        // than a compare-and-swap that returns if it reads `NOTIFIED` on failure.
        match self.state.swap(NOTIFIED, SeqCst) {
            EMPTY => return,    // no one was waiting
            NOTIFIED => return, // already unparked
            PARKED => {}        // gotta go wake someone up
            _ => panic!("inconsistent state in unpark"),
        }

        // There is a period between when the parked thread sets `state` to `PARKED` (or last
        // checked `state` in the case of a spurious wakeup) and when it actually waits on `cvar`.
        // If we were to notify during this period it would be ignored and then when the parked
        // thread went to sleep it would never wake up. Fortunately, it has `lock` locked at this
        // stage so we can acquire `lock` to wait until it is ready to receive the notification.
        //
        // Releasing `lock` before the call to `notify_one` means that when the parked thread wakes
        // it doesn't get woken only to have to wait for us to release `lock`.
        drop(self.lock.lock().unwrap());
        self.cvar.notify_one();
    }
}
<|endoftext|>


<commit_before>#[crate_id="github.com/sfackler/rust-mphf#mphf:0.0"];
#[crate_type="lib"];
#[feature(managed_boxes, macro_registrar)];

extern mod syntax;

use syntax::ast::{Name, TokenTree, LitStr, MutImmutable, Expr, ExprTup, ExprVec, ExprLit};
use syntax::codemap::{Span, NameAndSpan, MacroBang, ExpnInfo};
use syntax::ext::base::{SyntaxExtension,
                        ExtCtxt,
                        MacResult,
                        MRExpr,
                        NormalTT,
                        SyntaxExpanderTT,
                        SyntaxExpanderTTExpanderWithoutContext};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{COMMA, EOF, FAT_ARROW};

pub struct MphfMap<T> {
    #[doc(hidden)]
    entries: &'static [(&'static str, T)],
}

impl<T> Container for MphfMap<T> {
    fn len(&self) -> uint {
        self.entries.len()
    }
}

impl<T> Map<&'static str, T> for MphfMap<T> {
    fn find<'a>(&'a self, key: & &'static str) -> Option<&'a T> {
        self.entries.bsearch(|&(val, _)| val.cmp(key)).map(|idx| {
            let (_, ref val) = self.entries[idx];
            val
        })
    }
}

#[macro_registrar]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("mphf_map"),
             NormalTT(~SyntaxExpanderTT {
                expander: SyntaxExpanderTTExpanderWithoutContext(expand_mphf_map),
                span: None
             },
             None));
}

fn expand_mphf_map(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                tts.to_owned());
    let mut pairs = ~[];

    while parser.token != EOF {
        let key = parser.parse_expr();

        let key_str = match key.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(s, _) => s,
                    _ => cx.span_fatal(key.span, "expected string literal"),
                }
            }
            _ => cx.span_fatal(key.span, "expected string literal"),
        };

        if !parser.eat(&FAT_ARROW) {
            cx.span_fatal(parser.span, "expected `=>`");
        }

        let value = parser.parse_expr();

        pairs.push((key_str, key, value));

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_fatal(parser.span, "expected `,`");
        }
    }

    pairs.sort_by(|&(ref a, _, _), &(ref b, _, _)| a.cmp(b));

    let entries = pairs.move_iter()
        .map(|(_, key, value)| @Expr {
            id: 0,
            node: ExprTup(~[key, value]),
            span: Span {
                lo: key.span.lo,
                hi: value.span.hi,
                expn_info: Some(@ExpnInfo {
                    call_site: sp,
                    callee: NameAndSpan {
                        name: @"mphf_map",
                        format: MacroBang,
                        span: None,
                    }
                })
            }
        })
        .collect();
    let entries = @Expr {
        id: 0,
        node: ExprVec(entries, MutImmutable),
        span: Span {
            lo: sp.lo,
            hi: sp.hi,
            expn_info: Some(@ExpnInfo {
                call_site: sp,
                callee: NameAndSpan {
                    name: @"mphf_map",
                    format: MacroBang,
                    span: None,
                }
            })
        },
    };

    MRExpr(quote_expr!(cx, MphfMap { entries: &'static $entries }))
}
<commit_msg>Check for duplicate keys<commit_after>#[crate_id="github.com/sfackler/rust-mphf#mphf:0.0"];
#[crate_type="lib"];
#[feature(managed_boxes, macro_registrar)];

extern mod syntax;

use syntax::ast::{Name, TokenTree, LitStr, MutImmutable, Expr, ExprTup, ExprVec, ExprLit};
use syntax::codemap::{Span, NameAndSpan, MacroBang, ExpnInfo};
use syntax::ext::base::{SyntaxExtension,
                        ExtCtxt,
                        MacResult,
                        MRExpr,
                        NormalTT,
                        SyntaxExpanderTT,
                        SyntaxExpanderTTExpanderWithoutContext};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{COMMA, EOF, FAT_ARROW};

pub struct MphfMap<T> {
    #[doc(hidden)]
    entries: &'static [(&'static str, T)],
}

impl<T> Container for MphfMap<T> {
    fn len(&self) -> uint {
        self.entries.len()
    }
}

impl<T> Map<&'static str, T> for MphfMap<T> {
    fn find<'a>(&'a self, key: & &'static str) -> Option<&'a T> {
        self.entries.bsearch(|&(val, _)| val.cmp(key)).map(|idx| {
            let (_, ref val) = self.entries[idx];
            val
        })
    }
}

#[macro_registrar]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("mphf_map"),
             NormalTT(~SyntaxExpanderTT {
                expander: SyntaxExpanderTTExpanderWithoutContext(expand_mphf_map),
                span: None
             },
             None));
}

fn expand_mphf_map(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                tts.to_owned());
    let mut pairs = ~[];

    while parser.token != EOF {
        let key = parser.parse_expr();

        let key_str = match key.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(s, _) => s,
                    _ => cx.span_fatal(key.span, "expected string literal"),
                }
            }
            _ => cx.span_fatal(key.span, "expected string literal"),
        };

        if !parser.eat(&FAT_ARROW) {
            cx.span_fatal(parser.span, "expected `=>`");
        }

        let value = parser.parse_expr();

        pairs.push((key_str, key, value));

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_fatal(parser.span, "expected `,`");
        }
    }

    pairs.sort_by(|&(ref a, _, _), &(ref b, _, _)| a.cmp(b));

    for window in pairs.windows(2) {
        let (ref a, ref a_expr, _) = window[0];
        let (ref b, ref b_expr, _) = window[1];
        if a == b {
            cx.span_err(sp, format!("key {:s} duplicated", *a));
            cx.span_err(a_expr.span, "one occurance here");
            cx.span_err(b_expr.span, "one occurance here");
        }
    }

    let entries = pairs.move_iter()
        .map(|(_, key, value)| @Expr {
            id: 0,
            node: ExprTup(~[key, value]),
            span: Span {
                lo: key.span.lo,
                hi: value.span.hi,
                expn_info: Some(@ExpnInfo {
                    call_site: sp,
                    callee: NameAndSpan {
                        name: @"mphf_map",
                        format: MacroBang,
                        span: None,
                    }
                })
            }
        })
        .collect();
    let entries = @Expr {
        id: 0,
        node: ExprVec(entries, MutImmutable),
        span: Span {
            lo: sp.lo,
            hi: sp.hi,
            expn_info: Some(@ExpnInfo {
                call_site: sp,
                callee: NameAndSpan {
                    name: @"mphf_map",
                    format: MacroBang,
                    span: None,
                }
            })
        },
    };

    MRExpr(quote_expr!(cx, MphfMap { entries: &'static $entries }))
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::sync::Arc;

use buffer::Buffer;
use buffer::BufferViewRef;
use command_buffer::cmd::CommandsListSink;
use descriptor::descriptor_set::DescriptorSet;
use descriptor::descriptor_set::TrackedDescriptorSet;
use descriptor::descriptor_set::UnsafeDescriptorSetLayout;
use descriptor::descriptor_set::DescriptorPool;
use descriptor::descriptor_set::DescriptorPoolAlloc;
use descriptor::descriptor_set::UnsafeDescriptorSet;
use descriptor::descriptor_set::DescriptorWrite;
use descriptor::descriptor_set::StdDescriptorPool;
use descriptor::pipeline_layout::PipelineLayoutRef;
use device::Device;
use image::ImageView;
use image::sys::Layout;
use sync::AccessFlagBits;
use sync::PipelineStages;

/// A simple immutable descriptor set.
///
/// It is named "simple" because creating such a descriptor set allocates from a pool, and because
/// it can't be modified once created. It is sufficient for most usages, but in some situations
/// you may wish to use something more optimized instead.
///
/// In order to build a `SimpleDescriptorSet`, you need to use a `SimpleDescriptorSetBuilder`. But
/// the easiest way is to use the `simple_descriptor_set!` macro.
///
/// The template parameter of the `SimpleDescriptorSet` is very complex, and you shouldn't try to
/// express it explicitely. If you want to store your descriptor set in a struct or in a `Vec` for
/// example, you are encouraged to turn `SimpleDescriptorSet` into a `Box<DescriptorSet>` or a
/// `Arc<DescriptorSet>`.
///
/// # Example
// TODO:
pub struct SimpleDescriptorSet<R, P = Arc<StdDescriptorPool>> where P: DescriptorPool {
    inner: P::Alloc,
    resources: R,
    layout: Arc<UnsafeDescriptorSetLayout>
}

impl<R, P> SimpleDescriptorSet<R, P> where P: DescriptorPool {
    /// Returns the layout used to create this descriptor set.
    #[inline]
    pub fn set_layout(&self) -> &Arc<UnsafeDescriptorSetLayout> {
        &self.layout
    }
}

unsafe impl<R, P> DescriptorSet for SimpleDescriptorSet<R, P> where P: DescriptorPool {
    #[inline]
    fn inner(&self) -> &UnsafeDescriptorSet {
        self.inner.inner()
    }
}

unsafe impl<R, P> TrackedDescriptorSet for SimpleDescriptorSet<R, P>
    where R: SimpleDescriptorSetResourcesCollection,
          P: DescriptorPool
{
    #[inline]
    fn add_transition<'a>(&'a self, sink: &mut CommandsListSink<'a>) {
        self.resources.add_transition(sink)
    }
}

/// Builds a descriptor set in the form of a `SimpleDescriptorSet` object.
// TODO: more doc
#[macro_export]
macro_rules! simple_descriptor_set {
    ($layout:expr, $set_num:expr, {$($name:ident: $val:expr),*$(,)*}) => ({
        use $crate::descriptor::descriptor_set::SimpleDescriptorSetBuilder;
        use $crate::descriptor::descriptor_set::SimpleDescriptorSetBufferExt;

        let builder = SimpleDescriptorSetBuilder::new($layout, $set_num);

        $(
            let builder = $val.add_me(builder, stringify!($name));
        )*

        builder.build()
    });
}

/// Prototype of a `SimpleDescriptorSet`.
///
/// > **Note**: You are encouraged to use the `simple_descriptor_set!` macro instead of
/// > manipulating these internals.
///
/// # Example
// TODO: example here
pub struct SimpleDescriptorSetBuilder<L, R> {
    layout: L,
    set_id: usize,
    writes: Vec<DescriptorWrite>,
    resources: R,
}

impl<L> SimpleDescriptorSetBuilder<L, ()> where L: PipelineLayoutRef {
    /// Builds a new prototype for a `SimpleDescriptorSet`. Requires a reference to a pipeline
    /// layout, and the id of the set within the layout.
    ///
    /// # Panic
    ///
    /// - Panics if the set id is out of range.
    ///
    pub fn new(layout: L, set_id: usize) -> SimpleDescriptorSetBuilder<L, ()> {
        assert!(layout.desc().num_sets() > set_id);

        SimpleDescriptorSetBuilder {
            layout: layout,
            set_id: set_id,
            writes: Vec::new(),
            resources: (),
        }
    }
}

impl<L, R> SimpleDescriptorSetBuilder<L, R> where L: PipelineLayoutRef {
    /// Builds a `SimpleDescriptorSet` from the builder.
    pub fn build(self) -> SimpleDescriptorSet<R, Arc<StdDescriptorPool>> {
        // TODO: check that we filled everything
        let pool = Device::standard_descriptor_pool(self.layout.device());
        let set_layout = self.layout.descriptor_set_layout(self.set_id).unwrap().clone();       // FIXME: error

        let set = unsafe {
            let mut set = pool.alloc(&set_layout).unwrap();      // FIXME: error
            set.inner_mut().write(pool.device(), self.writes.into_iter());
            set
        };

        SimpleDescriptorSet {
            inner: set,
            resources: self.resources,
            layout: set_layout,
        }
    }
}

/// Trait implemented on buffer values that can be appended to a simple descriptor set builder.
pub unsafe trait SimpleDescriptorSetBufferExt<L, R> {
    type Out;

    // TODO: return Result
    fn add_me(self, i: SimpleDescriptorSetBuilder<L, R>, name: &str)
              -> SimpleDescriptorSetBuilder<L, Self::Out>;
}

unsafe impl<L, R, T> SimpleDescriptorSetBufferExt<L, R> for T
    where T: Buffer, L: PipelineLayoutRef
{
    type Out = (R, SimpleDescriptorSetBuf<T>);

    fn add_me(self, mut i: SimpleDescriptorSetBuilder<L, R>, name: &str)
              -> SimpleDescriptorSetBuilder<L, Self::Out>
    {
        let (set_id, binding_id) = i.layout.desc().descriptor_by_name(name).unwrap();    // TODO: Result instead
        assert_eq!(set_id, i.set_id);       // TODO: Result instead
        let desc = i.layout.desc().descriptor(set_id, binding_id).unwrap();     // TODO: Result instead

        // TODO: dispatch depending on the descriptor
        i.writes.push(unsafe { DescriptorWrite::uniform_buffer(binding_id as u32, &self) });

        SimpleDescriptorSetBuilder {
            layout: i.layout,
            set_id: i.set_id,
            writes: i.writes,
            resources: (i.resources, SimpleDescriptorSetBuf {
                buffer: self,
                write: !desc.readonly,
                stage: PipelineStages::none(),      // FIXME:
                access: AccessFlagBits::none(),     // FIXME:
            })
        }
    }
}

/// Internal trait related to the `SimpleDescriptorSet` system.
pub unsafe trait SimpleDescriptorSetResourcesCollection {
    #[inline]
    fn add_transition<'a>(&'a self, sink: &mut CommandsListSink<'a>);
}

unsafe impl SimpleDescriptorSetResourcesCollection for () {
    #[inline]
    fn add_transition<'a>(&'a self, _: &mut CommandsListSink<'a>) {
    }
}

/// Internal object related to the `SimpleDescriptorSet` system.
pub struct SimpleDescriptorSetBuf<B> {
    buffer: B,
    write: bool,
    stage: PipelineStages,
    access: AccessFlagBits,
}

unsafe impl<B> SimpleDescriptorSetResourcesCollection for SimpleDescriptorSetBuf<B>
    where B: Buffer
{
    #[inline]
    fn add_transition<'a>(&'a self, sink: &mut CommandsListSink<'a>) {
        // TODO: wrong values
        let stages = PipelineStages {
            compute_shader: true,
            all_graphics: true,
            .. PipelineStages::none()
        };

        let access = AccessFlagBits {
            uniform_read: true,
            shader_read: true,
            shader_write: true,
            .. AccessFlagBits::none()
        };

        sink.add_buffer_transition(&self.buffer, 0, self.buffer.size(), self.write, stages, access);
    }
}

/// Internal object related to the `SimpleDescriptorSet` system.
pub struct SimpleDescriptorSetBufView<V> where V: BufferViewRef {
    view: V,
    write: bool,
    stage: PipelineStages,
    access: AccessFlagBits,
}

unsafe impl<V> SimpleDescriptorSetResourcesCollection for SimpleDescriptorSetBufView<V>
    where V: BufferViewRef, V::Buffer: Buffer
{
    #[inline]
    fn add_transition<'a>(&'a self, sink: &mut CommandsListSink<'a>) {
        // TODO: wrong values
        let stages = PipelineStages {
            compute_shader: true,
            all_graphics: true,
            .. PipelineStages::none()
        };

        let access = AccessFlagBits {
            uniform_read: true,
            shader_read: true,
            shader_write: true,
            .. AccessFlagBits::none()
        };

        sink.add_buffer_transition(self.view.view().buffer(), 0, self.view.view().buffer().size(),
                                   self.write, stages, access);
    }
}

/// Internal object related to the `SimpleDescriptorSet` system.
pub struct SimpleDescriptorSetImg<I> {
    image: I,
    write: bool,
    first_mipmap: u32,
    num_mipmaps: u32,
    first_layer: u32,
    num_layers: u32,
    layout: Layout,
    stage: PipelineStages,
    access: AccessFlagBits,
}

unsafe impl<I> SimpleDescriptorSetResourcesCollection for SimpleDescriptorSetImg<I>
    where I: ImageView
{
    #[inline]
    fn add_transition<'a>(&'a self, sink: &mut CommandsListSink<'a>) {
        // TODO: wrong values
        let stages = PipelineStages {
            compute_shader: true,
            all_graphics: true,
            .. PipelineStages::none()
        };

        let access = AccessFlagBits {
            uniform_read: true,
            input_attachment_read: true,
            shader_read: true,
            shader_write: true,
            .. AccessFlagBits::none()
        };

        // FIXME: adjust layers & mipmaps with the view's parameters
        sink.add_image_transition(self.image.parent(), self.first_layer, self.num_layers,
                                  self.first_mipmap, self.num_mipmaps, self.write,
                                  self.layout, stages, access);
    }
}

unsafe impl<A, B> SimpleDescriptorSetResourcesCollection for (A, B)
    where A: SimpleDescriptorSetResourcesCollection,
          B: SimpleDescriptorSetResourcesCollection
{
    #[inline]
    fn add_transition<'a>(&'a self, sink: &mut CommandsListSink<'a>) {
        self.0.add_transition(sink);
        self.1.add_transition(sink);
    }
}
<commit_msg>More safety in the descriptor writes<commit_after>// Copyright (c) 2016 The vulkano developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::sync::Arc;

use buffer::Buffer;
use buffer::BufferViewRef;
use command_buffer::cmd::CommandsListSink;
use descriptor::descriptor::DescriptorType;
use descriptor::descriptor_set::DescriptorSet;
use descriptor::descriptor_set::TrackedDescriptorSet;
use descriptor::descriptor_set::UnsafeDescriptorSetLayout;
use descriptor::descriptor_set::DescriptorPool;
use descriptor::descriptor_set::DescriptorPoolAlloc;
use descriptor::descriptor_set::UnsafeDescriptorSet;
use descriptor::descriptor_set::DescriptorWrite;
use descriptor::descriptor_set::StdDescriptorPool;
use descriptor::pipeline_layout::PipelineLayoutRef;
use device::Device;
use image::ImageView;
use image::sys::Layout;
use sync::AccessFlagBits;
use sync::PipelineStages;

/// A simple immutable descriptor set.
///
/// It is named "simple" because creating such a descriptor set allocates from a pool, and because
/// it can't be modified once created. It is sufficient for most usages, but in some situations
/// you may wish to use something more optimized instead.
///
/// In order to build a `SimpleDescriptorSet`, you need to use a `SimpleDescriptorSetBuilder`. But
/// the easiest way is to use the `simple_descriptor_set!` macro.
///
/// The template parameter of the `SimpleDescriptorSet` is very complex, and you shouldn't try to
/// express it explicitely. If you want to store your descriptor set in a struct or in a `Vec` for
/// example, you are encouraged to turn `SimpleDescriptorSet` into a `Box<DescriptorSet>` or a
/// `Arc<DescriptorSet>`.
///
/// # Example
// TODO:
pub struct SimpleDescriptorSet<R, P = Arc<StdDescriptorPool>> where P: DescriptorPool {
    inner: P::Alloc,
    resources: R,
    layout: Arc<UnsafeDescriptorSetLayout>
}

impl<R, P> SimpleDescriptorSet<R, P> where P: DescriptorPool {
    /// Returns the layout used to create this descriptor set.
    #[inline]
    pub fn set_layout(&self) -> &Arc<UnsafeDescriptorSetLayout> {
        &self.layout
    }
}

unsafe impl<R, P> DescriptorSet for SimpleDescriptorSet<R, P> where P: DescriptorPool {
    #[inline]
    fn inner(&self) -> &UnsafeDescriptorSet {
        self.inner.inner()
    }
}

unsafe impl<R, P> TrackedDescriptorSet for SimpleDescriptorSet<R, P>
    where R: SimpleDescriptorSetResourcesCollection,
          P: DescriptorPool
{
    #[inline]
    fn add_transition<'a>(&'a self, sink: &mut CommandsListSink<'a>) {
        self.resources.add_transition(sink)
    }
}

/// Builds a descriptor set in the form of a `SimpleDescriptorSet` object.
// TODO: more doc
#[macro_export]
macro_rules! simple_descriptor_set {
    ($layout:expr, $set_num:expr, {$($name:ident: $val:expr),*$(,)*}) => ({
        use $crate::descriptor::descriptor_set::SimpleDescriptorSetBuilder;
        use $crate::descriptor::descriptor_set::SimpleDescriptorSetBufferExt;

        let builder = SimpleDescriptorSetBuilder::new($layout, $set_num);

        $(
            let builder = $val.add_me(builder, stringify!($name));
        )*

        builder.build()
    });
}

/// Prototype of a `SimpleDescriptorSet`.
///
/// > **Note**: You are encouraged to use the `simple_descriptor_set!` macro instead of
/// > manipulating these internals.
///
/// # Example
// TODO: example here
pub struct SimpleDescriptorSetBuilder<L, R> {
    layout: L,
    set_id: usize,
    writes: Vec<DescriptorWrite>,
    resources: R,
}

impl<L> SimpleDescriptorSetBuilder<L, ()> where L: PipelineLayoutRef {
    /// Builds a new prototype for a `SimpleDescriptorSet`. Requires a reference to a pipeline
    /// layout, and the id of the set within the layout.
    ///
    /// # Panic
    ///
    /// - Panics if the set id is out of range.
    ///
    pub fn new(layout: L, set_id: usize) -> SimpleDescriptorSetBuilder<L, ()> {
        assert!(layout.desc().num_sets() > set_id);

        SimpleDescriptorSetBuilder {
            layout: layout,
            set_id: set_id,
            writes: Vec::new(),
            resources: (),
        }
    }
}

impl<L, R> SimpleDescriptorSetBuilder<L, R> where L: PipelineLayoutRef {
    /// Builds a `SimpleDescriptorSet` from the builder.
    pub fn build(self) -> SimpleDescriptorSet<R, Arc<StdDescriptorPool>> {
        // TODO: check that we filled everything
        let pool = Device::standard_descriptor_pool(self.layout.device());
        let set_layout = self.layout.descriptor_set_layout(self.set_id).unwrap().clone();       // FIXME: error

        let set = unsafe {
            let mut set = pool.alloc(&set_layout).unwrap();      // FIXME: error
            set.inner_mut().write(pool.device(), self.writes.into_iter());
            set
        };

        SimpleDescriptorSet {
            inner: set,
            resources: self.resources,
            layout: set_layout,
        }
    }
}

/// Trait implemented on buffer values that can be appended to a simple descriptor set builder.
pub unsafe trait SimpleDescriptorSetBufferExt<L, R> {
    type Out;

    // TODO: return Result
    fn add_me(self, i: SimpleDescriptorSetBuilder<L, R>, name: &str)
              -> SimpleDescriptorSetBuilder<L, Self::Out>;
}

unsafe impl<L, R, T> SimpleDescriptorSetBufferExt<L, R> for T
    where T: Buffer, L: PipelineLayoutRef
{
    type Out = (R, SimpleDescriptorSetBuf<T>);

    fn add_me(self, mut i: SimpleDescriptorSetBuilder<L, R>, name: &str)
              -> SimpleDescriptorSetBuilder<L, Self::Out>
    {
        let (set_id, binding_id) = i.layout.desc().descriptor_by_name(name).unwrap();    // TODO: Result instead
        assert_eq!(set_id, i.set_id);       // TODO: Result instead
        let desc = i.layout.desc().descriptor(set_id, binding_id).unwrap();     // TODO: Result instead

        assert!(desc.array_count == 1);     // not implemented
        i.writes.push(match desc.ty.ty().unwrap() {
            DescriptorType::UniformBuffer => unsafe {
                DescriptorWrite::uniform_buffer(binding_id as u32, &self)
            },
            DescriptorType::StorageBuffer => unsafe {
                DescriptorWrite::storage_buffer(binding_id as u32, &self)
            },
            _ => panic!()
        });

        SimpleDescriptorSetBuilder {
            layout: i.layout,
            set_id: i.set_id,
            writes: i.writes,
            resources: (i.resources, SimpleDescriptorSetBuf {
                buffer: self,
                write: !desc.readonly,
                stage: PipelineStages::none(),      // FIXME:
                access: AccessFlagBits::none(),     // FIXME:
            })
        }
    }
}

/// Internal trait related to the `SimpleDescriptorSet` system.
pub unsafe trait SimpleDescriptorSetResourcesCollection {
    #[inline]
    fn add_transition<'a>(&'a self, sink: &mut CommandsListSink<'a>);
}

unsafe impl SimpleDescriptorSetResourcesCollection for () {
    #[inline]
    fn add_transition<'a>(&'a self, _: &mut CommandsListSink<'a>) {
    }
}

/// Internal object related to the `SimpleDescriptorSet` system.
pub struct SimpleDescriptorSetBuf<B> {
    buffer: B,
    write: bool,
    stage: PipelineStages,
    access: AccessFlagBits,
}

unsafe impl<B> SimpleDescriptorSetResourcesCollection for SimpleDescriptorSetBuf<B>
    where B: Buffer
{
    #[inline]
    fn add_transition<'a>(&'a self, sink: &mut CommandsListSink<'a>) {
        // TODO: wrong values
        let stages = PipelineStages {
            compute_shader: true,
            all_graphics: true,
            .. PipelineStages::none()
        };

        let access = AccessFlagBits {
            uniform_read: true,
            shader_read: true,
            shader_write: true,
            .. AccessFlagBits::none()
        };

        sink.add_buffer_transition(&self.buffer, 0, self.buffer.size(), self.write, stages, access);
    }
}

/// Internal object related to the `SimpleDescriptorSet` system.
pub struct SimpleDescriptorSetBufView<V> where V: BufferViewRef {
    view: V,
    write: bool,
    stage: PipelineStages,
    access: AccessFlagBits,
}

unsafe impl<V> SimpleDescriptorSetResourcesCollection for SimpleDescriptorSetBufView<V>
    where V: BufferViewRef, V::Buffer: Buffer
{
    #[inline]
    fn add_transition<'a>(&'a self, sink: &mut CommandsListSink<'a>) {
        // TODO: wrong values
        let stages = PipelineStages {
            compute_shader: true,
            all_graphics: true,
            .. PipelineStages::none()
        };

        let access = AccessFlagBits {
            uniform_read: true,
            shader_read: true,
            shader_write: true,
            .. AccessFlagBits::none()
        };

        sink.add_buffer_transition(self.view.view().buffer(), 0, self.view.view().buffer().size(),
                                   self.write, stages, access);
    }
}

/// Internal object related to the `SimpleDescriptorSet` system.
pub struct SimpleDescriptorSetImg<I> {
    image: I,
    write: bool,
    first_mipmap: u32,
    num_mipmaps: u32,
    first_layer: u32,
    num_layers: u32,
    layout: Layout,
    stage: PipelineStages,
    access: AccessFlagBits,
}

unsafe impl<I> SimpleDescriptorSetResourcesCollection for SimpleDescriptorSetImg<I>
    where I: ImageView
{
    #[inline]
    fn add_transition<'a>(&'a self, sink: &mut CommandsListSink<'a>) {
        // TODO: wrong values
        let stages = PipelineStages {
            compute_shader: true,
            all_graphics: true,
            .. PipelineStages::none()
        };

        let access = AccessFlagBits {
            uniform_read: true,
            input_attachment_read: true,
            shader_read: true,
            shader_write: true,
            .. AccessFlagBits::none()
        };

        // FIXME: adjust layers & mipmaps with the view's parameters
        sink.add_image_transition(self.image.parent(), self.first_layer, self.num_layers,
                                  self.first_mipmap, self.num_mipmaps, self.write,
                                  self.layout, stages, access);
    }
}

unsafe impl<A, B> SimpleDescriptorSetResourcesCollection for (A, B)
    where A: SimpleDescriptorSetResourcesCollection,
          B: SimpleDescriptorSetResourcesCollection
{
    #[inline]
    fn add_transition<'a>(&'a self, sink: &mut CommandsListSink<'a>) {
        self.0.add_transition(sink);
        self.1.add_transition(sink);
    }
}
<|endoftext|>