<commit_before>extern crate libanalysis;
extern crate relative_path;
extern crate test_utils;

use std::{
    collections::HashMap,
    path::{Path},
};

use relative_path::RelativePath;
use libanalysis::{AnalysisHost, FileId, FileResolver, JobHandle, CrateGraph, CrateId};
use test_utils::assert_eq_dbg;

struct FileMap(&'static [(u32, &'static str)]);

impl FileMap {
    fn path(&self, id: FileId) -> &'static Path {
        let s = self.0.iter()
            .find(|it| it.0 == id.0)
            .unwrap()
            .1;
        Path::new(s)
    }
}

impl FileResolver for FileMap {
    fn file_stem(&self, id: FileId) -> String {
        self.path(id).file_stem().unwrap().to_str().unwrap().to_string()
    }
    fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> {
        let path = rel.to_path(self.path(id));
        let path = path.to_str().unwrap();
        let path = RelativePath::new(&path[1..]).normalize();
        let &(id, _) = self.0.iter()
            .find(|it| path == RelativePath::new(&it.1[1..]).normalize())?;
        Some(FileId(id))
    }
}

#[test]
fn test_resolve_module() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));
    world.change_file(FileId(2), Some("".to_string()));

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    let (_handle, token) = JobHandle::new();
    let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into(), &token);
    assert_eq_dbg(
        r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
        &symbols,
    );

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo/mod.rs")
    ]));
    let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into(), &token);
    assert_eq_dbg(
        r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
        &symbols,
    );
}

#[test]
fn test_unresolved_module_diagnostic() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));

    let snap = world.analysis(FileMap(&[(1, "/lib.rs")]));
    let diagnostics = snap.diagnostics(FileId(1));
    assert_eq_dbg(
        r#"[Diagnostic {
            message: "unresolved module",
            range: [4; 7),
            fix: Some(SourceChange {
                label: "create module",
                source_file_edits: [],
                file_system_edits: [CreateFile { anchor: FileId(1), path: "../foo.rs" }],
                cursor_position: None }) }]"#,
        &diagnostics,
    );
}

#[test]
fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo {}".to_string()));

    let snap = world.analysis(FileMap(&[(1, "/lib.rs")]));
    let diagnostics = snap.diagnostics(FileId(1));
    assert_eq_dbg(
        r#"[]"#,
        &diagnostics,
    );
}

#[test]
fn test_resolve_parent_module() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));
    world.change_file(FileId(2), Some("".to_string()));

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    let symbols = snap.parent_module(FileId(2));
    assert_eq_dbg(
        r#"[(FileId(1), FileSymbol { name: "foo", node_range: [0; 8), kind: MODULE })]"#,
        &symbols,
    );
}

#[test]
fn test_resolve_crate_root() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));
    world.change_file(FileId(2), Some("".to_string()));

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    assert!(snap.crate_for(FileId(2)).is_empty());

    let crate_graph = CrateGraph {
        crate_roots: {
            let mut m = HashMap::new();
            m.insert(CrateId(1), FileId(1));
            m
        },
    };
    world.set_crate_graph(crate_graph);

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    assert_eq!(
        snap.crate_for(FileId(2)),
        vec![CrateId(1)],
    );
}
<commit_msg>fix tests on windows<commit_after>extern crate libanalysis;
extern crate relative_path;
extern crate test_utils;

use std::{
    collections::HashMap,
    path::{Path},
};

use relative_path::RelativePath;
use libanalysis::{AnalysisHost, FileId, FileResolver, JobHandle, CrateGraph, CrateId};
use test_utils::assert_eq_dbg;

struct FileMap(&'static [(u32, &'static str)]);

impl FileMap {
    fn path(&self, id: FileId) -> &'static Path {
        let s = self.0.iter()
            .find(|it| it.0 == id.0)
            .unwrap()
            .1;
        Path::new(s)
    }
}

impl FileResolver for FileMap {
    fn file_stem(&self, id: FileId) -> String {
        self.path(id).file_stem().unwrap().to_str().unwrap().to_string()
    }
    fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> {
        let path = {
            if rel.starts_with("..") {
                rel.strip_prefix("..").unwrap()
                    .to_path(&self.path(id).parent().unwrap())
            } else {
                rel.to_path(self.path(id))
            }
        };
        let path = &path.to_str().unwrap()[1..];
        let path = RelativePath::new(&path[0..]).normalize();
        let &(id, _) = self.0.iter()
            .find(|it| path == RelativePath::new(&it.1[0..]).normalize())?;
        Some(FileId(id))
    }
}

#[test]
fn test_resolve_module() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));
    world.change_file(FileId(2), Some("".to_string()));

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    let (_handle, token) = JobHandle::new();
    let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into(), &token);
    assert_eq_dbg(
        r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
        &symbols,
    );

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo/mod.rs")
    ]));
    let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into(), &token);
    assert_eq_dbg(
        r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
        &symbols,
    );
}

#[test]
fn test_unresolved_module_diagnostic() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));

    let snap = world.analysis(FileMap(&[(1, "/lib.rs")]));
    let diagnostics = snap.diagnostics(FileId(1));
    assert_eq_dbg(
        r#"[Diagnostic {
            message: "unresolved module",
            range: [4; 7),
            fix: Some(SourceChange {
                label: "create module",
                source_file_edits: [],
                file_system_edits: [CreateFile { anchor: FileId(1), path: "../foo.rs" }],
                cursor_position: None }) }]"#,
        &diagnostics,
    );
}

#[test]
fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo {}".to_string()));

    let snap = world.analysis(FileMap(&[(1, "/lib.rs")]));
    let diagnostics = snap.diagnostics(FileId(1));
    assert_eq_dbg(
        r#"[]"#,
        &diagnostics,
    );
}

#[test]
fn test_resolve_parent_module() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));
    world.change_file(FileId(2), Some("".to_string()));

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    let symbols = snap.parent_module(FileId(2));
    assert_eq_dbg(
        r#"[(FileId(1), FileSymbol { name: "foo", node_range: [0; 8), kind: MODULE })]"#,
        &symbols,
    );
}

#[test]
fn test_resolve_crate_root() {
    let mut world = AnalysisHost::new();
    world.change_file(FileId(1), Some("mod foo;".to_string()));
    world.change_file(FileId(2), Some("".to_string()));

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    assert!(snap.crate_for(FileId(2)).is_empty());

    let crate_graph = CrateGraph {
        crate_roots: {
            let mut m = HashMap::new();
            m.insert(CrateId(1), FileId(1));
            m
        },
    };
    world.set_crate_graph(crate_graph);

    let snap = world.analysis(FileMap(&[
        (1, "/lib.rs"),
        (2, "/foo.rs"),
    ]));
    assert_eq!(
        snap.crate_for(FileId(2)),
        vec![CrateId(1)],
    );
}
<|endoftext|>


<commit_before>//! The context or environment in which the language server functions. In our
//! server implementation this is know as the `WorldState`.
//!
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.

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

use crossbeam_channel::{unbounded, Receiver};
use lsp_types::Url;
use parking_lot::RwLock;
use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher};
use ra_ide::{
    Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsOptions, LibraryData,
    SourceRootId,
};
use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace};
use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
use relative_path::RelativePathBuf;
use stdx::format_to;

use crate::{
    diagnostics::{CheckFixes, DiagnosticCollection},
    feature_flags::FeatureFlags,
    main_loop::pending_requests::{CompletedRequest, LatestRequests},
    vfs_glob::{Glob, RustPackageFilterBuilder},
    LspError, Result,
};
use ra_db::ExternSourceId;
use rustc_hash::{FxHashMap, FxHashSet};

fn create_watcher(workspaces: &[ProjectWorkspace], options: &Options) -> CheckWatcher {
    // FIXME: Figure out the multi-workspace situation
    workspaces
        .iter()
        .find_map(|w| match w {
            ProjectWorkspace::Cargo { cargo, .. } => Some(cargo),
            ProjectWorkspace::Json { .. } => None,
        })
        .map(|cargo| {
            let cargo_project_root = cargo.workspace_root().to_path_buf();
            Some(CheckWatcher::new(&options.cargo_watch, cargo_project_root))
        })
        .unwrap_or_else(|| {
            log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
            None
        })
}

#[derive(Debug, Clone)]
pub struct Options {
    pub publish_decorations: bool,
    pub supports_location_link: bool,
    pub line_folding_only: bool,
    pub inlay_hints: InlayHintsOptions,
    pub rustfmt_args: Vec<String>,
    pub cargo_watch: CheckOptions,
    pub vscode_lldb: bool,
}

/// `WorldState` is the primary mutable state of the language server
///
/// The most interesting components are `vfs`, which stores a consistent
/// snapshot of the file systems, and `analysis_host`, which stores our
/// incremental salsa database.
#[derive(Debug)]
pub struct WorldState {
    pub options: Options,
    pub feature_flags: Arc<FeatureFlags>,
    pub roots: Vec<PathBuf>,
    pub workspaces: Arc<Vec<ProjectWorkspace>>,
    pub analysis_host: AnalysisHost,
    pub vfs: Arc<RwLock<Vfs>>,
    pub task_receiver: Receiver<VfsTask>,
    pub latest_requests: Arc<RwLock<LatestRequests>>,
    pub check_watcher: Option<CheckWatcher>,
    pub diagnostics: DiagnosticCollection,
}

/// An immutable snapshot of the world's state at a point in time.
pub struct WorldSnapshot {
    pub options: Options,
    pub feature_flags: Arc<FeatureFlags>,
    pub workspaces: Arc<Vec<ProjectWorkspace>>,
    pub analysis: Analysis,
    pub latest_requests: Arc<RwLock<LatestRequests>>,
    pub check_fixes: CheckFixes,
    vfs: Arc<RwLock<Vfs>>,
}

impl WorldState {
    pub fn new(
        folder_roots: Vec<PathBuf>,
        workspaces: Vec<ProjectWorkspace>,
        lru_capacity: Option<usize>,
        exclude_globs: &[Glob],
        watch: Watch,
        options: Options,
        feature_flags: FeatureFlags,
    ) -> WorldState {
        let mut change = AnalysisChange::new();

        let mut roots = Vec::new();
        roots.extend(folder_roots.iter().map(|path| {
            let mut filter = RustPackageFilterBuilder::default().set_member(true);
            for glob in exclude_globs.iter() {
                filter = filter.exclude(glob.clone());
            }
            RootEntry::new(path.clone(), filter.into_vfs_filter())
        }));
        for ws in workspaces.iter() {
            roots.extend(ws.to_roots().into_iter().map(|pkg_root| {
                let mut filter =
                    RustPackageFilterBuilder::default().set_member(pkg_root.is_member());
                for glob in exclude_globs.iter() {
                    filter = filter.exclude(glob.clone());
                }
                RootEntry::new(pkg_root.path().clone(), filter.into_vfs_filter())
            }));
        }

        let mut extern_dirs = FxHashSet::default();
        for ws in workspaces.iter() {
            extern_dirs.extend(ws.out_dirs());
        }

        let mut extern_source_roots = FxHashMap::default();

        roots.extend(extern_dirs.iter().map(|path| {
            let mut filter = RustPackageFilterBuilder::default().set_member(false);
            for glob in exclude_globs.iter() {
                filter = filter.exclude(glob.clone());
            }
            RootEntry::new(PathBuf::from(&path), filter.into_vfs_filter())
        }));

        let (task_sender, task_receiver) = unbounded();
        let task_sender = Box::new(move |t| task_sender.send(t).unwrap());
        let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch);

