<commit_before>#![feature(async_closure)]
#![feature(label_break_value)]
#![feature(never_type)]
#![feature(try_trait_v2)]

mod builtin_functions;
mod compiler;
mod database;
mod fuzzer;
mod input;
mod language_server;
mod vm;

use crate::{
    compiler::{
        ast_to_hir::AstToHir,
        cst_to_ast::CstToAst,
        error::CompilerError,
        hir::{self, CollectErrors},
        hir_to_lir::HirToLir,
        rcst_to_cst::RcstToCst,
        string_to_rcst::StringToRcst,
    },
    database::{Database, PROJECT_DIRECTORY},
    input::Input,
    language_server::utils::LspPositionConversion,
    vm::{use_provider::DbUseProvider, value::Closure, Vm},
};
use compiler::lir::Lir;
use fern::colors::{Color, ColoredLevelConfig};
use itertools::Itertools;
use language_server::CandyLanguageServer;
use log::{self, LevelFilter};
use notify::{watcher, RecursiveMode, Watcher};
use std::{
    env::current_dir,
    fs,
    path::PathBuf,
    sync::{mpsc::channel, Arc},
    time::Duration,
};
use structopt::StructOpt;
use tower_lsp::{LspService, Server};

#[derive(StructOpt, Debug)]
#[structopt(name = "candy", about = "The 🍭 Candy CLI.")]
enum CandyOptions {
    Build(CandyBuildOptions),
    Run(CandyRunOptions),
    Fuzz(CandyFuzzOptions),
    Lsp,
}

#[derive(StructOpt, Debug)]
struct CandyBuildOptions {
    #[structopt(long)]
    debug: bool,

    #[structopt(long)]
    watch: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[derive(StructOpt, Debug)]
struct CandyRunOptions {
    #[structopt(long)]
    debug: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[derive(StructOpt, Debug)]
struct CandyFuzzOptions {
    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[tokio::main]
async fn main() {
    init_logger();
    match CandyOptions::from_args() {
        CandyOptions::Build(options) => build(options),
        CandyOptions::Run(options) => run(options),
        CandyOptions::Fuzz(options) => fuzz(options).await,
        CandyOptions::Lsp => lsp().await,
    }
}

fn build(options: CandyBuildOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    raw_build(&options.file, options.debug);

    if options.watch {
        let (tx, rx) = channel();
        let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
        watcher
            .watch(&options.file, RecursiveMode::Recursive)
            .unwrap();
        loop {
            match rx.recv() {
                Ok(_) => {
                    raw_build(&options.file, options.debug);
                }
                Err(e) => log::error!("watch error: {e:#?}"),
            }
        }
    }
}
fn raw_build(file: &PathBuf, debug: bool) -> Option<Arc<Lir>> {
    let path_string = file.to_string_lossy();
    log::info!("Building `{path_string}`.");

    let input: Input = file.clone().into();
    let db = Database::default();

    log::debug!("Parsing string to RCST…");
    let rcst = db
        .rcst(input.clone())
        .unwrap_or_else(|err| panic!("Error parsing file `{}`: {:?}", path_string, err));
    if debug {
        let rcst_file = file.clone_with_extension("candy.rcst");
        fs::write(rcst_file, format!("{:#?}\n", rcst)).unwrap();
    }

    log::debug!("Turning RCST to CST…");
    let cst = db.cst(input.clone()).expect("RCST should have failed");
    if debug {
        let cst_file = file.clone_with_extension("candy.cst");
        fs::write(cst_file, format!("{:#?}\n", cst)).unwrap();
    }

    log::debug!("Abstracting CST to AST…");
    let (asts, ast_cst_id_map) = db
        .ast(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let ast_file = file.clone_with_extension("candy.ast");
        fs::write(
            ast_file,
            format!("{}\n", asts.iter().map(|ast| format!("{}", ast)).join("\n")),
        )
        .unwrap();

        let ast_to_cst_ids_file = file.clone_with_extension("candy.ast_to_cst_ids");
        fs::write(
            ast_to_cst_ids_file,
            ast_cst_id_map
                .keys()
                .into_iter()
                .sorted_by_key(|it| it.local)
                .map(|key| format!("{key} -> {}\n", ast_cst_id_map[key].0))
                .join(""),
        )
        .unwrap();
    }

    log::debug!("Turning AST to HIR…");
    let (hir, hir_ast_id_map) = db
        .hir(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let hir_file = file.clone_with_extension("candy.hir");
        fs::write(hir_file, format!("{}", hir)).unwrap();

        let hir_ast_id_file = file.clone_with_extension("candy.hir_to_ast_ids");
        fs::write(
            hir_ast_id_file,
            hir_ast_id_map
                .keys()
                .into_iter()
                .map(|key| format!("{key} -> {}\n", hir_ast_id_map[key]))
                .join(""),
        )
        .unwrap();
    }

    log::debug!("Lowering HIR to LIR…");
    let lir = db
        .lir(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let lir_file = file.clone_with_extension("candy.lir");
        fs::write(lir_file, format!("{lir}")).unwrap();
    }

    let mut errors = vec![];
    hir.collect_errors(&mut errors);
    for CompilerError { span, payload, .. } in errors {
        let (start_line, start_col) = db.offset_to_lsp(input.clone(), span.start);
        let (end_line, end_col) = db.offset_to_lsp(input.clone(), span.end);
        log::warn!("{start_line}:{start_col} – {end_line}:{end_col}: {payload:?}");
    }

    Some(lir)
}

fn run(options: CandyRunOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    let input: Input = options.file.clone().into();
    let db = Database::default();

    if raw_build(&options.file, false).is_none() {
        log::info!("Build failed.");
        return;
    };
    let module_closure = Closure::of_input(&db, input).unwrap();

    let path_string = options.file.to_string_lossy();
    log::info!("Running `{path_string}`.");

    let mut vm = Vm::new();
    let use_provider = DbUseProvider { db: &db };
    vm.set_up_module_closure_execution(&use_provider, module_closure);
    vm.run_synchronously_until_completion(&db).ok();

    if options.debug {
        let trace = vm.tracer.dump_call_tree();
        let trace_file = options.file.clone_with_extension("candy.trace");
        fs::write(trace_file.clone(), trace).unwrap();
        log::info!(
            "Trace has been written to `{}`.",
            trace_file.as_path().display()
        );
    }
}

async fn fuzz(options: CandyFuzzOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    log::debug!("Building `{}`.\n", options.file.display());

    let input: Input = options.file.clone().into();
    let db = Database::default();

    if raw_build(&options.file, false).is_none() {
        log::info!("Build failed.");
        return;
    }

    let path_string = options.file.to_string_lossy();
    log::debug!("Fuzzing `{path_string}`.");

    fuzzer::fuzz(&db, input).await;
}

async fn lsp() {
    log::info!("Starting language server…");
    let (service, socket) = LspService::new(CandyLanguageServer::from_client);
    Server::new(tokio::io::stdin(), tokio::io::stdout(), socket)
        .serve(service)
        .await;
}

fn init_logger() {
    let colors = ColoredLevelConfig::new().debug(Color::BrightBlack);
    fern::Dispatch::new()
        .format(move |out, message, record| {
            let color = colors.get_color(&record.level());
            out.finish(format_args!(
                "\x1B[{}m{} [{:>5}] {} {}\x1B[0m",
                color.to_fg_str(),
                chrono::Local::now().format("%H:%M:%S"),
                record.level(),
                record.target(),
                message
            ))
        })
        .level_for("candy::compiler::hir_to_lir", LevelFilter::Debug)
        .level_for("candy::compiler::string_to_rcst", LevelFilter::Debug)
        .level_for("candy::vm::builtin_functions", LevelFilter::Warn)
        .level_for("candy::vm::heap", LevelFilter::Debug)
        .level_for("candy::vm::vm", LevelFilter::Info)
        .level_for("lspower::transport", LevelFilter::Error)
        .level_for("salsa", LevelFilter::Error)
        .level_for("tokio_util", LevelFilter::Error)
        .chain(std::io::stderr())
        .apply()
        .unwrap();
}

trait CloneWithExtension {
    fn clone_with_extension(&self, extension: &'static str) -> Self;
}
impl CloneWithExtension for PathBuf {
    fn clone_with_extension(&self, extension: &'static str) -> Self {
        let mut path = self.clone();
        assert!(path.set_extension(extension));
        path
    }
}
<commit_msg>Ignore module_inception lint<commit_after>#![feature(async_closure)]
#![feature(label_break_value)]
#![feature(never_type)]
#![feature(try_trait_v2)]
#![allow(clippy::module_inception)]

mod builtin_functions;
mod compiler;
mod database;
mod fuzzer;
mod input;
mod language_server;
mod vm;

use crate::{
    compiler::{
        ast_to_hir::AstToHir,
        cst_to_ast::CstToAst,
        error::CompilerError,
        hir::{self, CollectErrors},
        hir_to_lir::HirToLir,
        rcst_to_cst::RcstToCst,
        string_to_rcst::StringToRcst,
    },
    database::{Database, PROJECT_DIRECTORY},
    input::Input,
    language_server::utils::LspPositionConversion,
    vm::{use_provider::DbUseProvider, value::Closure, Vm},
};
use compiler::lir::Lir;
use fern::colors::{Color, ColoredLevelConfig};
use itertools::Itertools;
use language_server::CandyLanguageServer;
use log::{self, LevelFilter};
use notify::{watcher, RecursiveMode, Watcher};
use std::{
    env::current_dir,
    fs,
    path::PathBuf,
    sync::{mpsc::channel, Arc},
    time::Duration,
};
use structopt::StructOpt;
use tower_lsp::{LspService, Server};

#[derive(StructOpt, Debug)]
#[structopt(name = "candy", about = "The 🍭 Candy CLI.")]
enum CandyOptions {
    Build(CandyBuildOptions),
    Run(CandyRunOptions),
    Fuzz(CandyFuzzOptions),
    Lsp,
}

#[derive(StructOpt, Debug)]
struct CandyBuildOptions {
    #[structopt(long)]
    debug: bool,

