<commit_before>use godcoin::{net::*, prelude::{KeyPair, PrivateKey, Wif}};
use rustyline::{error::ReadlineError, Editor};
use std::{path::PathBuf, io::{Read, Cursor}};
use reqwest::{Url, Client};

mod db;
mod parser;

use self::db::{Db, DbState, Password};

macro_rules! check_unlocked {
    ($self:expr) => {
        if $self.db.state() != DbState::Unlocked {
            return Err("wallet not unlocked".to_owned());
        }
    };
}

macro_rules! check_args {
    ($args:expr, $count:expr) => {
        if $args.len() != $count {
            return Err("Missing arguments or too many provided".to_owned());
        }
    };
}

pub struct Wallet {
    prompt: String,
    url: Url,
    db: Db,
}

impl Wallet {
    pub fn new(home: PathBuf) -> Wallet {
        let db = Db::new(home.join("db"));
        let prompt = (if db.state() == DbState::Locked {
            "locked>> "
        } else {
            "new>> "
        })
        .to_owned();
        Wallet { db, prompt, url: "http://localhost:7777".parse().unwrap() }
    }

    pub fn start(mut self) {
        let mut rl = Editor::<()>::new();
        loop {
            let readline = rl.readline(&self.prompt);
            match readline {
                Ok(line) => {
                    if line.is_empty() {
                        continue;
                    }
                    let mut args = parser::parse_line(&line);

                    match self.process_line(&mut args) {
                        Ok(store_history) => {
                            if store_history {
                                rl.add_history_entry(line);
                            } else {
                                sodiumoxide::utils::memzero(&mut line.into_bytes());
                            }
                        }
                        Err(s) => {
                            println!("{}", s);
                            sodiumoxide::utils::memzero(&mut line.into_bytes());
                        }
                    }

                    for a in args {
                        sodiumoxide::utils::memzero(&mut a.into_bytes());
                    }
                }
                Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {
                    println!("Closing walllet...");
                    break;
                }
                Err(err) => {
                    println!("Error reading input: {:?}", err);
                    break;
                }
            }
        }
    }

    fn process_line(&mut self, args: &mut Vec<String>) -> Result<bool, String> {
        if args.len() == 0 {
            return Ok(false);
        }
        match &*args[0] {
            "new" => {
                let state = self.db.state();
                if state != DbState::New {
                    if state == DbState::Locked {
                        println!("Use unlock to use the existing wallet");
                        return Ok(false);
                    } else if state == DbState::Unlocked {
                        println!("Existing wallet already unlocked");
                        return Ok(false);
                    } else {
                        return Err(format!("Unknown state: {:?}", state));
                    }
                }

                check_args!(args, 2);
                let pass = &Password(args.remove(1).into_bytes());
                self.db.set_password(pass);
                self.prompt = "locked>> ".to_owned();
                return Ok(false);
            }
            "unlock" => {
                let state = self.db.state();
                if state != DbState::Locked {
                    if state == DbState::New {
                        println!("A wallet has not yet been created, use new to create one");
                        return Ok(false);
                    } else if state == DbState::Unlocked {
                        println!("Wallet already unlocked");
                        return Ok(false);
                    }
                    return Err(format!("Unknown state: {:?}", state));
                }

                check_args!(args, 2);
                let pass = &Password(args.remove(1).into_bytes());
                if self.db.unlock(pass) {
                    self.prompt = "unlocked>> ".to_owned();
                } else {
                    println!("Failed to unlock wallet...incorrect password");
                }
                return Ok(false);
            }
            "create_account" => {
                check_unlocked!(self);
                let account = &args[1];
                if self.db.get_account(account).is_some() {
                    println!("Account already exists");
                    return Ok(true);
                }
                let key = KeyPair::gen_keypair();
                self.db.set_account(account, &key.1);
                println!("Public key => {}", key.0.to_wif());
                println!("Private key => {}", key.1.to_wif());
            }
            "import_account" => {
                check_unlocked!(self);
                check_args!(args, 3);
                let account = &args[1];
                let wif = PrivateKey::from_wif(&args[2]).map_err(|_| "Invalid wif".to_owned())?;
                for (acc, pair) in self.db.get_accounts() {
                    if &acc == account {
                        println!("Account already exists");
                        return Ok(true);
                    } else if pair.1 == wif.1 {
                        println!("Wif already exists under account `{}`", &acc);
                        return Ok(true);
                    }
                }
                self.db.set_account(account, &wif.1);
            }
            "get_account" => {
                check_unlocked!(self);
                check_args!(args, 2);
                let key = self.db.get_account(&args[1]);
                match key {
                    Some(key) => {
                        println!("Public key => {}", key.0.to_wif());
                        println!("Private key => {}", key.1.to_wif());
                    }
                    None => {
                        println!("Account not found");
                    }
                }
            }
            "delete_account" => {
                check_unlocked!(self);
                check_args!(args, 2);
                if self.db.del_account(&args[1]) {
                    println!("Account permanently deleted");
                } else {
                    println!("Account not found");
                }
            }
            "list_accounts" => {
                check_unlocked!(self);
                println!("Accounts:");
                for (acc, key) in self.db.get_accounts() {
                    println!("  {} => {}", acc, key.0.to_wif());
                }
            }
            "get_block" => {
                check_args!(args, 2);
                let height: u64 = args[1].parse().map_err(|_| {
                    "Failed to parse height argument".to_owned()
                })?;
                let client = Client::new();
                let res = client.post(self.url.clone())
                    .body(MsgRequest::GetBlock(height).serialize())
                    .send();
                match res {
                    Ok(mut res) => {
                        let len = res.content_length().unwrap_or(0);
                        let mut content = Vec::with_capacity(len as usize);
                        res.read_to_end(&mut content).map_err(|e| {
                            format!("{}", e)
                        })?;
                        let mut cursor = Cursor::<&[u8]>::new(&content);
                        let res = MsgResponse::deserialize(&mut cursor).map_err(|e| {
                            format!("Failed to deserialize response: {}", e)
                        })?;
                        println!("{:?}", res);
                    },
                    Err(e) => {
                        return Err(format!("{}", e))
                    }
                }
            }
            "help" => {
                Self::print_usage("Displaying help...");
            }
            _ => {
                Self::print_usage(&format!("Invalid command: {}", args[0]));
            }
        }
        Ok(true)
    }

    fn print_usage(header: &str) {
        let mut cmds = Vec::<[&str; 2]>::new();
        cmds.push(["help", "Display this help menu"]);
        cmds.push(["new <password>", "Create a new wallet"]);
        cmds.push(["unlock <password>", "Unlock an existing wallet"]);
        cmds.push(["create_account <account>", "Create an account"]);
        cmds.push(["import_account <account> <wif>", "Import an account"]);
        cmds.push(["delete_account <account>", "Delete an existing account"]);
        cmds.push(["get_account <account>", "Retrieve account information"]);
        cmds.push(["list_accounts", "List all accounts"]);
        cmds.push(["list_accounts", "List all accounts"]);

        let mut max_len = 0;
        for cmd in &cmds {
            assert!(cmd.len() == 2);
            let cmd_len = cmd[0].len();
            if cmd_len > max_len {
                max_len = cmd_len;
            }
        }

        println!("{}\n", header);
        for cmd in &cmds {
            let mut c = cmd[0].to_owned();
            if c.len() < max_len {
                for _ in 0..max_len - c.len() {
                    c.push(' ');
                }
            }
            println!("  {}  {}", c, cmd[1]);
        }
        println!("");
    }
}
<commit_msg>Pretty print the debug result of get_block<commit_after>use godcoin::{net::*, prelude::{KeyPair, PrivateKey, Wif}};
use rustyline::{error::ReadlineError, Editor};
use std::{path::PathBuf, io::{Read, Cursor}};
use reqwest::{Url, Client};

mod db;
mod parser;

use self::db::{Db, DbState, Password};

macro_rules! check_unlocked {
    ($self:expr) => {
        if $self.db.state() != DbState::Unlocked {
            return Err("wallet not unlocked".to_owned());
        }
    };
}

macro_rules! check_args {
    ($args:expr, $count:expr) => {
        if $args.len() != $count {
            return Err("Missing arguments or too many provided".to_owned());
        }
    };
}

pub struct Wallet {
    prompt: String,
    url: Url,
    db: Db,
}

impl Wallet {
    pub fn new(home: PathBuf) -> Wallet {
        let db = Db::new(home.join("db"));
        let prompt = (if db.state() == DbState::Locked {
            "locked>> "
        } else {
            "new>> "
        })
        .to_owned();
        Wallet { db, prompt, url: "http://localhost:7777".parse().unwrap() }
    }

    pub fn start(mut self) {
        let mut rl = Editor::<()>::new();
        loop {
            let readline = rl.readline(&self.prompt);
            match readline {
                Ok(line) => {
                    if line.is_empty() {
                        continue;
                    }
                    let mut args = parser::parse_line(&line);

                    match self.process_line(&mut args) {
                        Ok(store_history) => {
                            if store_history {
                                rl.add_history_entry(line);
                            } else {
                                sodiumoxide::utils::memzero(&mut line.into_bytes());
                            }
                        }
                        Err(s) => {
                            println!("{}", s);
                            sodiumoxide::utils::memzero(&mut line.into_bytes());
                        }
                    }

                    for a in args {
                        sodiumoxide::utils::memzero(&mut a.into_bytes());
                    }
                }
                Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {
                    println!("Closing walllet...");
                    break;
                }
                Err(err) => {
                    println!("Error reading input: {:?}", err);
                    break;
                }
            }
        }
    }

