<commit_before>//! Provides facilities for notifying when any spec files have changed
//! on disk. This is how we know when to start, stop, or restart
//! services in response to the various `hab svc` commands.

use super::spec_dir::SpecDir;
use crate::error::{Error,
                   Result};
use notify::{DebouncedEvent,
             RecommendedWatcher,
             RecursiveMode,
             Watcher};
use std::{sync::mpsc,
          thread::Builder,
          time::Duration};

/// How long should we wait to consolidate filesystem events?
///
/// This should strike a balance between responsiveness and
/// too-granular a series of events.
///
/// See https://docs.rs/notify/4.0.6/notify/trait.Watcher.html#tymethod.new
habitat_core::env_config_duration!(SpecWatcherDelay,
                                   HAB_SPEC_WATCHER_DELAY_MS => from_millis,
                                   // There's nothing particularly magical about 2s, particularly
                                   // since we're monitoring at such a coarse level ("something
                                   // happened in this directory").
                                   //
                                   // Smaller is probably fine, but you wouldn't want to go much
                                   // higher, as this could extend the amount of time you'd need
                                   // to wait before realizing you need to take action on a
                                   // service.
                                   Duration::from_secs(2));

// TODO (CM): A strong argument could be made for folding the
// SpecWatcher functionality into SpecDir itself.

/// Provides an abstraction layer over filesystem notifications for
/// spec files.
pub struct SpecWatcher {
    // Not actually used; only holding onto it for lifetime / Drop
    // purposes (`Drop` kills the threads that the watcher spawns to do
    // its work).
    _watcher: RecommendedWatcher,
    channel:  mpsc::Receiver<DebouncedEvent>,
}

impl SpecWatcher {
    /// Start up a separate thread to listen for filesystem
    /// events.
    pub fn run(spec_dir: &SpecDir) -> Result<SpecWatcher> {
        // The act of creating a `notify::Watcher` creates threads on
        // its own. It does not, however, allow you to set the _names_
        // of those threads.
        //
        // We're creating a SpecWatcher in a thread just so we can get
        // some control over the name of the threads that the
        // underlying `notify::Watcher` creates (_that_ is what this
        // function's documentation is referring to), which makes
        // monitoring and reasoning about the overall Supervisor
        // process easier. There's no other reason than that; if the
        // `notify` crate allowed us to name the threads, we could
        // just use a slightly modified version of `SpecWatcher::new`
        // instead.

        // I'd rather not have to do this clone, but it's a side
        // effect of this thread business. Better to eat it here
        // rather than at the callsite; having this function take a
        // reference is the true API we want.
        let dir = spec_dir.clone();

        Builder::new().name(String::from("spec-watcher"))
                      .spawn(move || Self::new(&dir))?
                      .join()
                      .map_err(|_| {
                          error!("SpecWatcher spawning thread panicked!");
                          Error::SpecWatcherNotCreated
                      })?
    }

    /// Isolates the pure creation logic of a `SpecWatcher`, separate
    /// from the thread-based creation we use in `SpecWatcher::run` to
    /// get control over the names of the resulting threads. If we
    /// didn't care what the resulting watcher threads were named,
    /// we'd just use this directly.
    fn new(spec_dir: &SpecDir) -> Result<SpecWatcher> {
        let (tx, rx) = mpsc::channel();
        let delay = SpecWatcherDelay::configured_value();
        let mut watcher = RecommendedWatcher::new(tx, delay.0)?;
        watcher.watch(spec_dir, RecursiveMode::NonRecursive)?;
        Ok(SpecWatcher { _watcher: watcher,
                         channel:  rx, })
    }