    #[structopt(long)]
    watch: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[derive(StructOpt, Debug)]
struct CandyRunOptions {
    #[structopt(long)]
    debug: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[derive(StructOpt, Debug)]
struct CandyFuzzOptions {
    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[tokio::main]
async fn main() {
    init_logger();
    match CandyOptions::from_args() {
        CandyOptions::Build(options) => build(options),
        CandyOptions::Run(options) => run(options),
        CandyOptions::Fuzz(options) => fuzz(options).await,
        CandyOptions::Lsp => lsp().await,
    }
}

fn build(options: CandyBuildOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    raw_build(&options.file, options.debug);

    if options.watch {
        let (tx, rx) = channel();
        let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
        watcher
            .watch(&options.file, RecursiveMode::Recursive)
            .unwrap();
        loop {
            match rx.recv() {
                Ok(_) => {
                    raw_build(&options.file, options.debug);
                }
                Err(e) => log::error!("watch error: {e:#?}"),
            }
        }
    }
}
fn raw_build(file: &PathBuf, debug: bool) -> Option<Arc<Lir>> {
    let path_string = file.to_string_lossy();
    log::info!("Building `{path_string}`.");

    let input: Input = file.clone().into();
    let db = Database::default();

    log::debug!("Parsing string to RCST…");
    let rcst = db
        .rcst(input.clone())
        .unwrap_or_else(|err| panic!("Error parsing file `{}`: {:?}", path_string, err));
    if debug {
        let rcst_file = file.clone_with_extension("candy.rcst");
        fs::write(rcst_file, format!("{:#?}\n", rcst)).unwrap();
    }

    log::debug!("Turning RCST to CST…");
    let cst = db.cst(input.clone()).expect("RCST should have failed");
    if debug {
        let cst_file = file.clone_with_extension("candy.cst");
        fs::write(cst_file, format!("{:#?}\n", cst)).unwrap();
    }

    log::debug!("Abstracting CST to AST…");
    let (asts, ast_cst_id_map) = db
        .ast(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let ast_file = file.clone_with_extension("candy.ast");
        fs::write(
            ast_file,
            format!("{}\n", asts.iter().map(|ast| format!("{}", ast)).join("\n")),
        )
        .unwrap();

        let ast_to_cst_ids_file = file.clone_with_extension("candy.ast_to_cst_ids");
        fs::write(
            ast_to_cst_ids_file,
            ast_cst_id_map
                .keys()
                .into_iter()
                .sorted_by_key(|it| it.local)
                .map(|key| format!("{key} -> {}\n", ast_cst_id_map[key].0))
                .join(""),
        )
        .unwrap();
    }

    log::debug!("Turning AST to HIR…");
    let (hir, hir_ast_id_map) = db
        .hir(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let hir_file = file.clone_with_extension("candy.hir");
        fs::write(hir_file, format!("{}", hir)).unwrap();

        let hir_ast_id_file = file.clone_with_extension("candy.hir_to_ast_ids");
        fs::write(
            hir_ast_id_file,
            hir_ast_id_map
                .keys()
                .into_iter()
                .map(|key| format!("{key} -> {}\n", hir_ast_id_map[key]))
                .join(""),
        )
        .unwrap();
    }

    log::debug!("Lowering HIR to LIR…");
    let lir = db
        .lir(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let lir_file = file.clone_with_extension("candy.lir");
        fs::write(lir_file, format!("{lir}")).unwrap();
    }

    let mut errors = vec![];
    hir.collect_errors(&mut errors);
    for CompilerError { span, payload, .. } in errors {
        let (start_line, start_col) = db.offset_to_lsp(input.clone(), span.start);
        let (end_line, end_col) = db.offset_to_lsp(input.clone(), span.end);
        log::warn!("{start_line}:{start_col} – {end_line}:{end_col}: {payload:?}");
    }

    Some(lir)
}

fn run(options: CandyRunOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    let input: Input = options.file.clone().into();
    let db = Database::default();

    if raw_build(&options.file, false).is_none() {
        log::info!("Build failed.");
        return;
    };
    let module_closure = Closure::of_input(&db, input).unwrap();

    let path_string = options.file.to_string_lossy();
    log::info!("Running `{path_string}`.");

    let mut vm = Vm::new();
    let use_provider = DbUseProvider { db: &db };
    vm.set_up_module_closure_execution(&use_provider, module_closure);
    vm.run_synchronously_until_completion(&db).ok();

    if options.debug {
        let trace = vm.tracer.dump_call_tree();
        let trace_file = options.file.clone_with_extension("candy.trace");
        fs::write(trace_file.clone(), trace).unwrap();
        log::info!(
            "Trace has been written to `{}`.",
            trace_file.as_path().display()
        );
    }
}

async fn fuzz(options: CandyFuzzOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    log::debug!("Building `{}`.\n", options.file.display());

    let input: Input = options.file.clone().into();
    let db = Database::default();

    if raw_build(&options.file, false).is_none() {
        log::info!("Build failed.");
        return;
    }

    let path_string = options.file.to_string_lossy();
    log::debug!("Fuzzing `{path_string}`.");

    fuzzer::fuzz(&db, input).await;
}

async fn lsp() {
    log::info!("Starting language server…");
    let (service, socket) = LspService::new(CandyLanguageServer::from_client);
    Server::new(tokio::io::stdin(), tokio::io::stdout(), socket)
        .serve(service)
        .await;
}

fn init_logger() {
    let colors = ColoredLevelConfig::new().debug(Color::BrightBlack);
    fern::Dispatch::new()
        .format(move |out, message, record| {
            let color = colors.get_color(&record.level());
            out.finish(format_args!(
                "\x1B[{}m{} [{:>5}] {} {}\x1B[0m",
                color.to_fg_str(),
                chrono::Local::now().format("%H:%M:%S"),
                record.level(),
                record.target(),
                message
            ))
        })
        .level_for("candy::compiler::hir_to_lir", LevelFilter::Debug)
        .level_for("candy::compiler::string_to_rcst", LevelFilter::Debug)
        .level_for("candy::vm::builtin_functions", LevelFilter::Warn)
        .level_for("candy::vm::heap", LevelFilter::Debug)
        .level_for("candy::vm::vm", LevelFilter::Info)
        .level_for("lspower::transport", LevelFilter::Error)
        .level_for("salsa", LevelFilter::Error)
        .level_for("tokio_util", LevelFilter::Error)
        .chain(std::io::stderr())
        .apply()
        .unwrap();
}

trait CloneWithExtension {
    fn clone_with_extension(&self, extension: &'static str) -> Self;
}
impl CloneWithExtension for PathBuf {
    fn clone_with_extension(&self, extension: &'static str) -> Self {
        let mut path = self.clone();
        assert!(path.set_extension(extension));
        path
    }
}
<|endoftext|>


<commit_before>//! Implementation of the [DBSCAN](https://en.wikipedia.org/wiki/DBSCAN)
//! clustering algorithm.

use matrix::SymmetricMatrix;
use std::collections::VecDeque;

/// Implementation of the [DBSCAN](https://en.wikipedia.org/wiki/DBSCAN)
/// clustering algorithm.
#[derive(Debug)]
pub struct DBSCAN<T> {
    eps: T,
    min_points: usize,
    clusters: Vec<Option<usize>>,
    visited: Vec<bool>,
    current_cluster: usize,
}

impl<T> DBSCAN<T>
    where T: Default + Copy + PartialOrd
{
    /// Creates a new DBSCAN instance.
    ///
    /// # Parameters
    ///
    /// * `eps` - The maximum distance between two points for them to be in the
    ///   same neighborhood.
    /// * `min_points` - The minimal number of points in a neighborhood for a
    ///   point to be considered as a core point.
    pub fn new(eps: T, min_points: usize) -> Self {
        DBSCAN {
            eps: eps,
            min_points: min_points,
            clusters: Vec::new(),
            visited: Vec::new(),
            current_cluster: 0,
        }
    }

    /// Performs DBSCAN clustering from the given distance matrix.
    ///
    /// # Returns
    ///
    /// Returns cluster labels for each point in the dataset. Noisy samples are
    /// set to `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use dbscan::DBSCAN;
    /// use dbscan::SymmetricMatrix;
    ///
    /// let mut dbscan = DBSCAN::new(1, 2);
    /// let mut m = SymmetricMatrix::<i8>::new(5);
    /// m.set(0, 1, 1);
    /// m.set(0, 2, 9);
    /// m.set(0, 3, 9);
    /// m.set(0, 4, 9);
    /// m.set(1, 2, 9);
    /// m.set(1, 3, 9);
    /// m.set(1, 4, 9);
    /// m.set(2, 3, 1);
    /// m.set(2, 4, 9);
    /// m.set(3, 4, 9);
    ///
    /// let clustering = dbscan.perform_clustering(&m);
    ///
    /// assert_eq!(clustering[0], Some(0));
    /// assert_eq!(clustering[1], Some(0));
    /// assert_eq!(clustering[2], Some(1));
    /// assert_eq!(clustering[3], Some(1));
    /// assert_eq!(clustering[4], None);
    /// ```
    ///
    /// In the above example, points `0` and `1` form a single cluster, points
    /// `2` and `3` form a different cluster, and point `4` does not belong any
    /// cluster (it is a noise point).
    pub fn perform_clustering(&mut self,
                              matrix: &SymmetricMatrix<T>) -> &Vec<Option<usize>> {
        self.clusters = vec![None; matrix.size()];
        self.visited = vec![false; matrix.size()];
        self.current_cluster = 0;

        for point in 0..matrix.size() {
            if self.visited[point] {
                continue;
            }

            self.visited[point] = true;
            let neighbors = self.region_query(matrix, point);
            if neighbors.len() >= self.min_points {
                self.expand_cluster(matrix, point, neighbors);
                self.current_cluster += 1;
            }
        }

        self.clusters.as_ref()
    }

    fn expand_cluster(&mut self,
                      matrix: &SymmetricMatrix<T>,
                      point: usize,
                      mut neighbors: VecDeque<usize>) {
        self.clusters[point] = Some(self.current_cluster);

        while let Some(other_point) = neighbors.pop_front() {
            if self.visited[other_point] {
                continue;
            }

            self.visited[other_point] = true;
            let mut other_neighbors = self.region_query(matrix, other_point);
            if other_neighbors.len() >= self.min_points {
                neighbors.append(&mut other_neighbors);
            }

            if self.clusters[other_point].is_none() {
                self.clusters[other_point] = Some(self.current_cluster);
            }
        }
    }

    fn region_query(&self,
                    matrix: &SymmetricMatrix<T>,
                    point: usize) -> VecDeque<usize> {
        let mut neighbors = VecDeque::new();
        for other_point in 0..matrix.size() {
            let dist = matrix.get(point, other_point);
            if dist <= self.eps {
                neighbors.push_back(other_point);
            }
        }
        neighbors
    }
}

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