    fn process_line(&mut self, args: &mut Vec<String>) -> Result<bool, String> {
        if args.len() == 0 {
            return Ok(false);
        }
        match &*args[0] {
            "new" => {
                let state = self.db.state();
                if state != DbState::New {
                    if state == DbState::Locked {
                        println!("Use unlock to use the existing wallet");
                        return Ok(false);
                    } else if state == DbState::Unlocked {
                        println!("Existing wallet already unlocked");
                        return Ok(false);
                    } else {
                        return Err(format!("Unknown state: {:?}", state));
                    }
                }

                check_args!(args, 2);
                let pass = &Password(args.remove(1).into_bytes());
                self.db.set_password(pass);
                self.prompt = "locked>> ".to_owned();
                return Ok(false);
            }
            "unlock" => {
                let state = self.db.state();
                if state != DbState::Locked {
                    if state == DbState::New {
                        println!("A wallet has not yet been created, use new to create one");
                        return Ok(false);
                    } else if state == DbState::Unlocked {
                        println!("Wallet already unlocked");
                        return Ok(false);
                    }
                    return Err(format!("Unknown state: {:?}", state));
                }

                check_args!(args, 2);
                let pass = &Password(args.remove(1).into_bytes());
                if self.db.unlock(pass) {
                    self.prompt = "unlocked>> ".to_owned();
                } else {
                    println!("Failed to unlock wallet...incorrect password");
                }
                return Ok(false);
            }
            "create_account" => {
                check_unlocked!(self);
                let account = &args[1];
                if self.db.get_account(account).is_some() {
                    println!("Account already exists");
                    return Ok(true);
                }
                let key = KeyPair::gen_keypair();
                self.db.set_account(account, &key.1);
                println!("Public key => {}", key.0.to_wif());
                println!("Private key => {}", key.1.to_wif());
            }
            "import_account" => {
                check_unlocked!(self);
                check_args!(args, 3);
                let account = &args[1];
                let wif = PrivateKey::from_wif(&args[2]).map_err(|_| "Invalid wif".to_owned())?;
                for (acc, pair) in self.db.get_accounts() {
                    if &acc == account {
                        println!("Account already exists");
                        return Ok(true);
                    } else if pair.1 == wif.1 {
                        println!("Wif already exists under account `{}`", &acc);
                        return Ok(true);
                    }
                }
                self.db.set_account(account, &wif.1);
            }
            "get_account" => {
                check_unlocked!(self);
                check_args!(args, 2);
                let key = self.db.get_account(&args[1]);
                match key {
                    Some(key) => {
                        println!("Public key => {}", key.0.to_wif());
                        println!("Private key => {}", key.1.to_wif());
                    }
                    None => {
                        println!("Account not found");
                    }
                }
            }
            "delete_account" => {
                check_unlocked!(self);
                check_args!(args, 2);
                if self.db.del_account(&args[1]) {
                    println!("Account permanently deleted");
                } else {
                    println!("Account not found");
                }
            }
            "list_accounts" => {
                check_unlocked!(self);
                println!("Accounts:");
                for (acc, key) in self.db.get_accounts() {
                    println!("  {} => {}", acc, key.0.to_wif());
                }
            }
            "get_block" => {
                check_args!(args, 2);
                let height: u64 = args[1].parse().map_err(|_| {
                    "Failed to parse height argument".to_owned()
                })?;
                let client = Client::new();
                let res = client.post(self.url.clone())
                    .body(MsgRequest::GetBlock(height).serialize())
                    .send();
                match res {
                    Ok(mut res) => {
                        let len = res.content_length().unwrap_or(0);
                        let mut content = Vec::with_capacity(len as usize);
                        res.read_to_end(&mut content).map_err(|e| {
                            format!("{}", e)
                        })?;
                        let mut cursor = Cursor::<&[u8]>::new(&content);
                        let res = MsgResponse::deserialize(&mut cursor).map_err(|e| {
                            format!("Failed to deserialize response: {}", e)
                        })?;
                        println!("{:#?}", res);
                    },
                    Err(e) => {
                        return Err(format!("{}", e))
                    }
                }
            }
            "help" => {
                Self::print_usage("Displaying help...");
            }
            _ => {
                Self::print_usage(&format!("Invalid command: {}", args[0]));
            }
        }
        Ok(true)
    }

    fn print_usage(header: &str) {
        let mut cmds = Vec::<[&str; 2]>::new();
        cmds.push(["help", "Display this help menu"]);
        cmds.push(["new <password>", "Create a new wallet"]);
        cmds.push(["unlock <password>", "Unlock an existing wallet"]);
        cmds.push(["create_account <account>", "Create an account"]);
        cmds.push(["import_account <account> <wif>", "Import an account"]);
        cmds.push(["delete_account <account>", "Delete an existing account"]);
        cmds.push(["get_account <account>", "Retrieve account information"]);
        cmds.push(["list_accounts", "List all accounts"]);
        cmds.push(["list_accounts", "List all accounts"]);

        let mut max_len = 0;
        for cmd in &cmds {
            assert!(cmd.len() == 2);
            let cmd_len = cmd[0].len();
            if cmd_len > max_len {
                max_len = cmd_len;
            }
        }

        println!("{}\n", header);
        for cmd in &cmds {
            let mut c = cmd[0].to_owned();
            if c.len() < max_len {
                for _ in 0..max_len - c.len() {
                    c.push(' ');
                }
            }
            println!("  {}  {}", c, cmd[1]);
        }
        println!("");
    }
}
<|endoftext|>


<commit_before>use crate::{common, ninja::get_ninja_exe, pkg, spinner::Spinner, version::Version};
use clap::{Arg, ArgMatches, Command};
use console::Style;
use ditto_config::{read_config, Config, PackageName, CONFIG_FILE_NAME};
use ditto_make::{self as make, BuildNinja, GetWarnings, PackageSources, Sources};
use fs2::FileExt;
use log::{debug, trace};
use miette::{IntoDiagnostic, Result, WrapErr};
use notify::Watcher;
use std::{
    collections::HashMap,
    env::current_exe,
    fs,
    io::{BufRead, BufReader, Write},
    path::{Path, PathBuf},
    process::{self, ExitStatus, Stdio},
    sync::mpsc,
    time::{Duration, Instant},
};

pub static COMPILE_SUBCOMMAND: &str = "compile";

pub fn command<'a>(name: &str) -> Command<'a> {
    Command::new(name).about("Build a project").arg(
        Arg::new("watch")
            .short('w')
            .long("watch")
            .help("Watch files for changes"),
    )
}

pub async fn run(matches: &ArgMatches, ditto_version: &Version) -> Result<()> {
    if matches.is_present("watch") {
        run_watch(matches, ditto_version).await
    } else {
        let status = run_once(matches, ditto_version).await?;
        process::exit(status.code().unwrap_or(0));
    }
}

struct EventForwarder {
    tx: mpsc::Sender<notify::Result<notify::Event>>,
    debounce_duration: Duration,
    last_run: Option<Instant>,
}

impl EventForwarder {
    fn new(tx: mpsc::Sender<notify::Result<notify::Event>>) -> Self {
        Self {
            tx,
            // Debounce 100ms seems reasonable
            debounce_duration: Duration::from_millis(100),
            last_run: None,
        }
    }
}

impl notify::EventHandler for EventForwarder {
    fn handle_event(&mut self, event: notify::Result<notify::Event>) {
        let now = Instant::now();
        if let Some(last_run) = self.last_run {
            // Debouncing
            if now.duration_since(last_run) > self.debounce_duration {
                if let Err(err) = self.tx.send(event) {
                    log::error!("Error sending notify event: {:?}", err);
                }
                self.last_run = Some(now);
            }
        } else {
            if let Err(err) = self.tx.send(event) {
                log::error!("Error sending notify event: {:?}", err);
            }
            self.last_run = Some(now);
        }
    }
}

pub async fn run_watch(matches: &ArgMatches, ditto_version: &Version) -> Result<()> {
    // Set up the channel
    let (tx, rx) = mpsc::channel();
    let mut watcher = notify::RecommendedWatcher::new(EventForwarder::new(tx)).into_diagnostic()?;

    // Watch ditto.toml and src/**
    // NOTE not watching packages as that seems wasteful...
    // package source isn't going to be touched the majority of the time?
    // We could consider watching packages that are symlinks (i.e. local)
    watcher
        .watch(
            &PathBuf::from(CONFIG_FILE_NAME),
            notify::RecursiveMode::NonRecursive,
        )
        .into_diagnostic()?;
    watcher
        .watch(
            // TODO use src config value
            &PathBuf::from("src"),
            notify::RecursiveMode::Recursive,
        )
        .into_diagnostic()?;

    // Clear screen initially
    // (other watching tools do this)
    clearscreen::clear()
        .into_diagnostic()
        .wrap_err("error clearing screen")?;

    //let print_done = || {
    //    println!("{}", Style::new().green().bold().apply_to("Done"));
    //};

    if let Err(err) = run_once(matches, ditto_version).await {
        // print the error but don't exit!
        eprintln!("{:?}", err);
    }
    //print_done();

    // Listen for changes...
    loop {
        let event = rx.recv().into_diagnostic()?;

        match event {
            Ok(notify::Event {
                kind: notify::EventKind::Modify(_),
                mut paths,
                ..
            }) if paths.len() == 1 => {
                let path = paths.pop().unwrap();
                let event_path_extension = path.extension().and_then(|ext| ext.to_str());
                // Be selective about what we re-run for.
                // I.e. don't re-run for foreign files etc.
                if matches!(
                    event_path_extension,
                    // ditto source file
                    Some("ditto") |
                    // config file
                    Some("toml")
                ) {
                    clearscreen::clear()
                        .into_diagnostic()
                        .wrap_err("error clearing screen")?;
                    if let Err(err) = run_once(matches, ditto_version).await {
                        // print the error but don't exit!
                        eprintln!("{:?}", err);
                    }
                    //print_done();
                }
            }
            other => {
                log::trace!("Ignoring notify event: {:?}", other);
            }
        }
    }
}

pub async fn run_once(_matches: &ArgMatches, ditto_version: &Version) -> Result<ExitStatus> {
    let config_path: PathBuf = [".", CONFIG_FILE_NAME].iter().collect();
    let config = read_config(&config_path)?;

    // Need to acquire a lock on the build directory as lots of `ditto make`
    // processes running concurrently will cause problems!
    let lock = acquire_lock(&config)?;
    debug!("Lock acquired");

    // Install/remove packages as needed
    // (this is a nicer pattern than requiring a run of a separate CLI command, IMO)
    if !config.dependencies.is_empty() {
        pkg::check_packages_up_to_date(&config)
            .await
            .wrap_err("error checking packages are up to date")?;
    }

    let now = Instant::now(); // for timing

    // Do the work
    let status = make(&config_path, &config, ditto_version).await?;

    lock.unlock()
        .into_diagnostic()
        .wrap_err("error releasing lock")?;

    debug!("make ran in {}ms", now.elapsed().as_millis());

    Ok(status)
}