    /// Returns `true` if _any_ filesystem events were detected in the
    /// watched directory.
    ///
    /// We are opting for this coarse granularity because it is
    /// difficult, if not impossible, to rely on the resulting stream
    /// of notification events as a 100% complete and accurate record
    /// of everything that takes place in the specs directory. For
    /// example, because of how we write files to temporary locations,
    /// then rename in order to achieve "atomic writes", it is
    /// possible to miss the events that would cause you to see this
    /// as a "rename"; instead, you would see a "new" file (even with
    /// a debounce time of 0 ms!)
    ///
    /// In any event, we would _still_ need to examine on-disk state
    /// of the directory and compare to our in-memory state to know
    /// exactly what to do in order to reconcile the two (i.e., you
    /// can't know whether to start, stop, or restart a service just
    /// by knowing that the spec file changed; you have to actually
    /// look at the current contents to figure that out).
    ///
    /// As a result, we're just using this as a coarse "something
    /// changed in the directory" signal. We are not filtering events
    /// to receive only those that affect `*.spec` files, so we may
    /// respond to modifications to temporary files, or indeed any
    /// file, within the specs directory (e.g., running `touch
    /// /hab/sup/default/specs/blahblah` would count as an event). It
    /// is possible to perform this filtering, of course, but it's not
    /// clear that the extra code would be worth it.
    pub fn has_events(&self) -> bool {
        let events = self.channel.try_iter().collect::<Vec<_>>();
        if events.is_empty() {
            false
        } else {
            trace!("SpecWatcher events: {:?}", events);
            true
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use habitat_common::locked_env_var;
    use std::{fs::File,
              io::{Error as IoError,
                   Write},
              result::Result as StdResult,
              thread};
    use tempfile::TempDir;

    locked_env_var!(HAB_SPEC_WATCHER_DELAY_MS, lock_delay_var);

    fn file_with_content<C>(dir: &TempDir, filename: &str, contents: C) -> StdResult<(), IoError>
        where C: Into<String>
    {
        let path = dir.path().join(filename);
        let mut buffer = File::create(&path)?;
        buffer.write_all(contents.into().as_bytes())
    }

    /// Sleep for the currently-configured debounce interval, plus a
    /// few milliseconds more, just to be certain our filesystem
    /// events have had plenty of time to process.
    fn wait_for_debounce_interval() {
        let wait_duration = SpecWatcherDelay::configured_value().0 + Duration::from_millis(2);
        error!("wait_for_debounce_interval: waiting {:?}", wait_duration);
        thread::sleep(wait_duration);
    }

    #[test]
    fn can_be_created() {
        let _delay = lock_delay_var();

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        assert!(SpecWatcher::run(&spec_dir).is_ok(),
                "Couldn't create a SpecWatcher!");
    }

    #[test]
    fn can_get_events_for_spec_files() {
        env_logger::try_init().ok();
        error!("can_get_events_for_spec_files starting");
        let _delay = lock_delay_var();

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        let sw = SpecWatcher::run(&spec_dir).expect("Couldn't create a SpecWatcher!");

        assert!(!sw.has_events(), "There should be no events to start");

        file_with_content(&dir, "foo.spec", "fooooooo").expect("couldn't create file");

        assert!(!sw.has_events(),
                "Need to allow for the debounce interval to pass before you can expect events");

        while !sw.has_events() {
            wait_for_debounce_interval();
        }

        assert!(!sw.has_events(),
                "Should be no more events after you've checked");
        error!("can_get_events_for_spec_files done");
    }

    /// Currently, the spec watcher will respond to changes to any
    /// file in the directory, whether it's a `*.spec` file or not.
    ///
    /// This would, for instance, pick up the temp files that
    /// operations like `hab svc stop` lay down before renaming them
    /// to their final `*.spec` form.
    #[test]
    fn can_get_events_for_non_spec_files() {
        env_logger::try_init().ok();
        error!("can_get_events_for_non_spec_files starting");
        let _delay = lock_delay_var();

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        let sw = SpecWatcher::run(&spec_dir).expect("Couldn't create a SpecWatcher!");

        assert!(!sw.has_events(), "There should be no events to start");

        file_with_content(&dir, "foo.abc123xyz", "fooooooo").expect("couldn't create file");

        assert!(!sw.has_events(),
                "Need to allow for the debounce interval to pass before you can expect events");

        while !sw.has_events() {
            wait_for_debounce_interval();
        }

        assert!(!sw.has_events(),
                "Should be no more events after you've checked");
        error!("can_get_events_for_non_spec_files done");
    }

    #[test]
    fn short_debounce_delays_also_work() {
        env_logger::try_init().ok();
        error!("short_debounce_delays_also_work starting");
        let delay = lock_delay_var();
        delay.set("1");

        // Just verifying that our delay variable works correctly
        assert_eq!(SpecWatcherDelay::configured_value().0,
                   Duration::from_millis(1));

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        let sw = SpecWatcher::run(&spec_dir).expect("Couldn't create a SpecWatcher!");

        assert!(!sw.has_events(), "There should be no events to start");

        file_with_content(&dir, "foo.spec", "fooooooo").expect("couldn't create file");

        assert!(!sw.has_events(),
                "Need to allow for the debounce interval to pass before you can expect events");

        while !sw.has_events() {
            wait_for_debounce_interval();
        }

        assert!(!sw.has_events(),
                "Should be no more events after you've checked");
        error!("short_debounce_delays_also_work starting");
    }
}
<commit_msg>remove assert for no events in spec watcher tests after adding files to be watched<commit_after>//! Provides facilities for notifying when any spec files have changed
//! on disk. This is how we know when to start, stop, or restart
//! services in response to the various `hab svc` commands.

use super::spec_dir::SpecDir;
use crate::error::{Error,
                   Result};
use notify::{DebouncedEvent,
             RecommendedWatcher,
             RecursiveMode,
             Watcher};
use std::{sync::mpsc,
          thread::Builder,
          time::Duration};

/// How long should we wait to consolidate filesystem events?
///
/// This should strike a balance between responsiveness and
/// too-granular a series of events.
///
/// See https://docs.rs/notify/4.0.6/notify/trait.Watcher.html#tymethod.new
habitat_core::env_config_duration!(SpecWatcherDelay,
                                   HAB_SPEC_WATCHER_DELAY_MS => from_millis,
                                   // There's nothing particularly magical about 2s, particularly
                                   // since we're monitoring at such a coarse level ("something
                                   // happened in this directory").
                                   //
                                   // Smaller is probably fine, but you wouldn't want to go much
                                   // higher, as this could extend the amount of time you'd need
                                   // to wait before realizing you need to take action on a
                                   // service.
                                   Duration::from_secs(2));

// TODO (CM): A strong argument could be made for folding the
// SpecWatcher functionality into SpecDir itself.

/// Provides an abstraction layer over filesystem notifications for
/// spec files.
pub struct SpecWatcher {
    // Not actually used; only holding onto it for lifetime / Drop
    // purposes (`Drop` kills the threads that the watcher spawns to do
    // its work).
    _watcher: RecommendedWatcher,
    channel:  mpsc::Receiver<DebouncedEvent>,
}

impl SpecWatcher {
    /// Start up a separate thread to listen for filesystem
    /// events.
    pub fn run(spec_dir: &SpecDir) -> Result<SpecWatcher> {
        // The act of creating a `notify::Watcher` creates threads on
        // its own. It does not, however, allow you to set the _names_
        // of those threads.
        //
        // We're creating a SpecWatcher in a thread just so we can get
        // some control over the name of the threads that the
        // underlying `notify::Watcher` creates (_that_ is what this
        // function's documentation is referring to), which makes
        // monitoring and reasoning about the overall Supervisor
        // process easier. There's no other reason than that; if the
        // `notify` crate allowed us to name the threads, we could
        // just use a slightly modified version of `SpecWatcher::new`
        // instead.

        // I'd rather not have to do this clone, but it's a side
        // effect of this thread business. Better to eat it here
        // rather than at the callsite; having this function take a
        // reference is the true API we want.
        let dir = spec_dir.clone();

        Builder::new().name(String::from("spec-watcher"))
                      .spawn(move || Self::new(&dir))?
                      .join()
                      .map_err(|_| {
                          error!("SpecWatcher spawning thread panicked!");
                          Error::SpecWatcherNotCreated
                      })?
    }

    /// Isolates the pure creation logic of a `SpecWatcher`, separate
    /// from the thread-based creation we use in `SpecWatcher::run` to
    /// get control over the names of the resulting threads. If we
    /// didn't care what the resulting watcher threads were named,
    /// we'd just use this directly.
    fn new(spec_dir: &SpecDir) -> Result<SpecWatcher> {
        let (tx, rx) = mpsc::channel();
        let delay = SpecWatcherDelay::configured_value();
        let mut watcher = RecommendedWatcher::new(tx, delay.0)?;
        watcher.watch(spec_dir, RecursiveMode::NonRecursive)?;
        Ok(SpecWatcher { _watcher: watcher,
                         channel:  rx, })
    }

    /// Returns `true` if _any_ filesystem events were detected in the
    /// watched directory.
    ///
    /// We are opting for this coarse granularity because it is
    /// difficult, if not impossible, to rely on the resulting stream
    /// of notification events as a 100% complete and accurate record
    /// of everything that takes place in the specs directory. For
    /// example, because of how we write files to temporary locations,
    /// then rename in order to achieve "atomic writes", it is
    /// possible to miss the events that would cause you to see this
    /// as a "rename"; instead, you would see a "new" file (even with
    /// a debounce time of 0 ms!)
    ///
    /// In any event, we would _still_ need to examine on-disk state
    /// of the directory and compare to our in-memory state to know
    /// exactly what to do in order to reconcile the two (i.e., you
    /// can't know whether to start, stop, or restart a service just
    /// by knowing that the spec file changed; you have to actually
    /// look at the current contents to figure that out).
    ///
    /// As a result, we're just using this as a coarse "something
    /// changed in the directory" signal. We are not filtering events
    /// to receive only those that affect `*.spec` files, so we may
    /// respond to modifications to temporary files, or indeed any
    /// file, within the specs directory (e.g., running `touch
    /// /hab/sup/default/specs/blahblah` would count as an event). It
    /// is possible to perform this filtering, of course, but it's not
    /// clear that the extra code would be worth it.
    pub fn has_events(&self) -> bool {
        let events = self.channel.try_iter().collect::<Vec<_>>();
        if events.is_empty() {
            false
        } else {
            trace!("SpecWatcher events: {:?}", events);
            true
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use habitat_common::locked_env_var;
    use std::{fs::File,
              io::{Error as IoError,
                   Write},
              result::Result as StdResult,
              thread};
    use tempfile::TempDir;

    locked_env_var!(HAB_SPEC_WATCHER_DELAY_MS, lock_delay_var);

    fn file_with_content<C>(dir: &TempDir, filename: &str, contents: C) -> StdResult<(), IoError>
        where C: Into<String>
    {
        let path = dir.path().join(filename);
        let mut buffer = File::create(&path)?;
        buffer.write_all(contents.into().as_bytes())
    }

    /// Sleep for the currently-configured debounce interval, plus a
    /// few milliseconds more, just to be certain our filesystem
    /// events have had plenty of time to process.
    fn wait_for_debounce_interval() {
        let wait_duration = SpecWatcherDelay::configured_value().0 + Duration::from_millis(2);
        error!("wait_for_debounce_interval: waiting {:?}", wait_duration);
        thread::sleep(wait_duration);
    }

    #[test]
    fn can_be_created() {
        let _delay = lock_delay_var();

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        assert!(SpecWatcher::run(&spec_dir).is_ok(),
                "Couldn't create a SpecWatcher!");
    }

    #[test]
    fn can_get_events_for_spec_files() {
        env_logger::try_init().ok();
        error!("can_get_events_for_spec_files starting");
        let _delay = lock_delay_var();

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        let sw = SpecWatcher::run(&spec_dir).expect("Couldn't create a SpecWatcher!");

        assert!(!sw.has_events(), "There should be no events to start");

        file_with_content(&dir, "foo.spec", "fooooooo").expect("couldn't create file");

        while !sw.has_events() {
            wait_for_debounce_interval();
        }

        assert!(!sw.has_events(),
                "Should be no more events after you've checked");
        error!("can_get_events_for_spec_files done");
    }

    /// Currently, the spec watcher will respond to changes to any
    /// file in the directory, whether it's a `*.spec` file or not.
    ///
    /// This would, for instance, pick up the temp files that
    /// operations like `hab svc stop` lay down before renaming them
    /// to their final `*.spec` form.
    #[test]
    fn can_get_events_for_non_spec_files() {
        env_logger::try_init().ok();
        error!("can_get_events_for_non_spec_files starting");
        let _delay = lock_delay_var();

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        let sw = SpecWatcher::run(&spec_dir).expect("Couldn't create a SpecWatcher!");

        assert!(!sw.has_events(), "There should be no events to start");

        file_with_content(&dir, "foo.abc123xyz", "fooooooo").expect("couldn't create file");

        while !sw.has_events() {
            wait_for_debounce_interval();
        }

        assert!(!sw.has_events(),
                "Should be no more events after you've checked");
        error!("can_get_events_for_non_spec_files done");
    }

    #[test]
    fn short_debounce_delays_also_work() {
        env_logger::try_init().ok();
        error!("short_debounce_delays_also_work starting");
        let delay = lock_delay_var();
        delay.set("1");

        // Just verifying that our delay variable works correctly
        assert_eq!(SpecWatcherDelay::configured_value().0,
                   Duration::from_millis(1));

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        let sw = SpecWatcher::run(&spec_dir).expect("Couldn't create a SpecWatcher!");

        assert!(!sw.has_events(), "There should be no events to start");

        file_with_content(&dir, "foo.spec", "fooooooo").expect("couldn't create file");

        while !sw.has_events() {
            wait_for_debounce_interval();
        }

        assert!(!sw.has_events(),
                "Should be no more events after you've checked");
        error!("short_debounce_delays_also_work starting");
    }
}
<|endoftext|>


<commit_before>use command_prelude::*;

use cargo::ops;

pub fn cli() -> App {
    subcommand("generate-lockfile")
        .about("Generate the lockfile for a project")
        .arg_manifest_path()
        .after_help(
            "\
If a lockfile is available, this command will ensure that all of the git
dependencies and/or registries dependencies are downloaded and locally
available. The network is never touched after a `cargo fetch` unless
the lockfile changes.

If the lockfile is not available, then this is the equivalent of
`cargo generate-lockfile`. A lockfile is generated and dependencies are also
all updated.
",
        )
}

pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
    let ws = args.workspace(config)?;
    ops::generate_lockfile(&ws)?;
    Ok(())
}
<commit_msg>Auto merge of #5774 - dwijnand:generate-lockfile-stutter, r=alexcrichton<commit_after>use command_prelude::*;

use cargo::ops;

pub fn cli() -> App {
    subcommand("generate-lockfile")
        .about("Generate the lockfile for a project")
        .arg_manifest_path()
}

pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
    let ws = args.workspace(config)?;
    ops::generate_lockfile(&ws)?;
    Ok(())
}
<|endoftext|>


<commit_before>use super::*;

pub fn create(wallet: &mut Wallet, args: &mut Vec<String>) -> Result<(), String> {
    check_unlocked!(wallet);
    check_args!(args, 1);
    let account = &args[1];
    if wallet.db.get_account(account).is_some() {
        println!("Account already exists");
        return Ok(());
    }
    let key = KeyPair::gen();
    wallet.db.set_account(account, &key.1);
    println!("Public key => {}", key.0.to_wif());
    println!("Private key => {}", key.1.to_wif());
    Ok(())
}

pub fn import(wallet: &mut Wallet, args: &mut Vec<String>) -> Result<(), String> {
    check_unlocked!(wallet);
    check_args!(args, 2);
    let account = &args[1];
    let wif = PrivateKey::from_wif(&args[2]).map_err(|_| "Invalid wif".to_owned())?;
    for (acc, pair) in wallet.db.get_accounts() {
        if &acc == account {
            println!("Account already exists");
            return Ok(());
        } else if pair.1 == wif.1 {
            println!("Wif already exists under account `{}`", &acc);
            return Ok(());
        }
    }
    wallet.db.set_account(account, &wif.1);
    Ok(())
}

pub fn get(wallet: &mut Wallet, args: &mut Vec<String>) -> Result<(), String> {
    check_unlocked!(wallet);
    check_args!(args, 1);
    let key = wallet.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");
        }
    }
    Ok(())
}

pub fn delete(wallet: &mut Wallet, args: &mut Vec<String>) -> Result<(), String> {
    check_unlocked!(wallet);
    check_args!(args, 1);
    if wallet.db.del_account(&args[1]) {
        println!("Account permanently deleted");
    } else {
        println!("Account not found");
    }
    Ok(())
}

pub fn list(wallet: &mut Wallet, _args: &mut Vec<String>) -> Result<(), String> {
    check_unlocked!(wallet);
    println!("Accounts:");
    for (acc, key) in wallet.db.get_accounts() {
        println!("  {} => {}", acc, key.0.to_wif());
    }
    Ok(())
}
<commit_msg>Print P2SH payable address when getting account info<commit_after>use super::*;

pub fn create(wallet: &mut Wallet, args: &mut Vec<String>) -> Result<(), String> {
    check_unlocked!(wallet);
    check_args!(args, 1);
    let account = &args[1];
    if wallet.db.get_account(account).is_some() {
        println!("Account already exists");
        return Ok(());
    }
    let key = KeyPair::gen();
    wallet.db.set_account(account, &key.1);
    println!("Public key => {}", key.0.to_wif());
    println!("Private key => {}", key.1.to_wif());
    Ok(())
}