    #[test]
    fn test_all_points_are_in_single_cluster_when_their_distance_is_zero() {
        let mut dbscan = DBSCAN::new(1, 2);
        let m = SymmetricMatrix::<i8>::new(2);

        let clustering = dbscan.perform_clustering(&m);

        assert_eq!(clustering[0], Some(0));
        assert_eq!(clustering[1], Some(0));
    }

    #[test]
    fn test_points_are_correctly_clustered_based_on_their_distance() {
        let mut dbscan = DBSCAN::new(1, 2);
        let mut m = SymmetricMatrix::<i8>::new(5);
        m.set(0, 1, 1);
        m.set(0, 2, 9);
        m.set(0, 3, 9);
        m.set(0, 4, 9);
        m.set(1, 2, 9);
        m.set(1, 3, 9);
        m.set(1, 4, 9);
        m.set(2, 3, 1);
        m.set(2, 4, 9);
        m.set(3, 4, 9);

        let clustering = dbscan.perform_clustering(&m);

        assert_eq!(clustering[0], Some(0));
        assert_eq!(clustering[1], Some(0));
        assert_eq!(clustering[2], Some(1));
        assert_eq!(clustering[3], Some(1));
        assert_eq!(clustering[4], None);
    }

    #[test]
    fn test_points_that_do_not_belong_to_any_cluster_are_none() {
        let mut dbscan = DBSCAN::new(1, 2);
        let m = SymmetricMatrix::<i8>::new(1);

        let clustering = dbscan.perform_clustering(&m);

        assert_eq!(clustering[0], None);
    }
}
<commit_msg>en-2017-01-01: Fix the DBSCAN implementation.<commit_after>//! Implementation of the [DBSCAN](https://en.wikipedia.org/wiki/DBSCAN)
//! clustering algorithm.

use matrix::SymmetricMatrix;
use std::collections::VecDeque;

/// Implementation of the [DBSCAN](https://en.wikipedia.org/wiki/DBSCAN)
/// clustering algorithm.
#[derive(Debug)]
pub struct DBSCAN<T> {
    eps: T,
    min_points: usize,
    clusters: Vec<Option<usize>>,
    visited: Vec<bool>,
    current_cluster: usize,
}

impl<T> DBSCAN<T>
    where T: Default + Copy + PartialOrd
{
    /// Creates a new DBSCAN instance.
    ///
    /// # Parameters
    ///
    /// * `eps` - The maximum distance between two points for them to be in the
    ///   same neighborhood.
    /// * `min_points` - The minimal number of points in a neighborhood for a
    ///   point to be considered as a core point.
    pub fn new(eps: T, min_points: usize) -> Self {
        DBSCAN {
            eps: eps,
            min_points: min_points,
            clusters: Vec::new(),
            visited: Vec::new(),
            current_cluster: 0,
        }
    }

    /// Performs DBSCAN clustering from the given distance matrix.
    ///
    /// # Returns
    ///
    /// Returns cluster labels for each point in the dataset. Noisy samples are
    /// set to `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use dbscan::DBSCAN;
    /// use dbscan::SymmetricMatrix;
    ///
    /// let mut dbscan = DBSCAN::new(1, 2);
    /// let mut m = SymmetricMatrix::<i8>::new(5);
    /// m.set(0, 1, 1);
    /// m.set(0, 2, 9);
    /// m.set(0, 3, 9);
    /// m.set(0, 4, 9);
    /// m.set(1, 2, 9);
    /// m.set(1, 3, 9);
    /// m.set(1, 4, 9);
    /// m.set(2, 3, 1);
    /// m.set(2, 4, 9);
    /// m.set(3, 4, 9);
    ///
    /// let clustering = dbscan.perform_clustering(&m);
    ///
    /// assert_eq!(clustering[0], Some(0));
    /// assert_eq!(clustering[1], Some(0));
    /// assert_eq!(clustering[2], Some(1));
    /// assert_eq!(clustering[3], Some(1));
    /// assert_eq!(clustering[4], None);
    /// ```
    ///
    /// In the above example, points `0` and `1` form a single cluster, points
    /// `2` and `3` form a different cluster, and point `4` does not belong any
    /// cluster (it is a noise point).
    pub fn perform_clustering(&mut self,
                              matrix: &SymmetricMatrix<T>) -> &Vec<Option<usize>> {
        self.clusters = vec![None; matrix.size()];
        self.visited = vec![false; matrix.size()];
        self.current_cluster = 0;

        for point in 0..matrix.size() {
            if self.visited[point] {
                continue;
            }

            self.visited[point] = true;
            let neighbors = self.region_query(matrix, point);
            if neighbors.len() >= self.min_points {
                self.expand_cluster(matrix, point, neighbors);
                self.current_cluster += 1;
            }
        }

        self.clusters.as_ref()
    }

    fn expand_cluster(&mut self,
                      matrix: &SymmetricMatrix<T>,
                      point: usize,
                      mut neighbors: VecDeque<usize>) {
        self.clusters[point] = Some(self.current_cluster);

        while let Some(other_point) = neighbors.pop_front() {
            if !self.visited[other_point] {
                self.visited[other_point] = true;
                let mut other_neighbors = self.region_query(matrix, other_point);
                if other_neighbors.len() >= self.min_points {
                    neighbors.append(&mut other_neighbors);
                }
            }
            if self.clusters[other_point].is_none() {
                self.clusters[other_point] = Some(self.current_cluster);
            }
        }
    }

    fn region_query(&self,
                    matrix: &SymmetricMatrix<T>,
                    point: usize) -> VecDeque<usize> {
        let mut neighbors = VecDeque::new();
        for other_point in 0..matrix.size() {
            let dist = matrix.get(point, other_point);
            if dist <= self.eps {
                neighbors.push_back(other_point);
            }
        }
        neighbors
    }
}

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

    #[test]
    fn test_all_points_are_in_single_cluster_when_their_distance_is_zero() {
        let mut dbscan = DBSCAN::new(1, 2);
        let m = SymmetricMatrix::<i8>::new(2);

        let clustering = dbscan.perform_clustering(&m);

        assert_eq!(clustering[0], Some(0));
        assert_eq!(clustering[1], Some(0));
    }

    #[test]
    fn test_points_are_correctly_clustered_based_on_their_distance() {
        let mut dbscan = DBSCAN::new(1, 2);
        let mut m = SymmetricMatrix::<i8>::new(5);
        m.set(0, 1, 1);
        m.set(0, 2, 9);
        m.set(0, 3, 9);
        m.set(0, 4, 9);
        m.set(1, 2, 9);
        m.set(1, 3, 9);
        m.set(1, 4, 9);
        m.set(2, 3, 1);
        m.set(2, 4, 9);
        m.set(3, 4, 9);

        let clustering = dbscan.perform_clustering(&m);

        assert_eq!(clustering[0], Some(0));
        assert_eq!(clustering[1], Some(0));
        assert_eq!(clustering[2], Some(1));
        assert_eq!(clustering[3], Some(1));
        assert_eq!(clustering[4], None);
    }

