<commit_before>//! FFI wrapper for `MessagePact` from pact_matching.

use crate::util::*;
use crate::{as_mut, as_ref, ffi_fn, safe_str};
use anyhow::{anyhow, Context};
use libc::c_char;
use std::iter::{self, Iterator};

// Necessary to make 'cbindgen' generate an opaque struct on the C side.
pub use pact_matching::models::message_pact::MessagePact;
use pact_matching::models::Consumer;
use pact_matching::models::Provider;

ffi_fn! {
    /// Construct a new `MessagePact` from the JSON string.
    /// The provided file name is used when generating error messages.
    fn message_pact_new_from_json(
        file_name: *const c_char,
        json_str: *const c_char
    ) -> *mut MessagePact {
        let file_name = safe_str!(file_name);
        let json_str = safe_str!(json_str);

        let json_value: serde_json::Value =
            serde_json::from_str(json_str)
            .context("error parsing json_str as JSON")?;

        let message_pact = MessagePact::from_json(
            &file_name.to_string(),
            &json_value,
        ).map_err(|e| anyhow!("{}", e))?;

        ptr::raw_to(message_pact)
    } {
        ptr::null_mut_to::<MessagePact>()
    }
}

ffi_fn! {
    /// Delete the `MessagePact` being pointed to.
    fn message_pact_delete(message_pact: *mut MessagePact) {
        ptr::drop_raw(message_pact);
    }
}

ffi_fn! {
    /// Get a pointer to the Consumer struct inside the MessagePact.
    /// This is a mutable borrow: The caller may mutate the Consumer
    /// through this pointer.
    ///
    /// # Errors
    ///
    /// This function will only fail if it is passed a NULL pointer.
    /// In the case of error, a NULL pointer will be returned.
    fn message_pact_get_consumer(message_pact: *mut MessagePact) -> *mut Consumer {
        let message_pact = as_mut!(message_pact);
        let consumer = &mut message_pact.consumer;
        consumer as *mut Consumer
    } {
        ptr::null_mut_to::<Consumer>()
    }
}

ffi_fn! {
    /// Get a pointer to the Provider struct inside the MessagePact.
    /// This is a mutable borrow: The caller may mutate the Provider
    /// through this pointer.
    ///
    /// # Errors
    ///
    /// This function will only fail if it is passed a NULL pointer.
    /// In the case of error, a NULL pointer will be returned.
    fn message_pact_get_provider(message_pact: *mut MessagePact) -> *mut Provider {
        let message_pact = as_mut!(message_pact);
        let provider = &mut message_pact.provider;
        provider as *mut Provider
    } {
        ptr::null_mut_to::<Provider>()
    }
}

ffi_fn! {
    /// Get a copy of the metadata value indexed by `key1` and `key2`.
    /// The returned string must be deleted with `string_delete`.
    ///
    /// Since it is a copy, the returned string may safely outlive
    /// the `Message`.
    ///
    /// The returned pointer will be NULL if the metadata does not contain
    /// the given key, or if an error occurred.
    ///
    /// # Errors
    ///
    /// On failure, this function will return a NULL pointer.
    ///
    /// This function may fail if the provided `key1` or `key2` strings contains
    /// invalid UTF-8, or if the Rust string contains embedded null ('\0')
    /// bytes.
    fn message_pact_find_metadata(message_pact: *const MessagePact, key1: *const c_char, key2: *const c_char) -> *const c_char {
        let message_pact = as_ref!(message_pact);
        let key1 = safe_str!(key1);
        let key2 = safe_str!(key2);
        let metadata = message_pact.metadata.get(key1).ok_or(anyhow::anyhow!("invalid metadata key (key 1)"))?;
        let value = metadata.get(key2).ok_or(anyhow::anyhow!("invalid metadata key (key 2)"))?;
        let value_ptr = string::to_c(value)?;
        value_ptr as *const c_char
    } {
        ptr::null_to::<c_char>()
    }
}

ffi_fn! {
    /// Get an iterator over the metadata of a message pact.
    ///
    /// This iterator carries a pointer to the message pact, and must
    /// not outlive the message pact.
    ///
    /// The message pact metadata also must not be modified during iteration. If it is,
    /// the old iterator must be deleted and a new iterator created.
    ///
    /// # Errors
    ///
    /// On failure, this function will return a NULL pointer.
    ///
    /// This function may fail if any of the Rust strings contain
    /// embedded null ('\0') bytes.
    fn message_pact_get_metadata_iter(message_pact: *mut MessagePact) -> *mut MessagePactMetadataIterator {
        let message_pact = as_mut!(message_pact);

        let keys = message_pact
            .metadata
            .iter()
            .flat_map(|(outer_key, sub_tree)| {
                let outer_key_repeater = iter::repeat(outer_key.clone());
                let inner_keys = sub_tree.keys().cloned();

                Iterator::zip(outer_key_repeater, inner_keys)
            })
            .collect();

        let iter = MessagePactMetadataIterator {
            keys,
            current: 0,
            message_pact: message_pact as *const MessagePact,
        };

        ptr::raw_to(iter)
    } {
        ptr::null_mut_to::<MessagePactMetadataIterator>()
    }
}

ffi_fn! {
    /// Get the next triple out of the iterator, if possible
    fn message_pact_metadata_iter_next(iter: *mut MessagePactMetadataIterator) -> *mut MessagePactMetadataTriple {
        let iter = as_mut!(iter);
        let message_pact = as_ref!(iter.message_pact);
        let (outer_key, inner_key) = iter.next().ok_or(anyhow::anyhow!("iter past the end of metadata"))?;

        let (outer_key, sub_tree) = message_pact
            .metadata
            .get_key_value(outer_key)
            .ok_or(anyhow::anyhow!("iter provided invalid metadata key"))?;

        let (inner_key, value) = sub_tree
            .get_key_value(inner_key)
            .ok_or(anyhow::anyhow!("iter provided invalid metadata key"))?;

        let triple = MessagePactMetadataTriple::new(outer_key, inner_key, value)?;

        ptr::raw_to(triple)
    } {
        ptr::null_mut_to::<MessagePactMetadataTriple>()
    }
}

ffi_fn! {
    /// Free the metadata iterator when you're done using it.
    fn message_pact_metadata_iter_delete(iter: *mut MessagePactMetadataIterator) {
        ptr::drop_raw(iter);
    }
}

ffi_fn! {
    /// Free a triple returned from `message_pact_metadata_iter_next`.
    fn message_pact_metadata_triple_delete(triple: *mut MessagePactMetadataTriple) {
        ptr::drop_raw(triple);
    }
}

/// An iterator that enables FFI iteration over metadata by putting all the keys on the heap
/// and tracking which one we're currently at.
///
/// This assumes no mutation of the underlying metadata happens while the iterator is live.
#[derive(Debug)]
pub struct MessagePactMetadataIterator {
    /// The metadata keys
    keys: Vec<(String, String)>,
    /// The current key
    current: usize,
    /// Pointer to the message.
    message_pact: *const MessagePact,
}

impl MessagePactMetadataIterator {
    fn next(&mut self) -> Option<(&str, &str)> {
        let idx = self.current;
        self.current += 1;
        self.keys.get(idx).map(|(outer_key, inner_key)| {
            (outer_key.as_ref(), inner_key.as_ref())
        })
    }
}

/// A triple, containing the outer key, inner key, and value, exported to the C-side.
#[derive(Debug)]
#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct MessagePactMetadataTriple {
    /// The outer key of the `MessagePact` metadata.
    outer_key: *const c_char,
    /// The inner key of the `MessagePact` metadata.
    inner_key: *const c_char,
    /// The value of the `MessagePact` metadata.
    value: *const c_char,
}

impl MessagePactMetadataTriple {
    fn new(
        outer_key: &str,
        inner_key: &str,
        value: &str,
    ) -> anyhow::Result<MessagePactMetadataTriple> {
        Ok(MessagePactMetadataTriple {
            outer_key: string::to_c(outer_key)? as *const c_char,
            inner_key: string::to_c(inner_key)? as *const c_char,
            value: string::to_c(value)? as *const c_char,
        })
    }
}