        for r in vfs_roots {
            let vfs_root_path = vfs.root2path(r);
            let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it));
            change.add_root(SourceRootId(r.0), is_local);
            change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string());

            // FIXME: add path2root in vfs to simpily this logic
            if extern_dirs.contains(&vfs_root_path) {
                extern_source_roots.insert(vfs_root_path, ExternSourceId(r.0));
            }
        }

        // FIXME: Read default cfgs from config
        let default_cfg_options = {
            let mut opts = get_rustc_cfg_options();
            opts.insert_atom("test".into());
            opts.insert_atom("debug_assertion".into());
            opts
        };

        // Create crate graph from all the workspaces
        let mut crate_graph = CrateGraph::default();
        let mut load = |path: &std::path::Path| {
            let vfs_file = vfs.load(path);
            vfs_file.map(|f| FileId(f.0))
        };

        let proc_macro_client =
            ProcMacroClient::extern_process(std::path::Path::new("ra_proc_macro_srv"));

        workspaces
            .iter()
            .map(|ws| {
                ws.to_crate_graph(
                    &default_cfg_options,
                    &extern_source_roots,
                    &proc_macro_client,
                    &mut load,
                )
            })
            .for_each(|graph| {
                crate_graph.extend(graph);
            });
        change.set_crate_graph(crate_graph);

        let check_watcher = create_watcher(&workspaces, &options);

        let mut analysis_host = AnalysisHost::new(lru_capacity);
        analysis_host.apply_change(change);
        WorldState {
            options,
            feature_flags: Arc::new(feature_flags),
            roots: folder_roots,
            workspaces: Arc::new(workspaces),
            analysis_host,
            vfs: Arc::new(RwLock::new(vfs)),
            task_receiver,
            latest_requests: Default::default(),
            check_watcher,
            diagnostics: Default::default(),
        }
    }

    pub fn update_configuration(
        &mut self,
        lru_capacity: Option<usize>,
        options: Options,
        feature_flags: FeatureFlags,
    ) {
        self.feature_flags = Arc::new(feature_flags);
        self.analysis_host.update_lru_capacity(lru_capacity);
        self.check_watcher = create_watcher(&self.workspaces, &options);
        self.options = options;
    }

    /// Returns a vec of libraries
    /// FIXME: better API here
    pub fn process_changes(
        &mut self,
        roots_scanned: &mut usize,
    ) -> Option<Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>> {
        let changes = self.vfs.write().commit_changes();
        if changes.is_empty() {
            return None;
        }
        let mut libs = Vec::new();
        let mut change = AnalysisChange::new();
        for c in changes {
            match c {
                VfsChange::AddRoot { root, files } => {
                    let root_path = self.vfs.read().root2path(root);
                    let is_local = self.roots.iter().any(|r| root_path.starts_with(r));
                    if is_local {
                        *roots_scanned += 1;
                        for (file, path, text) in files {
                            change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
                        }
                    } else {
                        let files = files
                            .into_iter()
                            .map(|(vfsfile, path, text)| (FileId(vfsfile.0), path, text))
                            .collect();
                        libs.push((SourceRootId(root.0), files));
                    }
                }
                VfsChange::AddFile { root, file, path, text } => {
                    change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
                }
                VfsChange::RemoveFile { root, file, path } => {
                    change.remove_file(SourceRootId(root.0), FileId(file.0), path)
                }
                VfsChange::ChangeFile { file, text } => {
                    change.change_file(FileId(file.0), text);
                }
            }
        }
        self.analysis_host.apply_change(change);
        Some(libs)
    }

    pub fn add_lib(&mut self, data: LibraryData) {
        let mut change = AnalysisChange::new();
        change.add_library(data);
        self.analysis_host.apply_change(change);
    }

    pub fn snapshot(&self) -> WorldSnapshot {
        WorldSnapshot {
            options: self.options.clone(),
            feature_flags: Arc::clone(&self.feature_flags),
            workspaces: Arc::clone(&self.workspaces),
            analysis: self.analysis_host.analysis(),
            vfs: Arc::clone(&self.vfs),
            latest_requests: Arc::clone(&self.latest_requests),
            check_fixes: Arc::clone(&self.diagnostics.check_fixes),
        }
    }

    pub fn maybe_collect_garbage(&mut self) {
        self.analysis_host.maybe_collect_garbage()
    }

    pub fn collect_garbage(&mut self) {
        self.analysis_host.collect_garbage()
    }

    pub fn complete_request(&mut self, request: CompletedRequest) {
        self.latest_requests.write().record(request)
    }
}

impl WorldSnapshot {
    pub fn analysis(&self) -> &Analysis {
        &self.analysis
    }

    pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> {
        let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
        let file = self.vfs.read().path2file(&path).ok_or_else(|| {
            // Show warning as this file is outside current workspace
            // FIXME: just handle such files, and remove `LspError::UNKNOWN_FILE`.
            LspError {
                code: LspError::UNKNOWN_FILE,
                message: "Rust file outside current workspace is not supported yet.".to_string(),
            }
        })?;
        Ok(FileId(file.0))
    }

    pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> {
        let path = self.vfs.read().file2path(VfsFile(id.0));
        let url = url_from_path_with_drive_lowercasing(path)?;

        Ok(url)
    }

    pub fn file_id_to_path(&self, id: FileId) -> PathBuf {
        self.vfs.read().file2path(VfsFile(id.0))
    }

    pub fn file_line_endings(&self, id: FileId) -> LineEndings {
        self.vfs.read().file_line_endings(VfsFile(id.0))
    }

    pub fn path_to_uri(&self, root: SourceRootId, path: &RelativePathBuf) -> Result<Url> {
        let base = self.vfs.read().root2path(VfsRoot(root.0));
        let path = path.to_path(base);
        let url = Url::from_file_path(&path)
            .map_err(|_| format!("can't convert path to url: {}", path.display()))?;
        Ok(url)
    }

    pub fn status(&self) -> String {
        let mut buf = String::new();
        if self.workspaces.is_empty() {
            buf.push_str("no workspaces\n")
        } else {
            buf.push_str("workspaces:\n");
            for w in self.workspaces.iter() {
                format_to!(buf, "{} packages loaded\n", w.n_packages());
            }
        }
        buf.push_str("\nanalysis:\n");
        buf.push_str(
            &self
                .analysis
                .status()
                .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()),
        );
        buf
    }

    pub fn workspace_root_for(&self, file_id: FileId) -> Option<&Path> {
        let path = self.vfs.read().file2path(VfsFile(file_id.0));
        self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path))
    }
}
<commit_msg>Rebase fixes<commit_after>//! The context or environment in which the language server functions. In our
//! server implementation this is know as the `WorldState`.
//!
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.

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

use crossbeam_channel::{unbounded, Receiver};
use lsp_types::Url;
use parking_lot::RwLock;
use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher};
use ra_ide::{
    Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsOptions, LibraryData,
    SourceRootId,
};
use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace};
use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
use relative_path::RelativePathBuf;
use stdx::format_to;

use crate::{
    diagnostics::{CheckFixes, DiagnosticCollection},
    feature_flags::FeatureFlags,
    main_loop::pending_requests::{CompletedRequest, LatestRequests},
    vfs_glob::{Glob, RustPackageFilterBuilder},
    LspError, Result,
};
use ra_db::ExternSourceId;
use rustc_hash::{FxHashMap, FxHashSet};

fn create_watcher(workspaces: &[ProjectWorkspace], options: &Options) -> Option<CheckWatcher> {
    // FIXME: Figure out the multi-workspace situation
    workspaces
        .iter()
        .find_map(|w| match w {
            ProjectWorkspace::Cargo { cargo, .. } => Some(cargo),
            ProjectWorkspace::Json { .. } => None,
        })
        .map(|cargo| {
            let cargo_project_root = cargo.workspace_root().to_path_buf();
            Some(CheckWatcher::new(&options.cargo_watch, cargo_project_root))
        })
        .unwrap_or_else(|| {
            log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
            None
        })
}

#[derive(Debug, Clone)]
pub struct Options {
    pub publish_decorations: bool,
    pub supports_location_link: bool,
    pub line_folding_only: bool,
    pub inlay_hints: InlayHintsOptions,
    pub rustfmt_args: Vec<String>,
    pub cargo_watch: CheckOptions,
    pub vscode_lldb: bool,
}

/// `WorldState` is the primary mutable state of the language server
///
/// The most interesting components are `vfs`, which stores a consistent
/// snapshot of the file systems, and `analysis_host`, which stores our
/// incremental salsa database.
#[derive(Debug)]
pub struct WorldState {
    pub options: Options,
    pub feature_flags: Arc<FeatureFlags>,
    pub roots: Vec<PathBuf>,
    pub workspaces: Arc<Vec<ProjectWorkspace>>,
    pub analysis_host: AnalysisHost,
    pub vfs: Arc<RwLock<Vfs>>,
    pub task_receiver: Receiver<VfsTask>,
    pub latest_requests: Arc<RwLock<LatestRequests>>,
    pub check_watcher: Option<CheckWatcher>,
    pub diagnostics: DiagnosticCollection,
}

/// An immutable snapshot of the world's state at a point in time.
pub struct WorldSnapshot {
    pub options: Options,
    pub feature_flags: Arc<FeatureFlags>,
    pub workspaces: Arc<Vec<ProjectWorkspace>>,
    pub analysis: Analysis,
    pub latest_requests: Arc<RwLock<LatestRequests>>,
    pub check_fixes: CheckFixes,
    vfs: Arc<RwLock<Vfs>>,
}

impl WorldState {
    pub fn new(
        folder_roots: Vec<PathBuf>,
        workspaces: Vec<ProjectWorkspace>,
        lru_capacity: Option<usize>,
        exclude_globs: &[Glob],
        watch: Watch,
        options: Options,
        feature_flags: FeatureFlags,
    ) -> WorldState {
        let mut change = AnalysisChange::new();

        let mut roots = Vec::new();
        roots.extend(folder_roots.iter().map(|path| {
            let mut filter = RustPackageFilterBuilder::default().set_member(true);
            for glob in exclude_globs.iter() {
                filter = filter.exclude(glob.clone());
            }
            RootEntry::new(path.clone(), filter.into_vfs_filter())
        }));
        for ws in workspaces.iter() {
            roots.extend(ws.to_roots().into_iter().map(|pkg_root| {
                let mut filter =
                    RustPackageFilterBuilder::default().set_member(pkg_root.is_member());
                for glob in exclude_globs.iter() {
                    filter = filter.exclude(glob.clone());
                }
                RootEntry::new(pkg_root.path().clone(), filter.into_vfs_filter())
            }));
        }

        let mut extern_dirs = FxHashSet::default();
        for ws in workspaces.iter() {
            extern_dirs.extend(ws.out_dirs());
        }

        let mut extern_source_roots = FxHashMap::default();

        roots.extend(extern_dirs.iter().map(|path| {
            let mut filter = RustPackageFilterBuilder::default().set_member(false);
            for glob in exclude_globs.iter() {
                filter = filter.exclude(glob.clone());
            }
            RootEntry::new(PathBuf::from(&path), filter.into_vfs_filter())
        }));

        let (task_sender, task_receiver) = unbounded();
        let task_sender = Box::new(move |t| task_sender.send(t).unwrap());
        let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch);