    #[test]
    fn test_neighboring_points_are_put_into_cluster_even_if_they_have_been_visited() {
        // In 2D, the points in this test are placed as follows:
        //
        //    0
        //      1
        //        2
        //
        // Epsilon is set to 1 and min_points to 3. When the first point is
        // checked (0), it is marked as visited. Since it has only a single
        // neighbor, the two points (0 and 1) cannot form a cluster because
        // min_points is 3. Then, the algorithm continues to point 1. It has
        // two neighbors (0 and
        // 2), so the three points (0, 1, 2) can form a cluster. In this test,
        // we ensure that even when the first point (0) has already been
        // marked as visited, it is put into the cluster because it is not
        // yet a member of any other cluster.
        let mut dbscan = DBSCAN::new(1, 3);
        let mut m = SymmetricMatrix::<i8>::new(3);
        m.set(0, 1, 1);
        m.set(0, 2, 2);
        m.set(1, 2, 1);

        let clustering = dbscan.perform_clustering(&m);

        assert_eq!(clustering[0], Some(0));
        assert_eq!(clustering[1], Some(0));
        assert_eq!(clustering[2], Some(0));
    }

    #[test]
    fn test_points_that_do_not_belong_to_any_cluster_are_none() {
        let mut dbscan = DBSCAN::new(1, 2);
        let m = SymmetricMatrix::<i8>::new(1);

        let clustering = dbscan.perform_clustering(&m);

        assert_eq!(clustering[0], None);
    }
}
<|endoftext|>


<commit_before>use redox::get_slice::GetSlice;
use redox::ops::DerefMut;
use redox::string::*;
use redox::vec::Vec;
use redox::boxed::Box;
use redox::fs::*;
use redox::io::*;
use redox::env::*;
use redox::time::Duration;
use redox::to_num::*;

/* Magic Macros { */
static mut application: *mut Application<'static> = 0 as *mut Application;

/// Execute a command
macro_rules! exec {
    ($cmd:expr) => ({
        unsafe {
            (*application).on_command(&$cmd.to_string());
        }
    })
}
/* } Magic Macros */

/// Structure which represents a Terminal's command.
/// This command structure contains a name, and the code which run the functionnality associated to this one, with zero, one or several argument(s).
/// # Example
/// ```
/// let my_command = Command {
///     name: "my_command",
///     main: box|args: &Vec<String>| {
///         println!("Say 'hello' to my command! :-D");
///     }
/// }
/// ```
pub struct Command<'a> {
    pub name: &'a str,
    pub main: Box<Fn(&Vec<String>)>,
}

impl<'a> Command<'a> {
    /// Return the vector of the commands
    // TODO: Use a more efficient collection instead
    pub fn vec() -> Vec<Self> {
        let mut commands: Vec<Self> = Vec::new();

        commands.push(Command {
            name: "cat",
            main: Box::new(|args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read: {}", path),
                    }
                } else {
                    println!("Failed to open file: {}", path);
                }
            }),
        });

        commands.push(Command {
            name: "cd",
            main: Box::new(|args: &Vec<String>| {
                match args.get(1) {
                    Some(path) => {
                        if !change_cwd(&path) {
                            println!("Bad path: {}", path);
                        }
                    }
                    None => println!("No path given")
                }
            }),
        });

        // Simple command to create a file, in the current directory
        // The file has got the name given as the first argument of the command
        // If the command have no arguments, the command don't create the file
        commands.push(Command {
            name: "cfile",
            main: box |args: &Vec<String>| {
                match args.get(1) {
                    Some(file_name) => {
                        File::create(file_name);
                    }
                    None => {
                        println!("Could not create a file without a name");
                    }
                }
            }
        });

        commands.push(Command {
            name: "echo",
            main: Box::new(|args: &Vec<String>| {
                let echo = args.iter()
                    .skip(1)
                    .fold(String::new(), |string, arg| string + " " + arg);
                println!("{}", echo.trim());
            }),
        });

        commands.push(Command {
            name: "else",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "exec",
            main: Box::new(|args: &Vec<String>| {
                if let Some(arg) = args.get(1) {
                    let mut args_str: Vec<&str> = Vec::new();
                    for arg in args.get_slice(Some(2), None) {
                        args_str.push(arg);
                    }

                    File::exec(arg, &args_str);
                }
            }),
        });

        commands.push(Command {
            name: "exit",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "fi",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "if",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "ls",
            main: Box::new(|args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(dir) = read_dir(&path) {
                    for entry in dir {
                        println!("{}", entry.path());
                    }
                } else {
                    println!("Failed to open directory: {}", path);
                }
            }),
        });

        commands.push(Command {
            name: "pwd",
            main: Box::new(|_: &Vec<String>| {
                if let Some(file) = File::open("") {
                    if let Some(path) = file.path() {
                        println!("{}", path);
                    } else {
                        println!("Could not get the path");
                    }
                } else {
                    println!("Could not open the working directory");
                }
            }),
        });

        commands.push(Command {
            name: "read",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "run",
            main: Box::new(|args: &Vec<String>| {
                if let Some(path) = args.get(1) {

                    let mut commands = String::new();
                    if let Some(mut file) = File::open(path) {
                        file.read_to_string(&mut commands);
                    }

                    for command in commands.split('\n') {
                        exec!(command);
                    }
                }
            }),
        });

        commands.push(Command {
            name: "sleep",
            main: Box::new(|args: &Vec<String>| {
                let secs = {
                    match args.get(1) {
                        Some(arg) => arg.to_num() as i64,
                        None => 0,
                    }
                };

                let nanos = {
                    match args.get(2) {
                        Some(arg) => arg.to_num() as i32,
                        None => 0,
                    }
                };

                println!("Sleep: {} {}", secs, nanos);
                let remaining = Duration::new(secs, nanos).sleep();
                println!("Remaining: {} {}", remaining.secs, remaining.nanos);
            }),
        });

        commands.push(Command {
            name: "send",
            main: Box::new(|args: &Vec<String>| {
                if args.len() < 3 {
                    println!("Error: incorrect arguments");
                    println!("Usage: send [url] [data]");
                    return;
                }

                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    println!("URL: {:?}", file.path());

                    let string: String = args.iter()
                        .skip(2)
                        .fold(String::new(), |s, arg| s + " " + arg)
                        + "\r\n\r\n";

                    match file.write(string.trim_left().as_bytes()) {
                        Some(size) => println!("Wrote {} bytes", size),
                        None => println!("Failed to write"),
                    }

                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            }),
        });

        // Simple command to create a file, in the current directory
        // The file has got the name given as the first argument of the command
        // If the command have no arguments, the command don't create the file
        commands.push(Command {
            name: "touch",
            main: Box::new(|args: &Vec<String>| {
                match args.get(1) {
                    Some(file_name) => if File::create(file_name).is_none() {
                        println!("Failed to create: {}", file_name);
                    },
                    None => println!("No name provided")
                }
            }),
        });

        commands.push(Command {
            name: "url_hex",
            main: Box::new(|args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    let mut vec: Vec<u8> = Vec::new();
                    match file.read_to_end(&mut vec) {
                        Some(_) => {
                            let mut line = "HEX:".to_string();
                            for byte in vec.iter() {
                                line = line + " " + &format!("{:X}", *byte);
                            }
                            println!("{}", line);
                        }
                        None => println!("Failed to read"),
                    }
                }
            }),
        });

        commands.push(Command {
            name: "wget",
            main: Box::new(|args: &Vec<String>| {
                if let Some(host) = args.get(1) {
                    if let Some(req) = args.get(2) {
                        if let Some(mut con) = File::open(&("tcp://".to_string() + host)) {
                            con.write(("GET ".to_string() + req + " HTTP/1.1").as_bytes());

                            let mut res = Vec::new();
                            con.read_to_end(&mut res);

                            if let Some(mut file) = File::open(&req) {
                                file.write(&res);
                            }
                        }
                    } else {
                        println!("No request given");
                    }
                } else {
                    println!("No url given");
                }
            }),
        });

        let command_list = commands.iter().fold(String::new(), |l , c| l + " " + c.name);

        commands.push(Command {
            name: "help",
            main: Box::new(move |_: &Vec<String>| {
                println!("Commands:{}", command_list);
            }),
         });

        commands
    }
}

/// A (env) variable
pub struct Variable {
    pub name: String,
    pub value: String,
}

pub struct Mode {
    value: bool,
}

/// An application
pub struct Application<'a> {
    commands: Vec<Command<'a>>,
    variables: Vec<Variable>,
    modes: Vec<Mode>,
}

impl<'a> Application<'a> {
    /// Create a new empty application
    pub fn new() -> Self {
        return Application {
            commands: Command::vec(),
            variables: Vec::new(),
            modes: Vec::new(),
        };
    }