// Ensure that the owned strings are freed when the triple is dropped.
//
// Notice that we're casting from a `*const c_char` to a `*mut c_char`.
// This may seem wrong, but is safe so long as it doesn't violate Rust's
// guarantees around immutable references, which this doesn't. In this case,
// the underlying data came from `CString::into_raw` which takes ownership
// of the `CString` and hands it off via a `*mut pointer`. We cast that pointer
// back to `*const` to limit the C-side from doing any shenanigans, since the
// pointed-to values live inside of the `Message` metadata `HashMap`, but
// cast back to `*mut` here so we can free the memory.
//
// The discussion here helps explain: https://github.com/rust-lang/rust-clippy/issues/4774
impl Drop for MessagePactMetadataTriple {
    fn drop(&mut self) {
        string::string_delete(self.outer_key as *mut c_char);
        string::string_delete(self.inner_key as *mut c_char);
        string::string_delete(self.value as *mut c_char);
    }
}
<commit_msg>Added message iterator for message pacts (#76)<commit_after>//! FFI wrapper for `MessagePact` from pact_matching.

use crate::util::*;
use crate::{as_mut, as_ref, ffi_fn, safe_str};
use anyhow::{anyhow, Context};
use libc::c_char;
use std::iter::{self, Iterator};

// Necessary to make 'cbindgen' generate an opaque struct on the C side.
pub use pact_matching::models::message_pact::MessagePact;
use pact_matching::models::Consumer;
use pact_matching::models::Provider;
use crate::models::message::Message;

ffi_fn! {
    /// Construct a new `MessagePact` from the JSON string.
    /// The provided file name is used when generating error messages.
    fn message_pact_new_from_json(
        file_name: *const c_char,
        json_str: *const c_char
    ) -> *mut MessagePact {
        let file_name = safe_str!(file_name);
        let json_str = safe_str!(json_str);

        let json_value: serde_json::Value =
            serde_json::from_str(json_str)
            .context("error parsing json_str as JSON")?;

        let message_pact = MessagePact::from_json(
            &file_name.to_string(),
            &json_value,
        ).map_err(|e| anyhow!("{}", e))?;

        ptr::raw_to(message_pact)
    } {
        ptr::null_mut_to::<MessagePact>()
    }
}

ffi_fn! {
    /// Delete the `MessagePact` being pointed to.
    fn message_pact_delete(message_pact: *mut MessagePact) {
        ptr::drop_raw(message_pact);
    }
}

ffi_fn! {
    /// Get a pointer to the Consumer struct inside the MessagePact.
    /// This is a mutable borrow: The caller may mutate the Consumer
    /// through this pointer.
    ///
    /// # Errors
    ///
    /// This function will only fail if it is passed a NULL pointer.
    /// In the case of error, a NULL pointer will be returned.
    fn message_pact_get_consumer(message_pact: *mut MessagePact) -> *mut Consumer {
        let message_pact = as_mut!(message_pact);
        let consumer = &mut message_pact.consumer;
        consumer as *mut Consumer
    } {
        ptr::null_mut_to::<Consumer>()
    }
}

ffi_fn! {
    /// Get a pointer to the Provider struct inside the MessagePact.
    /// This is a mutable borrow: The caller may mutate the Provider
    /// through this pointer.
    ///
    /// # Errors
    ///
    /// This function will only fail if it is passed a NULL pointer.
    /// In the case of error, a NULL pointer will be returned.
    fn message_pact_get_provider(message_pact: *mut MessagePact) -> *mut Provider {
        let message_pact = as_mut!(message_pact);
        let provider = &mut message_pact.provider;
        provider as *mut Provider
    } {
        ptr::null_mut_to::<Provider>()
    }
}

ffi_fn! {
    /// Get an iterator over the messages of a message pact.
    ///
    /// This iterator carries a pointer to the message pact, and must
    /// not outlive the message pact.
    ///
    /// The message pact messages also must not be modified during iteration.
    /// If they are, the old iterator must be deleted and a new iterator created.
    ///
    /// # Errors
    ///
    /// On failure, this function will return a NULL pointer.
    ///
    /// This function may fail if any of the Rust strings contain embedded
    /// null ('\0') bytes.
    fn message_pact_get_message_iter(message_pact: *mut MessagePact) -> *mut MessagePactMessageIterator {
        let message_pact = as_mut!(message_pact);
        let iter = MessagePactMessageIterator { current: 0, message_pact };
        ptr::raw_to(iter)
    } {
        ptr::null_mut_to::<MessagePactMessageIterator>()
    }
}

ffi_fn! {
    /// Get the next message from the message pact.
    fn message_pact_message_iter_next(iter: *mut MessagePactMessageIterator) -> *mut Message {
        let iter = as_mut!(iter);
        let message_pact = as_mut!(iter.message_pact);
        let index = iter.next();
        let message = message_pact
            .messages
            .get_mut(index)
            .ok_or(anyhow::anyhow!("iter past the end of messages"))?;
        message as *mut Message
    } {
        ptr::null_mut_to::<Message>()
    }
}

ffi_fn! {
    /// Delete the iterator.
    fn message_pact_message_iter_delete(iter: *mut MessagePactMessageIterator) {
        ptr::drop_raw(iter);
    }
}

ffi_fn! {
    /// Get a copy of the metadata value indexed by `key1` and `key2`.
    /// The returned string must be deleted with `string_delete`.
    ///
    /// Since it is a copy, the returned string may safely outlive
    /// the `Message`.
    ///
    /// The returned pointer will be NULL if the metadata does not contain
    /// the given key, or if an error occurred.
    ///
    /// # Errors
    ///
    /// On failure, this function will return a NULL pointer.
    ///
    /// This function may fail if the provided `key1` or `key2` strings contains
    /// invalid UTF-8, or if the Rust string contains embedded null ('\0')
    /// bytes.
    fn message_pact_find_metadata(message_pact: *const MessagePact, key1: *const c_char, key2: *const c_char) -> *const c_char {
        let message_pact = as_ref!(message_pact);
        let key1 = safe_str!(key1);
        let key2 = safe_str!(key2);
        let metadata = message_pact.metadata.get(key1).ok_or(anyhow::anyhow!("invalid metadata key (key 1)"))?;
        let value = metadata.get(key2).ok_or(anyhow::anyhow!("invalid metadata key (key 2)"))?;
        let value_ptr = string::to_c(value)?;
        value_ptr as *const c_char
    } {
        ptr::null_to::<c_char>()
    }
}

ffi_fn! {
    /// Get an iterator over the metadata of a message pact.
    ///
    /// This iterator carries a pointer to the message pact, and must
    /// not outlive the message pact.
    ///
    /// The message pact metadata also must not be modified during iteration. If it is,
    /// the old iterator must be deleted and a new iterator created.
    ///
    /// # Errors
    ///
    /// On failure, this function will return a NULL pointer.
    ///
    /// This function may fail if any of the Rust strings contain
    /// embedded null ('\0') bytes.
    fn message_pact_get_metadata_iter(message_pact: *mut MessagePact) -> *mut MessagePactMetadataIterator {
        let message_pact = as_mut!(message_pact);

        let keys = message_pact
            .metadata
            .iter()
            .flat_map(|(outer_key, sub_tree)| {
                let outer_key_repeater = iter::repeat(outer_key.clone());
                let inner_keys = sub_tree.keys().cloned();

                Iterator::zip(outer_key_repeater, inner_keys)
            })
            .collect();

        let iter = MessagePactMetadataIterator {
            keys,
            current: 0,
            message_pact: message_pact as *const MessagePact,
        };

        ptr::raw_to(iter)
    } {
        ptr::null_mut_to::<MessagePactMetadataIterator>()
    }
}

ffi_fn! {
    /// Get the next triple out of the iterator, if possible
    fn message_pact_metadata_iter_next(iter: *mut MessagePactMetadataIterator) -> *mut MessagePactMetadataTriple {
        let iter = as_mut!(iter);
        let message_pact = as_ref!(iter.message_pact);
        let (outer_key, inner_key) = iter.next().ok_or(anyhow::anyhow!("iter past the end of metadata"))?;

        let (outer_key, sub_tree) = message_pact
            .metadata
            .get_key_value(outer_key)
            .ok_or(anyhow::anyhow!("iter provided invalid metadata key"))?;

        let (inner_key, value) = sub_tree
            .get_key_value(inner_key)
            .ok_or(anyhow::anyhow!("iter provided invalid metadata key"))?;

        let triple = MessagePactMetadataTriple::new(outer_key, inner_key, value)?;

        ptr::raw_to(triple)
    } {
        ptr::null_mut_to::<MessagePactMetadataTriple>()
    }
}

ffi_fn! {
    /// Free the metadata iterator when you're done using it.
    fn message_pact_metadata_iter_delete(iter: *mut MessagePactMetadataIterator) {
        ptr::drop_raw(iter);
    }
}

ffi_fn! {
    /// Free a triple returned from `message_pact_metadata_iter_next`.
    fn message_pact_metadata_triple_delete(triple: *mut MessagePactMetadataTriple) {
        ptr::drop_raw(triple);
    }
}

/// An iterator over messages in a message pact.
#[derive(Debug)]
#[allow(missing_copy_implementations)]
pub struct MessagePactMessageIterator {
    current: usize,
    message_pact: *mut MessagePact,
}

impl MessagePactMessageIterator {
    /// Get the index of the next message in the message pact.
    fn next(&mut self) -> usize {
        let idx = self.current;
        self.current += 1;
        idx
    }
}

/// An iterator that enables FFI iteration over metadata by putting all the keys on the heap
/// and tracking which one we're currently at.
///
/// This assumes no mutation of the underlying metadata happens while the iterator is live.
#[derive(Debug)]
pub struct MessagePactMetadataIterator {
    /// The metadata keys
    keys: Vec<(String, String)>,
    /// The current key
    current: usize,
    /// Pointer to the message pact.
    message_pact: *const MessagePact,
}

impl MessagePactMetadataIterator {
    fn next(&mut self) -> Option<(&str, &str)> {
        let idx = self.current;
        self.current += 1;
        self.keys.get(idx).map(|(outer_key, inner_key)| {
            (outer_key.as_ref(), inner_key.as_ref())
        })
    }
}

/// A triple, containing the outer key, inner key, and value, exported to the C-side.
#[derive(Debug)]
#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct MessagePactMetadataTriple {
    /// The outer key of the `MessagePact` metadata.
    outer_key: *const c_char,
    /// The inner key of the `MessagePact` metadata.
    inner_key: *const c_char,
    /// The value of the `MessagePact` metadata.
    value: *const c_char,
}