async fn make(config_path: &Path, config: &Config, ditto_version: &Version) -> Result<ExitStatus> {
    let (build_ninja, get_warnings) = generate_build_ninja(config_path, config, ditto_version)
        .map_err(|err| {
            // This is a bit brittle, but we want parse errors encountered during
            // build planning to be indistinguishable from parse errors encountered
            // during the actual build
            if err.root_cause().to_string() == "syntax error" {
                //                                  ^^ BEWARE relying on this string is brittle!
                err
            } else {
                err.wrap_err("error generating build.ninja")
            }
        })?;

    trace!("build.ninja generated");

    let mut build_ninja_path = config.ditto_dir.to_path_buf();
    build_ninja_path.push("build");
    build_ninja_path.set_extension("ninja");

    {
        if !config.ditto_dir.exists() {
            fs::create_dir_all(&config.ditto_dir)
                .into_diagnostic()
                .wrap_err(format!(
                    "error creating {}",
                    config.ditto_dir.to_string_lossy()
                ))?;
        }

        let mut handle = fs::File::create(&build_ninja_path)
            .into_diagnostic()
            .wrap_err(format!(
                "error creating ninja build file: {:?}",
                build_ninja_path.to_string_lossy()
            ))?;

        handle
            .write_all(build_ninja.into_syntax().as_bytes())
            .into_diagnostic()
            .wrap_err(format!(
                "error writing {:?}",
                build_ninja_path.to_string_lossy()
            ))?;

        debug!(
            "build.ninja written to {:?}",
            build_ninja_path.to_string_lossy()
        );
    }

    static NINJA_STATUS_MESSAGE: &str = "__NINJA";

    let ninja_exe = get_ninja_exe().await?;
    let mut child = process::Command::new(&ninja_exe)
        .arg("-f")
        .arg(&build_ninja_path)
        .stdout(Stdio::piped())
        // Mark ninja status messages so we can push them to our own progress spinner
        .env("NINJA_STATUS", NINJA_STATUS_MESSAGE)
        // Don't strip color codes, we'll handle that
        // https://github.com/ninja-build/ninja/commit/bf7107bb864d0383028202e3f4a4228c02302961
        .env("CLICOLOR_FORCE", "1")
        // Pass `is_plain` logic down to CLI calls made by ninja
        .env("DITTO_PLAIN", common::is_plain().to_string())
        .spawn()
        .into_diagnostic()
        .wrap_err(format!(
            "error running ninja: {} -f {}",
            ninja_exe,
            build_ninja_path.to_string_lossy()
        ))?;

    let stdout = child.stdout.as_mut().unwrap();
    let stdout_reader = BufReader::new(stdout);
    let mut stdout_lines = stdout_reader.lines();
    if let Some(Ok(first_line)) = stdout_lines.next() {
        // NOTE relying on the format of ninja messages like this could break
        // if DITTO_NINJA is set to a ninja version with a different format
        if first_line.starts_with("ninja: no work to do") {
            // Nothing to do,
            // still need to print warnings though
            let warnings = get_warnings()?;
            if !warnings.is_empty() {
                let warnings_len = warnings.len();
                for (i, warning) in warnings.into_iter().enumerate() {
                    if i == warnings_len - 1 {
                        eprintln!("{:?}", warning);
                    } else {
                        eprint!("{:?}", warning);
                    }
                }
            } else {
                println!("{}", Style::new().white().dim().apply_to("Nothing to do"));
            }
            child
                .wait()
                .into_diagnostic()
                .wrap_err("ninja wasn't running?")
        } else {
            let mut spinner = Spinner::new();
            spinner.set_message(
                first_line
                    .trim_start_matches(NINJA_STATUS_MESSAGE)
                    .to_owned(),
            );

            // Our error/warning reports generally start with a blank line,
            // so we need to replicate that behavior when forwarding ninja
            // output for a consistent experience.
            let mut printed_initial_newline = false;
            while let Some(Ok(line)) = stdout_lines.next() {
                if line.starts_with(NINJA_STATUS_MESSAGE) {
                    spinner.set_message(line.trim_start_matches(NINJA_STATUS_MESSAGE).to_owned());
                } else if line.starts_with("ninja: build stopped: subcommand failed") {
                } else if console::strip_ansi_codes(&line).starts_with("FAILED") {
                    // The following line prints the command that was run (and failed)
                    // so swallow it
                    stdout_lines.next();
                } else {
                    if !printed_initial_newline {
                        spinner.println("\n");
                        printed_initial_newline = true
                    }
                    spinner.println(line);
                }
            }

            let status = child.wait().expect("error waiting for ninja to exit");
            spinner.finish();
            if status.success() {
                // Only print warnings if there wasn't an error
                let warnings = get_warnings()?;
                if !warnings.is_empty() {
                    let warnings_len = warnings.len();
                    for (i, warning) in warnings.into_iter().enumerate() {
                        if i == warnings_len - 1 {
                            eprintln!("{:?}", warning);
                        } else {
                            eprint!("{:?}", warning);
                        }
                    }
                }
            }
            Ok(status)
        }
    } else {
        unreachable!()
    }
}

fn generate_build_ninja(
    config_path: &Path,
    config: &Config,
    ditto_version: &Version,
) -> Result<(BuildNinja, GetWarnings)> {
    let mut build_dir = config.ditto_dir.to_path_buf();
    build_dir.push("build");
    build_dir.push(&ditto_version.semversion.to_string());

    let ditto_bin = current_exe()
        .into_diagnostic()
        .wrap_err("error getting current executable")?;

    let ditto_sources = find_ditto_files(&config.src_dir)?;

    let sources = Sources {
        config: config_path.to_path_buf(),
        ditto: ditto_sources,
    };

    let package_sources =
        get_package_sources(config).wrap_err("error finding ditto files in packages")?;

    make::generate_build_ninja(
        build_dir,
        ditto_bin,
        &ditto_version.semversion,
        COMPILE_SUBCOMMAND,
        sources,
        package_sources,
    )
}

fn get_package_sources(config: &Config) -> Result<PackageSources> {
    let mut package_sources = HashMap::new();
    for path in pkg::list_installed_packages(&pkg::mk_packages_dir(config))? {
        let package_name =
            PackageName::new_unchecked(path.file_name().unwrap().to_string_lossy().into_owned());
        let sources = get_sources_for_dir(&path)?;
        package_sources.insert(package_name, sources);
    }
    Ok(package_sources)
}

fn get_sources_for_dir(dir: &Path) -> Result<Sources> {
    let mut config_path = dir.to_path_buf();
    config_path.push(CONFIG_FILE_NAME);
    let config = read_config(&config_path)?;

    let mut src_dir = dir.to_path_buf();
    src_dir.push(config.src_dir);

    let ditto_sources = find_ditto_files(src_dir)?;
    Ok(Sources {
        config: config_path,
        ditto: ditto_sources,
    })
}

fn find_ditto_files<P: AsRef<Path>>(root: P) -> Result<Vec<PathBuf>> {
    make::find_ditto_files(root.as_ref())
        .into_diagnostic()
        .wrap_err(format!(
            "error finding ditto files in {}",
            root.as_ref().to_string_lossy()
        ))
}

static LOCK_FILE: &str = "_lock";

fn acquire_lock(config: &Config) -> Result<impl FileExt> {
    if !config.ditto_dir.exists() {
        debug!(
            "{} doesn't exist, creating",
            config.ditto_dir.to_string_lossy()
        );

        fs::create_dir_all(&config.ditto_dir)
            .into_diagnostic()
            .wrap_err(format!(
                "error creating {}",
                config.ditto_dir.to_string_lossy()
            ))?;
    }

    let mut lock_file = config.ditto_dir.to_path_buf();
    lock_file.push(LOCK_FILE);

    debug!("Opening lock file at {}", lock_file.to_string_lossy());
    let file = fs::OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(&lock_file)
        .into_diagnostic()
        .wrap_err(format!(
            "error opening lock file {}",
            lock_file.to_string_lossy()
        ))?;

    if file.try_lock_exclusive().is_ok() {
        Ok(file)
    } else {
        println!("Waiting for lock...");
        file.lock_exclusive()
            .into_diagnostic()
            .wrap_err("error waiting for lock")?;
        Ok(file)
    }
}
<commit_msg>Improve `make --watch` feedback<commit_after>use crate::{common, ninja::get_ninja_exe, pkg, spinner::Spinner, version::Version};
use clap::{Arg, ArgMatches, Command};
use console::Style;
use ditto_config::{read_config, Config, PackageName, CONFIG_FILE_NAME};
use ditto_make::{self as make, BuildNinja, GetWarnings, PackageSources, Sources};
use fs2::FileExt;
use log::{debug, trace};
use miette::{IntoDiagnostic, Result, WrapErr};
use notify::Watcher;
use std::{
    collections::HashMap,
    env::current_exe,
    fs,
    io::{BufRead, BufReader, Write},
    path::{Path, PathBuf},
    process::{self, ExitStatus, Stdio},
    sync::mpsc,
    time::{Duration, Instant},
};

pub static COMPILE_SUBCOMMAND: &str = "compile";

pub fn command<'a>(name: &str) -> Command<'a> {
    Command::new(name).about("Build a project").arg(
        Arg::new("watch")
            .short('w')
            .long("watch")
            .help("Watch files for changes"),
    )
}

pub async fn run(matches: &ArgMatches, ditto_version: &Version) -> Result<()> {
    if matches.is_present("watch") {
        run_watch(matches, ditto_version).await
    } else {
        run_once(matches, ditto_version).await?.exit()
    }
}

struct EventForwarder {
    tx: mpsc::Sender<notify::Result<notify::Event>>,
    debounce_duration: Duration,
    last_run: Option<Instant>,
}

impl EventForwarder {
    fn new(tx: mpsc::Sender<notify::Result<notify::Event>>) -> Self {
        Self {
            tx,
            // Debounce 100ms seems reasonable
            debounce_duration: Duration::from_millis(100),
            last_run: None,
        }
    }
}

impl notify::EventHandler for EventForwarder {
    fn handle_event(&mut self, event: notify::Result<notify::Event>) {
        let now = Instant::now();
        if let Some(last_run) = self.last_run {
            // Debouncing
            if now.duration_since(last_run) > self.debounce_duration {
                if let Err(err) = self.tx.send(event) {
                    log::error!("Error sending notify event: {:?}", err);
                }
                self.last_run = Some(now);
            }
        } else {
            if let Err(err) = self.tx.send(event) {
                log::error!("Error sending notify event: {:?}", err);
            }
            self.last_run = Some(now);
        }
    }
}