    fn on_command(&mut self, command_string: &str) {
        //Comment
        if command_string.starts_with('#') {
            return;
        }

        //Show variables
        if command_string == "$" {
            for variable in self.variables.iter() {
                println!("{}={}", variable.name, variable.value);
            }
            return;
        }

        //Explode into arguments, replace variables
        let mut args: Vec<String> = Vec::<String>::new();
        for arg in command_string.split(' ') {
            if !arg.is_empty() {
                if arg.starts_with('$') {
                    let name = arg[1 .. arg.len()].to_string();
                    for variable in self.variables.iter() {
                        if variable.name == name {
                            args.push(variable.value.clone());
                            break;
                        }
                    }
                } else {
                    args.push(arg.to_string());
                }
            }
        }

        //Execute commands
        if let Some(cmd) = args.get(0) {
            if cmd == "if" {
                let mut value = false;

                if let Some(left) = args.get(1) {
                    if let Some(cmp) = args.get(2) {
                        if let Some(right) = args.get(3) {
                            if cmp == "==" {
                                value = *left == *right;
                            } else if cmp == "!=" {
                                value = *left != *right;
                            } else if cmp == ">" {
                                value = left.to_num_signed() > right.to_num_signed();
                            } else if cmp == ">=" {
                                value = left.to_num_signed() >= right.to_num_signed();
                            } else if cmp == "<" {
                                value = left.to_num_signed() < right.to_num_signed();
                            } else if cmp == "<=" {
                                value = left.to_num_signed() <= right.to_num_signed();
                            } else {
                                println!("Unknown comparison: {}", cmp);
                            }
                        } else {
                            println!("No right hand side");
                        }
                    } else {
                        println!("No comparison operator");
                    }
                } else {
                    println!("No left hand side");
                }

                self.modes.insert(0, Mode { value: value });
                return;
            }

            if cmd == "else" {
                let mut syntax_error = false;
                match self.modes.get_mut(0) {
                    Some(mode) => mode.value = !mode.value,
                    None => syntax_error = true,
                }
                if syntax_error {
                    println!("Syntax error: else found with no previous if");
                }
                return;
            }

            if cmd == "fi" {
                let mut syntax_error = false;
                if !self.modes.is_empty() {
                    self.modes.remove(0);
                } else {
                    syntax_error = true;
                }
                if syntax_error {
                    println!("Syntax error: fi found with no previous if");
                }
                return;
            }

            for mode in self.modes.iter() {
                if !mode.value {
                    return;
                }
            }

            if cmd == "read" {
                for i in 1..args.len() {
                    if let Some(arg_original) = args.get(i) {
                        let arg = arg_original.trim();
                        print!("{}=", arg);
                        if let Some(value_original) = readln!() {
                            let value = value_original.trim();
                            self.set_var(arg, value);
                        }
                    }
                }
            }

            //Set variables
            if let Some(i) = cmd.find('=') {
                let name = cmd[0 .. i].trim();
                let mut value = cmd[i + 1 .. cmd.len()].trim().to_string();

                for i in 1..args.len() {
                    if let Some(arg) = args.get(i) {
                        value = value + " " + &arg;
                    }
                }

                self.set_var(name, &value);
                return;
            }

            //Commands
            for command in self.commands.iter() {
                if &command.name == cmd {
                    (*command.main)(&args);
                    return;
                }
            }

            println!("Unknown command: '{}'", cmd);
        }
    }

    pub fn set_var(&mut self, name: &str, value: &str){
        if name.is_empty() {
            return;
        }

        if value.is_empty() {
            let mut remove = -1;
            for i in 0..self.variables.len() {
                match self.variables.get(i) {
                    Some(variable) => if variable.name == name {
                        remove = i as isize;
                        break;
                    },
                    None => break,
                }
            }

            if remove >= 0 {
                self.variables.remove(remove as usize);
            }
        } else {
            for variable in self.variables.iter_mut() {
                if variable.name == name {
                    variable.value = value.to_string();
                    return;
                }
            }

            self.variables.push(Variable {
                name: name.to_string(),
                value: value.to_string(),
            });
        }
    }

    /// Method to return the current directory
    /// If the current directory cannot be found, a default string ("?") will be returned
    pub fn get_current_directory(&mut self) -> String {
        // Return the current path
        File::open("")
            .and_then(|file| file.path())
            .unwrap_or("?".to_string())
    }

    /// Run the application
    pub fn main(&mut self) {
        println!("Type help for a command list");
        if let Some(arg) = args().get(1) {
            let command = "run ".to_string() + arg;
            println!("user@redox:{}# {}", self.get_current_directory(), command);
            self.on_command(&command);
        }

        loop {
            for mode in self.modes.iter().rev() {
                if mode.value {
                    print!("+ ");
                } else {
                    print!("- ");
                }
            }
            print!("user@redox:{}# ", self.get_current_directory());
            if let Some(command_original) = readln!() {
                let command = command_original.trim();
                if command == "exit" {
                    println!("Exit temporarily blocked (due to using terminal as init)")
                    //break;
                } else if !command.is_empty() {
                    self.on_command(&command);
                }
            } else {
                println!("Failed to read from stdin");
            }
        }
    }
}

pub fn main() {
    unsafe {
        let mut app = Box::new(Application::new());
        application = app.deref_mut();
        app.main();
    }
}
<commit_msg>Remove duplicate code<commit_after>use redox::get_slice::GetSlice;
use redox::ops::DerefMut;
use redox::string::*;
use redox::vec::Vec;
use redox::boxed::Box;
use redox::fs::*;
use redox::io::*;
use redox::env::*;
use redox::time::Duration;
use redox::to_num::*;

/* Magic Macros { */
static mut application: *mut Application<'static> = 0 as *mut Application;

/// Execute a command
macro_rules! exec {
    ($cmd:expr) => ({
        unsafe {
            (*application).on_command(&$cmd.to_string());
        }
    })
}
/* } Magic Macros */

/// Structure which represents a Terminal's command.
/// This command structure contains a name, and the code which run the functionnality associated to this one, with zero, one or several argument(s).
/// # Example
/// ```
/// let my_command = Command {
///     name: "my_command",
///     main: box|args: &Vec<String>| {
///         println!("Say 'hello' to my command! :-D");
///     }
/// }
/// ```
pub struct Command<'a> {
    pub name: &'a str,
    pub main: Box<Fn(&Vec<String>)>,
}

impl<'a> Command<'a> {
    /// Return the vector of the commands
    // TODO: Use a more efficient collection instead
    pub fn vec() -> Vec<Self> {
        let mut commands: Vec<Self> = Vec::new();

        commands.push(Command {
            name: "cat",
            main: Box::new(|args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read: {}", path),
                    }
                } else {
                    println!("Failed to open file: {}", path);
                }
            }),
        });

        commands.push(Command {
            name: "cd",
            main: Box::new(|args: &Vec<String>| {
                match args.get(1) {
                    Some(path) => {
                        if !change_cwd(&path) {
                            println!("Bad path: {}", path);
                        }
                    }
                    None => println!("No path given")
                }
            }),
        });

        commands.push(Command {
            name: "echo",
            main: Box::new(|args: &Vec<String>| {
                let echo = args.iter()
                    .skip(1)
                    .fold(String::new(), |string, arg| string + " " + arg);
                println!("{}", echo.trim());
            }),
        });

        commands.push(Command {
            name: "else",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "exec",
            main: Box::new(|args: &Vec<String>| {
                if let Some(arg) = args.get(1) {
                    let mut args_str: Vec<&str> = Vec::new();
                    for arg in args.get_slice(Some(2), None) {
                        args_str.push(arg);
                    }

                    File::exec(arg, &args_str);
                }
            }),
        });

        commands.push(Command {
            name: "exit",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "fi",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "if",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "ls",
            main: Box::new(|args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(dir) = read_dir(&path) {
                    for entry in dir {
                        println!("{}", entry.path());
                    }
                } else {
                    println!("Failed to open directory: {}", path);
                }
            }),
        });

        commands.push(Command {
            name: "pwd",
            main: Box::new(|_: &Vec<String>| {
                if let Some(file) = File::open("") {
                    if let Some(path) = file.path() {
                        println!("{}", path);
                    } else {
                        println!("Could not get the path");
                    }
                } else {
                    println!("Could not open the working directory");
                }
            }),
        });

        commands.push(Command {
            name: "read",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "run",
            main: Box::new(|args: &Vec<String>| {
                if let Some(path) = args.get(1) {

                    let mut commands = String::new();
                    if let Some(mut file) = File::open(path) {
                        file.read_to_string(&mut commands);
                    }

                    for command in commands.split('\n') {
                        exec!(command);
                    }
                }
            }),
        });

        commands.push(Command {
            name: "sleep",
            main: Box::new(|args: &Vec<String>| {
                let secs = {
                    match args.get(1) {
                        Some(arg) => arg.to_num() as i64,
                        None => 0,
                    }
                };

                let nanos = {
                    match args.get(2) {
                        Some(arg) => arg.to_num() as i32,
                        None => 0,
                    }
                };

                println!("Sleep: {} {}", secs, nanos);
                let remaining = Duration::new(secs, nanos).sleep();
                println!("Remaining: {} {}", remaining.secs, remaining.nanos);
            }),
        });

        commands.push(Command {
            name: "send",
            main: Box::new(|args: &Vec<String>| {
                if args.len() < 3 {
                    println!("Error: incorrect arguments");
                    println!("Usage: send [url] [data]");
                    return;
                }

                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    println!("URL: {:?}", file.path());

                    let string: String = args.iter()
                        .skip(2)
                        .fold(String::new(), |s, arg| s + " " + arg)
                        + "\r\n\r\n";

                    match file.write(string.trim_left().as_bytes()) {
                        Some(size) => println!("Wrote {} bytes", size),
                        None => println!("Failed to write"),
                    }

                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            }),
        });

        // Simple command to create a file, in the current directory
        // The file has got the name given as the first argument of the command
        // If the command have no arguments, the command don't create the file
        commands.push(Command {
            name: "touch",
            main: Box::new(|args: &Vec<String>| {
                match args.get(1) {
                    Some(file_name) => if File::create(file_name).is_none() {
                        println!("Failed to create: {}", file_name);
                    },
                    None => println!("No name provided")
                }
            }),
        });

        commands.push(Command {
            name: "url_hex",
            main: Box::new(|args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    let mut vec: Vec<u8> = Vec::new();
                    match file.read_to_end(&mut vec) {
                        Some(_) => {
                            let mut line = "HEX:".to_string();
                            for byte in vec.iter() {
                                line = line + " " + &format!("{:X}", *byte);
                            }
                            println!("{}", line);
                        }
                        None => println!("Failed to read"),
                    }
                }
            }),
        });

        commands.push(Command {
            name: "wget",
            main: Box::new(|args: &Vec<String>| {
                if let Some(host) = args.get(1) {
                    if let Some(req) = args.get(2) {
                        if let Some(mut con) = File::open(&("tcp://".to_string() + host)) {
                            con.write(("GET ".to_string() + req + " HTTP/1.1").as_bytes());

                            let mut res = Vec::new();
                            con.read_to_end(&mut res);

                            if let Some(mut file) = File::open(&req) {
                                file.write(&res);
                            }
                        }
                    } else {
                        println!("No request given");
                    }
                } else {
                    println!("No url given");
                }
            }),
        });

        let command_list = commands.iter().fold(String::new(), |l , c| l + " " + c.name);

        commands.push(Command {
            name: "help",
            main: Box::new(move |_: &Vec<String>| {
                println!("Commands:{}", command_list);
            }),
         });

        commands
    }
}