impl MessagePactMetadataTriple {
    fn new(
        outer_key: &str,
        inner_key: &str,
        value: &str,
    ) -> anyhow::Result<MessagePactMetadataTriple> {
        Ok(MessagePactMetadataTriple {
            outer_key: string::to_c(outer_key)? as *const c_char,
            inner_key: string::to_c(inner_key)? as *const c_char,
            value: string::to_c(value)? as *const c_char,
        })
    }
}

// Ensure that the owned strings are freed when the triple is dropped.
//
// Notice that we're casting from a `*const c_char` to a `*mut c_char`.
// This may seem wrong, but is safe so long as it doesn't violate Rust's
// guarantees around immutable references, which this doesn't. In this case,
// the underlying data came from `CString::into_raw` which takes ownership
// of the `CString` and hands it off via a `*mut pointer`. We cast that pointer
// back to `*const` to limit the C-side from doing any shenanigans, since the
// pointed-to values live inside of the `Message` metadata `HashMap`, but
// cast back to `*mut` here so we can free the memory.
//
// The discussion here helps explain: https://github.com/rust-lang/rust-clippy/issues/4774
impl Drop for MessagePactMetadataTriple {
    fn drop(&mut self) {
        string::string_delete(self.outer_key as *mut c_char);
        string::string_delete(self.inner_key as *mut c_char);
        string::string_delete(self.value as *mut c_char);
    }
}
<|endoftext|>


<commit_before>//! Matching Rule definition expressions
//!
//! Parser for parsing matching rule definitions into a value, matching rules and generator tuple.
//!
//! The following are examples of matching rule definitions:
//! * `matching(type,'Name')` - type matcher
//! * `matching(number,100)` - number matcher
//! * `matching(datetime, 'yyyy-MM-dd','2000-01-01')` - datetime matcher with format string
//!

use anyhow::anyhow;
use log::debug;
use logos::{Lexer, Logos};
use crate::generators::Generator;
use crate::matchingrules::MatchingRule;

#[derive(Logos, Debug, PartialEq)]
enum MatcherDefinitionToken {
  #[token("matching")]
  Matching,

  #[token("(")]
  LeftBracket,

  #[token(")")]
  RightBracket,

  #[token(",")]
  Comma,

  #[regex("'[^']*'")]
  String,

  #[regex("[a-zA-Z]+")]
  Id,

  #[regex("-?[0-9]+", |lex| lex.slice().parse())]
  Int(i64),

  #[regex(r"-?[0-9]\.[0-9]+")]
  Decimal,

  #[regex(r"true|false")]
  Boolean,

  #[error]
  #[regex(r"[ \t\n\f]+", logos::skip)]
  Error
}

/// Parse a matcher definition into a tuple containing the example value, matching rule and any
/// generator.
/// The following are examples of matching rule definitions:
/// * `matching(type,'Name')` - type matcher
/// * `matching(number,100)` - number matcher
/// * `matching(datetime, 'yyyy-MM-dd','2000-01-01')` - datetime matcher with format string
pub fn parse_matcher_def(v: &str) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  let mut lex = MatcherDefinitionToken::lexer(v);
  let next = lex.next();
  debug!("First Token: {:?}", next);
  if let Some(token) = next {
    if token == MatcherDefinitionToken::Matching {
      let next = lex.next().ok_or(anyhow!("'{}' is not a valid value definition, expected '('", v))?;
      if next == MatcherDefinitionToken::LeftBracket {
        let result = parse_matching_def(&mut lex)?;
        let next = lex.next().ok_or(anyhow!("'{}' is not a valid value definition, expected ')'", v))?;
        if next == MatcherDefinitionToken::RightBracket {
          let next = lex.next();
          if next.is_none() {
            Ok(result)
          } else {
            Err(anyhow!("'{}' is not a valid value definition, got '{}' after the closing bracket", v, lex.remainder()))
          }
        } else {
          Err(anyhow!("'{}' is not a valid value definition, expected closing bracket, got '{}'", v, lex.slice()))
        }
      } else {
        Err(anyhow!("'{}' is not a valid value definition, expected '(', got '{}'", v, lex.remainder()))
      }
    } else {
      Ok((v.to_string(), None, None))
    }
  } else {
    Ok((v.to_string(), None, None))
  }
}

fn parse_matching_def(lex: &mut logos::Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected a matcher type"))?;
  if next == MatcherDefinitionToken::Id {
    match lex.slice() {
      "equality" => parse_equality(lex),
      "regex" => parse_regex(lex),
      "type" => parse_type(lex),
      "datetime" => parse_datetime(lex),
      "date" => parse_date(lex),
      "time" => parse_time(lex),
      "include" => parse_include(lex),
      "number" => parse_number(lex),
      "integer" => parse_integer(lex),
      "decimal" => parse_decimal(lex),
      "boolean" => parse_boolean(lex),
      _ => Err(anyhow!("Not a valid matcher definition, expected the type of matcher, got '{}'", lex.slice()))
    }
  } else {
    Err(anyhow!("Not a valid matcher definition, expected the type of matcher, got '{}'", lex.slice()))
  }
}

fn parse_equality(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value, Some(MatchingRule::Equality), None))
}

fn parse_regex(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let regex = parse_string(lex)?;
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value, Some(MatchingRule::Regex(regex)), None))
}

fn parse_type(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value, Some(MatchingRule::Type), None))
}

fn parse_datetime(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let format = parse_string(lex)?;
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value, Some(MatchingRule::Timestamp(format.clone())), Some(Generator::DateTime(Some(format.clone())))))
}

fn parse_date(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let format = parse_string(lex)?;
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value, Some(MatchingRule::Date(format.clone())), Some(Generator::Date(Some(format.clone())))))
}

fn parse_time(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let format = parse_string(lex)?;
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value, Some(MatchingRule::Time(format.clone())), Some(Generator::Time(Some(format.clone())))))
}

fn parse_include(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value.clone(), Some(MatchingRule::Include(value.clone())), None))
}

fn parse_number(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected a number"))?;
  if let MatcherDefinitionToken::Int(_) = next {
    Ok((lex.slice().to_string(), Some(MatchingRule::Number), None))
  } else if MatcherDefinitionToken::Decimal == next {
    Ok((lex.slice().to_string(), Some(MatchingRule::Number), None))
  } else {
    Err(anyhow!("Not a valid matcher definition, expected a number, got '{}'", lex.slice()))
  }
}

fn parse_integer(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected an integer"))?;
  if let MatcherDefinitionToken::Int(_) = next {
    Ok((lex.slice().to_string(), Some(MatchingRule::Integer), None))
  } else {
    Err(anyhow!("Not a valid matcher definition, expected an integer, got '{}'", lex.slice()))
  }
}

fn parse_decimal(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected a decimal number"))?;
  if let MatcherDefinitionToken::Int(_) = next {
    Ok((lex.slice().to_string(), Some(MatchingRule::Decimal), None))
  } else if MatcherDefinitionToken::Decimal == next {
    Ok((lex.slice().to_string(), Some(MatchingRule::Decimal), None))
  } else {
    Err(anyhow!("Not a valid matcher definition, expected a decimal number, got '{}'", lex.slice()))
  }
}

fn parse_boolean(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected a boolean"))?;
  if MatcherDefinitionToken::Boolean == next {
    Ok((lex.slice().to_string(), Some(MatchingRule::Boolean), None))
  } else {
    Err(anyhow!("Not a valid matcher definition, expected a boolean, got '{}'", lex.slice()))
  }
}