pub fn import(wallet: &mut Wallet, args: &mut Vec<String>) -> Result<(), String> {
    check_unlocked!(wallet);
    check_args!(args, 2);
    let account = &args[1];
    let wif = PrivateKey::from_wif(&args[2]).map_err(|_| "Invalid wif".to_owned())?;
    for (acc, pair) in wallet.db.get_accounts() {
        if &acc == account {
            println!("Account already exists");
            return Ok(());
        } else if pair.1 == wif.1 {
            println!("Wif already exists under account `{}`", &acc);
            return Ok(());
        }
    }
    wallet.db.set_account(account, &wif.1);
    Ok(())
}

pub fn get(wallet: &mut Wallet, args: &mut Vec<String>) -> Result<(), String> {
    check_unlocked!(wallet);
    check_args!(args, 1);
    let key = wallet.db.get_account(&args[1]);
    match key {
        Some(key) => {
            println!("Public key => {}", key.0.to_wif());
            println!("Private key => {}", key.1.to_wif());
            println!("P2SH address => {}", ScriptHash::from(key.0).to_wif());
        }
        None => {
            println!("Account not found");
        }
    }
    Ok(())
}

pub fn delete(wallet: &mut Wallet, args: &mut Vec<String>) -> Result<(), String> {
    check_unlocked!(wallet);
    check_args!(args, 1);
    if wallet.db.del_account(&args[1]) {
        println!("Account permanently deleted");
    } else {
        println!("Account not found");
    }
    Ok(())
}

pub fn list(wallet: &mut Wallet, _args: &mut Vec<String>) -> Result<(), String> {
    check_unlocked!(wallet);
    println!("Accounts:");
    for (acc, key) in wallet.db.get_accounts() {
        println!("  {} => {}", acc, key.0.to_wif());
    }
    Ok(())
}
<|endoftext|>


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

#![feature(associated_consts)]
#![feature(associated_type_defaults)]

struct S;

mod method {
    trait A {
        fn a(&self) { }
        const A: u8 = 0;
    }

    pub trait B {
        fn b(&self) { }
        const B: u8 = 0;
    }

    pub trait C: A + B {
        fn c(&self) { }
        const C: u8 = 0;
    }

    impl A for ::S {}
    impl B for ::S {}
    impl C for ::S {}

}

mod assoc_ty {
    trait A {
        type A = u8;
    }

    pub trait B {
        type B = u8;
    }

    pub trait C: A + B {
        type C = u8;
    }

    impl A for ::S {}
    impl B for ::S {}
    impl C for ::S {}

}

fn check_assoc_ty<T: assoc_ty::C>() {
    // A is private
    // B is pub, not in scope
    // C : A + B is pub, in scope
    use assoc_ty::C;

    // Associated types
    // A, B, C are resolved as trait items, their traits need to be in scope, not implemented yet
    let _: S::A; //~ ERROR ambiguous associated type
    let _: S::B; //~ ERROR ambiguous associated type
    let _: S::C; //~ ERROR ambiguous associated type
    // A, B, C are resolved as inherent items, their traits don't need to be in scope
    let _: T::A; //~ ERROR associated type `A` is private
    let _: T::B; // OK
    let _: T::C; // OK
}

fn main() {
    // A is private
    // B is pub, not in scope
    // C : A + B is pub, in scope
    use method::C;

    // Methods, method call
    // a, b, c are resolved as trait items, their traits need to be in scope
    S.a(); //~ ERROR no method named `a` found for type `S` in the current scope
    S.b(); //~ ERROR no method named `b` found for type `S` in the current scope
    S.c(); // OK
    // a, b, c are resolved as inherent items, their traits don't need to be in scope
    let c = &S as &C;
    c.a(); //~ ERROR method `a` is private
    c.b(); // OK
    c.c(); // OK

    // Methods, UFCS
    // a, b, c are resolved as trait items, their traits need to be in scope
    S::a(&S); //~ ERROR no associated item named `a` found for type `S` in the current scope
    S::b(&S); //~ ERROR no associated item named `b` found for type `S` in the current scope
    S::c(&S); // OK
    // a, b, c are resolved as inherent items, their traits don't need to be in scope
    C::a(&S); //~ ERROR method `a` is private
    C::b(&S); // OK
    C::c(&S); // OK

    // Associated constants
    // A, B, C are resolved as trait items, their traits need to be in scope
    S::A; //~ ERROR no associated item named `A` found for type `S` in the current scope
    S::B; //~ ERROR no associated item named `B` found for type `S` in the current scope
    S::C; // OK
    // A, B, C are resolved as inherent items, their traits don't need to be in scope
    C::A; //~ ERROR associated constant `A` is private
    C::B; // OK
    C::C; // OK
}
<commit_msg>Fix object safety violations in the test<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(associated_consts)]
#![feature(associated_type_defaults)]

struct S;

mod method {
    trait A {
        fn a(&self) { }
    }

    pub trait B {
        fn b(&self) { }
    }

    pub trait C: A + B {
        fn c(&self) { }
    }

    impl A for ::S {}
    impl B for ::S {}
    impl C for ::S {}
}

mod assoc_const {
    trait A {
        const A: u8 = 0;
    }

    pub trait B {
        const B: u8 = 0;
    }

    pub trait C: A + B {
        const C: u8 = 0;
    }

    impl A for ::S {}
    impl B for ::S {}
    impl C for ::S {}
}

mod assoc_ty {
    trait A {
        type A = u8;
    }

    pub trait B {
        type B = u8;
    }

    pub trait C: A + B {
        type C = u8;
    }

    impl A for ::S {}
    impl B for ::S {}
    impl C for ::S {}
}

fn check_method() {
    // A is private
    // B is pub, not in scope
    // C : A + B is pub, in scope
    use method::C;

    // Methods, method call
    // a, b, c are resolved as trait items, their traits need to be in scope
    S.a(); //~ ERROR no method named `a` found for type `S` in the current scope
    S.b(); //~ ERROR no method named `b` found for type `S` in the current scope
    S.c(); // OK
    // a, b, c are resolved as inherent items, their traits don't need to be in scope
    let c = &S as &C;
    c.a(); //~ ERROR method `a` is private
    c.b(); // OK
    c.c(); // OK

    // Methods, UFCS
    // a, b, c are resolved as trait items, their traits need to be in scope
    S::a(&S); //~ ERROR no associated item named `a` found for type `S` in the current scope
    S::b(&S); //~ ERROR no associated item named `b` found for type `S` in the current scope
    S::c(&S); // OK
    // a, b, c are resolved as inherent items, their traits don't need to be in scope
    C::a(&S); //~ ERROR method `a` is private
    C::b(&S); // OK
    C::c(&S); // OK
}

fn check_assoc_const() {
    // A is private
    // B is pub, not in scope
    // C : A + B is pub, in scope
    use assoc_const::C;

    // Associated constants
    // A, B, C are resolved as trait items, their traits need to be in scope
    S::A; //~ ERROR no associated item named `A` found for type `S` in the current scope
    S::B; //~ ERROR no associated item named `B` found for type `S` in the current scope
    S::C; // OK
    // A, B, C are resolved as inherent items, their traits don't need to be in scope
    C::A; //~ ERROR associated constant `A` is private
          //~^ ERROR the trait `assoc_const::C` cannot be made into an object
          //~| ERROR the trait bound `assoc_const::C: assoc_const::A` is not satisfied
    C::B; // ERROR the trait `assoc_const::C` cannot be made into an object
          //~^ ERROR the trait bound `assoc_const::C: assoc_const::B` is not satisfied
    C::C; // OK
}

fn check_assoc_ty<T: assoc_ty::C>() {
    // A is private
    // B is pub, not in scope
    // C : A + B is pub, in scope
    use assoc_ty::C;

    // Associated types
    // A, B, C are resolved as trait items, their traits need to be in scope, not implemented yet
    let _: S::A; //~ ERROR ambiguous associated type
    let _: S::B; //~ ERROR ambiguous associated type
    let _: S::C; //~ ERROR ambiguous associated type
    // A, B, C are resolved as inherent items, their traits don't need to be in scope
    let _: T::A; //~ ERROR associated type `A` is private
    let _: T::B; // OK
    let _: T::C; // OK
}

fn main() {}
<|endoftext|>


<commit_before>// use std::env;
use std::fs::File;
use std::io::Read;
use std::ptr;
use std::env;

#[macro_use]
extern crate syscall;

fn write(fd: usize, buf: &[u8]) {
    unsafe {
        syscall!(WRITE, fd, buf.as_ptr(), buf.len());
    }
}

fn open_as_fd_or_die(filename: &[u8]) -> usize {
    let o_rdonly = 0u64;
    let no_flags = 0u64;
    unsafe {
        let return_value = syscall!(OPEN, filename.as_ptr(), o_rdonly, no_flags);
        sanity_check_fd(return_value);
        return return_value;
    }
}

fn setns(fd: usize, nstype: usize) {
    unsafe {
        syscall!(SETNS, fd, nstype);
    }
}

fn close(fd: usize) {
    unsafe {
        syscall!(CLOSE, fd);
    }
}

fn sanity_check_fd(fd: usize) {
    // For some reason, syscall!(OPEN) returns usize, but I need to check its output against
    // -1. So I'm going to just check if it's >255.
    if fd > 255 {
        panic!("Failed to open a needed file. Bailing.");
    }
}

fn setgroups_zero() {
    // Clears auxilary groups. Probably not necessary, but whatever.
    unsafe {
        syscall!(SETGROUPS, 0);
    }
}

fn fchdir(fd: usize) {
    unsafe {
        syscall!(FCHDIR, fd);
    }
}

fn fork() -> usize {
    unsafe {
        syscall!(FORK)
    }
}

fn wait_all_children() {
    let all_children = -1isize;
    let no_options = 0usize;
    let nullptr = 0usize;
    unsafe {
        syscall!(WAIT4, all_children, no_options, nullptr, nullptr);
    }
}

fn setuid_setgid_1000() {
    // These are the right UID & GID because they are what Sandstorm
    // uses inside the user namespace.
    let one_thousand = 1000usize;
    unsafe {
        syscall!(SETUID, one_thousand);
        syscall!(SETGID, one_thousand);
    }
}

fn execve_bash(envp: std::vec::Vec<*const u8>) {
    let bash_path = "/bin/bash\0".as_bytes();
    let nullargv = 0usize;
    unsafe {
        syscall!(EXECVE, bash_path.as_ptr(), nullargv, envp.as_ptr());
    }
}