        for r in vfs_roots {
            let vfs_root_path = vfs.root2path(r);
            let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it));
            change.add_root(SourceRootId(r.0), is_local);
            change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string());

            // FIXME: add path2root in vfs to simpily this logic
            if extern_dirs.contains(&vfs_root_path) {
                extern_source_roots.insert(vfs_root_path, ExternSourceId(r.0));
            }
        }

        // FIXME: Read default cfgs from config
        let default_cfg_options = {
            let mut opts = get_rustc_cfg_options();
            opts.insert_atom("test".into());
            opts.insert_atom("debug_assertion".into());
            opts
        };

        // Create crate graph from all the workspaces
        let mut crate_graph = CrateGraph::default();
        let mut load = |path: &std::path::Path| {
            let vfs_file = vfs.load(path);
            vfs_file.map(|f| FileId(f.0))
        };

        let proc_macro_client =
            ProcMacroClient::extern_process(std::path::Path::new("ra_proc_macro_srv"));

        workspaces
            .iter()
            .map(|ws| {
                ws.to_crate_graph(
                    &default_cfg_options,
                    &extern_source_roots,
                    &proc_macro_client,
                    &mut load,
                )
            })
            .for_each(|graph| {
                crate_graph.extend(graph);
            });
        change.set_crate_graph(crate_graph);

        let check_watcher = create_watcher(&workspaces, &options);

        let mut analysis_host = AnalysisHost::new(lru_capacity);
        analysis_host.apply_change(change);
        WorldState {
            options,
            feature_flags: Arc::new(feature_flags),
            roots: folder_roots,
            workspaces: Arc::new(workspaces),
            analysis_host,
            vfs: Arc::new(RwLock::new(vfs)),
            task_receiver,
            latest_requests: Default::default(),
            check_watcher,
            diagnostics: Default::default(),
        }
    }

    pub fn update_configuration(
        &mut self,
        lru_capacity: Option<usize>,
        options: Options,
        feature_flags: FeatureFlags,
    ) {
        self.feature_flags = Arc::new(feature_flags);
        self.analysis_host.update_lru_capacity(lru_capacity);
        self.check_watcher = create_watcher(&self.workspaces, &options);
        self.options = options;
    }

    /// Returns a vec of libraries
    /// FIXME: better API here
    pub fn process_changes(
        &mut self,
        roots_scanned: &mut usize,
    ) -> Option<Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>> {
        let changes = self.vfs.write().commit_changes();
        if changes.is_empty() {
            return None;
        }
        let mut libs = Vec::new();
        let mut change = AnalysisChange::new();
        for c in changes {
            match c {
                VfsChange::AddRoot { root, files } => {
                    let root_path = self.vfs.read().root2path(root);
                    let is_local = self.roots.iter().any(|r| root_path.starts_with(r));
                    if is_local {
                        *roots_scanned += 1;
                        for (file, path, text) in files {
                            change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
                        }
                    } else {
                        let files = files
                            .into_iter()
                            .map(|(vfsfile, path, text)| (FileId(vfsfile.0), path, text))
                            .collect();
                        libs.push((SourceRootId(root.0), files));
                    }
                }
                VfsChange::AddFile { root, file, path, text } => {
                    change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
                }
                VfsChange::RemoveFile { root, file, path } => {
                    change.remove_file(SourceRootId(root.0), FileId(file.0), path)
                }
                VfsChange::ChangeFile { file, text } => {
                    change.change_file(FileId(file.0), text);
                }
            }
        }
        self.analysis_host.apply_change(change);
        Some(libs)
    }

    pub fn add_lib(&mut self, data: LibraryData) {
        let mut change = AnalysisChange::new();
        change.add_library(data);
        self.analysis_host.apply_change(change);
    }

    pub fn snapshot(&self) -> WorldSnapshot {
        WorldSnapshot {
            options: self.options.clone(),
            feature_flags: Arc::clone(&self.feature_flags),
            workspaces: Arc::clone(&self.workspaces),
            analysis: self.analysis_host.analysis(),
            vfs: Arc::clone(&self.vfs),
            latest_requests: Arc::clone(&self.latest_requests),
            check_fixes: Arc::clone(&self.diagnostics.check_fixes),
        }
    }

    pub fn maybe_collect_garbage(&mut self) {
        self.analysis_host.maybe_collect_garbage()
    }

    pub fn collect_garbage(&mut self) {
        self.analysis_host.collect_garbage()
    }

    pub fn complete_request(&mut self, request: CompletedRequest) {
        self.latest_requests.write().record(request)
    }
}

impl WorldSnapshot {
    pub fn analysis(&self) -> &Analysis {
        &self.analysis
    }

    pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> {
        let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
        let file = self.vfs.read().path2file(&path).ok_or_else(|| {
            // Show warning as this file is outside current workspace
            // FIXME: just handle such files, and remove `LspError::UNKNOWN_FILE`.
            LspError {
                code: LspError::UNKNOWN_FILE,
                message: "Rust file outside current workspace is not supported yet.".to_string(),
            }
        })?;
        Ok(FileId(file.0))
    }

    pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> {
        let path = self.vfs.read().file2path(VfsFile(id.0));
        let url = url_from_path_with_drive_lowercasing(path)?;

        Ok(url)
    }

    pub fn file_id_to_path(&self, id: FileId) -> PathBuf {
        self.vfs.read().file2path(VfsFile(id.0))
    }

    pub fn file_line_endings(&self, id: FileId) -> LineEndings {
        self.vfs.read().file_line_endings(VfsFile(id.0))
    }

    pub fn path_to_uri(&self, root: SourceRootId, path: &RelativePathBuf) -> Result<Url> {
        let base = self.vfs.read().root2path(VfsRoot(root.0));
        let path = path.to_path(base);
        let url = Url::from_file_path(&path)
            .map_err(|_| format!("can't convert path to url: {}", path.display()))?;
        Ok(url)
    }

    pub fn status(&self) -> String {
        let mut buf = String::new();
        if self.workspaces.is_empty() {
            buf.push_str("no workspaces\n")
        } else {
            buf.push_str("workspaces:\n");
            for w in self.workspaces.iter() {
                format_to!(buf, "{} packages loaded\n", w.n_packages());
            }
        }
        buf.push_str("\nanalysis:\n");
        buf.push_str(
            &self
                .analysis
                .status()
                .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()),
        );
        buf
    }

    pub fn workspace_root_for(&self, file_id: FileId) -> Option<&Path> {
        let path = self.vfs.read().file2path(VfsFile(file_id.0));
        self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path))
    }
}
<|endoftext|>


<commit_before>//! Shadowsocks service context

use std::{io, net::SocketAddr, sync::Arc};

use byte_string::ByteStr;
use log::warn;

use crate::{
    config::{ReplayAttackPolicy, ServerType},
    crypto::{v1::random_iv_or_salt, CipherKind},
    dns_resolver::DnsResolver,
    security::replay::ReplayProtector,
};

/// Service context
pub struct Context {
    // Protector against replay attack
    // The actual replay detection behavior is implemented in ReplayProtector
    replay_protector: ReplayProtector,
    // Policy against replay attack
    replay_policy: ReplayAttackPolicy,

    // trust-dns resolver, which supports REAL asynchronous resolving, and also customizable
    dns_resolver: Arc<DnsResolver>,

    // Connect IPv6 address first
    ipv6_first: bool,
}

/// `Context` for sharing between services
pub type SharedContext = Arc<Context>;

impl Context {
    /// Create a new `Context` for `Client` or `Server`
    pub fn new(config_type: ServerType) -> Context {
        Context {
            replay_protector: ReplayProtector::new(config_type),
            replay_policy: ReplayAttackPolicy::Default,
            dns_resolver: Arc::new(DnsResolver::system_resolver()),
            ipv6_first: false,
        }
    }

    /// Create a new `Context` shared
    pub fn new_shared(config_type: ServerType) -> SharedContext {
        SharedContext::new(Context::new(config_type))
    }

    /// Check if nonce exist or not
    ///
    /// If not, set into the current bloom filter
    #[inline(always)]
    fn check_nonce_and_set(&self, method: CipherKind, nonce: &[u8]) -> bool {
        match self.replay_policy {
            ReplayAttackPolicy::Ignore => false,
            _ => self.replay_protector.check_nonce_and_set(method, nonce),
        }
    }

    /// Generate nonce (IV or SALT)
    pub fn generate_nonce(&self, method: CipherKind, nonce: &mut [u8], unique: bool) {
        if nonce.is_empty() {
            return;
        }

        loop {
            random_iv_or_salt(nonce);

            // SECURITY: First 6 bytes of payload should be printable characters
            // Observation shows that prepending 6 bytes of printable characters to random payload will exempt it from blocking.
            // by 2022-01-13 gfw.report et al.
            #[cfg(feature = "security-iv-printable-prefix")]
            {
                const SECURITY_PRINTABLE_PREFIX_LEN: usize = 6;
                if nonce.len() >= SECURITY_PRINTABLE_PREFIX_LEN {
                    use rand::Rng;

                    // Printable characters follows definition of isprint in C/C++
                    static ASCII_PRINTABLE_CHARS: &[u8] = br##"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ "##;

                    let mut rng = rand::thread_rng();
                    for b in nonce.iter_mut().take(SECURITY_PRINTABLE_PREFIX_LEN) {
                        *b = ASCII_PRINTABLE_CHARS[rng.gen_range::<usize, _>(0..ASCII_PRINTABLE_CHARS.len())];
                    }
                }
            }

            // Salt already exists, generate a new one.
            if unique && self.check_nonce_and_set(method, nonce) {
                continue;
            }

            break;
        }
    }

    /// Check nonce replay
    pub fn check_nonce_replay(&self, method: CipherKind, nonce: &[u8]) -> io::Result<()> {
        if nonce.is_empty() {
            return Ok(());
        }

        #[allow(unused_mut)]
        let mut replay_policy = self.replay_policy;

        #[cfg(feature = "aead-cipher-2022")]
        if method.is_aead_2022() {
            // AEAD-2022 can't be ignored.
            replay_policy = ReplayAttackPolicy::Reject;
        }

        match replay_policy {
            ReplayAttackPolicy::Default | ReplayAttackPolicy::Ignore => Ok(()),
            ReplayAttackPolicy::Detect => {
                if self.replay_protector.check_nonce_and_set(method, nonce) {
                    warn!("detected repeated nonce (iv/salt) {:?}", ByteStr::new(nonce));
                }
                Ok(())
            }
            ReplayAttackPolicy::Reject => {
                if self.replay_protector.check_nonce_and_set(method, nonce) {
                    let err = io::Error::new(io::ErrorKind::Other, "detected repeated nonce (iv/salt)");
                    Err(err)
                } else {
                    Ok(())
                }
            }
        }
    }