fn parse_string(lex: &mut logos::Lexer<MatcherDefinitionToken>) -> anyhow::Result<String> {
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected a starting quote"))?;
  if next == MatcherDefinitionToken::String {
    Ok(lex.slice().trim_matches('\'').to_string())
  } else {
    Err(anyhow!("Not a valid matcher definition, expected a starting quote, got '{}'", lex.slice()))
  }
}

fn parse_comma(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<()> {
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected a ','"))?;
  if next == MatcherDefinitionToken::Comma {
    Ok(())
  } else {
    Err(anyhow!("Not a valid matcher definition, expected a comma, got '{}'", lex.slice()))
  }
}

#[cfg(test)]
mod test {
  use expectest::prelude::*;
  use crate::matchingrules::MatchingRule;
  use crate::generators::Generator::{DateTime, Date, Time};

  #[test]
  fn does_not_start_with_matching() {
    expect!(super::parse_matcher_def("").unwrap()).to(be_equal_to(("".to_string(), None, None)));
    expect!(super::parse_matcher_def("a, b, c").unwrap()).to(be_equal_to(("a, b, c".to_string(), None, None)));
    expect!(super::parse_matcher_def("matching some other text")).to(be_err());
  }

  #[test]
  fn parse_type_matcher() {
    expect!(super::parse_matcher_def("matching(type,'Name')").unwrap()).to(
      be_equal_to(("Name".to_string(), Some(MatchingRule::Type), None)));
    expect!(super::parse_matcher_def("matching( type, 'Name' )").unwrap()).to(
      be_equal_to(("Name".to_string(), Some(MatchingRule::Type), None)));
  }

  #[test]
  fn parse_number_matcher() {
    expect!(super::parse_matcher_def("matching(number,100)").unwrap()).to(
      be_equal_to(("100".to_string(), Some(MatchingRule::Number), None)));
    expect!(super::parse_matcher_def("matching(integer,100)").unwrap()).to(
      be_equal_to(("100".to_string(), Some(MatchingRule::Integer), None)));
    expect!(super::parse_matcher_def("matching(decimal,100)").unwrap()).to(
      be_equal_to(("100".to_string(), Some(MatchingRule::Decimal), None)));
  }

  #[test]
  fn parse_datetime_matcher() {
    expect!(super::parse_matcher_def("matching(datetime, 'yyyy-MM-dd','2000-01-01')").unwrap()).to(
      be_equal_to(("2000-01-01".to_string(),
                   Some(MatchingRule::Timestamp("yyyy-MM-dd".to_string())),
                   Some(DateTime(Some("yyyy-MM-dd".to_string()))))));
    expect!(super::parse_matcher_def("matching(date, 'yyyy-MM-dd','2000-01-01')").unwrap()).to(
      be_equal_to(("2000-01-01".to_string(),
                   Some(MatchingRule::Date("yyyy-MM-dd".to_string())),
                   Some(Date(Some("yyyy-MM-dd".to_string()))))));
    expect!(super::parse_matcher_def("matching(time, 'HH:mm:ss','12:00:00')").unwrap()).to(
      be_equal_to(("12:00:00".to_string(),
                   Some(MatchingRule::Time("HH:mm:ss".to_string())),
                   Some(Time(Some("HH:mm:ss".to_string()))))));
  }

  #[test]
  fn parse_regex_matcher() {
    expect!(super::parse_matcher_def("matching(regex,'\\w+', 'Fred')").unwrap()).to(
      be_equal_to(("Fred".to_string(), Some(MatchingRule::Regex("\\w+".to_string())), None)));
  }

  #[test]
  fn parse_boolean_matcher() {
    expect!(super::parse_matcher_def("matching(boolean,true)").unwrap()).to(
      be_equal_to(("true".to_string(), Some(MatchingRule::Boolean), None)));
  }

  #[test]
  fn parse_include_matcher() {
    expect!(super::parse_matcher_def("matching(include,'Name')").unwrap()).to(
      be_equal_to(("Name".to_string(), Some(MatchingRule::Include("Name".to_string())), None)));
  }
}
<commit_msg>feat(plugins): Updated matching rule definitions to include notEmpty and contentType<commit_after>//! Matching Rule definition expressions
//!
//! Parser for parsing matching rule definitions into a value, matching rules and generator tuple.
//!
//! The following are examples of matching rule definitions:
//! * `matching(type,'Name')` - type matcher
//! * `matching(number,100)` - number matcher
//! * `matching(datetime, 'yyyy-MM-dd','2000-01-01')` - datetime matcher with format string
//!

use anyhow::{anyhow, Error};
use log::debug;
use logos::{Lexer, Logos};
use crate::generators::Generator;
use crate::matchingrules::MatchingRule;
use crate::matchingrules::MatchingRule::NotEmpty;

#[derive(Logos, Debug, PartialEq)]
enum MatcherDefinitionToken {
  #[token("matching")]
  Matching,

  #[token("notEmpty")]
  NotEmpty,

  #[token("(")]
  LeftBracket,

  #[token(")")]
  RightBracket,

  #[token(",")]
  Comma,

  #[regex("'[^']*'")]
  String,

  #[regex("[a-zA-Z]+")]
  Id,

  #[regex("-?[0-9]+", |lex| lex.slice().parse())]
  Int(i64),

  #[regex(r"-?[0-9]\.[0-9]+")]
  Decimal,

  #[regex(r"true|false")]
  Boolean,

  #[error]
  #[regex(r"[ \t\n\f]+", logos::skip)]
  Error
}

/// Parse a matcher definition into a tuple containing the example value, matching rule and any
/// generator.
/// The following are examples of matching rule definitions:
/// * `matching(type,'Name')` - type matcher
/// * `matching(number,100)` - number matcher
/// * `matching(datetime, 'yyyy-MM-dd','2000-01-01')` - datetime matcher with format string
pub fn parse_matcher_def(v: &str) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  let mut lex = MatcherDefinitionToken::lexer(v);
  let next = lex.next();
  debug!("First Token: {:?}", next);
  if let Some(token) = next {
    if token == MatcherDefinitionToken::Matching {
      parse_matching(v, &mut lex)
    } else if token == MatcherDefinitionToken::NotEmpty {
      parse_not_empty(v, &mut lex)
    } else {
      Ok((v.to_string(), None, None))
    }
  } else {
    Ok((v.to_string(), None, None))
  }
}

fn parse_not_empty(v: &str, lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  let next = lex.next().ok_or(anyhow!("'{}' is not a valid value definition, expected '('", v))?;
  if next == MatcherDefinitionToken::LeftBracket {
    let value = parse_string(lex)?;
    let next = lex.next().ok_or(anyhow!("'{}' is not a valid value definition, expected ')'", v))?;
    if next == MatcherDefinitionToken::RightBracket {
      Ok((value, Some(NotEmpty), None))
    } else {
      Err(anyhow!("'{}' is not a valid value definition, expected closing bracket, got '{}'", v, lex.slice()))
    }
  } else {
    Err(anyhow!("'{}' is not a valid value definition, expected '(', got '{}'", v, lex.remainder()))
  }
}

fn parse_matching(v: &str, mut lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>), Error> {
  let next = lex.next().ok_or(anyhow!("'{}' is not a valid value definition, expected '('", v))?;
  if next == MatcherDefinitionToken::LeftBracket {
    let result = parse_matching_def(&mut lex)?;
    let next = lex.next().ok_or(anyhow!("'{}' is not a valid value definition, expected ')'", v))?;
    if next == MatcherDefinitionToken::RightBracket {
      let next = lex.next();
      if next.is_none() {
        Ok(result)
      } else {
        Err(anyhow!("'{}' is not a valid value definition, got '{}' after the closing bracket", v, lex.remainder()))
      }
    } else {
      Err(anyhow!("'{}' is not a valid value definition, expected closing bracket, got '{}'", v, lex.slice()))
    }
  } else {
    Err(anyhow!("'{}' is not a valid value definition, expected '(', got '{}'", v, lex.remainder()))
  }
}

fn parse_matching_def(lex: &mut logos::Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected a matcher type"))?;
  if next == MatcherDefinitionToken::Id {
    match lex.slice() {
      "equalTo" => parse_equality(lex),
      "regex" => parse_regex(lex),
      "type" => parse_type(lex),
      "datetime" => parse_datetime(lex),
      "date" => parse_date(lex),
      "time" => parse_time(lex),
      "include" => parse_include(lex),
      "number" => parse_number(lex),
      "integer" => parse_integer(lex),
      "decimal" => parse_decimal(lex),
      "boolean" => parse_boolean(lex),
      "contentType" => parse_content_type(lex),
      _ => Err(anyhow!("Not a valid matcher definition, expected the type of matcher, got '{}'", lex.slice()))
    }
  } else {
    Err(anyhow!("Not a valid matcher definition, expected the type of matcher, got '{}'", lex.slice()))
  }
}