pub async fn run_watch(matches: &ArgMatches, ditto_version: &Version) -> Result<()> {
    // Set up the channel
    let (tx, rx) = mpsc::channel();
    let mut watcher = notify::RecommendedWatcher::new(EventForwarder::new(tx)).into_diagnostic()?;

    // Watch ditto.toml and src/**
    // NOTE not watching packages as that seems wasteful...
    // package source isn't going to be touched the majority of the time?
    // We could consider watching packages that are symlinks (i.e. local)
    watcher
        .watch(
            &PathBuf::from(CONFIG_FILE_NAME),
            notify::RecursiveMode::NonRecursive,
        )
        .into_diagnostic()?;
    watcher
        .watch(
            // TODO use src config value
            &PathBuf::from("src"),
            notify::RecursiveMode::Recursive,
        )
        .into_diagnostic()?;

    run_once_watch(matches, ditto_version).await;

    // Listen for changes...
    loop {
        let event = rx.recv().into_diagnostic()?;

        match event {
            Ok(notify::Event {
                kind: notify::EventKind::Modify(_),
                mut paths,
                ..
            }) if paths.len() == 1 => {
                let path = paths.pop().unwrap();
                let event_path_extension = path.extension().and_then(|ext| ext.to_str());
                // Be selective about what we re-run for.
                // I.e. don't re-run for foreign files etc.
                if matches!(
                    event_path_extension,
                    // ditto source file
                    Some("ditto") |
                    // config file
                    Some("toml")
                ) {
                    run_once_watch(matches, ditto_version).await;
                }
            }
            other => {
                log::trace!("Ignoring notify event: {:?}", other);
            }
        }
    }

    async fn run_once_watch(matches: &ArgMatches, ditto_version: &Version) {
        if let Err(_err) = clearscreen::clear() {
            // doesn't matter, let it fail?
        }

        match run_once(matches, ditto_version).await {
            Err(err) => {
                // print the error but don't exit!
                eprintln!("{:?}", err);
            }
            Ok(WhatHappened::Nothing {
                warnings_printed: false,
                ..
            }) => println!("{}", Style::new().white().dim().apply_to("Nothing to do")),
            Ok(WhatHappened::Success {
                warnings_printed: false,
            }) => println!("{}", Style::new().green().bold().apply_to("All good!")),
            _ => {}
        }
    }
}

enum WhatHappened {
    Nothing {
        ninja_exit_status: ExitStatus,
        warnings_printed: bool,
    },
    Error {
        ninja_exit_status: ExitStatus,
    },
    Success {
        warnings_printed: bool,
    },
}

impl WhatHappened {
    fn exit(self) -> ! {
        match self {
            Self::Nothing {
                ninja_exit_status, ..
            } => process::exit(ninja_exit_status.code().unwrap_or(0)),
            Self::Error {
                ninja_exit_status, ..
            } => process::exit(ninja_exit_status.code().unwrap_or(0)),
            Self::Success { .. } => process::exit(0),
        }
    }
}

/// If successful returns the exit status of `ninja` and whether anything actually happened.
async fn run_once(_matches: &ArgMatches, ditto_version: &Version) -> Result<WhatHappened> {
    let config_path: PathBuf = [".", CONFIG_FILE_NAME].iter().collect();
    let config = read_config(&config_path)?;

    // Need to acquire a lock on the build directory as lots of `ditto make`
    // processes running concurrently will cause problems!
    let lock = acquire_lock(&config)?;
    debug!("Lock acquired");

    // Install/remove packages as needed
    // (this is a nicer pattern than requiring a run of a separate CLI command, IMO)
    if !config.dependencies.is_empty() {
        pkg::check_packages_up_to_date(&config)
            .await
            .wrap_err("error checking packages are up to date")?;
    }

    let now = Instant::now(); // for timing

    // Do the thing
    let result = make(&config_path, &config, ditto_version).await;

    lock.unlock()
        .into_diagnostic()
        .wrap_err("error releasing lock")?;

    debug!("make ran in {}ms", now.elapsed().as_millis());

    result
}

/// If successful returns the exit status of `ninja` and whether anything actually happened.
async fn make(
    config_path: &Path,
    config: &Config,
    ditto_version: &Version,
) -> Result<WhatHappened> {
    let (build_ninja, get_warnings) = generate_build_ninja(config_path, config, ditto_version)
        .map_err(|err| {
            // This is a bit brittle, but we want parse errors encountered during
            // build planning to be indistinguishable from parse errors encountered
            // during the actual build
            if err.root_cause().to_string() == "syntax error" {
                //                                  ^^ BEWARE relying on this string is brittle!
                err
            } else {
                err.wrap_err("error generating build.ninja")
            }
        })?;

    trace!("build.ninja generated");

    let mut build_ninja_path = config.ditto_dir.to_path_buf();
    build_ninja_path.push("build");
    build_ninja_path.set_extension("ninja");

    {
        if !config.ditto_dir.exists() {
            fs::create_dir_all(&config.ditto_dir)
                .into_diagnostic()
                .wrap_err(format!(
                    "error creating {}",
                    config.ditto_dir.to_string_lossy()
                ))?;
        }

        let mut handle = fs::File::create(&build_ninja_path)
            .into_diagnostic()
            .wrap_err(format!(
                "error creating ninja build file: {:?}",
                build_ninja_path.to_string_lossy()
            ))?;

        handle
            .write_all(build_ninja.into_syntax().as_bytes())
            .into_diagnostic()
            .wrap_err(format!(
                "error writing {:?}",
                build_ninja_path.to_string_lossy()
            ))?;

        debug!(
            "build.ninja written to {:?}",
            build_ninja_path.to_string_lossy()
        );
    }

    static NINJA_STATUS_MESSAGE: &str = "__NINJA";

    let ninja_exe = get_ninja_exe().await?;
    let mut child = process::Command::new(&ninja_exe)
        .arg("-f")
        .arg(&build_ninja_path)
        .stdout(Stdio::piped())
        // Mark ninja status messages so we can push them to our own progress spinner
        .env("NINJA_STATUS", NINJA_STATUS_MESSAGE)
        // Don't strip color codes, we'll handle that
        // https://github.com/ninja-build/ninja/commit/bf7107bb864d0383028202e3f4a4228c02302961
        .env("CLICOLOR_FORCE", "1")
        // Pass `is_plain` logic down to CLI calls made by ninja
        .env("DITTO_PLAIN", common::is_plain().to_string())
        .spawn()
        .into_diagnostic()
        .wrap_err(format!(
            "error running ninja: {} -f {}",
            ninja_exe,
            build_ninja_path.to_string_lossy()
        ))?;

    let stdout = child.stdout.as_mut().unwrap();
    let stdout_reader = BufReader::new(stdout);
    let mut stdout_lines = stdout_reader.lines();
    if let Some(Ok(first_line)) = stdout_lines.next() {
        // NOTE relying on the format of ninja messages like this could break
        // if DITTO_NINJA is set to a ninja version with a different format
        if first_line.starts_with("ninja: no work to do") {
            // Nothing to do,
            // still need to print warnings though
            let warnings = get_warnings()?;
            let warnings_printed = print_warnings(warnings);

            let ninja_exit_status = child
                .wait()
                .into_diagnostic()
                .wrap_err("ninja wasn't running?")?;

            return Ok(WhatHappened::Nothing {
                ninja_exit_status,
                warnings_printed,
            });
        } else {
            let mut spinner = Spinner::new();
            spinner.set_message(
                first_line
                    .trim_start_matches(NINJA_STATUS_MESSAGE)
                    .to_owned(),
            );

            // Our error/warning reports generally start with a blank line,
            // so we need to replicate that behavior when forwarding ninja
            // output for a consistent experience.
            let mut printed_initial_newline = false;
            while let Some(Ok(line)) = stdout_lines.next() {
                if line.starts_with(NINJA_STATUS_MESSAGE) {
                    spinner.set_message(line.trim_start_matches(NINJA_STATUS_MESSAGE).to_owned());
                } else if line.starts_with("ninja: build stopped: subcommand failed") {
                } else if console::strip_ansi_codes(&line).starts_with("FAILED") {
                    // The following line prints the command that was run (and failed)
                    // so swallow it
                    stdout_lines.next();
                } else {
                    if !printed_initial_newline {
                        spinner.println("\n");
                        printed_initial_newline = true
                    }
                    spinner.println(line);
                }
            }

            let ninja_exit_status = child.wait().expect("error waiting for ninja to exit");
            spinner.finish();
            if ninja_exit_status.success() {
                // Only print warnings if there wasn't an error
                let warnings = get_warnings()?;
                let warnings_printed = print_warnings(warnings);
                return Ok(WhatHappened::Success { warnings_printed });
            } else {
                return Ok(WhatHappened::Error { ninja_exit_status });
            }
        }
    } else {
        unreachable!()
    }

    fn print_warnings(warnings: Vec<miette::Report>) -> bool {
        if !warnings.is_empty() {
            let warnings_len = warnings.len();
            for (i, warning) in warnings.into_iter().enumerate() {
                if i == warnings_len - 1 {
                    eprintln!("{:?}", warning);
                } else {
                    eprint!("{:?}", warning);
                }
            }
            true
        } else {
            false
        }
    }
}

fn generate_build_ninja(
    config_path: &Path,
    config: &Config,
    ditto_version: &Version,
) -> Result<(BuildNinja, GetWarnings)> {
    let mut build_dir = config.ditto_dir.to_path_buf();
    build_dir.push("build");
    build_dir.push(&ditto_version.semversion.to_string());

    let ditto_bin = current_exe()
        .into_diagnostic()
        .wrap_err("error getting current executable")?;

    let ditto_sources = find_ditto_files(&config.src_dir)?;

    let sources = Sources {
        config: config_path.to_path_buf(),
        ditto: ditto_sources,
    };

    let package_sources =
        get_package_sources(config).wrap_err("error finding ditto files in packages")?;

    make::generate_build_ninja(
        build_dir,
        ditto_bin,
        &ditto_version.semversion,
        COMPILE_SUBCOMMAND,
        sources,
        package_sources,
    )
}

fn get_package_sources(config: &Config) -> Result<PackageSources> {
    let mut package_sources = HashMap::new();
    for path in pkg::list_installed_packages(&pkg::mk_packages_dir(config))? {
        let package_name =
            PackageName::new_unchecked(path.file_name().unwrap().to_string_lossy().into_owned());
        let sources = get_sources_for_dir(&path)?;
        package_sources.insert(package_name, sources);
    }
    Ok(package_sources)
}

fn get_sources_for_dir(dir: &Path) -> Result<Sources> {
    let mut config_path = dir.to_path_buf();
    config_path.push(CONFIG_FILE_NAME);
    let config = read_config(&config_path)?;

    let mut src_dir = dir.to_path_buf();
    src_dir.push(config.src_dir);

    let ditto_sources = find_ditto_files(src_dir)?;
    Ok(Sources {
        config: config_path,
        ditto: ditto_sources,
    })
}

fn find_ditto_files<P: AsRef<Path>>(root: P) -> Result<Vec<PathBuf>> {
    make::find_ditto_files(root.as_ref())
        .into_diagnostic()
        .wrap_err(format!(
            "error finding ditto files in {}",
            root.as_ref().to_string_lossy()
        ))
}

static LOCK_FILE: &str = "_lock";