    /// Set a DNS resolver
    ///
    /// The resolver should be wrapped in an `Arc`, because it could be shared with the other servers
    pub fn set_dns_resolver(&mut self, resolver: Arc<DnsResolver>) {
        self.dns_resolver = resolver;
    }

    /// Get the DNS resolver
    pub fn dns_resolver(&self) -> &Arc<DnsResolver> {
        &self.dns_resolver
    }

    /// Resolves DNS address to `SocketAddr`s
    #[allow(clippy::needless_lifetimes)]
    pub async fn dns_resolve<'a>(&self, addr: &'a str, port: u16) -> io::Result<impl Iterator<Item = SocketAddr> + 'a> {
        self.dns_resolver.resolve(addr, port).await
    }

    /// Try to connect IPv6 addresses first if hostname could be resolved to both IPv4 and IPv6
    pub fn set_ipv6_first(&mut self, ipv6_first: bool) {
        self.ipv6_first = ipv6_first;
    }

    /// Try to connect IPv6 addresses first if hostname could be resolved to both IPv4 and IPv6
    pub fn ipv6_first(&self) -> bool {
        self.ipv6_first
    }

    /// Set policy against replay attack
    pub fn set_replay_attack_policy(&mut self, replay_policy: ReplayAttackPolicy) {
        self.replay_policy = replay_policy;
    }

    /// Get policy against replay attach
    pub fn replay_attack_policy(&self) -> ReplayAttackPolicy {
        self.replay_policy
    }
}
<commit_msg>update: remove rnd from security-iv-printable-prefix feature (#844)<commit_after>//! Shadowsocks service context

use std::{io, net::SocketAddr, sync::Arc};

use byte_string::ByteStr;
use log::warn;

use crate::{
    config::{ReplayAttackPolicy, ServerType},
    crypto::{v1::random_iv_or_salt, CipherKind},
    dns_resolver::DnsResolver,
    security::replay::ReplayProtector,
};

/// Service context
pub struct Context {
    // Protector against replay attack
    // The actual replay detection behavior is implemented in ReplayProtector
    replay_protector: ReplayProtector,
    // Policy against replay attack
    replay_policy: ReplayAttackPolicy,

    // trust-dns resolver, which supports REAL asynchronous resolving, and also customizable
    dns_resolver: Arc<DnsResolver>,

    // Connect IPv6 address first
    ipv6_first: bool,
}

/// `Context` for sharing between services
pub type SharedContext = Arc<Context>;

impl Context {
    /// Create a new `Context` for `Client` or `Server`
    pub fn new(config_type: ServerType) -> Context {
        Context {
            replay_protector: ReplayProtector::new(config_type),
            replay_policy: ReplayAttackPolicy::Default,
            dns_resolver: Arc::new(DnsResolver::system_resolver()),
            ipv6_first: false,
        }
    }

    /// Create a new `Context` shared
    pub fn new_shared(config_type: ServerType) -> SharedContext {
        SharedContext::new(Context::new(config_type))
    }

    /// Check if nonce exist or not
    ///
    /// If not, set into the current bloom filter
    #[inline(always)]
    fn check_nonce_and_set(&self, method: CipherKind, nonce: &[u8]) -> bool {
        match self.replay_policy {
            ReplayAttackPolicy::Ignore => false,
            _ => self.replay_protector.check_nonce_and_set(method, nonce),
        }
    }

    /// Generate nonce (IV or SALT)
    pub fn generate_nonce(&self, method: CipherKind, nonce: &mut [u8], unique: bool) {
        if nonce.is_empty() {
            return;
        }

        loop {
            random_iv_or_salt(nonce);

            // SECURITY: First 6 bytes of payload should be printable characters
            // Observation shows that prepending 6 bytes of printable characters to random payload will exempt it from blocking.
            // by 2022-01-13 gfw.report et al.
            #[cfg(feature = "security-iv-printable-prefix")]
            {
                const SECURITY_PRINTABLE_PREFIX_LEN: usize = 6;
                if nonce.len() >= SECURITY_PRINTABLE_PREFIX_LEN {
                    // Printable characters use base64 letters instead
                    static ASCII_PRINTABLE_CHARS: &[u8] = br##"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"##;

                    for b in nonce.iter_mut().take(SECURITY_PRINTABLE_PREFIX_LEN) {
                        *b = ASCII_PRINTABLE_CHARS[(*b as usize) % ASCII_PRINTABLE_CHARS.len()];
                    }
                }
            }

            // Salt already exists, generate a new one.
            if unique && self.check_nonce_and_set(method, nonce) {
                continue;
            }

            break;
        }
    }

    /// Check nonce replay
    pub fn check_nonce_replay(&self, method: CipherKind, nonce: &[u8]) -> io::Result<()> {
        if nonce.is_empty() {
            return Ok(());
        }

        #[allow(unused_mut)]
        let mut replay_policy = self.replay_policy;

        #[cfg(feature = "aead-cipher-2022")]
        if method.is_aead_2022() {
            // AEAD-2022 can't be ignored.
            replay_policy = ReplayAttackPolicy::Reject;
        }

        match replay_policy {
            ReplayAttackPolicy::Default | ReplayAttackPolicy::Ignore => Ok(()),
            ReplayAttackPolicy::Detect => {
                if self.replay_protector.check_nonce_and_set(method, nonce) {
                    warn!("detected repeated nonce (iv/salt) {:?}", ByteStr::new(nonce));
                }
                Ok(())
            }
            ReplayAttackPolicy::Reject => {
                if self.replay_protector.check_nonce_and_set(method, nonce) {
                    let err = io::Error::new(io::ErrorKind::Other, "detected repeated nonce (iv/salt)");
                    Err(err)
                } else {
                    Ok(())
                }
            }
        }
    }

    /// Set a DNS resolver
    ///
    /// The resolver should be wrapped in an `Arc`, because it could be shared with the other servers
    pub fn set_dns_resolver(&mut self, resolver: Arc<DnsResolver>) {
        self.dns_resolver = resolver;
    }

    /// Get the DNS resolver
    pub fn dns_resolver(&self) -> &Arc<DnsResolver> {
        &self.dns_resolver
    }

    /// Resolves DNS address to `SocketAddr`s
    #[allow(clippy::needless_lifetimes)]
    pub async fn dns_resolve<'a>(&self, addr: &'a str, port: u16) -> io::Result<impl Iterator<Item = SocketAddr> + 'a> {
        self.dns_resolver.resolve(addr, port).await
    }

    /// Try to connect IPv6 addresses first if hostname could be resolved to both IPv4 and IPv6
    pub fn set_ipv6_first(&mut self, ipv6_first: bool) {
        self.ipv6_first = ipv6_first;
    }

    /// Try to connect IPv6 addresses first if hostname could be resolved to both IPv4 and IPv6
    pub fn ipv6_first(&self) -> bool {
        self.ipv6_first
    }

    /// Set policy against replay attack
    pub fn set_replay_attack_policy(&mut self, replay_policy: ReplayAttackPolicy) {
        self.replay_policy = replay_policy;
    }

    /// Get policy against replay attach
    pub fn replay_attack_policy(&self) -> ReplayAttackPolicy {
        self.replay_policy
    }
}

#[cfg(test)]
mod tests {
    use crate::config::ServerType;
    use crate::context::Context;
    use byte_string::ByteStr;
    use shadowsocks_crypto::CipherKind;

    #[test]
    fn generate_nonce() {
        let mut salt = vec![0u8; 64];
        let context = Context::new(ServerType::Server);
        context.generate_nonce(CipherKind::AES_128_GCM, &mut salt, false);
        println!("generate nonce printable ascii: {:?}", ByteStr::new(&salt));
    }

}
<|endoftext|>


<commit_before>// Implements the `zakopane checksum` subcommand.

use std::io::Read;
use std::io::Write;

use crate::structs::ChecksumCliOptions;
use crate::structs::ZakopaneError;

const READ_SIZE: usize = 2 << 20;

struct ChecksumWithPath {
    checksum: String,
    path: std::path::PathBuf,
}

impl ChecksumWithPath {
    pub fn new(checksum: String, path: std::path::PathBuf) -> Self {
        Self { checksum, path }
    }
}

type ChecksumResult = Result<ChecksumWithPath, ZakopaneError>;
type ChecksumTaskJoinHandle = tokio::task::JoinHandle<Vec<ChecksumWithPath>>;

struct ChecksumTaskDispatcherData {
    // Rate-limiter for spawned checksum tasks.
    semaphore: std::sync::Arc<tokio::sync::Semaphore>,

    cli_options: ChecksumCliOptions,

    // Counts total number of spawned checksum tasks.
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,

    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
}

impl ChecksumTaskDispatcherData {
    pub fn new(
        cli_options: ChecksumCliOptions,
        spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
        sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    ) -> Self {
        Self {
            semaphore: std::sync::Arc::new(tokio::sync::Semaphore::new(cli_options.max_tasks)),
            cli_options,
            spawn_counter,
            sender,
        }
    }
}

// Sends a checksum task result downstream to the collector task.
fn checksum_task_send_result(
    result: ChecksumResult,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
) {
    if let Err(e) = sender.blocking_send(result) {
        eprintln!("BUG: Sender::send() failed: {}", e);
    }
}

fn checksum_task_impl(path: std::path::PathBuf) -> ChecksumResult {
    let mut hasher = crypto_hash::Hasher::new(crypto_hash::Algorithm::SHA256);
    let mut buffer: Vec<u8> = vec![0; READ_SIZE];
    let mut file = std::fs::File::open(&path).map_err(ZakopaneError::Io)?;
    loop {
        let read_bytes = file.read(&mut buffer).map_err(ZakopaneError::Io)?;
        if read_bytes == 0 {
            let checksum = hasher
                .finish()
                .into_iter()
                .map(|byte| format!("{:02x}", byte))
                .collect::<Vec<String>>()
                .join("");
            return Ok(ChecksumWithPath::new(checksum, path));
        }
        hasher
            .write_all(&buffer[..read_bytes])
            .map_err(ZakopaneError::Io)?;
    }
}

// Represents a spawned checksum task.
//
// `_permit` is moved into this checksum task simply to hold onto the
// semaphore-dispensed resource for the duration of this task.
fn checksum_task(
    path: std::path::PathBuf,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    _permit: tokio::sync::OwnedSemaphorePermit,
) {
    let result = checksum_task_impl(path);
    checksum_task_send_result(result, sender);
}