fn parse_equality(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value, Some(MatchingRule::Equality), None))
}

fn parse_regex(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let regex = parse_string(lex)?;
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value, Some(MatchingRule::Regex(regex)), None))
}

fn parse_type(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value, Some(MatchingRule::Type), None))
}

fn parse_datetime(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let format = parse_string(lex)?;
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value, Some(MatchingRule::Timestamp(format.clone())), Some(Generator::DateTime(Some(format.clone())))))
}

fn parse_date(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let format = parse_string(lex)?;
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value, Some(MatchingRule::Date(format.clone())), Some(Generator::Date(Some(format.clone())))))
}

fn parse_time(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let format = parse_string(lex)?;
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value, Some(MatchingRule::Time(format.clone())), Some(Generator::Time(Some(format.clone())))))
}

fn parse_include(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value.clone(), Some(MatchingRule::Include(value.clone())), None))
}

fn parse_content_type(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let ct = parse_string(lex)?;
  parse_comma(lex)?;
  let value = parse_string(lex)?;
  Ok((value.clone(), Some(MatchingRule::ContentType(ct.clone())), None))
}

fn parse_number(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected a number"))?;
  if let MatcherDefinitionToken::Int(_) = next {
    Ok((lex.slice().to_string(), Some(MatchingRule::Number), None))
  } else if MatcherDefinitionToken::Decimal == next {
    Ok((lex.slice().to_string(), Some(MatchingRule::Number), None))
  } else {
    Err(anyhow!("Not a valid matcher definition, expected a number, got '{}'", lex.slice()))
  }
}

fn parse_integer(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected an integer"))?;
  if let MatcherDefinitionToken::Int(_) = next {
    Ok((lex.slice().to_string(), Some(MatchingRule::Integer), None))
  } else {
    Err(anyhow!("Not a valid matcher definition, expected an integer, got '{}'", lex.slice()))
  }
}

fn parse_decimal(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected a decimal number"))?;
  if let MatcherDefinitionToken::Int(_) = next {
    Ok((lex.slice().to_string(), Some(MatchingRule::Decimal), None))
  } else if MatcherDefinitionToken::Decimal == next {
    Ok((lex.slice().to_string(), Some(MatchingRule::Decimal), None))
  } else {
    Err(anyhow!("Not a valid matcher definition, expected a decimal number, got '{}'", lex.slice()))
  }
}