fn acquire_lock(config: &Config) -> Result<impl FileExt> {
    if !config.ditto_dir.exists() {
        debug!(
            "{} doesn't exist, creating",
            config.ditto_dir.to_string_lossy()
        );

        fs::create_dir_all(&config.ditto_dir)
            .into_diagnostic()
            .wrap_err(format!(
                "error creating {}",
                config.ditto_dir.to_string_lossy()
            ))?;
    }

    let mut lock_file = config.ditto_dir.to_path_buf();
    lock_file.push(LOCK_FILE);

    debug!("Opening lock file at {}", lock_file.to_string_lossy());
    let file = fs::OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(&lock_file)
        .into_diagnostic()
        .wrap_err(format!(
            "error opening lock file {}",
            lock_file.to_string_lossy()
        ))?;

    if file.try_lock_exclusive().is_ok() {
        Ok(file)
    } else {
        println!("Waiting for lock...");
        file.lock_exclusive()
            .into_diagnostic()
            .wrap_err("error waiting for lock")?;
        Ok(file)
    }
}
<|endoftext|>


<commit_before>extern crate gcc;
extern crate regex;

use std::process::Command;
use std::env;
use regex::Regex;

fn main() {
    // 1. Build the object file from source using node-gyp.
    build_object_file();

    // 2. Link the library from the object file using gcc.
    link_library();
}

#[cfg(unix)]
fn npm() -> Command {
    Command::new("npm")
}

#[cfg(windows)]
fn npm() -> Command {
    let mut cmd = Command::new("cmd.exe");
    cmd.args(&["/C", "npm"]);
    cmd
}

fn build_object_file() {
    if cfg!(windows) {
        // Downcase all the npm environment variables to ensure they are read by node-gyp.
        for (key, value) in env::vars() {
            if key.starts_with("NPM_CONFIG") {
                env::remove_var(&key);
                env::set_var(key.to_lowercase(), value);
            }
        }
    }

    // Ensure that all package.json dependencies and dev dependencies are installed.
    npm().args(&["install", "--silent"]).status().ok().expect("Failed to run \"npm install\" for neon-runtime!");

    // Run `node-gyp configure` in verbose mode to read node_root_dir on Windows.
    let output = npm()
        .args(&["run", if debug() { "configure-debug" } else { "configure-release" }])
        .output()
        .expect("Failed to run \"node-gyp configure\" for neon-runtime!");

    if cfg!(windows) {
        let node_gyp_output = String::from_utf8_lossy(&output.stderr);
        let version_regex = Regex::new(r"node@(?P<version>\d+\.\d+\.\d+)\s+\|\s+(?P<platform>\w+)\s+\|\s(?P<arch>ia32|x64)").unwrap();
        let captures = version_regex.captures(&node_gyp_output).unwrap();
        println!("cargo:node_arch={}", &captures["arch"]);
        let node_root_dir_flag_pattern = "'-Dnode_root_dir=";
        let node_root_dir_start_index = node_gyp_output
            .find(node_root_dir_flag_pattern)
            .map(|i| i + node_root_dir_flag_pattern.len())
            .expect("Couldn't find node_root_dir in node-gyp output.");
        let node_root_dir_end_index = node_gyp_output[node_root_dir_start_index..].find("'").unwrap() + node_root_dir_start_index;
        println!("cargo:node_root_dir={}", &node_gyp_output[node_root_dir_start_index..node_root_dir_end_index]);
        let node_lib_file_flag_pattern = "'-Dnode_lib_file=";
        let node_lib_file_start_index = node_gyp_output
            .find(node_lib_file_flag_pattern)
            .map(|i| i + node_lib_file_flag_pattern.len())
            .expect("Couldn't find node_lib_file in node-gyp output.");
        let node_lib_file_end_index = node_gyp_output[node_lib_file_start_index..].find("'").unwrap() + node_lib_file_start_index;
        let node_lib_file = &node_gyp_output[node_lib_file_start_index..node_lib_file_end_index];
        println!("cargo:node_lib_file={}", node_lib_file);
    }

    // Run `node-gyp build`.
    npm()
        .args(&["run", if debug() { "build-debug" } else { "build-release" }])
        .status()
        .ok()
        .expect("Failed to run \"node-gyp build\" for neon-runtime!");
}

// Link the built object file into a static library.
fn link_library() {
    let configuration = if debug() { "Debug" } else { "Release" };
    let object_path = if cfg!(unix) {
        format!("build/{}/obj.target/neon/src/neon.o", configuration)
    } else {
        format!("build\\{}\\obj\\neon\\neon.obj", configuration)
    };

    gcc::Build::new().object(object_path).compile("libneon.a");
}

fn debug() -> bool {
    match env::var("DEBUG") {
        Ok(s) => s == "true",
        Err(_) => false
    }
}
<commit_msg>a little cleanup, refactoring, and commenting in build.rs<commit_after>extern crate gcc;
extern crate regex;

use std::process::Command;
use std::env;
use regex::Regex;

fn main() {
    // 1. Build the object file from source using node-gyp.
    build_object_file();

    // 2. Link the library from the object file using gcc.
    link_library();
}

#[cfg(unix)]
fn npm() -> Command {
    Command::new("npm")
}

#[cfg(windows)]
fn npm() -> Command {
    let mut cmd = Command::new("cmd.exe");
    cmd.args(&["/C", "npm"]);
    cmd
}

// The node-gyp output includes platform information in a string
// that looks like:
//
//     gyp info using node@8.3.0 | win32 | x64
#[cfg(windows)]
fn parse_node_arch(node_gyp_output: &str) -> String {
    let version_regex = Regex::new(r"node@(?P<version>\d+\.\d+\.\d+)\s+\|\s+(?P<platform>\w+)\s+\|\s(?P<arch>ia32|x64)").unwrap();
    let captures = version_regex.captures(&node_gyp_output).unwrap();
    String::from(&captures["arch"])
}

// The node-gyp output includes the root directory of shared resources
// for the Node installation in a string that looks like:
//
//     '-Dnode_root_dir=C:\\Users\\dherman\\.node-gyp\\8.3.0'
#[cfg(windows)]
fn parse_node_root_dir(node_gyp_output: &str) -> &str {
    let node_root_dir_flag_pattern = "'-Dnode_root_dir=";
    let node_root_dir_start_index = node_gyp_output
        .find(node_root_dir_flag_pattern)
        .map(|i| i + node_root_dir_flag_pattern.len())
        .expect("Couldn't find node_root_dir in node-gyp output.");
    let node_root_dir_end_index = node_gyp_output[node_root_dir_start_index..].find("'").unwrap() + node_root_dir_start_index;
    &node_gyp_output[node_root_dir_start_index..node_root_dir_end_index]
}

// The node-gyp output includes the name of the shared Node library file.
// In NPM versions <= v5.0.3, this was just the filename by itself, e.g.:
//
//     '-Dnode_lib_file=node.lib'
//
// In NPM versions >= v5.3.0, this was a templated absolute path with a
// reference to a gyp variable, e.g.:
//
//     '-Dnode_lib_file=C:\\Users\\dherman\\.node-gyp\\8.3.0\\<(target_arch)\\node.lib'
//
// Either way, we simply extract the value here. The `neon-build` crate
// processes it further.
#[cfg(windows)]
fn parse_node_lib_file(node_gyp_output: &str) -> &str {
    let node_lib_file_flag_pattern = "'-Dnode_lib_file=";
    let node_lib_file_start_index = node_gyp_output
        .find(node_lib_file_flag_pattern)
        .map(|i| i + node_lib_file_flag_pattern.len())
        .expect("Couldn't find node_lib_file in node-gyp output.");
    let node_lib_file_end_index = node_gyp_output[node_lib_file_start_index..].find("'").unwrap() + node_lib_file_start_index;
    &node_gyp_output[node_lib_file_start_index..node_lib_file_end_index]
}

fn build_object_file() {
    if cfg!(windows) {
        // Downcase all the npm environment variables to ensure they are read by node-gyp.
        for (key, value) in env::vars() {
            if key.starts_with("NPM_CONFIG") {
                env::remove_var(&key);
                env::set_var(key.to_lowercase(), value);
            }
        }
    }

    // Ensure that all package.json dependencies and dev dependencies are installed.
    npm().args(&["install", "--silent"]).status().ok().expect("Failed to run \"npm install\" for neon-runtime!");

    // Run `node-gyp configure` in verbose mode to read node_root_dir on Windows.
    let output = npm()
        .args(&["run", if debug() { "configure-debug" } else { "configure-release" }])
        .output()
        .expect("Failed to run \"node-gyp configure\" for neon-runtime!");

    if cfg!(windows) {
        let node_gyp_output = String::from_utf8_lossy(&output.stderr);
        println!("cargo:node_arch={}", parse_node_arch(&node_gyp_output));
        println!("cargo:node_root_dir={}", parse_node_root_dir(&node_gyp_output));
        println!("cargo:node_lib_file={}", parse_node_lib_file(&node_gyp_output));
    }

    // Run `node-gyp build`.
    npm()
        .args(&["run", if debug() { "build-debug" } else { "build-release" }])
        .status()
        .ok()
        .expect("Failed to run \"node-gyp build\" for neon-runtime!");
}

// Link the built object file into a static library.
fn link_library() {
    let configuration = if debug() { "Debug" } else { "Release" };
    let object_path = if cfg!(unix) {
        format!("build/{}/obj.target/neon/src/neon.o", configuration)
    } else {
        format!("build\\{}\\obj\\neon\\neon.obj", configuration)
    };

    gcc::Build::new().object(object_path).compile("libneon.a");
}

fn debug() -> bool {
    match env::var("DEBUG") {
        Ok(s) => s == "true",
        Err(_) => false
    }
}
<|endoftext|>


<commit_before>use query_builder::{QueryBuilder, BuildQueryResult};
use query_source::QuerySource;

pub trait AsChangeset {
    type Changeset: Changeset;

    fn as_changeset(self) -> Self::Changeset;
}

pub trait Changeset {
    type Target: QuerySource;

    fn to_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult;
}

impl<T> AsChangeset for T where
    T: Changeset,
{
    type Changeset = Self;

    fn as_changeset(self) -> Self::Changeset {
        self
    }
}
<commit_msg>Implement `Changeset` for `Vec<T>` and `Box<T>`<commit_after>use query_builder::{QueryBuilder, BuildQueryResult};
use query_source::QuerySource;

pub trait AsChangeset {
    type Changeset: Changeset;

    fn as_changeset(self) -> Self::Changeset;
}

pub trait Changeset {
    type Target: QuerySource;

    fn to_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult;
}

impl<T> AsChangeset for T where
    T: Changeset,
{
    type Changeset = Self;

    fn as_changeset(self) -> Self::Changeset {
        self
    }
}

impl<T: Changeset> Changeset for Vec<T> {
    type Target = T::Target;

    fn to_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult {
        for (i, changeset) in self.iter().enumerate() {
            if i != 0 {
                out.push_sql(", ");
            }
            try!(changeset.to_sql(out))
        }
        Ok(())
    }
}

impl<T: Changeset> Changeset for Box<T> {
    type Target = T::Target;

    fn to_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult {
        (&**self).to_sql(out)
    }
}
<|endoftext|>