/// A (env) variable
pub struct Variable {
    pub name: String,
    pub value: String,
}

pub struct Mode {
    value: bool,
}

/// An application
pub struct Application<'a> {
    commands: Vec<Command<'a>>,
    variables: Vec<Variable>,
    modes: Vec<Mode>,
}

impl<'a> Application<'a> {
    /// Create a new empty application
    pub fn new() -> Self {
        return Application {
            commands: Command::vec(),
            variables: Vec::new(),
            modes: Vec::new(),
        };
    }

    fn on_command(&mut self, command_string: &str) {
        //Comment
        if command_string.starts_with('#') {
            return;
        }

        //Show variables
        if command_string == "$" {
            for variable in self.variables.iter() {
                println!("{}={}", variable.name, variable.value);
            }
            return;
        }

        //Explode into arguments, replace variables
        let mut args: Vec<String> = Vec::<String>::new();
        for arg in command_string.split(' ') {
            if !arg.is_empty() {
                if arg.starts_with('$') {
                    let name = arg[1 .. arg.len()].to_string();
                    for variable in self.variables.iter() {
                        if variable.name == name {
                            args.push(variable.value.clone());
                            break;
                        }
                    }
                } else {
                    args.push(arg.to_string());
                }
            }
        }

        //Execute commands
        if let Some(cmd) = args.get(0) {
            if cmd == "if" {
                let mut value = false;

                if let Some(left) = args.get(1) {
                    if let Some(cmp) = args.get(2) {
                        if let Some(right) = args.get(3) {
                            if cmp == "==" {
                                value = *left == *right;
                            } else if cmp == "!=" {
                                value = *left != *right;
                            } else if cmp == ">" {
                                value = left.to_num_signed() > right.to_num_signed();
                            } else if cmp == ">=" {
                                value = left.to_num_signed() >= right.to_num_signed();
                            } else if cmp == "<" {
                                value = left.to_num_signed() < right.to_num_signed();
                            } else if cmp == "<=" {
                                value = left.to_num_signed() <= right.to_num_signed();
                            } else {
                                println!("Unknown comparison: {}", cmp);
                            }
                        } else {
                            println!("No right hand side");
                        }
                    } else {
                        println!("No comparison operator");
                    }
                } else {
                    println!("No left hand side");
                }

                self.modes.insert(0, Mode { value: value });
                return;
            }

            if cmd == "else" {
                let mut syntax_error = false;
                match self.modes.get_mut(0) {
                    Some(mode) => mode.value = !mode.value,
                    None => syntax_error = true,
                }
                if syntax_error {
                    println!("Syntax error: else found with no previous if");
                }
                return;
            }

            if cmd == "fi" {
                let mut syntax_error = false;
                if !self.modes.is_empty() {
                    self.modes.remove(0);
                } else {
                    syntax_error = true;
                }
                if syntax_error {
                    println!("Syntax error: fi found with no previous if");
                }
                return;
            }

            for mode in self.modes.iter() {
                if !mode.value {
                    return;
                }
            }

            if cmd == "read" {
                for i in 1..args.len() {
                    if let Some(arg_original) = args.get(i) {
                        let arg = arg_original.trim();
                        print!("{}=", arg);
                        if let Some(value_original) = readln!() {
                            let value = value_original.trim();
                            self.set_var(arg, value);
                        }
                    }
                }
            }

            //Set variables
            if let Some(i) = cmd.find('=') {
                let name = cmd[0 .. i].trim();
                let mut value = cmd[i + 1 .. cmd.len()].trim().to_string();

                for i in 1..args.len() {
                    if let Some(arg) = args.get(i) {
                        value = value + " " + &arg;
                    }
                }

                self.set_var(name, &value);
                return;
            }

            //Commands
            for command in self.commands.iter() {
                if &command.name == cmd {
                    (*command.main)(&args);
                    return;
                }
            }

            println!("Unknown command: '{}'", cmd);
        }
    }

    pub fn set_var(&mut self, name: &str, value: &str){
        if name.is_empty() {
            return;
        }

        if value.is_empty() {
            let mut remove = -1;
            for i in 0..self.variables.len() {
                match self.variables.get(i) {
                    Some(variable) => if variable.name == name {
                        remove = i as isize;
                        break;
                    },
                    None => break,
                }
            }

            if remove >= 0 {
                self.variables.remove(remove as usize);
            }
        } else {
            for variable in self.variables.iter_mut() {
                if variable.name == name {
                    variable.value = value.to_string();
                    return;
                }
            }

            self.variables.push(Variable {
                name: name.to_string(),
                value: value.to_string(),
            });
        }
    }

    /// Method to return the current directory
    /// If the current directory cannot be found, a default string ("?") will be returned
    pub fn get_current_directory(&mut self) -> String {
        // Return the current path
        File::open("")
            .and_then(|file| file.path())
            .unwrap_or("?".to_string())
    }

    /// Run the application
    pub fn main(&mut self) {
        println!("Type help for a command list");
        if let Some(arg) = args().get(1) {
            let command = "run ".to_string() + arg;
            println!("user@redox:{}# {}", self.get_current_directory(), command);
            self.on_command(&command);
        }

        loop {
            for mode in self.modes.iter().rev() {
                if mode.value {
                    print!("+ ");
                } else {
                    print!("- ");
                }
            }
            print!("user@redox:{}# ", self.get_current_directory());
            if let Some(command_original) = readln!() {
                let command = command_original.trim();
                if command == "exit" {
                    println!("Exit temporarily blocked (due to using terminal as init)")
                    //break;
                } else if !command.is_empty() {
                    self.on_command(&command);
                }
            } else {
                println!("Failed to read from stdin");
            }
        }
    }
}

pub fn main() {
    unsafe {
        let mut app = Box::new(Application::new());
        application = app.deref_mut();
        app.main();
    }
}
<|endoftext|>


<commit_before><commit_msg>add support for == operator on Termios structs<commit_after><|endoftext|>


<commit_before>// Copyright 2014 Rich Lane.

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

#[feature(globs)];
#[feature(macro_rules)];

extern mod extra;
extern mod cgmath;
extern mod noise;

use extra::time::precise_time_ns;

use cgmath::vector::Vector;
use cgmath::vector::Vec3;

use terrain::Terrain;
use spiral::Spiral;

mod terrain;
mod spiral;

pub static N: uint = 4;
pub static CHUNK_SIZE: int = 32;
pub static WORLD_SEED: u32 = 42;
pub static WORLD_HEIGHT: int = 4;

fn main() {
    let mut spiral = Spiral::<f64>::new(N);

    let start_time = precise_time_ns();

    for v in spiral {
        let p = Vec3::new(v.x, 0.0, v.y).mul_s(CHUNK_SIZE as f64);
        Terrain::gen(WORLD_SEED, p);
    }

    let end_time = precise_time_ns();

    println!("average : {}us",
            (end_time - start_time)/(1000*N as u64));
}
<commit_msg>improve terrain benchmark<commit_after>// Copyright 2014 Rich Lane.

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

#[feature(globs)];
#[feature(macro_rules)];

extern mod extra;
extern mod cgmath;
extern mod noise;

use extra::time::precise_time_ns;

use cgmath::vector::Vector;
use cgmath::vector::Vec3;

use terrain::Terrain;

mod terrain;

pub static NUM_ITERS: uint = 1;
pub static VISIBLE_RADIUS: uint = 8;
pub static CHUNK_SIZE: int = 32;
pub static WORLD_SEED: u32 = 42;

fn main() {
    let mut times = ~[];

    let camera_position = Vec3::new(0.0, 0.0, 0.0);
    for &c in nearby_chunk_coords(camera_position).iter() {
        let p = Vec3::new(c.x as f64, c.y as f64, c.z as f64).mul_s(CHUNK_SIZE as f64);
        let start_time = precise_time_ns();
        Terrain::gen(WORLD_SEED, p);
        let end_time = precise_time_ns();
        times.push((end_time - start_time)/1000);
    }

    times.sort();

    println!("{} chunks generated", times.len());
    println!("minimum : {}us", times[0]);
    println!("median  : {}us", times[times.len()/2]);
    println!("maximum : {}us", times[times.len()-1]);
}