fn parse_boolean(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<(String, Option<MatchingRule>, Option<Generator>)> {
  parse_comma(lex)?;
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected a boolean"))?;
  if MatcherDefinitionToken::Boolean == next {
    Ok((lex.slice().to_string(), Some(MatchingRule::Boolean), None))
  } else {
    Err(anyhow!("Not a valid matcher definition, expected a boolean, got '{}'", lex.slice()))
  }
}

fn parse_string(lex: &mut logos::Lexer<MatcherDefinitionToken>) -> anyhow::Result<String> {
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected a starting quote"))?;
  if next == MatcherDefinitionToken::String {
    Ok(lex.slice().trim_matches('\'').to_string())
  } else {
    Err(anyhow!("Not a valid matcher definition, expected a starting quote, got '{}'", lex.slice()))
  }
}

fn parse_comma(lex: &mut Lexer<MatcherDefinitionToken>) -> anyhow::Result<()> {
  let next = lex.next()
    .ok_or(anyhow!("Not a valid matcher definition, expected a ','"))?;
  if next == MatcherDefinitionToken::Comma {
    Ok(())
  } else {
    Err(anyhow!("Not a valid matcher definition, expected a comma, got '{}'", lex.slice()))
  }
}

#[cfg(test)]
mod test {
  use expectest::prelude::*;
  use crate::matchingrules::MatchingRule;
  use crate::generators::Generator::{DateTime, Date, Time};

  #[test]
  fn does_not_start_with_matching() {
    expect!(super::parse_matcher_def("").unwrap()).to(be_equal_to(("".to_string(), None, None)));
    expect!(super::parse_matcher_def("a, b, c").unwrap()).to(be_equal_to(("a, b, c".to_string(), None, None)));
    expect!(super::parse_matcher_def("matching some other text")).to(be_err());
  }

  #[test]
  fn parse_type_matcher() {
    expect!(super::parse_matcher_def("matching(type,'Name')").unwrap()).to(
      be_equal_to(("Name".to_string(), Some(MatchingRule::Type), None)));
    expect!(super::parse_matcher_def("matching( type, 'Name' )").unwrap()).to(
      be_equal_to(("Name".to_string(), Some(MatchingRule::Type), None)));
  }

  #[test]
  fn parse_number_matcher() {
    expect!(super::parse_matcher_def("matching(number,100)").unwrap()).to(
      be_equal_to(("100".to_string(), Some(MatchingRule::Number), None)));
    expect!(super::parse_matcher_def("matching(integer,100)").unwrap()).to(
      be_equal_to(("100".to_string(), Some(MatchingRule::Integer), None)));
    expect!(super::parse_matcher_def("matching(decimal,100)").unwrap()).to(
      be_equal_to(("100".to_string(), Some(MatchingRule::Decimal), None)));
  }

  #[test]
  fn parse_datetime_matcher() {
    expect!(super::parse_matcher_def("matching(datetime, 'yyyy-MM-dd','2000-01-01')").unwrap()).to(
      be_equal_to(("2000-01-01".to_string(),
                   Some(MatchingRule::Timestamp("yyyy-MM-dd".to_string())),
                   Some(DateTime(Some("yyyy-MM-dd".to_string()))))));
    expect!(super::parse_matcher_def("matching(date, 'yyyy-MM-dd','2000-01-01')").unwrap()).to(
      be_equal_to(("2000-01-01".to_string(),
                   Some(MatchingRule::Date("yyyy-MM-dd".to_string())),
                   Some(Date(Some("yyyy-MM-dd".to_string()))))));
    expect!(super::parse_matcher_def("matching(time, 'HH:mm:ss','12:00:00')").unwrap()).to(
      be_equal_to(("12:00:00".to_string(),
                   Some(MatchingRule::Time("HH:mm:ss".to_string())),
                   Some(Time(Some("HH:mm:ss".to_string()))))));
  }

  #[test]
  fn parse_regex_matcher() {
    expect!(super::parse_matcher_def("matching(regex,'\\w+', 'Fred')").unwrap()).to(
      be_equal_to(("Fred".to_string(), Some(MatchingRule::Regex("\\w+".to_string())), None)));
  }

  #[test]
  fn parse_boolean_matcher() {
    expect!(super::parse_matcher_def("matching(boolean,true)").unwrap()).to(
      be_equal_to(("true".to_string(), Some(MatchingRule::Boolean), None)));
  }

  #[test]
  fn parse_include_matcher() {
    expect!(super::parse_matcher_def("matching(include,'Name')").unwrap()).to(
      be_equal_to(("Name".to_string(), Some(MatchingRule::Include("Name".to_string())), None)));
  }

  #[test]
  fn parse_equals_matcher() {
    expect!(super::parse_matcher_def("matching(equalTo,'Name')").unwrap()).to(
      be_equal_to(("Name".to_string(), Some(MatchingRule::Equality), None)));
  }

  #[test]
  fn parse_content_type_matcher() {
    expect!(super::parse_matcher_def("matching(contentType,'Name', 'Value')").unwrap()).to(
      be_equal_to(("Value".to_string(), Some(MatchingRule::ContentType("Name".to_string())), None)));
  }

  #[test]
  fn parse_not_empty() {
    expect!(super::parse_matcher_def("notEmpty('Value')").unwrap()).to(
      be_equal_to(("Value".to_string(), Some(MatchingRule::NotEmpty), None)));
  }
}
<|endoftext|>


<commit_before>#[cfg(unix)]
use super::unix::{self as os_impl};
#[cfg(windows)]
use super::windows::{self as os_impl};

use std::io;

/// Completes when a "ctrl-c" notification is sent to the process.
///
/// While signals are handled very differently between Unix and Windows, both
/// platforms support receiving a signal on "ctrl-c". This function provides a
/// portable API for receiving this notification.
///
/// Once the returned future is polled, a listener is registered. The future
/// will complete on the first received `ctrl-c` **after** the initial call to
/// either `Future::poll` or `.await`.
///
/// # Caveats
///
/// On Unix platforms, the first time that a `Signal` instance is registered for a
/// particular signal kind, an OS signal-handler is installed which replaces the
/// default platform behavior when that signal is received, **for the duration of
/// the entire process**.
///
/// For example, Unix systems will terminate a process by default when it
/// receives a signal generated by "CTRL+C" on the terminal. But, when a
/// `ctrl_c` stream is created to listen for this signal, the time it arrives,
/// it will be translated to a stream event, and the process will continue to
/// execute.  **Even if this `Signal` instance is dropped, subsequent SIGINT
/// deliveries will end up captured by Tokio, and the default platform behavior
/// will NOT be reset**.
///
/// Thus, applications should take care to ensure the expected signal behavior
/// occurs as expected after listening for specific signals.
///
/// # Examples
///
/// ```rust,no_run
/// use tokio::signal;
///
/// #[tokio::main]
/// async fn main() {
///     println!("waiting for ctrl-c");
///
///     signal::ctrl_c().await.expect("failed to listen for event");
///
///     println!("received ctrl-c event");
/// }
/// ```
pub async fn ctrl_c() -> io::Result<()> {
    os_impl::ctrl_c()?.recv().await;
    Ok(())
}
<commit_msg>signal: add example with background listener (#4171)<commit_after>#[cfg(unix)]
use super::unix::{self as os_impl};
#[cfg(windows)]
use super::windows::{self as os_impl};

use std::io;

/// Completes when a "ctrl-c" notification is sent to the process.
///
/// While signals are handled very differently between Unix and Windows, both
/// platforms support receiving a signal on "ctrl-c". This function provides a
/// portable API for receiving this notification.
///
/// Once the returned future is polled, a listener is registered. The future
/// will complete on the first received `ctrl-c` **after** the initial call to
/// either `Future::poll` or `.await`.
///
/// # Caveats
///
/// On Unix platforms, the first time that a `Signal` instance is registered for a
/// particular signal kind, an OS signal-handler is installed which replaces the
/// default platform behavior when that signal is received, **for the duration of
/// the entire process**.
///
/// For example, Unix systems will terminate a process by default when it
/// receives a signal generated by "CTRL+C" on the terminal. But, when a
/// `ctrl_c` stream is created to listen for this signal, the time it arrives,
/// it will be translated to a stream event, and the process will continue to
/// execute.  **Even if this `Signal` instance is dropped, subsequent SIGINT
/// deliveries will end up captured by Tokio, and the default platform behavior
/// will NOT be reset**.
///
/// Thus, applications should take care to ensure the expected signal behavior
/// occurs as expected after listening for specific signals.
///
/// # Examples
///
/// ```rust,no_run
/// use tokio::signal;
///
/// #[tokio::main]
/// async fn main() {
///     println!("waiting for ctrl-c");
///
///     signal::ctrl_c().await.expect("failed to listen for event");
///
///     println!("received ctrl-c event");
/// }
/// ```
///
/// Listen in the background:
///
/// ```rust,no_run
/// tokio::spawn(async move {
///     tokio::signal::ctrl_c().await.unwrap();
///     // Your handler here
/// });
/// ```
pub async fn ctrl_c() -> io::Result<()> {
    os_impl::ctrl_c()?.recv().await;
    Ok(())
}
<|endoftext|>


<commit_before>//! Keyboard utility functions; allow querying state of keyboard keys and modifiers.
//!
//! Example:
//!
//! ```rust, compile
//! use ggez::event::{self, EventHandler, KeyCode, KeyMods};
//! use ggez::{graphics, nalgebra as na, timer};
//! use ggez::input::keyboard;
//! use ggez::{Context, GameResult};
//!
//! struct MainState {
//!     position_x: f32,
//! }
//!
//! impl EventHandler for MainState {
//!     fn update(&mut self, ctx: &mut Context) -> GameResult {
//!         // Increase or decrease `position_x` by 0.5, or by 5.0 if Shift is held.
//!         if keyboard::is_key_pressed(ctx, KeyCode::Right) {
//!             if keyboard::is_mod_active(ctx, KeyMods::SHIFT) {
//!                 self.position_x += 4.5;
//!             }
//!             self.position_x += 0.5;
//!         } else if keyboard::is_key_pressed(ctx, KeyCode::Left) {
//!             if keyboard::is_mod_active(ctx, KeyMods::SHIFT) {
//!                 self.position_x -= 4.5;
//!             }
//!             self.position_x -= 0.5;
//!         }
//!         Ok(())
//!     }
//!
//!     fn draw(&mut self, ctx: &mut Context) -> GameResult {
//!         graphics::clear(ctx, [0.1, 0.2, 0.3, 1.0].into());
//!         // Create a circle at `position_x` and draw
//!         let circle = graphics::Mesh::new_circle(
//!             ctx,
//!             graphics::DrawMode::fill(),
//!             na::Point2::new(self.position_x, 380.0),
//!             100.0,
//!             2.0,
//!             graphics::WHITE,
//!         )?;
//!         graphics::draw(ctx, &circle, graphics::DrawParam::default())?;
//!         graphics::present(ctx)?;
//!         timer::yield_now();
//!         Ok(())
//!     }
//!
//!     fn key_down_event(&mut self, ctx: &mut Context, key: KeyCode, mods: KeyMods, _: bool) {
//!         match key {
//!             // Quit if Shift+Ctrl+Q is pressed.
//!             KeyCode::Q => {
//!                 if mods.contains(KeyMods::SHIFT & KeyMods::CTRL) {
//!                     println!("Terminating!");
//!                     event::quit(ctx);
//!                 } else if mods.contains(KeyMods::SHIFT) || mods.contains(KeyMods::CTRL) {
//!                     println!("You need to hold both Shift and Control to quit.");
//!                 } else {
//!                     println!("Now you're not even trying!");
//!                 }
//!             }
//!             _ => (),
//!         }
//!     }
//! }
//! ```

use crate::context::Context;

use std::collections::HashSet;
use winit::ModifiersState;
/// A key code.
pub use winit::VirtualKeyCode as KeyCode;

bitflags! {
    /// Bitflags describing the state of keyboard modifiers, such as `Control` or `Shift`.
    #[derive(Default)]
    pub struct KeyMods: u8 {
        /// No modifiers; equivalent to `KeyMods::default()` and
        /// [`KeyMods::empty()`](struct.KeyMods.html#method.empty).
        const NONE  = 0b0000_0000;
        /// Left or right Shift key.
        const SHIFT = 0b0000_0001;
        /// Left or right Control key.
        const CTRL  = 0b0000_0010;
        /// Left or right Alt key.
        const ALT   = 0b0000_0100;
        /// Left or right Win/Cmd/equivalent key.
        const LOGO  = 0b0000_1000;
    }
}

impl From<ModifiersState> for KeyMods {
    fn from(state: ModifiersState) -> Self {
        let mut keymod = KeyMods::empty();
        if state.shift {
            keymod |= Self::SHIFT;
        }
        if state.ctrl {
            keymod |= Self::CTRL;
        }
        if state.alt {
            keymod |= Self::ALT;
        }
        if state.logo {
            keymod |= Self::LOGO;
        }
        keymod
    }
}

/// Tracks held down keyboard keys, active keyboard modifiers,
/// and figures out if the system is sending repeat keystrokes.
#[derive(Clone, Debug)]
pub struct KeyboardContext {
    active_modifiers: KeyMods,
    /// A simple mapping of which key code has been pressed.
    /// We COULD use a `Vec<bool>` but turning Rust enums to and from
    /// integers is unsafe and a set really is what we want anyway.
    pressed_keys_set: HashSet<KeyCode>,

    // These two are necessary for tracking key-repeat.
    last_pressed: Option<KeyCode>,
    current_pressed: Option<KeyCode>,
}

impl KeyboardContext {
    pub(crate) fn new() -> Self {
        Self {
            active_modifiers: KeyMods::empty(),
            // We just use 256 as a number Big Enough For Keyboard Keys to try to avoid resizing.
            pressed_keys_set: HashSet::with_capacity(256),
            last_pressed: None,
            current_pressed: None,
        }
    }

    pub(crate) fn set_key(&mut self, key: KeyCode, pressed: bool) {
        if pressed {
            let _ = self.pressed_keys_set.insert(key);
            self.last_pressed = self.current_pressed;
            self.current_pressed = Some(key);
        } else {
            let _ = self.pressed_keys_set.remove(&key);
            self.current_pressed = None;
        }

        self.set_key_modifier(key, pressed);
    }

    /// Take a modifier key code and alter our state.
    ///
    /// Double check that this edge handling is necessary;
    /// winit sounds like it should do this for us,
    /// see https://docs.rs/winit/0.18.0/winit/struct.KeyboardInput.html#structfield.modifiers
    ///
    /// ...more specifically, we should refactor all this to consistant-ify events a bit and
    /// make winit do more of the work.
    /// But to quote Scott Pilgrim, "This is... this is... Booooooring."
    fn set_key_modifier(&mut self, key: KeyCode, pressed: bool) {
        if pressed {
            match key {
                KeyCode::LShift | KeyCode::RShift => self.active_modifiers |= KeyMods::SHIFT,
                KeyCode::LControl | KeyCode::RControl => self.active_modifiers |= KeyMods::CTRL,
                KeyCode::LAlt | KeyCode::RAlt => self.active_modifiers |= KeyMods::ALT,
                KeyCode::LWin | KeyCode::RWin => self.active_modifiers |= KeyMods::LOGO,
                _ => (),
            }
        } else {
            match key {
                KeyCode::LShift | KeyCode::RShift => self.active_modifiers -= KeyMods::SHIFT,
                KeyCode::LControl | KeyCode::RControl => self.active_modifiers -= KeyMods::CTRL,
                KeyCode::LAlt | KeyCode::RAlt => self.active_modifiers -= KeyMods::ALT,
                KeyCode::LWin | KeyCode::RWin => self.active_modifiers -= KeyMods::LOGO,
                _ => (),
            }
        }
    }

    pub(crate) fn set_modifiers(&mut self, keymods: KeyMods) {
        self.active_modifiers = keymods;
    }

    pub(crate) fn is_key_pressed(&self, key: KeyCode) -> bool {
        self.pressed_keys_set.contains(&key)
    }

    pub(crate) fn is_key_repeated(&self) -> bool {
        if self.last_pressed.is_some() {
            self.last_pressed == self.current_pressed
        } else {
            false
        }
    }

    pub(crate) fn pressed_keys(&self) -> &HashSet<KeyCode> {
        &self.pressed_keys_set
    }

    pub(crate) fn active_mods(&self) -> KeyMods {
        self.active_modifiers
    }
}

impl Default for KeyboardContext {
    fn default() -> Self {
        Self::new()
    }
}

/// Checks if a key is currently pressed down.
pub fn is_key_pressed(ctx: &Context, key: KeyCode) -> bool {
    ctx.keyboard_context.is_key_pressed(key)
}

/// Checks if the last keystroke sent by the system is repeated,
/// like when a key is held down for a period of time.
pub fn is_key_repeated(ctx: &Context) -> bool {
    ctx.keyboard_context.is_key_repeated()
}

/// Returns a reference to the set of currently pressed keys.
pub fn pressed_keys(ctx: &Context) -> &HashSet<KeyCode> {
    ctx.keyboard_context.pressed_keys()
}

/// Checks if keyboard modifier (or several) is active.
pub fn is_mod_active(ctx: &Context, keymods: KeyMods) -> bool {
    ctx.keyboard_context.active_mods().contains(keymods)
}

/// Returns currently active keyboard modifiers.
pub fn active_mods(ctx: &Context) -> KeyMods {
    ctx.keyboard_context.active_mods()
}

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

    #[test]
    fn key_mod_conversions() {
        assert_eq!(
            KeyMods::empty(),
            KeyMods::from(ModifiersState {
                shift: false,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT | KeyMods::ALT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: true,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT | KeyMods::ALT | KeyMods::CTRL,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: true,
                alt: true,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT - KeyMods::ALT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            (KeyMods::SHIFT | KeyMods::ALT) - KeyMods::ALT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT - (KeyMods::ALT | KeyMods::SHIFT),
            KeyMods::from(ModifiersState {
                shift: false,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
    }

    #[test]
    fn pressed_keys_tracking() {
        let mut keyboard = KeyboardContext::new();
        assert_eq!(keyboard.pressed_keys(), &[].iter().cloned().collect());
        assert!(!keyboard.is_key_pressed(KeyCode::A));
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::A].iter().cloned().collect()
        );
        assert!(keyboard.is_key_pressed(KeyCode::A));
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.pressed_keys(), &[].iter().cloned().collect());
        assert!(!keyboard.is_key_pressed(KeyCode::A));
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::A].iter().cloned().collect()
        );
        assert!(keyboard.is_key_pressed(KeyCode::A));
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::A].iter().cloned().collect()
        );
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::A, KeyCode::B].iter().cloned().collect()
        );
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::A, KeyCode::B].iter().cloned().collect()
        );
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::B].iter().cloned().collect()
        );
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::B].iter().cloned().collect()
        );
        keyboard.set_key(KeyCode::B, false);
        assert_eq!(keyboard.pressed_keys(), &[].iter().cloned().collect());
    }

    #[test]
    fn keyboard_modifiers() {
        let mut keyboard = KeyboardContext::new();

        // this test is mostly useless and is primarily for code coverage
        assert_eq!(keyboard.active_mods(), KeyMods::default());
        keyboard.set_modifiers(KeyMods::from(ModifiersState {
            shift: true,
            ctrl: true,
            alt: true,
            logo: true,
        }));

        // these test the workaround for https://github.com/tomaka/winit/issues/600
        assert_eq!(
            keyboard.active_mods(),
            KeyMods::SHIFT | KeyMods::CTRL | KeyMods::ALT | KeyMods::LOGO
        );
        keyboard.set_key(KeyCode::LControl, false);
        assert_eq!(
            keyboard.active_mods(),
            KeyMods::SHIFT | KeyMods::ALT | KeyMods::LOGO
        );
        keyboard.set_key(KeyCode::RAlt, false);
        assert_eq!(keyboard.active_mods(), KeyMods::SHIFT | KeyMods::LOGO);
        keyboard.set_key(KeyCode::LWin, false);
        assert_eq!(keyboard.active_mods(), KeyMods::SHIFT);
    }

    #[test]
    fn repeated_keys_tracking() {
        let mut keyboard = KeyboardContext::new();
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), true);
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), true);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.is_key_repeated(), true);
    }
}
<commit_msg>Fix keyboard documentation<commit_after>//! Keyboard utility functions; allow querying state of keyboard keys and modifiers.
//!
//! Example:
//!
//! ```rust, compile
//! use ggez::event::{self, EventHandler, KeyCode, KeyMods};
//! use ggez::{graphics, nalgebra as na, timer};
//! use ggez::input::keyboard;
//! use ggez::{Context, GameResult};
//!
//! struct MainState {
//!     position_x: f32,
//! }
//!
//! impl EventHandler for MainState {
//!     fn update(&mut self, ctx: &mut Context) -> GameResult {
//!         // Increase or decrease `position_x` by 0.5, or by 5.0 if Shift is held.
//!         if keyboard::is_key_pressed(ctx, KeyCode::Right) {
//!             if keyboard::is_mod_active(ctx, KeyMods::SHIFT) {
//!                 self.position_x += 4.5;
//!             }
//!             self.position_x += 0.5;
//!         } else if keyboard::is_key_pressed(ctx, KeyCode::Left) {
//!             if keyboard::is_mod_active(ctx, KeyMods::SHIFT) {
//!                 self.position_x -= 4.5;
//!             }
//!             self.position_x -= 0.5;
//!         }
//!         Ok(())
//!     }
//!
//!     fn draw(&mut self, ctx: &mut Context) -> GameResult {
//!         graphics::clear(ctx, [0.1, 0.2, 0.3, 1.0].into());
//!         // Create a circle at `position_x` and draw
//!         let circle = graphics::Mesh::new_circle(
//!             ctx,
//!             graphics::DrawMode::fill(),
//!             na::Point2::new(self.position_x, 380.0),
//!             100.0,
//!             2.0,
//!             graphics::WHITE,
//!         )?;
//!         graphics::draw(ctx, &circle, graphics::DrawParam::default())?;
//!         graphics::present(ctx)?;
//!         timer::yield_now();
//!         Ok(())
//!     }
//!
//!     fn key_down_event(&mut self, ctx: &mut Context, key: KeyCode, mods: KeyMods, _: bool) {
//!         match key {
//!             // Quit if Shift+Ctrl+Q is pressed.
//!             KeyCode::Q => {
//!                 if mods.contains(KeyMods::SHIFT) && mods.contains(KeyMods::CTRL) {
//!                     println!("Terminating!");
//!                     event::quit(ctx);
//!                 } else if mods.contains(KeyMods::SHIFT) || mods.contains(KeyMods::CTRL) {
//!                     println!("You need to hold both Shift and Control to quit.");
//!                 } else {
//!                     println!("Now you're not even trying!");
//!                 }
//!             }
//!             _ => (),
//!         }
//!     }
//! }
//! ```