<commit_before>extern crate rusty_machine as rm;
extern crate rand;

// https://github.com/AtheMathmo/rusty-machine/pull/101/files
use rm::linalg::{Matrix, BaseMatrix};
use rand::{Rng, ThreadRng};
use std::collections::HashMap;

fn generate_arm(content_id: &usize, arms: &HashMap<usize, Arm>, feature_dim: &usize) -> Arm {
    let arm: Arm;
    if !arms.contains_key(content_id) {
        arm = Arm::new(*feature_dim, *content_id, 0.0001, 0.0);
    }else{
        arm = arms.get(content_id).unwrap().clone();
    };
    arm
}

#[derive(Debug, Clone)]
struct Arm{
    content_id : usize,
    alpha : f64,
    norm_mean : Matrix<f64>,
    cov_matrix: Matrix<f64>,
    win_rate : f64,
    win : f64,
    lose : f64,
}

impl Arm {
    fn new(feature_dim: usize, content_id: usize, alpha: f64, win_rate: f64) -> Arm {
        Arm {
            content_id: content_id,
            norm_mean: Matrix::zeros(1, feature_dim),
            cov_matrix: Matrix::ones(1, feature_dim),
            alpha: alpha,
            win_rate: win_rate,
            win: 0.0,
            lose: 0.0,
        }
    }

    fn update(&mut self, features: Matrix<f64>, is_click: bool) {
        // error of diag
        self.cov_matrix += Matrix::new(self.cov_matrix.rows(),
                                        self.cov_matrix.cols(),
                                        (&features.transpose()*&features).diag());
        let cost_of_click = is_click as i8 as f64;

        let feat_mul_cost_vec = features.iter().map(|x| cost_of_click*x).collect::<Vec<f64>>();
        let feat_mul_cost = Matrix::new(features.rows(), features.cols(), feat_mul_cost_vec);
        self.norm_mean += feat_mul_cost;

        if is_click{
            self.win += 1.;
        } else{
            self.lose += 1.;
        }
        self.win_rate = &self.win/(&self.win + &self.lose);
    }

    fn predict(&mut self, features: &Matrix<f64>) -> f64 {
        let one_div_cov_vec = self.cov_matrix.iter().map(|x| 1.0/x).collect::<Vec<f64>>();
        let one_div_cov = Matrix::new(self.cov_matrix.rows(),
                                      self.cov_matrix.cols(), one_div_cov_vec);
        // Since the covariance matrix preserves only the diagonal components,
        // it suffices to take the inverse matrix
        let theta = &one_div_cov.elemul(&self.norm_mean);
        // Again, the inverse matrix of the covariance matrix
        // is computed by taking reciprocal
        let mut tmp: f64 = ((features.elemul(&one_div_cov))*features.transpose()).data()[0];
        tmp = &tmp.sqrt()*&self.alpha;
        (theta*features.transpose()).data()[0] + &tmp
    }

    fn print_result(&mut self) {
        println!("content_id:{}, total_num:{}, win_rate:{}",
                &self.content_id, &self.win+&self.lose, &self.win_rate);
    }
}

struct Viewer{
    gender: String,
    rng: ThreadRng,
}

impl Viewer {
    fn new(gender: String) -> Viewer{
        Viewer{
            gender: gender,
            rng: rand::thread_rng(),
        }
    }

    fn view(&mut self, content_id: &usize) -> bool{
        if &self.gender=="man" {
            // Men are easy to click on ads with id 5 or less
            if *content_id < 6 {
                return Some(self.rng.next_f32()).and_then(|n| if n>0.3 {Some(true)} else {Some(false)}).unwrap();
            } else{
                return Some(self.rng.next_f32()).and_then(|n| if n>0.7 {Some(true)} else {Some(false)}).unwrap();
            }
        } else {
            // Women are easy to click on ads with id 6 or higher
            if *content_id > 5{
                return Some(self.rng.next_f32()).and_then(|n| if n>0.3 {Some(true)} else {Some(false)}).unwrap();
            } else {
                return Some(self.rng.next_f32()).and_then(|n| if n>0.7 {Some(true)} else {Some(false)}).unwrap();
            }
        }
    }
}

struct Rulet{
    rng: ThreadRng,
}

impl Rulet{
    fn new() -> Rulet {
        Rulet{
            rng: rand::thread_rng(),
        }
    }

    fn generate_features(&mut self, viewer: &Viewer) -> Matrix<f64> {
        let features = Some(&viewer.gender)
            .and_then(|gender|
                if gender=="man" {Some(Matrix::new(1,2,vec![1.,0.]))}
                else {Some(Matrix::new(1,2,vec![0.,1.]))}
            ).unwrap();
        features
    }

    fn generate_content(&mut self) -> usize{
        self.rng.gen_range(0, 10)
    }

    fn generate_gender(&mut self) -> String {
        if self.rng.next_f32() > 0.5{
            return "man".to_string();
        } else{
            return "women".to_string();
        }
    }
}

fn main() {
    // コピーを作るのではなく、本体は一つでそれにアクセスできる変数を増やした感じ
    // 引数に渡した時も所有権の移動が起きる
    // 参照を得るときに&mutを使うことでmutableな参照を得ることができ、それを受け取った側は値を変更できる。mutableな参照を得られるのはmutableな変数に対してのみ。
    // 可変な参照を誰かが借用している間は、それが返却されるまで不変な参照を含め他の参照は一切取得できない
    /*Context is for men and women only
    Men are easy to click on ads with id 5 or less
    Women are easy to click on ads with id 6 or higher
    */
    let feature_dim = 2;
    let num_of_views = 10000;
    let mut rulet = Rulet::new();

    let mut content_id: usize;
    let mut features: Matrix<f64>;
    let mut is_clicked: bool;
    let mut arms: HashMap<usize, Arm> = HashMap::new();

    for step in 0..num_of_views {
        let mut viewer = Viewer::new(rulet.generate_gender());

        content_id = rulet.generate_content();
        features = rulet.generate_features(&viewer);
        is_clicked = viewer.view(&content_id);

        let mut arm = generate_arm(&content_id, &arms, &feature_dim);
        arm.update(features, is_clicked);
        arms.remove(&content_id);
        arms.insert(content_id, arm.clone());

        // Confirmation of prediction accuracy when the number of data is small
        if step<2000 {
            &arm.print_result();
        }
    }

    let man_mat: Matrix<f64> = Matrix::new(1,2, vec![1.,0.]);
    let woman_mat: Matrix<f64> = Matrix::new(1,2, vec![0.,1.]);

    println!("print result======");
    for (_, arm) in arms.iter() {
        let mut arm = arm.clone();  // impl needs mutable self
        &arm.print_result();
        println!("Click rate when men browse: {}", &arm.predict(&man_mat) );
        println!("Click rate when women browse: {}", &arm.predict(&woman_mat) );
    }

}
<commit_msg>remove comment<commit_after>extern crate rusty_machine as rm;
extern crate rand;

use rm::linalg::{Matrix, BaseMatrix};
use rand::{Rng, ThreadRng};
use std::collections::HashMap;

fn generate_arm(content_id: &usize, arms: &HashMap<usize, Arm>, feature_dim: &usize) -> Arm {
    let arm: Arm;
    if !arms.contains_key(content_id) {
        arm = Arm::new(*feature_dim, *content_id, 0.0001, 0.0);
    }else{
        arm = arms.get(content_id).unwrap().clone();
    };
    arm
}

#[derive(Debug, Clone)]
struct Arm{
    content_id : usize,
    alpha : f64,
    norm_mean : Matrix<f64>,
    cov_matrix: Matrix<f64>,
    win_rate : f64,
    win : f64,
    lose : f64,
}

impl Arm {
    fn new(feature_dim: usize, content_id: usize, alpha: f64, win_rate: f64) -> Arm {
        Arm {
            content_id: content_id,
            norm_mean: Matrix::zeros(1, feature_dim),
            cov_matrix: Matrix::ones(1, feature_dim),
            alpha: alpha,
            win_rate: win_rate,
            win: 0.0,
            lose: 0.0,
        }
    }

    fn update(&mut self, features: Matrix<f64>, is_click: bool) {
        // error of diag
        self.cov_matrix += Matrix::new(self.cov_matrix.rows(),
                                        self.cov_matrix.cols(),
                                        (&features.transpose()*&features).diag());
        let cost_of_click = is_click as i8 as f64;

        let feat_mul_cost_vec = features.iter().map(|x| cost_of_click*x).collect::<Vec<f64>>();
        let feat_mul_cost = Matrix::new(features.rows(), features.cols(), feat_mul_cost_vec);
        self.norm_mean += feat_mul_cost;

        if is_click{
            self.win += 1.;
        } else{
            self.lose += 1.;
        }
        self.win_rate = &self.win/(&self.win + &self.lose);
    }

    fn predict(&mut self, features: &Matrix<f64>) -> f64 {
        let one_div_cov_vec = self.cov_matrix.iter().map(|x| 1.0/x).collect::<Vec<f64>>();
        let one_div_cov = Matrix::new(self.cov_matrix.rows(),
                                      self.cov_matrix.cols(), one_div_cov_vec);
        // Since the covariance matrix preserves only the diagonal components,
        // it suffices to take the inverse matrix
        let theta = &one_div_cov.elemul(&self.norm_mean);
        // Again, the inverse matrix of the covariance matrix
        // is computed by taking reciprocal
        let mut tmp: f64 = ((features.elemul(&one_div_cov))*features.transpose()).data()[0];
        tmp = &tmp.sqrt()*&self.alpha;
        (theta*features.transpose()).data()[0] + &tmp
    }

    fn print_result(&mut self) {
        println!("content_id:{}, total_num:{}, win_rate:{}",
                &self.content_id, &self.win+&self.lose, &self.win_rate);
    }
}

struct Viewer{
    gender: String,
    rng: ThreadRng,
}

impl Viewer {
    fn new(gender: String) -> Viewer{
        Viewer{
            gender: gender,
            rng: rand::thread_rng(),
        }
    }

    fn view(&mut self, content_id: &usize) -> bool{
        if &self.gender=="man" {
            // Men are easy to click on ads with id 5 or less
            if *content_id < 6 {
                return Some(self.rng.next_f32()).and_then(|n| if n>0.3 {Some(true)} else {Some(false)}).unwrap();
            } else{
                return Some(self.rng.next_f32()).and_then(|n| if n>0.7 {Some(true)} else {Some(false)}).unwrap();
            }
        } else {
            // Women are easy to click on ads with id 6 or higher
            if *content_id > 5{
                return Some(self.rng.next_f32()).and_then(|n| if n>0.3 {Some(true)} else {Some(false)}).unwrap();
            } else {
                return Some(self.rng.next_f32()).and_then(|n| if n>0.7 {Some(true)} else {Some(false)}).unwrap();
            }
        }
    }
}