fn nearby_chunk_coords(p: Vec3<f64>) -> ~[Vec3<i64>] {
    let cur_chunk_coord = Vec3::new(p.x as i64, p.y as i64, p.z as i64).div_s(CHUNK_SIZE as i64);
    let r = VISIBLE_RADIUS as i64;

    let mut coords = ~[];

    for x in range(-r, r+1) {
        for y in range(-r, r+1) {
            for z in range(-r, r+1) {
                let c = Vec3::new(x, y, z);
                if c.dot(&c) < r*r {
                    coords.push(c);
                }
            }
        }
    }

    coords.sort_by(|b,a| b.dot(b).cmp(&a.dot(a)));

    for c in coords.mut_iter() {
        c.add_self_v(&cur_chunk_coord);
    }

    coords
}
<|endoftext|>


<commit_before>use std::process::{Command, Stdio, Child};
use std::io::Write;

pub fn spawn(sandbox: &str,
             command: &str,
             syscalls: &str,
             playpen_args: &[&str],
             args: &[&str],
             timeout: Option<usize>,
             merge_stderr: bool)
             -> Result<Child, String> {
    let mut cmd = Command::new("sudo");
    cmd.arg("playpen")
       .arg(sandbox)
       .arg(format!("--syscalls-file={}", syscalls))
       .args(playpen_args);
    if let Some(x) = timeout {
        cmd.arg(format!("--timeout={}", x));
    }
    cmd.arg("--")
       .arg(command)
       .args(args)
       .stdin(Stdio::piped())
       .stdout(Stdio::piped());
    if merge_stderr {
        cmd.stderr(Stdio::piped());
    } else {
        cmd.stderr(Stdio::inherit());
    }
    cmd.spawn().map_err(|x| format!("couldn't playpen_exec; {}", x))
}

pub fn exec_wait(sandbox: &str,
                 command: &str,
                 syscalls: &str,
                 playpen_args: &[&str],
                 args: &[&str],
                 input: &str,
                 timeout: usize)
                 -> Result<String, String> {
    let mut child = try!(spawn(sandbox, command, syscalls, playpen_args, args, Some(timeout), true));
    if let Some(ref mut x) = child.stdin {
        try!(x.write_all(input.as_bytes())
              .map_err(|x| format!("couldn't write to stdin; {}", x)));
    } else {
        return Err("no stdin?".to_owned());
    }
    let output = try!(child.wait_with_output()
                           .map_err(|x| format!("wait_with_output failed; {}", x)));
    Ok({
        let mut out = String::from_utf8_lossy(&output.stdout).into_owned();
        if !out.ends_with("\n") { out.push_str("\n"); }
        out.push_str(&*String::from_utf8_lossy(&output.stderr));
        out})
}
<commit_msg>Trim output<commit_after>use std::process::{Command, Stdio, Child};
use std::io::Write;

pub fn spawn(sandbox: &str,
             command: &str,
             syscalls: &str,
             playpen_args: &[&str],
             args: &[&str],
             timeout: Option<usize>,
             merge_stderr: bool)
             -> Result<Child, String> {
    let mut cmd = Command::new("sudo");
    cmd.arg("playpen")
       .arg(sandbox)
       .arg(format!("--syscalls-file={}", syscalls))
       .args(playpen_args);
    if let Some(x) = timeout {
        cmd.arg(format!("--timeout={}", x));
    }
    cmd.arg("--")
       .arg(command)
       .args(args)
       .stdin(Stdio::piped())
       .stdout(Stdio::piped());
    if merge_stderr {
        cmd.stderr(Stdio::piped());
    } else {
        cmd.stderr(Stdio::inherit());
    }
    cmd.spawn().map_err(|x| format!("couldn't playpen_exec; {}", x))
}

pub fn exec_wait(sandbox: &str,
                 command: &str,
                 syscalls: &str,
                 playpen_args: &[&str],
                 args: &[&str],
                 input: &str,
                 timeout: usize)
                 -> Result<String, String> {
    let mut child = try!(spawn(sandbox, command, syscalls, playpen_args, args, Some(timeout), true));
    if let Some(ref mut x) = child.stdin {
        try!(x.write_all(input.as_bytes())
              .map_err(|x| format!("couldn't write to stdin; {}", x)));
    } else {
        return Err("no stdin?".to_owned());
    }
    let output = try!(child.wait_with_output()
                           .map_err(|x| format!("wait_with_output failed; {}", x)));
    Ok({
        let mut out = String::from_utf8_lossy(&output.stdout).trim().to_owned();
        out.push_str("\n");
        out.push_str(String::from_utf8_lossy(&output.stderr).trim());
        out.trim().to_owned()})
}
<|endoftext|>


<commit_before>use vangers::{
    config,
    render::{ScreenTargets, COLOR_FORMAT, DEPTH_FORMAT},
};

use env_logger;
use futures::executor::{LocalPool, LocalSpawner};
use log::info;
use winit::{
    event,
    event_loop::{ControlFlow, EventLoop},
    window::{Window, WindowBuilder},
};

pub trait Application {
    fn on_key(&mut self, input: event::KeyboardInput) -> bool;
    fn on_mouse_wheel(&mut self, _delta: event::MouseScrollDelta) {}
    fn on_cursor_move(&mut self, _position: (f64, f64)) {}
    fn on_mouse_button(&mut self, _state: event::ElementState, _button: event::MouseButton) {}
    fn resize(&mut self, _device: &wgpu::Device, _extent: wgpu::Extent3d) {}
    fn reload(&mut self, device: &wgpu::Device);
    fn update(
        &mut self,
        device: &wgpu::Device,
        delta: f32,
        spawner: &LocalSpawner,
    ) -> Vec<wgpu::CommandBuffer>;
    fn draw(
        &mut self,
        device: &wgpu::Device,
        targets: ScreenTargets,
        spawner: &LocalSpawner,
    ) -> wgpu::CommandBuffer;
}

pub struct Harness {
    task_pool: LocalPool,
    event_loop: EventLoop<()>,
    window: Window,
    pub device: wgpu::Device,
    pub queue: wgpu::Queue,
    surface: wgpu::Surface,
    swap_chain: wgpu::SwapChain,
    pub extent: wgpu::Extent3d,
    reload_on_focus: bool,
    depth_target: wgpu::TextureView,
}

impl Harness {
    pub fn init(title: &str) -> (Self, config::Settings) {
        env_logger::init();
        let mut task_pool = LocalPool::new();

        info!("Loading the settings");
        let settings = config::Settings::load("config/settings.ron");
        let extent = wgpu::Extent3d {
            width: settings.window.size[0],
            height: settings.window.size[1],
            depth: 1,
        };

        info!("Initializing the window");
        let instance = wgpu::Instance::new(settings.backend.to_wgpu());
        let event_loop = EventLoop::new();
        let window = WindowBuilder::new()
            .with_title(title)
            .with_inner_size(winit::dpi::PhysicalSize::new(extent.width, extent.height))
            .with_resizable(true)
            .build(&event_loop)
            .unwrap();
        let surface = unsafe { instance.create_surface(&window) };

        info!("Initializing the device");
        let adapter = task_pool
            .run_until(instance.request_adapter(&wgpu::RequestAdapterOptions {
                power_preference: wgpu::PowerPreference::Default,
                compatible_surface: Some(&surface),
            }))
            .expect("Unable to initialize GPU via the selected backend.");
        let (device, queue) = task_pool
            .run_until(adapter.request_device(
                &wgpu::DeviceDescriptor {
                    features: wgpu::Features::empty(),
                    limits: wgpu::Limits::default(),
                    shader_validation: true,
                },
                None,
            ))
            .unwrap();

        let sc_desc = wgpu::SwapChainDescriptor {
            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
            format: COLOR_FORMAT,
            width: extent.width,
            height: extent.height,
            present_mode: wgpu::PresentMode::Mailbox,
        };
        let swap_chain = device.create_swap_chain(&surface, &sc_desc);
        let depth_target = device
            .create_texture(&wgpu::TextureDescriptor {
                label: Some("Depth"),
                size: extent,
                mip_level_count: 1,
                sample_count: 1,
                dimension: wgpu::TextureDimension::D2,
                format: DEPTH_FORMAT,
                usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
            })
            .create_view(&wgpu::TextureViewDescriptor::default());

        let harness = Harness {
            task_pool,
            event_loop,
            window: window,
            device,
            queue,
            surface,
            swap_chain,
            extent,
            reload_on_focus: settings.window.reload_on_focus,
            depth_target,
        };

        (harness, settings)
    }

    pub fn main_loop<A: 'static + Application>(self, mut app: A) {
        use std::time;

        let mut last_time = time::Instant::now();
        let mut needs_reload = false;
        let Harness {
            mut task_pool,
            event_loop,
            window,
            device,
            queue,
            surface,
            mut swap_chain,
            mut extent,
            reload_on_focus,
            mut depth_target,
        } = self;