use crate::context::Context;

use std::collections::HashSet;
use winit::ModifiersState;
/// A key code.
pub use winit::VirtualKeyCode as KeyCode;

bitflags! {
    /// Bitflags describing the state of keyboard modifiers, such as `Control` or `Shift`.
    #[derive(Default)]
    pub struct KeyMods: u8 {
        /// No modifiers; equivalent to `KeyMods::default()` and
        /// [`KeyMods::empty()`](struct.KeyMods.html#method.empty).
        const NONE  = 0b0000_0000;
        /// Left or right Shift key.
        const SHIFT = 0b0000_0001;
        /// Left or right Control key.
        const CTRL  = 0b0000_0010;
        /// Left or right Alt key.
        const ALT   = 0b0000_0100;
        /// Left or right Win/Cmd/equivalent key.
        const LOGO  = 0b0000_1000;
    }
}

impl From<ModifiersState> for KeyMods {
    fn from(state: ModifiersState) -> Self {
        let mut keymod = KeyMods::empty();
        if state.shift {
            keymod |= Self::SHIFT;
        }
        if state.ctrl {
            keymod |= Self::CTRL;
        }
        if state.alt {
            keymod |= Self::ALT;
        }
        if state.logo {
            keymod |= Self::LOGO;
        }
        keymod
    }
}