// Represents the spawned collection task.
async fn collector_task(
    mut receiver: tokio::sync::mpsc::Receiver<ChecksumResult>,
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
) -> Vec<ChecksumWithPath> {
    let mut sums: Vec<ChecksumWithPath> = Vec::new();
    let mut errors: usize = 0;
    loop {
        while let Some(result) = receiver.recv().await {
            match result {
                Ok(digest_with_path) => sums.push(digest_with_path),
                Err(_) => errors += 1,
            }
        }
        if sums.len() + errors == spawn_counter.load(std::sync::atomic::Ordering::SeqCst) {
            break;
        }
    }
    if errors != 0 {
        eprintln!("WARNING: {} errors collected", errors);
    }
    sums
}

// Spawns the collector task that listens for checksum results
// provided by the checksum tasks.
async fn spawn_collector(
    options: ChecksumCliOptions,
) -> (ChecksumTaskDispatcherData, ChecksumTaskJoinHandle) {
    let (sender, receiver) = tokio::sync::mpsc::channel(options.max_tasks);
    let spawn_counter = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let join_handle = tokio::task::spawn(collector_task(receiver, spawn_counter.clone()));
    (
        ChecksumTaskDispatcherData::new(options, spawn_counter, sender),
        join_handle,
    )
}

async fn spawn_checksum_tasks(context: ChecksumTaskDispatcherData) {
    let walk_iter = walkdir::WalkDir::new(&context.cli_options.path).into_iter();
    // The `filter_entry()` call is crafted s.t.
    // *    we skip and don't descend into hidden directories
    // *    unless the hidden directory is the target directory, because
    //      the target directory is always the first yielded value from
    //      the `WalkDir`.
    for entry in walk_iter.filter_entry(|e| {
        e.path() == &context.cli_options.path
            || !e
                .file_name()
                .to_str()
                .map(|path| path.starts_with("."))
                .unwrap_or(false)
    }) {
        if let Ok(direntry) = entry {
            let path = direntry.into_path();
            if let Ok(metadata) = std::fs::symlink_metadata(&path) {
                if !metadata.file_type().is_file() {
                    continue;
                }
            } else {
                continue;
            }

            context
                .spawn_counter
                .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
            let permit = context.semaphore.clone().acquire_owned().await.unwrap();
            let sender = context.sender.clone();
            tokio::task::spawn_blocking(move || checksum_task(path, sender, permit));
        }
    }
}

// Pretty-prints the sorted `checksums` in a format much like what the
// `sha256sum` binary outputs.
//
// Note that the standard zakopane snapshot header is not added here.
fn pretty_format_checksums(path: std::path::PathBuf, checksums: Vec<ChecksumWithPath>) -> String {
    let mut buffer: Vec<String> = checksums
        .into_iter()
        .map(|e| {
            format!(
                "{}  ./{}",
                e.checksum,
                e.path.strip_prefix(&path).unwrap().to_str().unwrap()
            )
        })
        .collect();
    buffer.push(String::new());
    buffer.join("\n")
}

async fn checksum_impl(options: ChecksumCliOptions) -> String {
    let path = options.path.clone();
    let (dispatcher_data, join_handle) = spawn_collector(options).await;
    spawn_checksum_tasks(dispatcher_data).await;
    let mut checksums: Vec<ChecksumWithPath> = join_handle.await.unwrap();
    checksums.sort_by(|a, b| a.path.cmp(&b.path));
    pretty_format_checksums(path, checksums)
}

pub fn checksum(options: ChecksumCliOptions) -> String {
    let runtime = tokio::runtime::Runtime::new().unwrap();
    runtime.block_on(checksum_impl(options))
}
<commit_msg>checksum: Implement weighted semaphore hogging<commit_after>// Implements the `zakopane checksum` subcommand.

use std::convert::TryInto;
use std::io::Read;
use std::io::Write;

use crate::structs::ChecksumCliOptions;
use crate::structs::ZakopaneError;

const READ_SIZE: usize = 2 << 20;
const BIG_FILE_BYTES: usize = 2 << 27;

struct ChecksumWithPath {
    checksum: String,
    path: std::path::PathBuf,
}

impl ChecksumWithPath {
    pub fn new(checksum: String, path: std::path::PathBuf) -> Self {
        Self { checksum, path }
    }
}

type ChecksumResult = Result<ChecksumWithPath, ZakopaneError>;
type ChecksumTaskJoinHandle = tokio::task::JoinHandle<Vec<ChecksumWithPath>>;

struct ChecksumTaskDispatcherData {
    // Rate-limiter for spawned checksum tasks.
    semaphore: std::sync::Arc<tokio::sync::Semaphore>,

    cli_options: ChecksumCliOptions,

    // Counts total number of spawned checksum tasks.
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,

    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
}

impl ChecksumTaskDispatcherData {
    pub fn new(
        cli_options: ChecksumCliOptions,
        spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
        sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    ) -> Self {
        Self {
            semaphore: std::sync::Arc::new(tokio::sync::Semaphore::new(cli_options.max_tasks)),
            cli_options,
            spawn_counter,
            sender,
        }
    }
}

struct FileDetails {
    is_file: bool,
    is_big: bool,
}

fn get_file_details(path: &std::path::PathBuf) -> FileDetails {
    let metadata = match std::fs::symlink_metadata(path) {
        Ok(m) => m,
        Err(_) => {
            return FileDetails {
                is_file: false,
                is_big: false,
            }
        }
    };

    if !metadata.file_type().is_file() {
        return FileDetails {
            is_file: false,
            is_big: false,
        };
    }
    if metadata.len() > BIG_FILE_BYTES.try_into().unwrap() {
        return FileDetails {
            is_file: true,
            is_big: true,
        };
    }
    FileDetails {
        is_file: true,
        is_big: false,
    }
}

// Sends a checksum task result downstream to the collector task.
fn checksum_task_send_result(
    result: ChecksumResult,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
) {
    if let Err(e) = sender.blocking_send(result) {
        eprintln!("BUG: Sender::send() failed: {}", e);
    }
}

fn checksum_task_impl(path: std::path::PathBuf) -> ChecksumResult {
    let mut hasher = crypto_hash::Hasher::new(crypto_hash::Algorithm::SHA256);
    let mut buffer: Vec<u8> = vec![0; READ_SIZE];
    let mut file = std::fs::File::open(&path).map_err(ZakopaneError::Io)?;
    loop {
        let read_bytes = file.read(&mut buffer).map_err(ZakopaneError::Io)?;
        if read_bytes == 0 {
            let checksum = hasher
                .finish()
                .into_iter()
                .map(|byte| format!("{:02x}", byte))
                .collect::<Vec<String>>()
                .join("");
            return Ok(ChecksumWithPath::new(checksum, path));
        }
        hasher
            .write_all(&buffer[..read_bytes])
            .map_err(ZakopaneError::Io)?;
    }
}

// Represents a spawned checksum task.
//
// `_permit` is moved into this checksum task simply to hold onto the
// semaphore-dispensed resource for the duration of this task.
fn checksum_task(
    path: std::path::PathBuf,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    _permit: tokio::sync::OwnedSemaphorePermit,
) {
    let result = checksum_task_impl(path);
    checksum_task_send_result(result, sender);
}

// Represents the spawned collection task.
async fn collector_task(
    mut receiver: tokio::sync::mpsc::Receiver<ChecksumResult>,
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
) -> Vec<ChecksumWithPath> {
    let mut sums: Vec<ChecksumWithPath> = Vec::new();
    let mut errors: usize = 0;
    loop {
        while let Some(result) = receiver.recv().await {
            match result {
                Ok(digest_with_path) => sums.push(digest_with_path),
                Err(_) => errors += 1,
            }
        }
        if sums.len() + errors == spawn_counter.load(std::sync::atomic::Ordering::SeqCst) {
            break;
        }
    }
    if errors != 0 {
        eprintln!("WARNING: {} errors collected", errors);
    }
    sums
}

// Spawns the collector task that listens for checksum results
// provided by the checksum tasks.
async fn spawn_collector(
    options: ChecksumCliOptions,
) -> (ChecksumTaskDispatcherData, ChecksumTaskJoinHandle) {
    let (sender, receiver) = tokio::sync::mpsc::channel(options.max_tasks);
    let spawn_counter = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let join_handle = tokio::task::spawn(collector_task(receiver, spawn_counter.clone()));
    (
        ChecksumTaskDispatcherData::new(options, spawn_counter, sender),
        join_handle,
    )
}

async fn spawn_checksum_tasks(context: ChecksumTaskDispatcherData) {
    let walk_iter = walkdir::WalkDir::new(&context.cli_options.path).into_iter();
    // The `filter_entry()` call is crafted s.t.
    // *    we skip and don't descend into hidden directories
    // *    unless the hidden directory is the target directory, because
    //      the target directory is always the first yielded value from
    //      the `WalkDir`.
    for entry in walk_iter.filter_entry(|e| {
        e.path() == &context.cli_options.path
            || !e
                .file_name()
                .to_str()
                .map(|path| path.starts_with("."))
                .unwrap_or(false)
    }) {
        if let Ok(direntry) = entry {
            let path = direntry.into_path();
            let file_details = get_file_details(&path);
            if !file_details.is_file {
                continue;
            }

            // If the file is "big" (arbitrarily defined), attempt to
            // seize all permits to force us momentarily to work with a
            // bit less I/O contention.
            let permit = if file_details.is_big {
                context.semaphore.clone().acquire_owned().await.unwrap()
            } else {
                context
                    .semaphore
                    .clone()
                    .acquire_many_owned(context.cli_options.max_tasks.try_into().unwrap())
                    .await
                    .unwrap()
            };

            context
                .spawn_counter
                .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
            let sender = context.sender.clone();
            tokio::task::spawn_blocking(move || checksum_task(path, sender, permit));
        }
    }
}

// Pretty-prints the sorted `checksums` in a format much like what the
// `sha256sum` binary outputs.
//
// Note that the standard zakopane snapshot header is not added here.
fn pretty_format_checksums(path: std::path::PathBuf, checksums: Vec<ChecksumWithPath>) -> String {
    let mut buffer: Vec<String> = checksums
        .into_iter()
        .map(|e| {
            format!(
                "{}  ./{}",
                e.checksum,
                e.path.strip_prefix(&path).unwrap().to_str().unwrap()
            )
        })
        .collect();
    buffer.push(String::new());
    buffer.join("\n")
}

async fn checksum_impl(options: ChecksumCliOptions) -> String {
    let path = options.path.clone();
    let (dispatcher_data, join_handle) = spawn_collector(options).await;
    spawn_checksum_tasks(dispatcher_data).await;
    let mut checksums: Vec<ChecksumWithPath> = join_handle.await.unwrap();
    checksums.sort_by(|a, b| a.path.cmp(&b.path));
    pretty_format_checksums(path, checksums)
}

pub fn checksum(options: ChecksumCliOptions) -> String {
    let runtime = tokio::runtime::Runtime::new().unwrap();
    runtime.block_on(checksum_impl(options))
}
<|endoftext|>


<commit_before>// External modules:
use darwin_rs::{Individual, SimulationBuilder, Population, PopulationBuilder};

// Internal modules:
use config::PanolutionConfig;

pub struct ImageArrangement {
    file_name: String,
    pos_x: u64,
    pos_y: u64,
    rotation: f64,
}