struct Rulet{
    rng: ThreadRng,
}

impl Rulet{
    fn new() -> Rulet {
        Rulet{
            rng: rand::thread_rng(),
        }
    }

    fn generate_features(&mut self, viewer: &Viewer) -> Matrix<f64> {
        let features = Some(&viewer.gender)
            .and_then(|gender|
                if gender=="man" {Some(Matrix::new(1,2,vec![1.,0.]))}
                else {Some(Matrix::new(1,2,vec![0.,1.]))}
            ).unwrap();
        features
    }

    fn generate_content(&mut self) -> usize{
        self.rng.gen_range(0, 10)
    }

    fn generate_gender(&mut self) -> String {
        if self.rng.next_f32() > 0.5{
            return "man".to_string();
        } else{
            return "women".to_string();
        }
    }
}

fn main() {
    /*Context is for men and women only
    Men are easy to click on ads with id 5 or less
    Women are easy to click on ads with id 6 or higher
    */
    let feature_dim = 2;
    let num_of_views = 10000;
    let mut rulet = Rulet::new();

    let mut content_id: usize;
    let mut features: Matrix<f64>;
    let mut is_clicked: bool;
    let mut arms: HashMap<usize, Arm> = HashMap::new();

    for step in 0..num_of_views {
        let mut viewer = Viewer::new(rulet.generate_gender());

        content_id = rulet.generate_content();
        features = rulet.generate_features(&viewer);
        is_clicked = viewer.view(&content_id);

        let mut arm = generate_arm(&content_id, &arms, &feature_dim);
        arm.update(features, is_clicked);
        arms.remove(&content_id);
        arms.insert(content_id, arm.clone());
    }

    let man_mat: Matrix<f64> = Matrix::new(1,2, vec![1.,0.]);
    let woman_mat: Matrix<f64> = Matrix::new(1,2, vec![0.,1.]);

    println!("print result======");
    for (_, arm) in arms.iter() {
        let mut arm = arm.clone();  // impl needs mutable self
        &arm.print_result();
        println!("Click rate when men browse: {}", &arm.predict(&man_mat) );
        println!("Click rate when women browse: {}", &arm.predict(&woman_mat) );
    }

}
<|endoftext|>


<commit_before>use super::{fake_signature::FakeSignature, AggregatePublicKey};
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, PrefixedHexVisitor};
use ssz::{
    decode_ssz_list, hash, ssz_encode, Decodable, DecodeError, Encodable, SszStream, TreeHash,
};

const SIGNATURE_LENGTH: usize = 48;

/// A BLS aggregate signature.
///
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
/// serialization).
#[derive(Debug, PartialEq, Clone, Default, Eq)]
pub struct FakeAggregateSignature {
    bytes: Vec<u8>,
}

impl FakeAggregateSignature {
    /// Creates a new all-zero's signature
    pub fn new() -> Self {
        Self::zero()
    }

    /// Creates a new all-zero's signature
    pub fn zero() -> Self {
        Self {
            bytes: vec![0; SIGNATURE_LENGTH],
        }
    }

    /// Does glorious nothing.
    pub fn add(&mut self, _signature: &FakeSignature) {
        // Do nothing.
    }

    /// _Always_ returns `true`.
    pub fn verify(
        &self,
        _msg: &[u8],
        _domain: u64,
        _aggregate_public_key: &AggregatePublicKey,
    ) -> bool {
        true
    }

    /// _Always_ returns `true`.
    pub fn verify_multiple(
        &self,
        _messages: &[&[u8]],
        _domain: u64,
        _aggregate_public_keys: &[&AggregatePublicKey],
    ) -> bool {
        true
    }
}

impl Encodable for FakeAggregateSignature {
    fn ssz_append(&self, s: &mut SszStream) {
        s.append_vec(&self.bytes);
    }
}

impl Decodable for FakeAggregateSignature {
    fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
        let (sig_bytes, i) = decode_ssz_list(bytes, i)?;
        Ok((FakeAggregateSignature { bytes: sig_bytes }, i))
    }
}

impl Serialize for FakeAggregateSignature {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&hex_encode(ssz_encode(self)))
    }
}

impl<'de> Deserialize<'de> for FakeAggregateSignature {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?;
        let (obj, _) = <_>::ssz_decode(&bytes[..], 0)
            .map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
        Ok(obj)
    }
}

impl TreeHash for FakeAggregateSignature {
    fn hash_tree_root(&self) -> Vec<u8> {
        hash(&self.bytes)
    }
}

#[cfg(test)]
mod tests {
    use super::super::{Keypair, Signature};
    use super::*;
    use ssz::ssz_encode;

    #[test]
    pub fn test_ssz_round_trip() {
        let keypair = Keypair::random();

        let mut original = FakeAggregateSignature::new();
        original.add(&Signature::new(&[42, 42], 0, &keypair.sk));

        let bytes = ssz_encode(&original);
        let (decoded, _) = FakeAggregateSignature::ssz_decode(&bytes, 0).unwrap();

        assert_eq!(original, decoded);
    }
}
<commit_msg>Impl `add_aggregate` for FakeAggSig<commit_after>use super::{fake_signature::FakeSignature, AggregatePublicKey};
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, PrefixedHexVisitor};
use ssz::{
    decode_ssz_list, hash, ssz_encode, Decodable, DecodeError, Encodable, SszStream, TreeHash,
};

const SIGNATURE_LENGTH: usize = 48;

/// A BLS aggregate signature.
///
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
/// serialization).
#[derive(Debug, PartialEq, Clone, Default, Eq)]
pub struct FakeAggregateSignature {
    bytes: Vec<u8>,
}

impl FakeAggregateSignature {
    /// Creates a new all-zero's signature
    pub fn new() -> Self {
        Self::zero()
    }

    /// Creates a new all-zero's signature
    pub fn zero() -> Self {
        Self {
            bytes: vec![0; SIGNATURE_LENGTH],
        }
    }

    /// Does glorious nothing.
    pub fn add(&mut self, _signature: &FakeSignature) {
        // Do nothing.
    }

    /// Does glorious nothing.
    pub fn add_aggregate(&mut self, _agg_sig: &FakeAggregateSignature) {
        // Do nothing.
    }

    /// _Always_ returns `true`.
    pub fn verify(
        &self,
        _msg: &[u8],
        _domain: u64,
        _aggregate_public_key: &AggregatePublicKey,
    ) -> bool {
        true
    }

    /// _Always_ returns `true`.
    pub fn verify_multiple(
        &self,
        _messages: &[&[u8]],
        _domain: u64,
        _aggregate_public_keys: &[&AggregatePublicKey],
    ) -> bool {
        true
    }
}

impl Encodable for FakeAggregateSignature {
    fn ssz_append(&self, s: &mut SszStream) {
        s.append_vec(&self.bytes);
    }
}

impl Decodable for FakeAggregateSignature {
    fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
        let (sig_bytes, i) = decode_ssz_list(bytes, i)?;
        Ok((FakeAggregateSignature { bytes: sig_bytes }, i))
    }
}

impl Serialize for FakeAggregateSignature {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&hex_encode(ssz_encode(self)))
    }
}

impl<'de> Deserialize<'de> for FakeAggregateSignature {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?;
        let (obj, _) = <_>::ssz_decode(&bytes[..], 0)
            .map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
        Ok(obj)
    }
}

impl TreeHash for FakeAggregateSignature {
    fn hash_tree_root(&self) -> Vec<u8> {
        hash(&self.bytes)
    }
}

#[cfg(test)]
mod tests {
    use super::super::{Keypair, Signature};
    use super::*;
    use ssz::ssz_encode;

    #[test]
    pub fn test_ssz_round_trip() {
        let keypair = Keypair::random();

        let mut original = FakeAggregateSignature::new();
        original.add(&Signature::new(&[42, 42], 0, &keypair.sk));

        let bytes = ssz_encode(&original);
        let (decoded, _) = FakeAggregateSignature::ssz_decode(&bytes, 0).unwrap();

        assert_eq!(original, decoded);
    }
}
<|endoftext|>


<commit_before>// iterators2.rs
// In this module, you'll learn some of unique advantages that iterators can offer
// Step 1. Complete the `capitalize_first` function to pass the first two cases
// Step 2. Apply the `capitalize_first` function to a vector of strings, ensuring that it returns a vector of strings as well
// Step 3. Apply the `capitalize_first` function again to a list, but try and ensure it returns a single string
// As always, there are hints below!

pub fn capitalize_first(input: &str) -> String {
    let mut c = input.chars();
    match c.next() {
        None => String::new(),
        Some(first) => first.collect::<String>() + c.as_str(),
    }
}

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

    // Step 1.
    // Tests that verify your `capitalize_first` function implementation
    #[test]
    fn test_success() {
        assert_eq!(capitalize_first("hello"), "Hello");
    }

    #[test]
    fn test_empty() {
        assert_eq!(capitalize_first(""), "");
    }

    // Step 2.
    #[test]
    fn test_iterate_string_vec() {
        let words = vec!["hello", "world"];
        let capitalized_words: Vec<String> = // TODO
        assert_eq!(capitalized_words, ["Hello", "World"]);
    }

    #[test]
    fn test_iterate_into_string() {
        let words = vec!["hello", " ", "world"];
        let capitalized_words = // TODO
        assert_eq!(capitalized_words, "Hello World");
    }
}

// Step 1
// You need to call something on `first` before it can be collected
// Currently it's type is `char`. Have a look at the methods that are available on that type:
// https://doc.rust-lang.org/std/primitive.char.html

// Step 2
// First you'll need to turn the Vec into an iterator
// Then you'll need to apply your function unto each item in the vector
// P.s. Don't forget to collect() at the end!

// Step 3.
// This is very similar to the previous test. The only real change is that you will need to
// alter the type that collect is coerced into. For a bonus you could try doing this with a
// turbofish
<commit_msg>chore: Correct typo<commit_after>// iterators2.rs
// In this module, you'll learn some of unique advantages that iterators can offer
// Step 1. Complete the `capitalize_first` function to pass the first two cases
// Step 2. Apply the `capitalize_first` function to a vector of strings, ensuring that it returns a vector of strings as well
// Step 3. Apply the `capitalize_first` function again to a list, but try and ensure it returns a single string
// As always, there are hints below!