/// Tracks held down keyboard keys, active keyboard modifiers,
/// and figures out if the system is sending repeat keystrokes.
#[derive(Clone, Debug)]
pub struct KeyboardContext {
    active_modifiers: KeyMods,
    /// A simple mapping of which key code has been pressed.
    /// We COULD use a `Vec<bool>` but turning Rust enums to and from
    /// integers is unsafe and a set really is what we want anyway.
    pressed_keys_set: HashSet<KeyCode>,

    // These two are necessary for tracking key-repeat.
    last_pressed: Option<KeyCode>,
    current_pressed: Option<KeyCode>,
}

impl KeyboardContext {
    pub(crate) fn new() -> Self {
        Self {
            active_modifiers: KeyMods::empty(),
            // We just use 256 as a number Big Enough For Keyboard Keys to try to avoid resizing.
            pressed_keys_set: HashSet::with_capacity(256),
            last_pressed: None,
            current_pressed: None,
        }
    }

    pub(crate) fn set_key(&mut self, key: KeyCode, pressed: bool) {
        if pressed {
            let _ = self.pressed_keys_set.insert(key);
            self.last_pressed = self.current_pressed;
            self.current_pressed = Some(key);
        } else {
            let _ = self.pressed_keys_set.remove(&key);
            self.current_pressed = None;
        }

        self.set_key_modifier(key, pressed);
    }

    /// Take a modifier key code and alter our state.
    ///
    /// Double check that this edge handling is necessary;
    /// winit sounds like it should do this for us,
    /// see https://docs.rs/winit/0.18.0/winit/struct.KeyboardInput.html#structfield.modifiers
    ///
    /// ...more specifically, we should refactor all this to consistant-ify events a bit and
    /// make winit do more of the work.
    /// But to quote Scott Pilgrim, "This is... this is... Booooooring."
    fn set_key_modifier(&mut self, key: KeyCode, pressed: bool) {
        if pressed {
            match key {
                KeyCode::LShift | KeyCode::RShift => self.active_modifiers |= KeyMods::SHIFT,
                KeyCode::LControl | KeyCode::RControl => self.active_modifiers |= KeyMods::CTRL,
                KeyCode::LAlt | KeyCode::RAlt => self.active_modifiers |= KeyMods::ALT,
                KeyCode::LWin | KeyCode::RWin => self.active_modifiers |= KeyMods::LOGO,
                _ => (),
            }
        } else {
            match key {
                KeyCode::LShift | KeyCode::RShift => self.active_modifiers -= KeyMods::SHIFT,
                KeyCode::LControl | KeyCode::RControl => self.active_modifiers -= KeyMods::CTRL,
                KeyCode::LAlt | KeyCode::RAlt => self.active_modifiers -= KeyMods::ALT,
                KeyCode::LWin | KeyCode::RWin => self.active_modifiers -= KeyMods::LOGO,
                _ => (),
            }
        }
    }

    pub(crate) fn set_modifiers(&mut self, keymods: KeyMods) {
        self.active_modifiers = keymods;
    }

    pub(crate) fn is_key_pressed(&self, key: KeyCode) -> bool {
        self.pressed_keys_set.contains(&key)
    }

    pub(crate) fn is_key_repeated(&self) -> bool {
        if self.last_pressed.is_some() {
            self.last_pressed == self.current_pressed
        } else {
            false
        }
    }

    pub(crate) fn pressed_keys(&self) -> &HashSet<KeyCode> {
        &self.pressed_keys_set
    }

    pub(crate) fn active_mods(&self) -> KeyMods {
        self.active_modifiers
    }
}

impl Default for KeyboardContext {
    fn default() -> Self {
        Self::new()
    }
}

/// Checks if a key is currently pressed down.
pub fn is_key_pressed(ctx: &Context, key: KeyCode) -> bool {
    ctx.keyboard_context.is_key_pressed(key)
}

/// Checks if the last keystroke sent by the system is repeated,
/// like when a key is held down for a period of time.
pub fn is_key_repeated(ctx: &Context) -> bool {
    ctx.keyboard_context.is_key_repeated()
}

/// Returns a reference to the set of currently pressed keys.
pub fn pressed_keys(ctx: &Context) -> &HashSet<KeyCode> {
    ctx.keyboard_context.pressed_keys()
}

/// Checks if keyboard modifier (or several) is active.
pub fn is_mod_active(ctx: &Context, keymods: KeyMods) -> bool {
    ctx.keyboard_context.active_mods().contains(keymods)
}

/// Returns currently active keyboard modifiers.
pub fn active_mods(ctx: &Context) -> KeyMods {
    ctx.keyboard_context.active_mods()
}

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

    #[test]
    fn key_mod_conversions() {
        assert_eq!(
            KeyMods::empty(),
            KeyMods::from(ModifiersState {
                shift: false,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT | KeyMods::ALT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: true,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT | KeyMods::ALT | KeyMods::CTRL,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: true,
                alt: true,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT - KeyMods::ALT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            (KeyMods::SHIFT | KeyMods::ALT) - KeyMods::ALT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT - (KeyMods::ALT | KeyMods::SHIFT),
            KeyMods::from(ModifiersState {
                shift: false,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
    }

    #[test]
    fn pressed_keys_tracking() {
        let mut keyboard = KeyboardContext::new();
        assert_eq!(keyboard.pressed_keys(), &[].iter().cloned().collect());
        assert!(!keyboard.is_key_pressed(KeyCode::A));
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::A].iter().cloned().collect()
        );
        assert!(keyboard.is_key_pressed(KeyCode::A));
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.pressed_keys(), &[].iter().cloned().collect());
        assert!(!keyboard.is_key_pressed(KeyCode::A));
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::A].iter().cloned().collect()
        );
        assert!(keyboard.is_key_pressed(KeyCode::A));
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::A].iter().cloned().collect()
        );
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::A, KeyCode::B].iter().cloned().collect()
        );
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::A, KeyCode::B].iter().cloned().collect()
        );
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::B].iter().cloned().collect()
        );
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(
            keyboard.pressed_keys(),
            &[KeyCode::B].iter().cloned().collect()
        );
        keyboard.set_key(KeyCode::B, false);
        assert_eq!(keyboard.pressed_keys(), &[].iter().cloned().collect());
    }

    #[test]
    fn keyboard_modifiers() {
        let mut keyboard = KeyboardContext::new();

        // this test is mostly useless and is primarily for code coverage
        assert_eq!(keyboard.active_mods(), KeyMods::default());
        keyboard.set_modifiers(KeyMods::from(ModifiersState {
            shift: true,
            ctrl: true,
            alt: true,
            logo: true,
        }));

        // these test the workaround for https://github.com/tomaka/winit/issues/600
        assert_eq!(
            keyboard.active_mods(),
            KeyMods::SHIFT | KeyMods::CTRL | KeyMods::ALT | KeyMods::LOGO
        );
        keyboard.set_key(KeyCode::LControl, false);
        assert_eq!(
            keyboard.active_mods(),
            KeyMods::SHIFT | KeyMods::ALT | KeyMods::LOGO
        );
        keyboard.set_key(KeyCode::RAlt, false);
        assert_eq!(keyboard.active_mods(), KeyMods::SHIFT | KeyMods::LOGO);
        keyboard.set_key(KeyCode::LWin, false);
        assert_eq!(keyboard.active_mods(), KeyMods::SHIFT);
    }

    #[test]
    fn repeated_keys_tracking() {
        let mut keyboard = KeyboardContext::new();
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), true);
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), true);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.is_key_repeated(), true);
    }
}
<|endoftext|>