fn get_envp(pidStrRef: &str) -> std::vec::Vec<*const u8> {
    // Grab the environ from pid 1048 so that when we execve a shell
    // at the end, we can provide the environment.

    let path = "/proc/".to_string() + pidStrRef + &"/environ".to_string();
    let mut envp: &[*const u8];
    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path, why),
        Ok(mut file) => {
            let mut s = String::new();
            match file.read_to_string(&mut s) {
                Err(why) => panic!("couldn't read {}: {}", path, why),
                Ok(_) => {
                    let mut v: Vec<String> = s.split('\0').map(
                        |x| x.to_string() + "\0").collect();
                    v.push("__terminator".to_string());
                    let all_environ_arguments: Vec<*const u8> = v.iter().map(|x| (
                        if x == "__terminator" { ptr::null() }
                        else { x.as_ptr() }
                        )).collect();
                    return all_environ_arguments;
                }
            }
        }
    };
}

fn main() {
    let pidStrRef = &env::args().nth(1).expect("Panicking: expected argv to have 2 items.").to_string();
    // Grab the environ from pid 1048 so that when we execve a shell
    // at the end, we can provide the environment.
    let result = get_envp(pidStrRef);

    // let filename: &[u8] = b"/usr/bin/sensors\x00";     // <-- Make c strings like this
    // let argv1: &[u8] = b"/usr/bin/sensors\x00";
    // let argv2: &[u8] = b"-h\x00";
    // let argv: &[int] = [                               // <-- store them in this
    // ::core::intrinsics::transmute(argv1.as_ptr()), // <-- transmuting
    // ::core::intrinsics::transmute(argv2.as_ptr()),
    // 0                                              // <-- and NULL terminate
    // ];
    // let envp: &[int] = [0];let target_environ
    write(1,
          ("Attaching to process ID ".to_string() + pidStrRef + &"...\n".to_string()).as_bytes());
    let userns_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/user\0").as_bytes());
    let ipc_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/ipc\0").as_bytes());
    let uts_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/uts\0").as_bytes());
    let net_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/net\0").as_bytes());
    let pid_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/pid\0").as_bytes());
    let mnt_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/mnt\0").as_bytes());
    let cwd_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/cwd\0").as_bytes());
    setgroups_zero();
    setns(userns_fd, 0x10000000usize); // CLONE_NEWUSER
    close(userns_fd);
    setns(ipc_fd, 0x08000000usize); // CLONE_NEWIPC
    close(ipc_fd);
    setns(uts_fd, 0x04000000usize); // CLONE_NEWUTS
    close(uts_fd);
    setns(net_fd, 0x40000000usize); // CLONE_NEWNET
    close(net_fd);
    setns(pid_fd, 0x20000000usize); // CLONE_NEWPID
    close(pid_fd);
    setns(mnt_fd, 0x00020000usize); // CLONE_NEWNS which I guess is mount namespaces
    close(mnt_fd);
    fchdir(cwd_fd);
    close(9);
    // fork, and do a handful of things in the child before we execve bash.
    let fork_result = fork();
    if fork_result == 0 {
        // in the child
        setuid_setgid_1000();
        write(1, "About to start shell...\n\0".as_bytes());
        execve_bash(result);
    } else {
        // in the parent
        wait_all_children();
    }
}

// open("/proc/1048/ns/user", O_RDONLY)    = 3
// open("/proc/1048/ns/ipc", O_RDONLY)     = 4
// open("/proc/1048/ns/uts", O_RDONLY)     = 5
// open("/proc/1048/ns/net", O_RDONLY)     = 6
// open("/proc/1048/ns/pid", O_RDONLY)     = 7
// open("/proc/1048/ns/mnt", O_RDONLY)     = 8
// open("/proc/1048/cwd", O_RDONLY)        = 9
// setgroups(0, [])                        = 0
// setns(3, CLONE_NEWUSER)                 = 0
// close(3)                                = 0
// setns(4, CLONE_NEWIPC)                  = 0
// close(4)                                = 0
// setns(5, CLONE_NEWUTS)                  = 0
// close(5)                                = 0
// setns(6, CLONE_NEWNET)                  = 0
// close(6)                                = 0
// setns(7, CLONE_NEWPID)                  = 0
// close(7)                                = 0
// setns(8, CLONE_NEWNS)                   = 0
// close(8)                                = 0
// fchdir(9)                               = 0
// close(9)                                = 0
<commit_msg>Remove one unused variable<commit_after>// use std::env;
use std::fs::File;
use std::io::Read;
use std::ptr;
use std::env;

#[macro_use]
extern crate syscall;

fn write(fd: usize, buf: &[u8]) {
    unsafe {
        syscall!(WRITE, fd, buf.as_ptr(), buf.len());
    }
}

fn open_as_fd_or_die(filename: &[u8]) -> usize {
    let o_rdonly = 0u64;
    let no_flags = 0u64;
    unsafe {
        let return_value = syscall!(OPEN, filename.as_ptr(), o_rdonly, no_flags);
        sanity_check_fd(return_value);
        return return_value;
    }
}

fn setns(fd: usize, nstype: usize) {
    unsafe {
        syscall!(SETNS, fd, nstype);
    }
}

fn close(fd: usize) {
    unsafe {
        syscall!(CLOSE, fd);
    }
}

fn sanity_check_fd(fd: usize) {
    // For some reason, syscall!(OPEN) returns usize, but I need to check its output against
    // -1. So I'm going to just check if it's >255.
    if fd > 255 {
        panic!("Failed to open a needed file. Bailing.");
    }
}

fn setgroups_zero() {
    // Clears auxilary groups. Probably not necessary, but whatever.
    unsafe {
        syscall!(SETGROUPS, 0);
    }
}

fn fchdir(fd: usize) {
    unsafe {
        syscall!(FCHDIR, fd);
    }
}

fn fork() -> usize {
    unsafe {
        syscall!(FORK)
    }
}

fn wait_all_children() {
    let all_children = -1isize;
    let no_options = 0usize;
    let nullptr = 0usize;
    unsafe {
        syscall!(WAIT4, all_children, no_options, nullptr, nullptr);
    }
}

fn setuid_setgid_1000() {
    // These are the right UID & GID because they are what Sandstorm
    // uses inside the user namespace.
    let one_thousand = 1000usize;
    unsafe {
        syscall!(SETUID, one_thousand);
        syscall!(SETGID, one_thousand);
    }
}

fn execve_bash(envp: std::vec::Vec<*const u8>) {
    let bash_path = "/bin/bash\0".as_bytes();
    let nullargv = 0usize;
    unsafe {
        syscall!(EXECVE, bash_path.as_ptr(), nullargv, envp.as_ptr());
    }
}

fn get_envp(pidStrRef: &str) -> std::vec::Vec<*const u8> {
    // Grab the environ from pid 1048 so that when we execve a shell
    // at the end, we can provide the environment.

    let path = "/proc/".to_string() + pidStrRef + &"/environ".to_string();
    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path, why),
        Ok(mut file) => {
            let mut s = String::new();
            match file.read_to_string(&mut s) {
                Err(why) => panic!("couldn't read {}: {}", path, why),
                Ok(_) => {
                    let mut v: Vec<String> = s.split('\0').map(
                        |x| x.to_string() + "\0").collect();
                    v.push("__terminator".to_string());
                    let all_environ_arguments: Vec<*const u8> = v.iter().map(|x| (
                        if x == "__terminator" { ptr::null() }
                        else { x.as_ptr() }
                        )).collect();
                    return all_environ_arguments;
                }
            }
        }
    };
}

fn main() {
    let pidStrRef = &env::args().nth(1).expect("Panicking: expected argv to have 2 items.").to_string();
    // Grab the environ from pid 1048 so that when we execve a shell
    // at the end, we can provide the environment.
    let result = get_envp(pidStrRef);

    // let filename: &[u8] = b"/usr/bin/sensors\x00";     // <-- Make c strings like this
    // let argv1: &[u8] = b"/usr/bin/sensors\x00";
    // let argv2: &[u8] = b"-h\x00";
    // let argv: &[int] = [                               // <-- store them in this
    // ::core::intrinsics::transmute(argv1.as_ptr()), // <-- transmuting
    // ::core::intrinsics::transmute(argv2.as_ptr()),
    // 0                                              // <-- and NULL terminate
    // ];
    // let envp: &[int] = [0];let target_environ
    write(1,
          ("Attaching to process ID ".to_string() + pidStrRef + &"...\n".to_string()).as_bytes());
    let userns_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/user\0").as_bytes());
    let ipc_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/ipc\0").as_bytes());
    let uts_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/uts\0").as_bytes());
    let net_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/net\0").as_bytes());
    let pid_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/pid\0").as_bytes());
    let mnt_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/mnt\0").as_bytes());
    let cwd_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/cwd\0").as_bytes());
    setgroups_zero();
    setns(userns_fd, 0x10000000usize); // CLONE_NEWUSER
    close(userns_fd);
    setns(ipc_fd, 0x08000000usize); // CLONE_NEWIPC
    close(ipc_fd);
    setns(uts_fd, 0x04000000usize); // CLONE_NEWUTS
    close(uts_fd);
    setns(net_fd, 0x40000000usize); // CLONE_NEWNET
    close(net_fd);
    setns(pid_fd, 0x20000000usize); // CLONE_NEWPID
    close(pid_fd);
    setns(mnt_fd, 0x00020000usize); // CLONE_NEWNS which I guess is mount namespaces
    close(mnt_fd);
    fchdir(cwd_fd);
    close(9);
    // fork, and do a handful of things in the child before we execve bash.
    let fork_result = fork();
    if fork_result == 0 {
        // in the child
        setuid_setgid_1000();
        write(1, "About to start shell...\n\0".as_bytes());
        execve_bash(result);
    } else {
        // in the parent
        wait_all_children();
    }
}

// open("/proc/1048/ns/user", O_RDONLY)    = 3
// open("/proc/1048/ns/ipc", O_RDONLY)     = 4
// open("/proc/1048/ns/uts", O_RDONLY)     = 5
// open("/proc/1048/ns/net", O_RDONLY)     = 6
// open("/proc/1048/ns/pid", O_RDONLY)     = 7
// open("/proc/1048/ns/mnt", O_RDONLY)     = 8
// open("/proc/1048/cwd", O_RDONLY)        = 9
// setgroups(0, [])                        = 0
// setns(3, CLONE_NEWUSER)                 = 0
// close(3)                                = 0
// setns(4, CLONE_NEWIPC)                  = 0
// close(4)                                = 0
// setns(5, CLONE_NEWUTS)                  = 0
// close(5)                                = 0
// setns(6, CLONE_NEWNET)                  = 0
// close(6)                                = 0
// setns(7, CLONE_NEWPID)                  = 0
// close(7)                                = 0
// setns(8, CLONE_NEWNS)                   = 0
// close(8)                                = 0
// fchdir(9)                               = 0
// close(9)                                = 0
<|endoftext|>