pub fn capitalize_first(input: &str) -> String {
    let mut c = input.chars();
    match c.next() {
        None => String::new(),
        Some(first) => first.collect::<String>() + c.as_str(),
    }
}

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

    // Step 1.
    // Tests that verify your `capitalize_first` function implementation
    #[test]
    fn test_success() {
        assert_eq!(capitalize_first("hello"), "Hello");
    }

    #[test]
    fn test_empty() {
        assert_eq!(capitalize_first(""), "");
    }

    // Step 2.
    #[test]
    fn test_iterate_string_vec() {
        let words = vec!["hello", "world"];
        let capitalized_words: Vec<String> = // TODO
        assert_eq!(capitalized_words, ["Hello", "World"]);
    }

    #[test]
    fn test_iterate_into_string() {
        let words = vec!["hello", " ", "world"];
        let capitalized_words = // TODO
        assert_eq!(capitalized_words, "Hello World");
    }
}

// Step 1
// You need to call something on `first` before it can be collected
// Currently its type is `char`. Have a look at the methods that are available on that type:
// https://doc.rust-lang.org/std/primitive.char.html

// Step 2
// First you'll need to turn the Vec into an iterator
// Then you'll need to apply your function unto each item in the vector
// P.s. Don't forget to collect() at the end!

// Step 3.
// This is very similar to the previous test. The only real change is that you will need to
// alter the type that collect is coerced into. For a bonus you could try doing this with a
// turbofish
<|endoftext|>


<commit_before>use crate::cell::UnsafeCell;
use crate::mem;
use crate::mem::MaybeUninit;
use crate::sync::atomic::{AtomicU32, Ordering};
use crate::sys::cloudabi::abi;
use crate::sys::rwlock::{self, RWLock};

extern "C" {
    #[thread_local]
    static __pthread_thread_id: abi::tid;
}

// Implement Mutex using an RWLock. This doesn't introduce any
// performance overhead in this environment, as the operations would be
// implemented identically.
pub struct Mutex(RWLock);

pub type MovableMutex = Box<Mutex>;

pub unsafe fn raw(m: &Mutex) -> *mut AtomicU32 {
    rwlock::raw(&m.0)
}

impl Mutex {
    pub const fn new() -> Mutex {
        Mutex(RWLock::new())
    }

    pub unsafe fn init(&mut self) {
        // This function should normally reinitialize the mutex after
        // moving it to a different memory address. This implementation
        // does not require adjustments after moving.
    }

    pub unsafe fn try_lock(&self) -> bool {
        self.0.try_write()
    }

    pub unsafe fn lock(&self) {
        self.0.write()
    }

    pub unsafe fn unlock(&self) {
        self.0.write_unlock()
    }

    pub unsafe fn destroy(&self) {
        self.0.destroy()
    }
}

pub struct ReentrantMutex {
    lock: UnsafeCell<MaybeUninit<AtomicU32>>,
    recursion: UnsafeCell<MaybeUninit<u32>>,
}

impl ReentrantMutex {
    pub const unsafe fn uninitialized() -> ReentrantMutex {
        ReentrantMutex {
            lock: UnsafeCell::new(MaybeUninit::uninit()),
            recursion: UnsafeCell::new(MaybeUninit::uninit()),
        }
    }

    pub unsafe fn init(&self) {
        *self.lock.get() = MaybeUninit::new(AtomicU32::new(abi::LOCK_UNLOCKED.0));
        *self.recursion.get() = MaybeUninit::new(0);
    }

    pub unsafe fn try_lock(&self) -> bool {
        // Attempt to acquire the lock.
        let lock = (*self.lock.get()).as_mut_ptr();
        let recursion = (*self.recursion.get()).as_mut_ptr();
        if let Err(old) = (*lock).compare_exchange(
            abi::LOCK_UNLOCKED.0,
            __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
            Ordering::Acquire,
            Ordering::Relaxed,
        ) {
            // If we fail to acquire the lock, it may be the case
            // that we've already acquired it and may need to recurse.
            if old & !abi::LOCK_KERNEL_MANAGED.0 == __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0 {
                *recursion += 1;
                true
            } else {
                false
            }
        } else {
            // Success.
            assert_eq!(*recursion, 0, "Mutex has invalid recursion count");
            true
        }
    }

    pub unsafe fn lock(&self) {
        if !self.try_lock() {
            // Call into the kernel to acquire a write lock.
            let lock = self.lock.get();
            let subscription = abi::subscription {
                type_: abi::eventtype::LOCK_WRLOCK,
                union: abi::subscription_union {
                    lock: abi::subscription_lock {
                        lock: lock as *mut abi::lock,
                        lock_scope: abi::scope::PRIVATE,
                    },
                },
                ..mem::zeroed()
            };
            let mut event = MaybeUninit::<abi::event>::uninit();
            let mut nevents = MaybeUninit::<usize>::uninit();
            let ret = abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr());
            assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire mutex");
            let event = event.assume_init();
            assert_eq!(event.error, abi::errno::SUCCESS, "Failed to acquire mutex");
        }
    }

    pub unsafe fn unlock(&self) {
        let lock = (*self.lock.get()).as_mut_ptr();
        let recursion = (*self.recursion.get()).as_mut_ptr();
        assert_eq!(
            (*lock).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
            __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
            "This mutex is locked by a different thread"
        );

        if *recursion > 0 {
            *recursion -= 1;
        } else if !(*lock)
            .compare_exchange(
                __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
                abi::LOCK_UNLOCKED.0,
                Ordering::Release,
                Ordering::Relaxed,
            )
            .is_ok()
        {
            // Lock is managed by kernelspace. Call into the kernel
            // to unblock waiting threads.
            let ret = abi::lock_unlock(lock as *mut abi::lock, abi::scope::PRIVATE);
            assert_eq!(ret, abi::errno::SUCCESS, "Failed to unlock a mutex");
        }
    }

    pub unsafe fn destroy(&self) {
        let lock = (*self.lock.get()).as_mut_ptr();
        let recursion = (*self.recursion.get()).as_mut_ptr();
        assert_eq!(
            (*lock).load(Ordering::Relaxed),
            abi::LOCK_UNLOCKED.0,
            "Attempted to destroy locked mutex"
        );
        assert_eq!(*recursion, 0, "Recursion counter invalid");
    }
}
<commit_msg>No longer put cloudabi mutexes in a box.<commit_after>use crate::cell::UnsafeCell;
use crate::mem;
use crate::mem::MaybeUninit;
use crate::sync::atomic::{AtomicU32, Ordering};
use crate::sys::cloudabi::abi;
use crate::sys::rwlock::{self, RWLock};

extern "C" {
    #[thread_local]
    static __pthread_thread_id: abi::tid;
}

// Implement Mutex using an RWLock. This doesn't introduce any
// performance overhead in this environment, as the operations would be
// implemented identically.
pub struct Mutex(RWLock);

pub type MovableMutex = Mutex;

pub unsafe fn raw(m: &Mutex) -> *mut AtomicU32 {
    rwlock::raw(&m.0)
}

impl Mutex {
    pub const fn new() -> Mutex {
        Mutex(RWLock::new())
    }

    pub unsafe fn init(&mut self) {
        // This function should normally reinitialize the mutex after
        // moving it to a different memory address. This implementation
        // does not require adjustments after moving.
    }

    pub unsafe fn try_lock(&self) -> bool {
        self.0.try_write()
    }

    pub unsafe fn lock(&self) {
        self.0.write()
    }

    pub unsafe fn unlock(&self) {
        self.0.write_unlock()
    }

    pub unsafe fn destroy(&self) {
        self.0.destroy()
    }
}

pub struct ReentrantMutex {
    lock: UnsafeCell<MaybeUninit<AtomicU32>>,
    recursion: UnsafeCell<MaybeUninit<u32>>,
}

impl ReentrantMutex {
    pub const unsafe fn uninitialized() -> ReentrantMutex {
        ReentrantMutex {
            lock: UnsafeCell::new(MaybeUninit::uninit()),
            recursion: UnsafeCell::new(MaybeUninit::uninit()),
        }
    }

    pub unsafe fn init(&self) {
        *self.lock.get() = MaybeUninit::new(AtomicU32::new(abi::LOCK_UNLOCKED.0));
        *self.recursion.get() = MaybeUninit::new(0);
    }

    pub unsafe fn try_lock(&self) -> bool {
        // Attempt to acquire the lock.
        let lock = (*self.lock.get()).as_mut_ptr();
        let recursion = (*self.recursion.get()).as_mut_ptr();
        if let Err(old) = (*lock).compare_exchange(
            abi::LOCK_UNLOCKED.0,
            __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
            Ordering::Acquire,
            Ordering::Relaxed,
        ) {
            // If we fail to acquire the lock, it may be the case
            // that we've already acquired it and may need to recurse.
            if old & !abi::LOCK_KERNEL_MANAGED.0 == __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0 {
                *recursion += 1;
                true
            } else {
                false
            }
        } else {
            // Success.
            assert_eq!(*recursion, 0, "Mutex has invalid recursion count");
            true
        }
    }

    pub unsafe fn lock(&self) {
        if !self.try_lock() {
            // Call into the kernel to acquire a write lock.
            let lock = self.lock.get();
            let subscription = abi::subscription {
                type_: abi::eventtype::LOCK_WRLOCK,
                union: abi::subscription_union {
                    lock: abi::subscription_lock {
                        lock: lock as *mut abi::lock,
                        lock_scope: abi::scope::PRIVATE,
                    },
                },
                ..mem::zeroed()
            };
            let mut event = MaybeUninit::<abi::event>::uninit();
            let mut nevents = MaybeUninit::<usize>::uninit();
            let ret = abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr());
            assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire mutex");
            let event = event.assume_init();
            assert_eq!(event.error, abi::errno::SUCCESS, "Failed to acquire mutex");
        }
    }

    pub unsafe fn unlock(&self) {
        let lock = (*self.lock.get()).as_mut_ptr();
        let recursion = (*self.recursion.get()).as_mut_ptr();
        assert_eq!(
            (*lock).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
            __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
            "This mutex is locked by a different thread"
        );

        if *recursion > 0 {
            *recursion -= 1;
        } else if !(*lock)
            .compare_exchange(
                __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
                abi::LOCK_UNLOCKED.0,
                Ordering::Release,
                Ordering::Relaxed,
            )
            .is_ok()
        {
            // Lock is managed by kernelspace. Call into the kernel
            // to unblock waiting threads.
            let ret = abi::lock_unlock(lock as *mut abi::lock, abi::scope::PRIVATE);
            assert_eq!(ret, abi::errno::SUCCESS, "Failed to unlock a mutex");
        }
    }

    pub unsafe fn destroy(&self) {
        let lock = (*self.lock.get()).as_mut_ptr();
        let recursion = (*self.recursion.get()).as_mut_ptr();
        assert_eq!(
            (*lock).load(Ordering::Relaxed),
            abi::LOCK_UNLOCKED.0,
            "Attempted to destroy locked mutex"
        );
        assert_eq!(*recursion, 0, "Recursion counter invalid");
    }
}
<|endoftext|>