pub fn optimize(arrangement: Option<&Vec<ImageArrangement>>, config: &PanolutionConfig, thumbnail_path: &Vec<String>) -> Vec<ImageArrangement> {
    let mut result = Vec::new();

    if let Some(arrangement) = arrangement {
        result = arrangement.iter().zip(thumbnail_path).map(|(arrangement, path)| ImageArrangement {
                file_name: path.clone(),
                pos_x: arrangement.pos_x,
                pos_y: arrangement.pos_y,
                rotation: arrangement.rotation,
            }
        ).collect();
    } else {
        result = thumbnail_path.iter().map(|path| ImageArrangement {
                file_name: path.clone(),
                pos_x: 0,
                pos_y: 0,
                rotation: 0.0
            }
        ).collect();
    }

    result
}
<commit_msg>adds solution<commit_after>// External modules:
use darwin_rs::{Individual, SimulationBuilder, Population, PopulationBuilder};
use image;
use image::{GenericImage, FilterType};

// Internal modules:
use config::PanolutionConfig;

pub struct ImageArrangement {
    file_name: String,
    pos_x: u64,
    pos_y: u64,
    rotation: f64,
}

pub struct Solution {
    arrangement: Vec<ImageArrangement>,
}

fn run_darwin(arrangement: Vec<ImageArrangement>, max_iteration: u64) -> Vec<ImageArrangement> {
    arrangement
}

pub fn optimize(arrangement: Option<&Vec<ImageArrangement>>, config: &PanolutionConfig, thumbnail_path: &Vec<String>) -> Vec<ImageArrangement> {
    let mut result = Vec::new();

    // Prepare data
    if let Some(arrangement) = arrangement {
        result = arrangement.iter().zip(thumbnail_path).map(|(arrangement, path)| ImageArrangement {
                file_name: path.clone(),
                pos_x: arrangement.pos_x,
                pos_y: arrangement.pos_y,
                rotation: arrangement.rotation,
            }
        ).collect();
    } else {
        result = thumbnail_path.iter().map(|path| ImageArrangement {
                file_name: path.clone(),
                pos_x: 0,
                pos_y: 0,
                rotation: 0.0
            }
        ).collect();
    }

    run_darwin(result, config.max_iteration)
}
<|endoftext|>


<commit_before>extern crate bip_dht;
extern crate bip_handshake;
extern crate bip_util;
extern crate log;

use std::collections::{HashSet};
use std::io::{self, Read};
use std::net::{SocketAddr};
use std::thread::{self};

use bip_dht::{DhtBuilder, Router};
use bip_handshake::{Handshaker};
use bip_util::bt::{InfoHash, PeerId};

use log::{LogRecord, LogLevel, LogMetadata, LogLevelFilter};

struct SimpleLogger;

impl log::Log for SimpleLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            println!("{} - {}", record.level(), record.args());
        }
    }
}

struct SimpleHandshaker {
    filter: HashSet<SocketAddr>,
    count: usize
}

impl Handshaker for SimpleHandshaker {
    /// Type of stream used to receive connections from.
    type Stream = ();

    /// Unique peer id used to identify ourselves to other peers.
    fn id(&self) -> PeerId {
        [0u8; 20].into()
    }

    /// Advertise port that is being listened on by the handshaker.
    ///
    /// It is important that this is the external port that the peer will be sending data
    /// to. This is relevant if the client employs nat traversal via upnp or other means.
    fn port(&self) -> u16 {
        6889
    }

    /// Initiates a handshake with the given socket address.
    fn connect(&mut self, _: Option<PeerId>, _: InfoHash, addr: SocketAddr) {
        if self.filter.contains(&addr) {
            return
        }

        self.filter.insert(addr);
        self.count += 1;
        println!("Received new peer {:?}, total unique peers {}", addr, self.count);
    }

    /// Adds a filter that is applied to handshakes before they are initiated or completed.
    fn filter<F>(&mut self, _: Box<F>) where F: Fn(SocketAddr) -> bool + Send {
        ()
    }

    /// Stream that connections for the specified hash are sent to after they are successful.
    ///
    /// Connections MAY be dropped if all streams for a given hash are destroyed.
    fn stream(&self, _: InfoHash) -> () {
        ()
    }
}

fn main() {
    log::set_logger(|m| {
        m.set(LogLevelFilter::max());
        Box::new(SimpleLogger)
    }).unwrap();
    let hash = InfoHash::from_bytes(b"My Unique Info Hash");

    let handshaker = SimpleHandshaker{ filter: HashSet::new(), count: 0 };
    let dht = DhtBuilder::with_router(Router::Transmission)
    .set_read_only(false).start_mainline(handshaker).unwrap();

    // Spawn a thread to listen to and report events
    let events = dht.events();
    thread::spawn(move || {
        for event in events {
            println!("\nReceived Dht Event {:?}", event);
        }
    });

    // Let the user announce or search on our info hash
    let stdin = io::stdin();
    let stdin_lock = stdin.lock();
    for byte in stdin_lock.bytes() {
        match &[byte.unwrap()] {
            b"a" => dht.search(hash.into(), true),
            b"s" => dht.search(hash.into(), false),
            _   => ()
        }
    }
}<commit_msg>Update debug.rs<commit_after>extern crate bip_dht;
extern crate bip_handshake;
extern crate bip_util;
extern crate log;

use std::collections::{HashSet};
use std::io::{self, Read};
use std::net::{SocketAddr, Ipv4Addr, SocketAddrV4, ToSocketAddrs};
use std::thread::{self};

use bip_dht::{DhtBuilder, Router};
use bip_handshake::{Handshaker};
use bip_util::bt::{InfoHash, PeerId};

use log::{LogRecord, LogLevel, LogMetadata, LogLevelFilter};

struct SimpleLogger;

impl log::Log for SimpleLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            println!("{} - {}", record.level(), record.args());
        }
    }
}

struct SimpleHandshaker {
    filter: HashSet<SocketAddr>,
    count: usize
}

impl Handshaker for SimpleHandshaker {
    /// Type of stream used to receive connections from.
    type Stream = ();

    /// Unique peer id used to identify ourselves to other peers.
    fn id(&self) -> PeerId {
        [0u8; 20].into()
    }

    /// Advertise port that is being listened on by the handshaker.
    ///
    /// It is important that this is the external port that the peer will be sending data
    /// to. This is relevant if the client employs nat traversal via upnp or other means.
    fn port(&self) -> u16 {
        6889
    }

    /// Initiates a handshake with the given socket address.
    fn connect(&mut self, _: Option<PeerId>, _: InfoHash, addr: SocketAddr) {
        if self.filter.contains(&addr) {
            return
        }

        self.filter.insert(addr);
        self.count += 1;
        println!("Received new peer {:?}, total unique peers {}", addr, self.count);
    }

    /// Adds a filter that is applied to handshakes before they are initiated or completed.
    fn filter<F>(&mut self, _: Box<F>) where F: Fn(&SocketAddr) -> bool + Send {
        ()
    }

    /// Stream that connections for the specified hash are sent to after they are successful.
    ///
    /// Connections MAY be dropped if all streams for a given hash are destroyed.
    fn stream(&self, _: InfoHash) -> () {
        ()
    }
}

fn main() {
    log::set_logger(|m| {
        m.set(LogLevelFilter::max());
        Box::new(SimpleLogger)
    }).unwrap();
    let hash = InfoHash::from_bytes(b"My Unique Info Hash");

    let handshaker = SimpleHandshaker{ filter: HashSet::new(), count: 0 };
    let router = "46.101.197.175:443".to_socket_addrs().unwrap().next().unwrap();
    let dht = DhtBuilder::with_router(Router::Custom(router)).set_source_addr(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 6889)))
    .set_read_only(false).start_mainline(handshaker).unwrap();

    // Spawn a thread to listen to and report events
    let events = dht.events();
    thread::spawn(move || {
        for event in events {
            println!("\nReceived Dht Event {:?}", event);
        }
    });

    // Let the user announce or search on our info hash
    let stdin = io::stdin();
    let stdin_lock = stdin.lock();
    for byte in stdin_lock.bytes() {
        match &[byte.unwrap()] {
            b"a" => dht.search(hash.into(), true),
            b"s" => dht.search(hash.into(), false),
            _   => ()
        }
    }
}<|endoftext|>


<commit_before>extern crate pancurses;

use backend;
use event::{Event, Key};

use self::super::find_closest;
use std::cell::Cell;
use theme::{Color, ColorStyle, Effect};
use utf8;

pub struct Concrete {
    window: pancurses::Window,
    current_style: Cell<ColorStyle>,
}

impl backend::Backend for Concrete {
    fn init() -> Self {
        ::std::env::set_var("ESCDELAY", "25");
        let window = pancurses::initscr();
        window.keypad(true);
        pancurses::noecho();
        pancurses::cbreak();
        pancurses::start_color();
        pancurses::curs_set(0);
        window.bkgd(pancurses::COLOR_PAIR(ColorStyle::Background.id() as u64));

        Concrete {
            window: window,
            current_style: Cell::new(ColorStyle::Background),
        }
    }

    fn screen_size(&self) -> (usize, usize) {
        let (y, x) = self.window.get_max_yx();
        (x as usize, y as usize)
    }

    fn has_colors(&self) -> bool {
        pancurses::has_colors()
    }

    fn finish(&mut self) {
        pancurses::endwin();
    }

    fn init_color_style(&mut self, style: ColorStyle, foreground: &Color,
                        background: &Color) {
        pancurses::init_pair(style.id(),
                             find_closest(foreground) as i16,
                             find_closest(background) as i16);
    }

    fn with_color<F: FnOnce()>(&self, color: ColorStyle, f: F) {
        // TODO: pancurses doesn't have an `attr_get` equivalent
        // let mut current_style: pancurses::attr_t = 0;
        // let mut current_color: i16 = 0;
        // pancurses::attr_get(&mut current_style, &mut current_color);
        let current_style = self.current_style.get();

        let style = pancurses::COLOR_PAIR(color.id() as u64);
        self.window.attron(style);

        self.current_style.set(color);
        f();
        self.current_style.set(current_style);

        // self.window.attroff(style);
        self.window.attron(pancurses::COLOR_PAIR(current_style.id() as u64));
    }

    fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
        let style = match effect {
            // A_REVERSE, taken from ncurses
            Effect::Reverse => 1 << (10 + 8u32),
            Effect::Simple => pancurses::A_NORMAL,
        };
        self.window.attron(style);
        f();
        self.window.attroff(style);
    }

    fn clear(&self) {
        self.window.clear();
    }

    fn refresh(&mut self) {
        self.window.refresh();
    }

    fn print_at(&self, (x, y): (usize, usize), text: &str) {
        self.window.mvaddstr(y as i32, x as i32, text);
    }

    fn poll_event(&self) -> Event {
        // TODO: there seems to not be any indication
        // of Ctrl/Alt/Shift in these :v
        if let Some(ev) = self.window.getch() {
            match ev {
                pancurses::Input::Character('\n') => Event::Key(Key::Enter),
                // TODO: wait for a very short delay. If more keys are
                // pipelined, it may be an escape sequence.
                pancurses::Input::Character('\u{1b}') => Event::Key(Key::Esc),
                pancurses::Input::Character('\u{7f}') => {
                    Event::Key(Key::Backspace)
                }
                pancurses::Input::Character(c) if 32 <= (c as u32) &&
                                                  (c as u32) <= 255 => {
                    Event::Char(utf8::read_char(c as u8, || {
                            self.window.getch().and_then(|i| match i {
                                pancurses::Input::Character(c) => {
                                    Some(c as u8)
                                }
                                _ => None,
                            })
                        })
                        .unwrap())
                }
                pancurses::Input::Character(c) => Event::Unknown(c as i32),
                // TODO: Some key combos are not recognized by pancurses,
                // but are sent as Unknown. We could still parse them here.
                pancurses::Input::Unknown(i) => Event::Unknown(i),
                // TODO: I honestly have no fucking idea what KeyCodeYes is
                pancurses::Input::KeyCodeYes => Event::Refresh,
                pancurses::Input::KeyBreak => Event::Key(Key::PauseBreak),
                pancurses::Input::KeyDown => Event::Key(Key::Down),
                pancurses::Input::KeyUp => Event::Key(Key::Up),
                pancurses::Input::KeyLeft => Event::Key(Key::Left),
                pancurses::Input::KeyRight => Event::Key(Key::Right),
                pancurses::Input::KeyHome => Event::Key(Key::Home),
                pancurses::Input::KeyBackspace => Event::Key(Key::Backspace),
                pancurses::Input::KeyF0 => Event::Key(Key::F0),
                pancurses::Input::KeyF1 => Event::Key(Key::F1),
                pancurses::Input::KeyF2 => Event::Key(Key::F2),
                pancurses::Input::KeyF3 => Event::Key(Key::F3),
                pancurses::Input::KeyF4 => Event::Key(Key::F4),
                pancurses::Input::KeyF5 => Event::Key(Key::F5),
                pancurses::Input::KeyF6 => Event::Key(Key::F6),
                pancurses::Input::KeyF7 => Event::Key(Key::F7),
                pancurses::Input::KeyF8 => Event::Key(Key::F8),
                pancurses::Input::KeyF9 => Event::Key(Key::F9),
                pancurses::Input::KeyF10 => Event::Key(Key::F10),
                pancurses::Input::KeyF11 => Event::Key(Key::F11),
                pancurses::Input::KeyF12 => Event::Key(Key::F12),
                pancurses::Input::KeyF13 => Event::Shift(Key::F1),
                pancurses::Input::KeyF14 => Event::Shift(Key::F2),
                pancurses::Input::KeyF15 => Event::Shift(Key::F3),
                pancurses::Input::KeyDL => Event::Refresh,
                pancurses::Input::KeyIL => Event::Refresh,
                pancurses::Input::KeyDC => Event::Key(Key::Del),
                pancurses::Input::KeyIC => Event::Key(Key::Ins),
                pancurses::Input::KeyEIC => Event::Refresh,
                pancurses::Input::KeyClear => Event::Refresh,
                pancurses::Input::KeyEOS => Event::Refresh,
                pancurses::Input::KeyEOL => Event::Refresh,
                pancurses::Input::KeySF => Event::Shift(Key::Down),
                pancurses::Input::KeySR => Event::Shift(Key::Up),
                pancurses::Input::KeyNPage => Event::Key(Key::PageDown),
                pancurses::Input::KeyPPage => Event::Key(Key::PageUp),
                pancurses::Input::KeySTab => Event::Shift(Key::Tab),
                pancurses::Input::KeyCTab => Event::Ctrl(Key::Tab),
                pancurses::Input::KeyCATab => Event::CtrlAlt(Key::Tab),
                pancurses::Input::KeyEnter => Event::Key(Key::Enter),
                pancurses::Input::KeySReset => Event::Refresh,
                pancurses::Input::KeyReset => Event::Refresh,
                pancurses::Input::KeyPrint => Event::Refresh,
                pancurses::Input::KeyLL => Event::Refresh,
                pancurses::Input::KeyAbort => Event::Refresh,
                pancurses::Input::KeySHelp => Event::Refresh,
                pancurses::Input::KeyLHelp => Event::Refresh,
                pancurses::Input::KeyBTab => Event::Shift(Key::Tab),
                pancurses::Input::KeyBeg => Event::Refresh,
                pancurses::Input::KeyCancel => Event::Refresh,
                pancurses::Input::KeyClose => Event::Refresh,
                pancurses::Input::KeyCommand => Event::Refresh,
                pancurses::Input::KeyCopy => Event::Refresh,
                pancurses::Input::KeyCreate => Event::Refresh,
                pancurses::Input::KeyEnd => Event::Key(Key::End),
                pancurses::Input::KeyExit => Event::Refresh,
                pancurses::Input::KeyFind => Event::Refresh,
                pancurses::Input::KeyHelp => Event::Refresh,
                pancurses::Input::KeyMark => Event::Refresh,
                pancurses::Input::KeyMessage => Event::Refresh,
                pancurses::Input::KeyMove => Event::Refresh,
                pancurses::Input::KeyNext => Event::Refresh,
                pancurses::Input::KeyOpen => Event::Refresh,
                pancurses::Input::KeyOptions => Event::Refresh,
                pancurses::Input::KeyPrevious => Event::Refresh,
                pancurses::Input::KeyRedo => Event::Refresh,
                pancurses::Input::KeyReference => Event::Refresh,
                pancurses::Input::KeyRefresh => Event::Refresh,
                pancurses::Input::KeyReplace => Event::Refresh,
                pancurses::Input::KeyRestart => Event::Refresh,
                pancurses::Input::KeyResume => Event::Refresh,
                pancurses::Input::KeySave => Event::Refresh,
                pancurses::Input::KeySBeg => Event::Refresh,
                pancurses::Input::KeySCancel => Event::Refresh,
                pancurses::Input::KeySCommand => Event::Refresh,
                pancurses::Input::KeySCopy => Event::Refresh,
                pancurses::Input::KeySCreate => Event::Refresh,
                pancurses::Input::KeySDC => Event::Shift(Key::Del),
                pancurses::Input::KeySDL => Event::Refresh,
                pancurses::Input::KeySelect => Event::Refresh,
                pancurses::Input::KeySEnd => Event::Shift(Key::End),
                pancurses::Input::KeySEOL => Event::Refresh,
                pancurses::Input::KeySExit => Event::Refresh,
                pancurses::Input::KeySFind => Event::Refresh,
                pancurses::Input::KeySHome => Event::Shift(Key::Home),
                pancurses::Input::KeySIC => Event::Shift(Key::Ins),
                pancurses::Input::KeySLeft => Event::Shift(Key::Left),
                pancurses::Input::KeySMessage => Event::Refresh,
                pancurses::Input::KeySMove => Event::Refresh,
                pancurses::Input::KeySNext => Event::Shift(Key::PageDown),
                pancurses::Input::KeySOptions => Event::Refresh,
                pancurses::Input::KeySPrevious => Event::Shift(Key::PageUp),
                pancurses::Input::KeySPrint => Event::Refresh,
                pancurses::Input::KeySRedo => Event::Refresh,
                pancurses::Input::KeySReplace => Event::Refresh,
                pancurses::Input::KeySRight => Event::Shift(Key::Right),
                pancurses::Input::KeySResume => Event::Refresh,
                pancurses::Input::KeySSave => Event::Refresh,
                pancurses::Input::KeySSuspend => Event::Refresh,
                pancurses::Input::KeySUndo => Event::Refresh,
                pancurses::Input::KeySuspend => Event::Refresh,
                pancurses::Input::KeyUndo => Event::Refresh,
                pancurses::Input::KeyResize => Event::WindowResize,
                pancurses::Input::KeyEvent => Event::Refresh,
                // TODO: mouse support
                pancurses::Input::KeyMouse => Event::Refresh,
                pancurses::Input::KeyA1 => Event::Refresh,
                pancurses::Input::KeyA3 => Event::Refresh,
                pancurses::Input::KeyB2 => Event::Key(Key::NumpadCenter),
                pancurses::Input::KeyC1 => Event::Refresh,
                pancurses::Input::KeyC3 => Event::Refresh,
            }
        } else {
            Event::Refresh
        }
    }

    fn set_refresh_rate(&mut self, fps: u32) {
        if fps == 0 {
            self.window.timeout(-1);
        } else {
            self.window.timeout(1000 / fps as i32);
        }
    }
}
<commit_msg>Correctly handle backspace in pancurses backend on Windows<commit_after>extern crate pancurses;

use backend;
use event::{Event, Key};

use self::super::find_closest;
use std::cell::Cell;
use theme::{Color, ColorStyle, Effect};
use utf8;

pub struct Concrete {
    window: pancurses::Window,
    current_style: Cell<ColorStyle>,
}

impl backend::Backend for Concrete {
    fn init() -> Self {
        ::std::env::set_var("ESCDELAY", "25");
        let window = pancurses::initscr();
        window.keypad(true);
        pancurses::noecho();
        pancurses::cbreak();
        pancurses::start_color();
        pancurses::curs_set(0);
        window.bkgd(pancurses::COLOR_PAIR(ColorStyle::Background.id() as u64));

        Concrete {
            window: window,
            current_style: Cell::new(ColorStyle::Background),
        }
    }

    fn screen_size(&self) -> (usize, usize) {
        let (y, x) = self.window.get_max_yx();
        (x as usize, y as usize)
    }

    fn has_colors(&self) -> bool {
        pancurses::has_colors()
    }

    fn finish(&mut self) {
        pancurses::endwin();
    }

    fn init_color_style(&mut self, style: ColorStyle, foreground: &Color,
                        background: &Color) {
        pancurses::init_pair(style.id(),
                             find_closest(foreground) as i16,
                             find_closest(background) as i16);
    }

    fn with_color<F: FnOnce()>(&self, color: ColorStyle, f: F) {
        // TODO: pancurses doesn't have an `attr_get` equivalent
        // let mut current_style: pancurses::attr_t = 0;
        // let mut current_color: i16 = 0;
        // pancurses::attr_get(&mut current_style, &mut current_color);
        let current_style = self.current_style.get();

        let style = pancurses::COLOR_PAIR(color.id() as u64);
        self.window.attron(style);

        self.current_style.set(color);
        f();
        self.current_style.set(current_style);

        // self.window.attroff(style);
        self.window.attron(pancurses::COLOR_PAIR(current_style.id() as u64));
    }

    fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
        let style = match effect {
            // A_REVERSE, taken from ncurses
            Effect::Reverse => 1 << (10 + 8u32),
            Effect::Simple => pancurses::A_NORMAL,
        };
        self.window.attron(style);
        f();
        self.window.attroff(style);
    }

    fn clear(&self) {
        self.window.clear();
    }

    fn refresh(&mut self) {
        self.window.refresh();
    }

    fn print_at(&self, (x, y): (usize, usize), text: &str) {
        self.window.mvaddstr(y as i32, x as i32, text);
    }

    fn poll_event(&self) -> Event {
        // TODO: there seems to not be any indication
        // of Ctrl/Alt/Shift in these :v
        if let Some(ev) = self.window.getch() {
            match ev {
                pancurses::Input::Character('\n') => Event::Key(Key::Enter),
                // TODO: wait for a very short delay. If more keys are
                // pipelined, it may be an escape sequence.
                pancurses::Input::Character('\u{1b}') => Event::Key(Key::Esc),
                pancurses::Input::Character('\u{7f}') |
                pancurses::Input::Character('\u{8}') => Event::Key(Key::Backspace),
                pancurses::Input::Character(c) if 32 <= (c as u32) &&
                                                  (c as u32) <= 255 => {
                    Event::Char(utf8::read_char(c as u8, || {
                            self.window.getch().and_then(|i| match i {
                                pancurses::Input::Character(c) => {
                                    Some(c as u8)
                                }
                                _ => None,
                            })
                        })
                        .unwrap())
                }
                pancurses::Input::Character(c) => Event::Unknown(c as i32),
                // TODO: Some key combos are not recognized by pancurses,
                // but are sent as Unknown. We could still parse them here.
                pancurses::Input::Unknown(i) => Event::Unknown(i),
                // TODO: I honestly have no fucking idea what KeyCodeYes is
                pancurses::Input::KeyCodeYes => Event::Refresh,
                pancurses::Input::KeyBreak => Event::Key(Key::PauseBreak),
                pancurses::Input::KeyDown => Event::Key(Key::Down),
                pancurses::Input::KeyUp => Event::Key(Key::Up),
                pancurses::Input::KeyLeft => Event::Key(Key::Left),
                pancurses::Input::KeyRight => Event::Key(Key::Right),
                pancurses::Input::KeyHome => Event::Key(Key::Home),
                pancurses::Input::KeyBackspace => Event::Key(Key::Backspace),
                pancurses::Input::KeyF0 => Event::Key(Key::F0),
                pancurses::Input::KeyF1 => Event::Key(Key::F1),
                pancurses::Input::KeyF2 => Event::Key(Key::F2),
                pancurses::Input::KeyF3 => Event::Key(Key::F3),
                pancurses::Input::KeyF4 => Event::Key(Key::F4),
                pancurses::Input::KeyF5 => Event::Key(Key::F5),
                pancurses::Input::KeyF6 => Event::Key(Key::F6),
                pancurses::Input::KeyF7 => Event::Key(Key::F7),
                pancurses::Input::KeyF8 => Event::Key(Key::F8),
                pancurses::Input::KeyF9 => Event::Key(Key::F9),
                pancurses::Input::KeyF10 => Event::Key(Key::F10),
                pancurses::Input::KeyF11 => Event::Key(Key::F11),
                pancurses::Input::KeyF12 => Event::Key(Key::F12),
                pancurses::Input::KeyF13 => Event::Shift(Key::F1),
                pancurses::Input::KeyF14 => Event::Shift(Key::F2),
                pancurses::Input::KeyF15 => Event::Shift(Key::F3),
                pancurses::Input::KeyDL => Event::Refresh,
                pancurses::Input::KeyIL => Event::Refresh,
                pancurses::Input::KeyDC => Event::Key(Key::Del),
                pancurses::Input::KeyIC => Event::Key(Key::Ins),
                pancurses::Input::KeyEIC => Event::Refresh,
                pancurses::Input::KeyClear => Event::Refresh,
                pancurses::Input::KeyEOS => Event::Refresh,
                pancurses::Input::KeyEOL => Event::Refresh,
                pancurses::Input::KeySF => Event::Shift(Key::Down),
                pancurses::Input::KeySR => Event::Shift(Key::Up),
                pancurses::Input::KeyNPage => Event::Key(Key::PageDown),
                pancurses::Input::KeyPPage => Event::Key(Key::PageUp),
                pancurses::Input::KeySTab => Event::Shift(Key::Tab),
                pancurses::Input::KeyCTab => Event::Ctrl(Key::Tab),
                pancurses::Input::KeyCATab => Event::CtrlAlt(Key::Tab),
                pancurses::Input::KeyEnter => Event::Key(Key::Enter),
                pancurses::Input::KeySReset => Event::Refresh,
                pancurses::Input::KeyReset => Event::Refresh,
                pancurses::Input::KeyPrint => Event::Refresh,
                pancurses::Input::KeyLL => Event::Refresh,
                pancurses::Input::KeyAbort => Event::Refresh,
                pancurses::Input::KeySHelp => Event::Refresh,
                pancurses::Input::KeyLHelp => Event::Refresh,
                pancurses::Input::KeyBTab => Event::Shift(Key::Tab),
                pancurses::Input::KeyBeg => Event::Refresh,
                pancurses::Input::KeyCancel => Event::Refresh,
                pancurses::Input::KeyClose => Event::Refresh,
                pancurses::Input::KeyCommand => Event::Refresh,
                pancurses::Input::KeyCopy => Event::Refresh,
                pancurses::Input::KeyCreate => Event::Refresh,
                pancurses::Input::KeyEnd => Event::Key(Key::End),
                pancurses::Input::KeyExit => Event::Refresh,
                pancurses::Input::KeyFind => Event::Refresh,
                pancurses::Input::KeyHelp => Event::Refresh,
                pancurses::Input::KeyMark => Event::Refresh,
                pancurses::Input::KeyMessage => Event::Refresh,
                pancurses::Input::KeyMove => Event::Refresh,
                pancurses::Input::KeyNext => Event::Refresh,
                pancurses::Input::KeyOpen => Event::Refresh,
                pancurses::Input::KeyOptions => Event::Refresh,
                pancurses::Input::KeyPrevious => Event::Refresh,
                pancurses::Input::KeyRedo => Event::Refresh,
                pancurses::Input::KeyReference => Event::Refresh,
                pancurses::Input::KeyRefresh => Event::Refresh,
                pancurses::Input::KeyReplace => Event::Refresh,
                pancurses::Input::KeyRestart => Event::Refresh,
                pancurses::Input::KeyResume => Event::Refresh,
                pancurses::Input::KeySave => Event::Refresh,
                pancurses::Input::KeySBeg => Event::Refresh,
                pancurses::Input::KeySCancel => Event::Refresh,
                pancurses::Input::KeySCommand => Event::Refresh,
                pancurses::Input::KeySCopy => Event::Refresh,
                pancurses::Input::KeySCreate => Event::Refresh,
                pancurses::Input::KeySDC => Event::Shift(Key::Del),
                pancurses::Input::KeySDL => Event::Refresh,
                pancurses::Input::KeySelect => Event::Refresh,
                pancurses::Input::KeySEnd => Event::Shift(Key::End),
                pancurses::Input::KeySEOL => Event::Refresh,
                pancurses::Input::KeySExit => Event::Refresh,
                pancurses::Input::KeySFind => Event::Refresh,
                pancurses::Input::KeySHome => Event::Shift(Key::Home),
                pancurses::Input::KeySIC => Event::Shift(Key::Ins),
                pancurses::Input::KeySLeft => Event::Shift(Key::Left),
                pancurses::Input::KeySMessage => Event::Refresh,
                pancurses::Input::KeySMove => Event::Refresh,
                pancurses::Input::KeySNext => Event::Shift(Key::PageDown),
                pancurses::Input::KeySOptions => Event::Refresh,
                pancurses::Input::KeySPrevious => Event::Shift(Key::PageUp),
                pancurses::Input::KeySPrint => Event::Refresh,
                pancurses::Input::KeySRedo => Event::Refresh,
                pancurses::Input::KeySReplace => Event::Refresh,
                pancurses::Input::KeySRight => Event::Shift(Key::Right),
                pancurses::Input::KeySResume => Event::Refresh,
                pancurses::Input::KeySSave => Event::Refresh,
                pancurses::Input::KeySSuspend => Event::Refresh,
                pancurses::Input::KeySUndo => Event::Refresh,
                pancurses::Input::KeySuspend => Event::Refresh,
                pancurses::Input::KeyUndo => Event::Refresh,
                pancurses::Input::KeyResize => Event::WindowResize,
                pancurses::Input::KeyEvent => Event::Refresh,
                // TODO: mouse support
                pancurses::Input::KeyMouse => Event::Refresh,
                pancurses::Input::KeyA1 => Event::Refresh,
                pancurses::Input::KeyA3 => Event::Refresh,
                pancurses::Input::KeyB2 => Event::Key(Key::NumpadCenter),
                pancurses::Input::KeyC1 => Event::Refresh,
                pancurses::Input::KeyC3 => Event::Refresh,
            }
        } else {
            Event::Refresh
        }
    }

    fn set_refresh_rate(&mut self, fps: u32) {
        if fps == 0 {
            self.window.timeout(-1);
        } else {
            self.window.timeout(1000 / fps as i32);
        }
    }
}
<|endoftext|>


<commit_before>use errors;

// see OSC Type Tag String: http://opensoundcontrol.org/spec-1_0
// padding: zero bytes (n*4)
pub enum OscType {
    OscInt(i32),
    OscFloat(f32),
    OscString(String), // padding
    OscBlob(Vec<u8>), // padding
    OscTimetag(u64),
    // nonstandard argument types
    // ignore them if not implemented
    OscLong(i64), // big-endian
    OscDouble(f64),
    OscChar(u8), // padding
    OscColor(u32), // RGBA
    OscMidi(OscMidiType),
    OscTrue,
    OscFalse,
    OscNil,
    OscInf,
    OscArray(Vec<OscType>),
}

pub struct OscMidiType {
    port: u8,
    status: u8,
    data1: u8, // maybe use an enum for data?
    data2: u8,
}

/// An *osc packet* can contain an *osc message* or a bundle of nested messages
/// which is called *osc bundle*.
pub enum OscPacket {
    Message(OscMessage),
    Bundle(OscBundle),
}

pub struct OscMessage;
pub struct OscBundle;

pub type OscResult<T> = Result<T, errors::OscError>;<commit_msg>Remove useless comments<commit_after>use errors;

// see OSC Type Tag String: http://opensoundcontrol.org/spec-1_0
// padding: zero bytes (n*4)
pub enum OscType {
    OscInt(i32),
    OscFloat(f32),
    OscString(String),
    OscBlob(Vec<u8>),
    OscTimetag(u64),
    // nonstandard argument types
    // ignore them if not implemented
    OscLong(i64),
    OscDouble(f64),
    OscColor(u32), // RGBA
    OscChar(u8),
    OscMidi(OscMidiType),
    OscTrue,
    OscFalse,
    OscNil,
    OscInf,
    OscArray(Vec<OscType>),
}

pub struct OscMidiType {
    port: u8,
    status: u8,
    data1: u8, // maybe use an enum for data?
    data2: u8,
}

/// An *osc packet* can contain an *osc message* or a bundle of nested messages
/// which is called *osc bundle*.
pub enum OscPacket {
    Message(OscMessage),
    Bundle(OscBundle),
}

pub struct OscMessage;
pub struct OscBundle;

pub type OscResult<T> = Result<T, errors::OscError>;<|endoftext|>