<commit_before>use log::trace;

use crate::msr;
use crate::vmcs_fields::PinBasedControlsVmxPreemption;
use crate::{
    vcpu::get_current_vcpu,
    vmcs_fields::{CpuBasedControlsInterruptWindowExiting, VmcsField},
    vmx::{vmread, vmwrite},
};

const VM_PREEMPTION_TIMER_VALUE: u64 = 0xffff;
const INTERRUPT_COUNT: usize = 256;
pub struct VirtualLocalInterruptController {
    total_interrupts_count: usize,
    delayed_delivery_interrupts: [usize; INTERRUPT_COUNT],
    requested_poll_of_interrupts_on_next_preemption_timer: bool,
    we_should_disable_preemption_timer: bool,
}

fn get_local_interrupt_controller() -> &'static mut VirtualLocalInterruptController {
    unsafe { &mut *get_current_vcpu().virtual_local_interrupt_controller }
}

fn delay_interrupt_delivery(interrupt_number: u64) {
    let interrupt_controller = get_local_interrupt_controller();
    let count = &mut interrupt_controller.delayed_delivery_interrupts[interrupt_number as usize];
    *count += 1;
    interrupt_controller.total_interrupts_count = interrupt_controller
        .total_interrupts_count
        .checked_add(1)
        .unwrap();
}

fn check_for_highest_priority_interrupt() -> Option<u64> {
    let interrupt_controller = get_local_interrupt_controller();
    if interrupt_controller.total_interrupts_count == 0 {
        return None;
    }
    for (interrupt_number, count) in &mut interrupt_controller
        .delayed_delivery_interrupts
        .iter_mut()
        .enumerate()
    {
        if *count > 0 {
            *count -= 1;
            interrupt_controller.total_interrupts_count -= 1;
            return Some(interrupt_number as u64);
        }
    }
    unreachable!();
}

// See 33.3.3.4 Generation of Virtual Interrupt Events by VMM
fn vmx_is_guest_interruptable() -> bool {
    let guest_rflags = vmread(VmcsField::GuestRFlags).unwrap();
    // Is the guest interrupt flag clear?
    if (guest_rflags & 0x200) == 0 {
        return false;
    }
    // Are interrupts blocked by a hardware state like sti?
    // Remember, interrupts won't be injected until the instruction *after* sti.
    // There are other instructions which do this too, see vol 3 33.3.3.4
    let guest_interruptability_info = vmread(VmcsField::GuestInterruptibilityInfo).unwrap();
    // See Table 24-3. Format of Interruptibility State
    guest_interruptability_info == 0
}

fn vmx_inject_interrupt_into_guest(vector: u64) -> Result<(), x86::vmx::VmFail> {
    assert!(vector < INTERRUPT_COUNT as u64);
    let interrupt_info = vector | (1 << 31) | (0 << 8); // valid, external interrupts
    crate::debug::breakpoint();
    vmwrite(VmcsField::VmEntryIntrInfoField, interrupt_info)
}

fn vmx_configure_interrupts_wakeup() {
    let pair = msr::rdmsr(msr::Msr::Ia32VmxProcBasedControls);
    let disallowed_exit_values = pair.edx;

    // If we are allowed to set interrupt window exiting, set it.
    if u64::from(disallowed_exit_values) & CpuBasedControlsInterruptWindowExiting == 0 {
        trace!("CPU allows interrupt window exiting");
        let mut cpu_based_controls = vmread(VmcsField::CpuBasedVmExecControl).unwrap();
        cpu_based_controls |= CpuBasedControlsInterruptWindowExiting;
        vmwrite(VmcsField::CpuBasedVmExecControl, cpu_based_controls).unwrap();
    } else {
        trace!("CPU does not allow interrupt window exiting, use a preemption timer instead");
        let local_interrupt_controller = get_local_interrupt_controller();
        let mut pin_based_controls = vmread(VmcsField::PinBasedVmExecControl).unwrap();
        if (pin_based_controls & PinBasedControlsVmxPreemption) == 0 {
            pin_based_controls |= PinBasedControlsVmxPreemption;
            vmwrite(VmcsField::PinBasedVmExecControl, pin_based_controls).unwrap();
            local_interrupt_controller.we_should_disable_preemption_timer = true;
        } else {
            local_interrupt_controller.we_should_disable_preemption_timer = false;
        }
        vmwrite(
            VmcsField::VmxPreemptionTimerValue,
            VM_PREEMPTION_TIMER_VALUE,
        )
        .unwrap();
        local_interrupt_controller.requested_poll_of_interrupts_on_next_preemption_timer = true;
    }
}

fn vmx_deconfigure_timer_wakeup() -> Result<(), x86::vmx::VmFail> {
    let mut pin_based_controls = vmread(VmcsField::PinBasedVmExecControl)?;
    pin_based_controls &= !PinBasedControlsVmxPreemption;
    vmwrite(VmcsField::PinBasedVmExecControl, pin_based_controls)
}

fn vmx_deconfigure_interrupt_window_exiting() -> Result<(), x86::vmx::VmFail> {
    let mut cpu_based_controls = vmread(VmcsField::CpuBasedVmExecControl)?;
    cpu_based_controls &= !CpuBasedControlsInterruptWindowExiting;
    vmwrite(VmcsField::CpuBasedVmExecControl, cpu_based_controls)
}

pub fn received_external_interrupt() -> Result<(), x86::vmx::VmFail> {
    let interrupt_info = vmread(VmcsField::VmExitIntrInfo)?;
    let interrupt_number = interrupt_info & 0xff;
    trace!("Received external interrupt {:x}", interrupt_info);

    if vmx_is_guest_interruptable() {
        trace!("Guest is interruptable");
        vmx_inject_interrupt_into_guest(interrupt_number)
    } else {
        trace!("Guest is not interruptable, delay delivery");
        delay_interrupt_delivery(interrupt_number);
        vmx_configure_interrupts_wakeup();
        Ok(())
    }
}

pub fn received_preemption_timer() -> Result<(), x86::vmx::VmFail> {
    let local_interrupt_controller = get_local_interrupt_controller();
    if local_interrupt_controller.requested_poll_of_interrupts_on_next_preemption_timer
        && vmx_is_guest_interruptable()
    {
        if let Some(interrupt_number) = check_for_highest_priority_interrupt() {
            trace!("Delivering interrupt into guest");
            vmx_inject_interrupt_into_guest(interrupt_number as u64)?;
        }

        if local_interrupt_controller.total_interrupts_count == 0
            && local_interrupt_controller.we_should_disable_preemption_timer
        {
            trace!("No more external interrupts cached and we should disable the preemption timer, disabling interrupt window exiting");
            vmx_deconfigure_timer_wakeup()?;
            local_interrupt_controller.we_should_disable_preemption_timer = false;
            local_interrupt_controller.requested_poll_of_interrupts_on_next_preemption_timer =
                false;
        }
    }
    Ok(())
}

pub fn received_interrupt_window_exit() -> Result<(), x86::vmx::VmFail> {
    let local_interrupt_controller = get_local_interrupt_controller();
    assert!(vmx_is_guest_interruptable());
    if let Some(interrupt_number) = check_for_highest_priority_interrupt() {
        trace!("Delivering interrupt into guest");
        vmx_inject_interrupt_into_guest(interrupt_number)?;
    }
    if local_interrupt_controller.total_interrupts_count == 0 {
        trace!("No more external interrupts cached, disabling interrupt window exiting");
        vmx_deconfigure_interrupt_window_exiting()?;
    }
    Ok(())
}
<commit_msg>Fix clippy lint 'the operation is ineffective'<commit_after>use log::trace;

use crate::msr;
use crate::vmcs_fields::PinBasedControlsVmxPreemption;
use crate::{
    vcpu::get_current_vcpu,
    vmcs_fields::{CpuBasedControlsInterruptWindowExiting, VmcsField},
    vmx::{vmread, vmwrite},
};

const VM_PREEMPTION_TIMER_VALUE: u64 = 0xffff;
const INTERRUPT_COUNT: usize = 256;
pub struct VirtualLocalInterruptController {
    total_interrupts_count: usize,
    delayed_delivery_interrupts: [usize; INTERRUPT_COUNT],
    requested_poll_of_interrupts_on_next_preemption_timer: bool,
    we_should_disable_preemption_timer: bool,
}

fn get_local_interrupt_controller() -> &'static mut VirtualLocalInterruptController {
    unsafe { &mut *get_current_vcpu().virtual_local_interrupt_controller }
}

fn delay_interrupt_delivery(interrupt_number: u64) {
    let interrupt_controller = get_local_interrupt_controller();
    let count = &mut interrupt_controller.delayed_delivery_interrupts[interrupt_number as usize];
    *count += 1;
    interrupt_controller.total_interrupts_count = interrupt_controller
        .total_interrupts_count
        .checked_add(1)
        .unwrap();
}

fn check_for_highest_priority_interrupt() -> Option<u64> {
    let interrupt_controller = get_local_interrupt_controller();
    if interrupt_controller.total_interrupts_count == 0 {
        return None;
    }
    for (interrupt_number, count) in &mut interrupt_controller
        .delayed_delivery_interrupts
        .iter_mut()
        .enumerate()
    {
        if *count > 0 {
            *count -= 1;
            interrupt_controller.total_interrupts_count -= 1;
            return Some(interrupt_number as u64);
        }
    }
    unreachable!();
}

// See 33.3.3.4 Generation of Virtual Interrupt Events by VMM
fn vmx_is_guest_interruptable() -> bool {
    let guest_rflags = vmread(VmcsField::GuestRFlags).unwrap();
    // Is the guest interrupt flag clear?
    if (guest_rflags & 0x200) == 0 {
        return false;
    }
    // Are interrupts blocked by a hardware state like sti?
    // Remember, interrupts won't be injected until the instruction *after* sti.
    // There are other instructions which do this too, see vol 3 33.3.3.4
    let guest_interruptability_info = vmread(VmcsField::GuestInterruptibilityInfo).unwrap();
    // See Table 24-3. Format of Interruptibility State
    guest_interruptability_info == 0
}

const VM_ENTRY_INTERRUPT_INFO_VALID: u64 = 1 << 31;
const VM_ENTRY_INTERRUPT_INFO_TYPE_EXTERNAL_INTERRUPT: u64 = 0 << 8;
fn vmx_inject_interrupt_into_guest(vector: u64) -> Result<(), x86::vmx::VmFail> {
    assert!(vector < INTERRUPT_COUNT as u64);
    let interrupt_info = vector | VM_ENTRY_INTERRUPT_INFO_VALID | VM_ENTRY_INTERRUPT_INFO_TYPE_EXTERNAL_INTERRUPT;
    crate::debug::breakpoint();
    vmwrite(VmcsField::VmEntryIntrInfoField, interrupt_info)
}