        event_loop.run(move |event, _, control_flow| {
            let _ = window;
            *control_flow = ControlFlow::Poll;
            task_pool.run_until_stalled();

            match event {
                event::Event::WindowEvent {
                    event: event::WindowEvent::Resized(size),
                    ..
                } => {
                    info!("Resizing to {:?}", size);
                    extent = wgpu::Extent3d {
                        width: size.width,
                        height: size.height,
                        depth: 1,
                    };
                    let sc_desc = wgpu::SwapChainDescriptor {
                        usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
                        format: COLOR_FORMAT,
                        width: size.width,
                        height: size.height,
                        present_mode: wgpu::PresentMode::Mailbox,
                    };
                    swap_chain = device.create_swap_chain(&surface, &sc_desc);
                    depth_target = device
                        .create_texture(&wgpu::TextureDescriptor {
                            label: Some("Depth"),
                            size: extent,
                            mip_level_count: 1,
                            sample_count: 1,
                            dimension: wgpu::TextureDimension::D2,
                            format: DEPTH_FORMAT,
                            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
                        })
                        .create_view(&wgpu::TextureViewDescriptor::default());
                    app.resize(&device, extent);
                }
                event::Event::WindowEvent { event, .. } => match event {
                    event::WindowEvent::Focused(false) => {
                        needs_reload = reload_on_focus;
                    }
                    event::WindowEvent::Focused(true) if needs_reload => {
                        info!("Reloading shaders");
                        app.reload(&device);
                        needs_reload = false;
                    }
                    event::WindowEvent::CloseRequested => {
                        *control_flow = ControlFlow::Exit;
                    }
                    event::WindowEvent::KeyboardInput { input, .. } => {
                        if !app.on_key(input) {
                            *control_flow = ControlFlow::Exit;
                        }
                    }
                    event::WindowEvent::MouseWheel { delta, .. } => app.on_mouse_wheel(delta),
                    event::WindowEvent::CursorMoved { position, .. } => {
                        app.on_cursor_move(position.into())
                    }
                    event::WindowEvent::MouseInput { state, button, .. } => {
                        app.on_mouse_button(state, button)
                    }
                    _ => {}
                },
                event::Event::MainEventsCleared => {
                    let spawner = task_pool.spawner();
                    let duration = time::Instant::now() - last_time;
                    last_time += duration;
                    let delta = duration.as_secs() as f32 + duration.subsec_nanos() as f32 * 1.0e-9;

                    let update_command_buffers = app.update(&device, delta, &spawner);
                    if !update_command_buffers.is_empty() {
                        queue.submit(update_command_buffers);
                    }

                    match swap_chain.get_current_frame() {
                        Ok(frame) => {
                            let targets = ScreenTargets {
                                extent,
                                color: &frame.output.view,
                                depth: &depth_target,
                            };
                            let render_commane_buffer = app.draw(&device, targets, &spawner);
                            queue.submit(Some(render_commane_buffer));
                        }
                        Err(_) => {}
                    };
                }
                _ => (),
            }
        });
    }
}
<commit_msg>Fix variable name typo<commit_after>use vangers::{
    config,
    render::{ScreenTargets, COLOR_FORMAT, DEPTH_FORMAT},
};

use env_logger;
use futures::executor::{LocalPool, LocalSpawner};
use log::info;
use winit::{
    event,
    event_loop::{ControlFlow, EventLoop},
    window::{Window, WindowBuilder},
};

pub trait Application {
    fn on_key(&mut self, input: event::KeyboardInput) -> bool;
    fn on_mouse_wheel(&mut self, _delta: event::MouseScrollDelta) {}
    fn on_cursor_move(&mut self, _position: (f64, f64)) {}
    fn on_mouse_button(&mut self, _state: event::ElementState, _button: event::MouseButton) {}
    fn resize(&mut self, _device: &wgpu::Device, _extent: wgpu::Extent3d) {}
    fn reload(&mut self, device: &wgpu::Device);
    fn update(
        &mut self,
        device: &wgpu::Device,
        delta: f32,
        spawner: &LocalSpawner,
    ) -> Vec<wgpu::CommandBuffer>;
    fn draw(
        &mut self,
        device: &wgpu::Device,
        targets: ScreenTargets,
        spawner: &LocalSpawner,
    ) -> wgpu::CommandBuffer;
}

pub struct Harness {
    task_pool: LocalPool,
    event_loop: EventLoop<()>,
    window: Window,
    pub device: wgpu::Device,
    pub queue: wgpu::Queue,
    surface: wgpu::Surface,
    swap_chain: wgpu::SwapChain,
    pub extent: wgpu::Extent3d,
    reload_on_focus: bool,
    depth_target: wgpu::TextureView,
}

impl Harness {
    pub fn init(title: &str) -> (Self, config::Settings) {
        env_logger::init();
        let mut task_pool = LocalPool::new();

        info!("Loading the settings");
        let settings = config::Settings::load("config/settings.ron");
        let extent = wgpu::Extent3d {
            width: settings.window.size[0],
            height: settings.window.size[1],
            depth: 1,
        };

        info!("Initializing the window");
        let instance = wgpu::Instance::new(settings.backend.to_wgpu());
        let event_loop = EventLoop::new();
        let window = WindowBuilder::new()
            .with_title(title)
            .with_inner_size(winit::dpi::PhysicalSize::new(extent.width, extent.height))
            .with_resizable(true)
            .build(&event_loop)
            .unwrap();
        let surface = unsafe { instance.create_surface(&window) };

        info!("Initializing the device");
        let adapter = task_pool
            .run_until(instance.request_adapter(&wgpu::RequestAdapterOptions {
                power_preference: wgpu::PowerPreference::Default,
                compatible_surface: Some(&surface),
            }))
            .expect("Unable to initialize GPU via the selected backend.");
        let (device, queue) = task_pool
            .run_until(adapter.request_device(
                &wgpu::DeviceDescriptor {
                    features: wgpu::Features::empty(),
                    limits: wgpu::Limits::default(),
                    shader_validation: true,
                },
                None,
            ))
            .unwrap();

        let sc_desc = wgpu::SwapChainDescriptor {
            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
            format: COLOR_FORMAT,
            width: extent.width,
            height: extent.height,
            present_mode: wgpu::PresentMode::Mailbox,
        };
        let swap_chain = device.create_swap_chain(&surface, &sc_desc);
        let depth_target = device
            .create_texture(&wgpu::TextureDescriptor {
                label: Some("Depth"),
                size: extent,
                mip_level_count: 1,
                sample_count: 1,
                dimension: wgpu::TextureDimension::D2,
                format: DEPTH_FORMAT,
                usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
            })
            .create_view(&wgpu::TextureViewDescriptor::default());

        let harness = Harness {
            task_pool,
            event_loop,
            window: window,
            device,
            queue,
            surface,
            swap_chain,
            extent,
            reload_on_focus: settings.window.reload_on_focus,
            depth_target,
        };

        (harness, settings)
    }

    pub fn main_loop<A: 'static + Application>(self, mut app: A) {
        use std::time;

        let mut last_time = time::Instant::now();
        let mut needs_reload = false;
        let Harness {
            mut task_pool,
            event_loop,
            window,
            device,
            queue,
            surface,
            mut swap_chain,
            mut extent,
            reload_on_focus,
            mut depth_target,
        } = self;

        event_loop.run(move |event, _, control_flow| {
            let _ = window;
            *control_flow = ControlFlow::Poll;
            task_pool.run_until_stalled();

            match event {
                event::Event::WindowEvent {
                    event: event::WindowEvent::Resized(size),
                    ..
                } => {
                    info!("Resizing to {:?}", size);
                    extent = wgpu::Extent3d {
                        width: size.width,
                        height: size.height,
                        depth: 1,
                    };
                    let sc_desc = wgpu::SwapChainDescriptor {
                        usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
                        format: COLOR_FORMAT,
                        width: size.width,
                        height: size.height,
                        present_mode: wgpu::PresentMode::Mailbox,
                    };
                    swap_chain = device.create_swap_chain(&surface, &sc_desc);
                    depth_target = device
                        .create_texture(&wgpu::TextureDescriptor {
                            label: Some("Depth"),
                            size: extent,
                            mip_level_count: 1,
                            sample_count: 1,
                            dimension: wgpu::TextureDimension::D2,
                            format: DEPTH_FORMAT,
                            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
                        })
                        .create_view(&wgpu::TextureViewDescriptor::default());
                    app.resize(&device, extent);
                }
                event::Event::WindowEvent { event, .. } => match event {
                    event::WindowEvent::Focused(false) => {
                        needs_reload = reload_on_focus;
                    }
                    event::WindowEvent::Focused(true) if needs_reload => {
                        info!("Reloading shaders");
                        app.reload(&device);
                        needs_reload = false;
                    }
                    event::WindowEvent::CloseRequested => {
                        *control_flow = ControlFlow::Exit;
                    }
                    event::WindowEvent::KeyboardInput { input, .. } => {
                        if !app.on_key(input) {
                            *control_flow = ControlFlow::Exit;
                        }
                    }
                    event::WindowEvent::MouseWheel { delta, .. } => app.on_mouse_wheel(delta),
                    event::WindowEvent::CursorMoved { position, .. } => {
                        app.on_cursor_move(position.into())
                    }
                    event::WindowEvent::MouseInput { state, button, .. } => {
                        app.on_mouse_button(state, button)
                    }
                    _ => {}
                },
                event::Event::MainEventsCleared => {
                    let spawner = task_pool.spawner();
                    let duration = time::Instant::now() - last_time;
                    last_time += duration;
                    let delta = duration.as_secs() as f32 + duration.subsec_nanos() as f32 * 1.0e-9;

                    let update_command_buffers = app.update(&device, delta, &spawner);
                    if !update_command_buffers.is_empty() {
                        queue.submit(update_command_buffers);
                    }

                    match swap_chain.get_current_frame() {
                        Ok(frame) => {
                            let targets = ScreenTargets {
                                extent,
                                color: &frame.output.view,
                                depth: &depth_target,
                            };
                            let render_command_buffer = app.draw(&device, targets, &spawner);
                            queue.submit(Some(render_command_buffer));
                        }
                        Err(_) => {}
                    };
                }
                _ => (),
            }
        });
    }
}
<|endoftext|>