fn vmx_configure_interrupts_wakeup() {
    let pair = msr::rdmsr(msr::Msr::Ia32VmxProcBasedControls);
    let disallowed_exit_values = pair.edx;

    // If we are allowed to set interrupt window exiting, set it.
    if u64::from(disallowed_exit_values) & CpuBasedControlsInterruptWindowExiting == 0 {
        trace!("CPU allows interrupt window exiting");
        let mut cpu_based_controls = vmread(VmcsField::CpuBasedVmExecControl).unwrap();
        cpu_based_controls |= CpuBasedControlsInterruptWindowExiting;
        vmwrite(VmcsField::CpuBasedVmExecControl, cpu_based_controls).unwrap();
    } else {
        trace!("CPU does not allow interrupt window exiting, use a preemption timer instead");
        let local_interrupt_controller = get_local_interrupt_controller();
        let mut pin_based_controls = vmread(VmcsField::PinBasedVmExecControl).unwrap();
        if (pin_based_controls & PinBasedControlsVmxPreemption) == 0 {
            pin_based_controls |= PinBasedControlsVmxPreemption;
            vmwrite(VmcsField::PinBasedVmExecControl, pin_based_controls).unwrap();
            local_interrupt_controller.we_should_disable_preemption_timer = true;
        } else {
            local_interrupt_controller.we_should_disable_preemption_timer = false;
        }
        vmwrite(
            VmcsField::VmxPreemptionTimerValue,
            VM_PREEMPTION_TIMER_VALUE,
        )
        .unwrap();
        local_interrupt_controller.requested_poll_of_interrupts_on_next_preemption_timer = true;
    }
}

fn vmx_deconfigure_timer_wakeup() -> Result<(), x86::vmx::VmFail> {
    let mut pin_based_controls = vmread(VmcsField::PinBasedVmExecControl)?;
    pin_based_controls &= !PinBasedControlsVmxPreemption;
    vmwrite(VmcsField::PinBasedVmExecControl, pin_based_controls)
}

fn vmx_deconfigure_interrupt_window_exiting() -> Result<(), x86::vmx::VmFail> {
    let mut cpu_based_controls = vmread(VmcsField::CpuBasedVmExecControl)?;
    cpu_based_controls &= !CpuBasedControlsInterruptWindowExiting;
    vmwrite(VmcsField::CpuBasedVmExecControl, cpu_based_controls)
}

pub fn received_external_interrupt() -> Result<(), x86::vmx::VmFail> {
    let interrupt_info = vmread(VmcsField::VmExitIntrInfo)?;
    let interrupt_number = interrupt_info & 0xff;
    trace!("Received external interrupt {:x}", interrupt_info);

    if vmx_is_guest_interruptable() {
        trace!("Guest is interruptable");
        vmx_inject_interrupt_into_guest(interrupt_number)
    } else {
        trace!("Guest is not interruptable, delay delivery");
        delay_interrupt_delivery(interrupt_number);
        vmx_configure_interrupts_wakeup();
        Ok(())
    }
}

pub fn received_preemption_timer() -> Result<(), x86::vmx::VmFail> {
    let local_interrupt_controller = get_local_interrupt_controller();
    if local_interrupt_controller.requested_poll_of_interrupts_on_next_preemption_timer
        && vmx_is_guest_interruptable()
    {
        if let Some(interrupt_number) = check_for_highest_priority_interrupt() {
            trace!("Delivering interrupt into guest");
            vmx_inject_interrupt_into_guest(interrupt_number as u64)?;
        }

        if local_interrupt_controller.total_interrupts_count == 0
            && local_interrupt_controller.we_should_disable_preemption_timer
        {
            trace!("No more external interrupts cached and we should disable the preemption timer, disabling interrupt window exiting");
            vmx_deconfigure_timer_wakeup()?;
            local_interrupt_controller.we_should_disable_preemption_timer = false;
            local_interrupt_controller.requested_poll_of_interrupts_on_next_preemption_timer =
                false;
        }
    }
    Ok(())
}

pub fn received_interrupt_window_exit() -> Result<(), x86::vmx::VmFail> {
    let local_interrupt_controller = get_local_interrupt_controller();
    assert!(vmx_is_guest_interruptable());
    if let Some(interrupt_number) = check_for_highest_priority_interrupt() {
        trace!("Delivering interrupt into guest");
        vmx_inject_interrupt_into_guest(interrupt_number)?;
    }
    if local_interrupt_controller.total_interrupts_count == 0 {
        trace!("No more external interrupts cached, disabling interrupt window exiting");
        vmx_deconfigure_interrupt_window_exiting()?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::iter::Peekable;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathToken {
    Root,
    Field(String),
    Index(usize),
    Star,
    StarIndex
}

fn peek<I>(chars: &mut Peekable<I>) -> Option<(usize, char)> where I: Iterator<Item = (usize, char)> {
    chars.peek().map(|tup| (tup.0.clone(), tup.1.clone()))
}

fn is_identifier_char(ch: char) -> bool {
    ch.is_alphabetic() || ch.is_numeric() || ch == '_' || ch == '-'
}

// identifier -> a-zA-Z0-9+
fn identifier<I>(ch: char, chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    id.push(ch);
    let mut next_char = peek(chars);
    while next_char.is_some() {
        let ch = next_char.unwrap();
        if is_identifier_char(ch.1) {
            chars.next();
            id.push(ch.1);
        } else if ch.1 == '.' || ch.1 == '\'' || ch.1 == '[' {
            break;
        } else {
            return Err(format!("\"{}\" is not allowed in an identifier in path expression \"{}\" at index {}",
                ch.1, path, ch.0));
        }
        next_char = peek(chars);
    }
    tokens.push(PathToken::Field(id));
    Ok(())
}

// path_identifier -> identifier | *
fn path_identifier<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize) -> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    match chars.next() {
        Some(ch) => match ch.1 {
            '*' => {
                tokens.push(PathToken::Star);
                Ok(())
            },
            c if is_identifier_char(c) => {
              identifier(c, chars, tokens, path)?;
              Ok(())
            },
            _ => Err(format!("Expected either a \"*\" or path identifier in path expression \"{}\" at index {}",
                path, ch.0))
        },
        None => Err(format!("Expected a path after \".\" in path expression \"{}\" at index {}",
            path, index))
    }
}

// string_path -> [^']+
fn string_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize) -> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    let mut next_char = peek(chars);
    if next_char.is_some() {
        chars.next();
        let mut ch = next_char.unwrap();
        next_char = peek(chars);
        while ch.1 != '\'' && next_char.is_some() {
            id.push(ch.1);
            chars.next();
            ch = next_char.unwrap();
            next_char = peek(chars);
        }
        if ch.1 == '\'' {
            if id.is_empty() {
                Err(format!("Empty strings are not allowed in path expression \"{}\" at index {}", path, ch.0))
            } else {
                tokens.push(PathToken::Field(id));
                Ok(())
            }
        } else {
            Err(format!("Unterminated string in path expression \"{}\" at index {}", path, ch.0))
        }
    } else {
        Err(format!("Unterminated string in path expression \"{}\" at index {}", path, index))
    }
}

// index_path -> [0-9]+
fn index_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String) -> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    let mut next_char = chars.next();
    id.push(next_char.unwrap().1);
    next_char = peek(chars);
    while next_char.is_some() {
        let ch = next_char.unwrap();
        if ch.1.is_numeric() {
            id.push(ch.1);
            chars.next();
        } else {
            break;
        }
        next_char = peek(chars);
    }

    if let Some(ch) = next_char {
        if ch.1 != ']' {
            return Err(format!("Indexes can only consist of numbers or a \"*\", found \"{}\" instead in path expression \"{}\" at index {}",
                ch.1, path, ch.0))
        }
    }

    tokens.push(PathToken::Index(id.parse().unwrap()));
    Ok(())
}

// bracket_path -> (string_path | index | *) ]
fn bracket_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize) -> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut ch = peek(chars);
    match ch {
        Some(c) => {
            if c.1 == '\'' {
              chars.next();
              string_path(chars, tokens, path, c.0)?
            } else if c.1.is_numeric() {
              index_path(chars, tokens, path)?
            } else if c.1 == '*' {
                chars.next();
                tokens.push(PathToken::StarIndex);
            } else if c.1 == ']' {
                return Err(format!("Empty bracket expressions are not allowed in path expression \"{}\" at index {}",
                    path, c.0));
            } else {
                return Err(format!("Indexes can only consist of numbers or a \"*\", found \"{}\" instead in path expression \"{}\" at index {}",
                    c.1, path, c.0));
            };
            ch = peek(chars);
            match ch {
                Some(c) => if c.1 != ']' {
                    Err(format!("Unterminated brackets, found \"{}\" instead of \"]\" in path expression \"{}\" at index {}",
                        c.1, path, c.0))
                } else {
                    chars.next();
                    Ok(())
                },
                None => Err(format!("Unterminated brackets in path expression \"{}\" at index {}",
                    path, path.len() - 1))
            }
        },
        None => Err(format!("Expected a \"'\" (single qoute) or a digit in path expression \"{}\" after index {}",
            path, index))
    }
}

// path_exp -> (dot-path | bracket-path)*
fn path_exp<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String) -> Result<(), String>
    where I: Iterator<Item = (usize, char)> {
    let mut next_char = chars.next();
    while next_char.is_some() {
        let ch = next_char.unwrap();
        match ch.1 {
            '.' => path_identifier(chars, tokens, path, ch.0)?,
            '[' => bracket_path(chars, tokens, path, ch.0)?,
            _ => return Err(format!("Expected a \".\" or \"[\" instead of \"{}\" in path expression \"{}\" at index {}",
                ch.1, path, ch.0))
        }
        next_char = chars.next();
    }
    Ok(())
}

pub fn parse_path_exp(path: String) -> Result<Vec<PathToken>, String> {
    let mut tokens = vec![];

    // parse_path_exp -> $ path_exp | empty
    let mut chars = path.chars().enumerate().peekable();
    match chars.next() {
      Some(ch) => {
        match ch.1 {
          '$' => {
            tokens.push(PathToken::Root);
            path_exp(&mut chars, &mut tokens, &path)?;
            Ok(tokens)
          },
          c if c.is_alphabetic() || c.is_numeric() => {
            tokens.push(PathToken::Root);
            identifier(c, &mut chars, &mut tokens, &path)?;
            path_exp(&mut chars, &mut tokens, &path)?;
            Ok(tokens)
          },
          _ => Err(format ! ("Path expression \"{}\" does not start with a root marker \"$\"", path))
        }
      },
      None => Ok(tokens)
    }
}

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

    #[test]
    fn parse_path_exp_handles_empty_string() {
        expect!(parse_path_exp(s!(""))).to(be_ok().value(vec![]));
    }

    #[test]
    fn parse_path_exp_handles_root() {
        expect!(parse_path_exp(s!("$"))).to(be_ok().value(vec![PathToken::Root]));
    }

    #[test]
    fn parse_path_exp_handles_missing_root() {
        expect!(parse_path_exp(s!("adsjhaskjdh")))
          .to(be_ok().value(vec![PathToken::Root, PathToken::Field(s!("adsjhaskjdh"))]));
    }

    #[test]
    fn parse_path_exp_handles_missing_path() {
        expect!(parse_path_exp(s!("$adsjhaskjdh"))).to(
            be_err().value(s!("Expected a \".\" or \"[\" instead of \"a\" in path expression \"$adsjhaskjdh\" at index 1")));
    }

    #[test]
    fn parse_path_exp_handles_missing_path_name() {
        expect!(parse_path_exp(s!("$."))).to(
            be_err().value(s!("Expected a path after \".\" in path expression \"$.\" at index 1")));
        expect!(parse_path_exp(s!("$.a.b.c."))).to(
            be_err().value(s!("Expected a path after \".\" in path expression \"$.a.b.c.\" at index 7")));
    }

    #[test]
    fn parse_path_exp_handles_invalid_identifiers() {
        expect!(parse_path_exp(s!("$.abc!"))).to(
            be_err().value(s!("\"!\" is not allowed in an identifier in path expression \"$.abc!\" at index 5")));
        expect!(parse_path_exp(s!("$.a.b.c.}"))).to(
            be_err().value(s!("Expected either a \"*\" or path identifier in path expression \"$.a.b.c.}\" at index 8")));
    }

    #[test]
    fn parse_path_exp_with_simple_identifiers() {
      expect!(parse_path_exp(s!("$.a"))).to(
          be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a"))]));
      expect!(parse_path_exp(s!("$.a.b.c"))).to(
          be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("b")),
          PathToken::Field(s!("c"))]));
      expect!(parse_path_exp(s!("a.b.c"))).to(
        be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("b")),
                           PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_handles_underscores_and_dashes() {
        expect!(parse_path_exp(s!("$.user_id.user-id"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("user_id")),
                               PathToken::Field(s!("user-id"))])
        );
        expect!(parse_path_exp(s!("$._id"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("_id"))])
        );
    }

    #[test]
    fn parse_path_exp_with_star_instead_of_identifiers() {
        expect!(parse_path_exp(s!("$.*"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Star]));
        expect!(parse_path_exp(s!("$.a.*.c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Star,
            PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_with_bracket_notation() {
        expect!(parse_path_exp(s!("$['val1']"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("val1"))]));
        expect!(parse_path_exp(s!("$.a['val@1.'].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("val@1.")),
            PathToken::Field(s!("c"))]));
        expect!(parse_path_exp(s!("$.a[1].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Index(1),
            PathToken::Field(s!("c"))]));
        expect!(parse_path_exp(s!("$.a[*].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::StarIndex,
            PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_with_invalid_bracket_notation() {
        expect!(parse_path_exp(s!("$["))).to(
            be_err().value(s!("Expected a \"'\" (single qoute) or a digit in path expression \"$[\" after index 1")));
        expect!(parse_path_exp(s!("$['"))).to(
            be_err().value(s!("Unterminated string in path expression \"$['\" at index 2")));
        expect!(parse_path_exp(s!("$['Unterminated string"))).to(
            be_err().value(s!("Unterminated string in path expression \"$['Unterminated string\" at index 21")));
        expect!(parse_path_exp(s!("$['']"))).to(
            be_err().value(s!("Empty strings are not allowed in path expression \"$['']\" at index 3")));
        expect!(parse_path_exp(s!("$['test'.b.c"))).to(
            be_err().value(s!("Unterminated brackets, found \".\" instead of \"]\" in path expression \"$['test'.b.c\" at index 8")));
        expect!(parse_path_exp(s!("$['test'"))).to(
            be_err().value(s!("Unterminated brackets in path expression \"$['test'\" at index 7")));
        expect!(parse_path_exp(s!("$['test']b.c"))).to(
            be_err().value(s!("Expected a \".\" or \"[\" instead of \"b\" in path expression \"$[\'test\']b.c\" at index 9")));
    }

    #[test]
    fn parse_path_exp_with_invalid_bracket_index_notation() {
        expect!(parse_path_exp(s!("$[dhghh]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"d\" instead in path expression \"$[dhghh]\" at index 2")));
        expect!(parse_path_exp(s!("$[12abc]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"a\" instead in path expression \"$[12abc]\" at index 4")));
        expect!(parse_path_exp(s!("$[]"))).to(
            be_err().value(s!("Empty bracket expressions are not allowed in path expression \"$[]\" at index 2")));
        expect!(parse_path_exp(s!("$[-1]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"-\" instead in path expression \"$[-1]\" at index 2")));
    }
}
<commit_msg>feat: add colons to the allowed path characters<commit_after>use std::iter::Peekable;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathToken {
    Root,
    Field(String),
    Index(usize),
    Star,
    StarIndex
}

fn peek<I>(chars: &mut Peekable<I>) -> Option<(usize, char)> where I: Iterator<Item = (usize, char)> {
    chars.peek().map(|tup| (tup.0.clone(), tup.1.clone()))
}

fn is_identifier_char(ch: char) -> bool {
    ch.is_alphabetic() || ch.is_numeric() || ch == '_' || ch == '-' || ch == ':'
}

// identifier -> a-zA-Z0-9+
fn identifier<I>(ch: char, chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String)-> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    id.push(ch);
    let mut next_char = peek(chars);
    while next_char.is_some() {
        let ch = next_char.unwrap();
        if is_identifier_char(ch.1) {
            chars.next();
            id.push(ch.1);
        } else if ch.1 == '.' || ch.1 == '\'' || ch.1 == '[' {
            break;
        } else {
            return Err(format!("\"{}\" is not allowed in an identifier in path expression \"{}\" at index {}",
                ch.1, path, ch.0));
        }
        next_char = peek(chars);
    }
    tokens.push(PathToken::Field(id));
    Ok(())
}

// path_identifier -> identifier | *
fn path_identifier<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize) -> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    match chars.next() {
        Some(ch) => match ch.1 {
            '*' => {
                tokens.push(PathToken::Star);
                Ok(())
            },
            c if is_identifier_char(c) => {
              identifier(c, chars, tokens, path)?;
              Ok(())
            },
            _ => Err(format!("Expected either a \"*\" or path identifier in path expression \"{}\" at index {}",
                path, ch.0))
        },
        None => Err(format!("Expected a path after \".\" in path expression \"{}\" at index {}",
            path, index))
    }
}

// string_path -> [^']+
fn string_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize) -> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    let mut next_char = peek(chars);
    if next_char.is_some() {
        chars.next();
        let mut ch = next_char.unwrap();
        next_char = peek(chars);
        while ch.1 != '\'' && next_char.is_some() {
            id.push(ch.1);
            chars.next();
            ch = next_char.unwrap();
            next_char = peek(chars);
        }
        if ch.1 == '\'' {
            if id.is_empty() {
                Err(format!("Empty strings are not allowed in path expression \"{}\" at index {}", path, ch.0))
            } else {
                tokens.push(PathToken::Field(id));
                Ok(())
            }
        } else {
            Err(format!("Unterminated string in path expression \"{}\" at index {}", path, ch.0))
        }
    } else {
        Err(format!("Unterminated string in path expression \"{}\" at index {}", path, index))
    }
}

// index_path -> [0-9]+
fn index_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String) -> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut id = String::new();
    let mut next_char = chars.next();
    id.push(next_char.unwrap().1);
    next_char = peek(chars);
    while next_char.is_some() {
        let ch = next_char.unwrap();
        if ch.1.is_numeric() {
            id.push(ch.1);
            chars.next();
        } else {
            break;
        }
        next_char = peek(chars);
    }

    if let Some(ch) = next_char {
        if ch.1 != ']' {
            return Err(format!("Indexes can only consist of numbers or a \"*\", found \"{}\" instead in path expression \"{}\" at index {}",
                ch.1, path, ch.0))
        }
    }

    tokens.push(PathToken::Index(id.parse().unwrap()));
    Ok(())
}

// bracket_path -> (string_path | index | *) ]
fn bracket_path<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String, index: usize) -> Result<(), String>
    where I: Iterator<Item = (usize, char)>  {
    let mut ch = peek(chars);
    match ch {
        Some(c) => {
            if c.1 == '\'' {
              chars.next();
              string_path(chars, tokens, path, c.0)?
            } else if c.1.is_numeric() {
              index_path(chars, tokens, path)?
            } else if c.1 == '*' {
                chars.next();
                tokens.push(PathToken::StarIndex);
            } else if c.1 == ']' {
                return Err(format!("Empty bracket expressions are not allowed in path expression \"{}\" at index {}",
                    path, c.0));
            } else {
                return Err(format!("Indexes can only consist of numbers or a \"*\", found \"{}\" instead in path expression \"{}\" at index {}",
                    c.1, path, c.0));
            };
            ch = peek(chars);
            match ch {
                Some(c) => if c.1 != ']' {
                    Err(format!("Unterminated brackets, found \"{}\" instead of \"]\" in path expression \"{}\" at index {}",
                        c.1, path, c.0))
                } else {
                    chars.next();
                    Ok(())
                },
                None => Err(format!("Unterminated brackets in path expression \"{}\" at index {}",
                    path, path.len() - 1))
            }
        },
        None => Err(format!("Expected a \"'\" (single qoute) or a digit in path expression \"{}\" after index {}",
            path, index))
    }
}

// path_exp -> (dot-path | bracket-path)*
fn path_exp<I>(chars: &mut Peekable<I>, tokens: &mut Vec<PathToken>, path: &String) -> Result<(), String>
    where I: Iterator<Item = (usize, char)> {
    let mut next_char = chars.next();
    while next_char.is_some() {
        let ch = next_char.unwrap();
        match ch.1 {
            '.' => path_identifier(chars, tokens, path, ch.0)?,
            '[' => bracket_path(chars, tokens, path, ch.0)?,
            _ => return Err(format!("Expected a \".\" or \"[\" instead of \"{}\" in path expression \"{}\" at index {}",
                ch.1, path, ch.0))
        }
        next_char = chars.next();
    }
    Ok(())
}

pub fn parse_path_exp(path: String) -> Result<Vec<PathToken>, String> {
    let mut tokens = vec![];

    // parse_path_exp -> $ path_exp | empty
    let mut chars = path.chars().enumerate().peekable();
    match chars.next() {
      Some(ch) => {
        match ch.1 {
          '$' => {
            tokens.push(PathToken::Root);
            path_exp(&mut chars, &mut tokens, &path)?;
            Ok(tokens)
          },
          c if c.is_alphabetic() || c.is_numeric() => {
            tokens.push(PathToken::Root);
            identifier(c, &mut chars, &mut tokens, &path)?;
            path_exp(&mut chars, &mut tokens, &path)?;
            Ok(tokens)
          },
          _ => Err(format ! ("Path expression \"{}\" does not start with a root marker \"$\"", path))
        }
      },
      None => Ok(tokens)
    }
}

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

    #[test]
    fn parse_path_exp_handles_empty_string() {
        expect!(parse_path_exp(s!(""))).to(be_ok().value(vec![]));
    }

    #[test]
    fn parse_path_exp_handles_root() {
        expect!(parse_path_exp(s!("$"))).to(be_ok().value(vec![PathToken::Root]));
    }

    #[test]
    fn parse_path_exp_handles_missing_root() {
        expect!(parse_path_exp(s!("adsjhaskjdh")))
          .to(be_ok().value(vec![PathToken::Root, PathToken::Field(s!("adsjhaskjdh"))]));
    }

    #[test]
    fn parse_path_exp_handles_missing_path() {
        expect!(parse_path_exp(s!("$adsjhaskjdh"))).to(
            be_err().value(s!("Expected a \".\" or \"[\" instead of \"a\" in path expression \"$adsjhaskjdh\" at index 1")));
    }

    #[test]
    fn parse_path_exp_handles_missing_path_name() {
        expect!(parse_path_exp(s!("$."))).to(
            be_err().value(s!("Expected a path after \".\" in path expression \"$.\" at index 1")));
        expect!(parse_path_exp(s!("$.a.b.c."))).to(
            be_err().value(s!("Expected a path after \".\" in path expression \"$.a.b.c.\" at index 7")));
    }

    #[test]
    fn parse_path_exp_handles_invalid_identifiers() {
        expect!(parse_path_exp(s!("$.abc!"))).to(
            be_err().value(s!("\"!\" is not allowed in an identifier in path expression \"$.abc!\" at index 5")));
        expect!(parse_path_exp(s!("$.a.b.c.}"))).to(
            be_err().value(s!("Expected either a \"*\" or path identifier in path expression \"$.a.b.c.}\" at index 8")));
    }

    #[test]
    fn parse_path_exp_with_simple_identifiers() {
      expect!(parse_path_exp(s!("$.a"))).to(
          be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a"))]));
      expect!(parse_path_exp(s!("$.a.b.c"))).to(
          be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("b")),
          PathToken::Field(s!("c"))]));
      expect!(parse_path_exp(s!("a.b.c"))).to(
        be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("b")),
                           PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_handles_underscores_and_dashes() {
        expect!(parse_path_exp(s!("$.user_id.user-id"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("user_id")),
                               PathToken::Field(s!("user-id"))])
        );
        expect!(parse_path_exp(s!("$._id"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("_id"))])
        );
        expect!(parse_path_exp(s!("$.id:test"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("id:test"))])
        );
    }

    #[test]
    fn parse_path_exp_with_star_instead_of_identifiers() {
        expect!(parse_path_exp(s!("$.*"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Star]));
        expect!(parse_path_exp(s!("$.a.*.c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Star,
            PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_with_bracket_notation() {
        expect!(parse_path_exp(s!("$['val1']"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("val1"))]));
        expect!(parse_path_exp(s!("$.a['val@1.'].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Field(s!("val@1.")),
            PathToken::Field(s!("c"))]));
        expect!(parse_path_exp(s!("$.a[1].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::Index(1),
            PathToken::Field(s!("c"))]));
        expect!(parse_path_exp(s!("$.a[*].c"))).to(
            be_ok().value(vec![PathToken::Root, PathToken::Field(s!("a")), PathToken::StarIndex,
            PathToken::Field(s!("c"))]));
    }

    #[test]
    fn parse_path_exp_with_invalid_bracket_notation() {
        expect!(parse_path_exp(s!("$["))).to(
            be_err().value(s!("Expected a \"'\" (single qoute) or a digit in path expression \"$[\" after index 1")));
        expect!(parse_path_exp(s!("$['"))).to(
            be_err().value(s!("Unterminated string in path expression \"$['\" at index 2")));
        expect!(parse_path_exp(s!("$['Unterminated string"))).to(
            be_err().value(s!("Unterminated string in path expression \"$['Unterminated string\" at index 21")));
        expect!(parse_path_exp(s!("$['']"))).to(
            be_err().value(s!("Empty strings are not allowed in path expression \"$['']\" at index 3")));
        expect!(parse_path_exp(s!("$['test'.b.c"))).to(
            be_err().value(s!("Unterminated brackets, found \".\" instead of \"]\" in path expression \"$['test'.b.c\" at index 8")));
        expect!(parse_path_exp(s!("$['test'"))).to(
            be_err().value(s!("Unterminated brackets in path expression \"$['test'\" at index 7")));
        expect!(parse_path_exp(s!("$['test']b.c"))).to(
            be_err().value(s!("Expected a \".\" or \"[\" instead of \"b\" in path expression \"$[\'test\']b.c\" at index 9")));
    }

    #[test]
    fn parse_path_exp_with_invalid_bracket_index_notation() {
        expect!(parse_path_exp(s!("$[dhghh]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"d\" instead in path expression \"$[dhghh]\" at index 2")));
        expect!(parse_path_exp(s!("$[12abc]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"a\" instead in path expression \"$[12abc]\" at index 4")));
        expect!(parse_path_exp(s!("$[]"))).to(
            be_err().value(s!("Empty bracket expressions are not allowed in path expression \"$[]\" at index 2")));
        expect!(parse_path_exp(s!("$[-1]"))).to(
            be_err().value(s!("Indexes can only consist of numbers or a \"*\", found \"-\" instead in path expression \"$[-1]\" at index 2")));
    }
}
<|endoftext|>


<commit_before>pub const NODE: &str = "0";
pub const NYM: &str = "1";
pub const GET_TXN: &str = "3";
pub const ATTRIB: &str = "100";
pub const SCHEMA: &str = "101";
pub const CRED_DEF: &str = "102";
pub const GET_ATTR: &str = "104";
pub const GET_NYM: &str = "105";
pub const GET_SCHEMA: &str = "107";
pub const GET_CRED_DEF: &str = "108";
pub const POOL_UPGRADE: &str = "109";
pub const POOL_RESTART: &str = "118";
pub const POOL_CONFIG: &str = "111";
pub const REVOC_REG_DEF: &str = "113";
pub const REVOC_REG_ENTRY: &str = "114";
pub const GET_REVOC_REG_DEF: &str = "115";
pub const GET_REVOC_REG: &str = "116";
pub const GET_REVOC_REG_DELTA: &str = "117";
pub const GET_VALIDATOR_INFO: &str = "119";
pub const AUTH_RULE: &str = "120";
pub const GET_DDO: &str = "120";//TODO change number

pub const WRITE_REQUESTS: [&str; 10] = [NODE, NYM, ATTRIB, SCHEMA, CRED_DEF, POOL_UPGRADE, POOL_CONFIG, REVOC_REG_DEF, REVOC_REG_ENTRY];

pub const TRUSTEE: &str = "0";
pub const STEWARD: &str = "2";
pub const TRUST_ANCHOR: &str = "101";
pub const NETWORK_MONITOR: &str = "201";
pub const ROLE_REMOVE: &str = "";

pub fn txn_name_to_code(txn: &str) -> Option<&str> {
    if WRITE_REQUESTS.contains(&txn) {
        return Some(txn)
    }

    match txn {
        "NODE" => Some(NODE),
        "NYM" => Some(NYM),
        "ATTRIB" => Some(ATTRIB),
        "SCHEMA" => Some(SCHEMA),
        "CRED_DEF" | "CLAIM_DEF" => Some(CRED_DEF),
        "POOL_UPGRADE" => Some(POOL_UPGRADE),
        "POOL_CONFIG" => Some(POOL_CONFIG),
        "REVOC_REG_DEF" => Some(REVOC_REG_DEF),
        "REVOC_REG_ENTRY" => Some(REVOC_REG_ENTRY),
        _ => None
    }
}<commit_msg>Fixed compilation error<commit_after>pub const NODE: &str = "0";
pub const NYM: &str = "1";
pub const GET_TXN: &str = "3";
pub const ATTRIB: &str = "100";
pub const SCHEMA: &str = "101";
pub const CRED_DEF: &str = "102";
pub const GET_ATTR: &str = "104";
pub const GET_NYM: &str = "105";
pub const GET_SCHEMA: &str = "107";
pub const GET_CRED_DEF: &str = "108";
pub const POOL_UPGRADE: &str = "109";
pub const POOL_RESTART: &str = "118";
pub const POOL_CONFIG: &str = "111";
pub const REVOC_REG_DEF: &str = "113";
pub const REVOC_REG_ENTRY: &str = "114";
pub const GET_REVOC_REG_DEF: &str = "115";
pub const GET_REVOC_REG: &str = "116";
pub const GET_REVOC_REG_DELTA: &str = "117";
pub const GET_VALIDATOR_INFO: &str = "119";
pub const AUTH_RULE: &str = "120";
pub const GET_DDO: &str = "120";//TODO change number

pub const WRITE_REQUESTS: [&str; 9] = [NODE, NYM, ATTRIB, SCHEMA, CRED_DEF, POOL_UPGRADE, POOL_CONFIG, REVOC_REG_DEF, REVOC_REG_ENTRY];

pub const TRUSTEE: &str = "0";
pub const STEWARD: &str = "2";
pub const TRUST_ANCHOR: &str = "101";
pub const NETWORK_MONITOR: &str = "201";
pub const ROLE_REMOVE: &str = "";

pub fn txn_name_to_code(txn: &str) -> Option<&str> {
    if WRITE_REQUESTS.contains(&txn) {
        return Some(txn)
    }

    match txn {
        "NODE" => Some(NODE),
        "NYM" => Some(NYM),
        "ATTRIB" => Some(ATTRIB),
        "SCHEMA" => Some(SCHEMA),
        "CRED_DEF" | "CLAIM_DEF" => Some(CRED_DEF),
        "POOL_UPGRADE" => Some(POOL_UPGRADE),
        "POOL_CONFIG" => Some(POOL_CONFIG),
        "REVOC_REG_DEF" => Some(REVOC_REG_DEF),
        "REVOC_REG_ENTRY" => Some(REVOC_REG_ENTRY),
        _ => None
    }
}<|endoftext|>