use super::ValidationError;
use crate::{ElicitCommunicator, ElicitResult, Elicitation, Prompt};
use elicitation_derive::contract_type;
use elicitation_macros::instrumented_impl;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
macro_rules! impl_integer_default_wrapper {
($primitive:ty, $wrapper:ident, $min:expr, $max:expr) => {
#[doc = concat!("Default wrapper for ", stringify!($primitive), " (unconstrained).")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
#[schemars(description = concat!(stringify!($primitive), " value"))]
pub struct $wrapper(
#[schemars(range(min = $min, max = $max))]
$primitive
);
rmcp::elicit_safe!($wrapper);
impl $wrapper {
pub fn new(value: $primitive) -> Self {
Self(value)
}
pub fn get(&self) -> $primitive {
self.0
}
pub fn into_inner(self) -> $primitive {
self.0
}
}
paste::paste! {
crate::default_style!($wrapper => [<$wrapper Style>]);
impl Prompt for $wrapper {
fn prompt() -> Option<&'static str> {
Some(concat!("Please enter a ", stringify!($primitive), ":"))
}
}
impl Elicitation for $wrapper {
type Style = [<$wrapper Style>];
#[tracing::instrument(skip(communicator))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
let prompt = Self::prompt().unwrap();
tracing::debug!(concat!("Eliciting ", stringify!($wrapper), " with server-side send_prompt"));
let response = communicator.send_prompt(prompt).await?;
let value: $primitive = response.trim().parse().map_err(|e| {
crate::ElicitError::new(crate::ElicitErrorKind::ParseError(
format!("Failed to parse {}: {}", stringify!($primitive), e),
))
})?;
Ok(Self::new(value))
}
}
}
};
}
#[contract_type(requires = "value > 0", ensures = "result.get() > 0")]
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
)]
#[schemars(description = "Positive integer value (> 0)")]
pub struct I8Positive(#[schemars(range(min = 1))] i8);
rmcp::elicit_safe!(I8Positive);
#[cfg_attr(not(kani), instrumented_impl)]
impl I8Positive {
pub fn new(value: i8) -> Result<Self, ValidationError> {
if value > 0 {
Ok(Self(value))
} else {
Err(ValidationError::NotPositive(value as i128))
}
}
pub fn get(&self) -> i8 {
self.0
}
pub fn into_inner(self) -> i8 {
self.0
}
}
crate::default_style!(I8Positive => I8PositiveStyle);
#[cfg_attr(not(kani), instrumented_impl)]
impl Prompt for I8Positive {
fn prompt() -> Option<&'static str> {
Some("Please enter a positive number (> 0):")
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl Elicitation for I8Positive {
type Style = I8PositiveStyle;
#[tracing::instrument(skip(communicator), fields(type_name = "I8Positive"))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting I8Positive (positive i8 value)");
loop {
let value = i8::elicit(communicator).await?;
match Self::new(value) {
Ok(positive) => {
tracing::debug!(value, "Valid I8Positive constructed");
return Ok(positive);
}
Err(e) => {
tracing::warn!(value, error = %e, "Invalid I8Positive, re-prompting");
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn i8_positive_new_valid() {
let result = I8Positive::new(1);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 1);
}
#[test]
fn i8_positive_new_zero_invalid() {
let result = I8Positive::new(0);
assert!(result.is_err());
}
#[test]
fn i8_positive_new_negative_invalid() {
let result = I8Positive::new(-1);
assert!(result.is_err());
}
#[test]
fn i8_positive_into_inner() {
let positive = I8Positive::new(42).unwrap();
let value: i8 = positive.into_inner();
assert_eq!(value, 42);
}
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
)]
#[schemars(description = "Non-negative integer value (>= 0)")]
pub struct I8NonNegative(#[schemars(range(min = 0))] i8);
rmcp::elicit_safe!(I8NonNegative);
#[cfg_attr(not(kani), instrumented_impl)]
#[cfg_attr(not(kani), instrumented_impl)]
#[cfg_attr(not(kani), instrumented_impl)]
impl I8NonNegative {
pub fn new(value: i8) -> Result<Self, ValidationError> {
if value >= 0 {
Ok(Self(value))
} else {
Err(ValidationError::Negative(value as i128))
}
}
pub fn get(&self) -> i8 {
self.0
}
pub fn into_inner(self) -> i8 {
self.0
}
}
crate::default_style!(I8NonNegative => I8NonNegativeStyle);
#[cfg_attr(not(kani), instrumented_impl)]
impl Prompt for I8NonNegative {
fn prompt() -> Option<&'static str> {
Some("Please enter a non-negative number (>= 0):")
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl Elicitation for I8NonNegative {
type Style = I8NonNegativeStyle;
#[tracing::instrument(skip(communicator), fields(type_name = "I8NonNegative"))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting I8NonNegative (non-negative i8 value)");
loop {
let value = i8::elicit(communicator).await?;
match Self::new(value) {
Ok(non_negative) => {
tracing::debug!(value, "Valid I8NonNegative constructed");
return Ok(non_negative);
}
Err(e) => {
tracing::warn!(value, error = %e, "Invalid I8NonNegative, re-prompting");
}
}
}
}
}
#[contract_type(requires = "value != 0", ensures = "result.get() != 0")]
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
)]
#[schemars(description = "Non-zero integer value (!= 0)")]
pub struct I8NonZero(i8);
rmcp::elicit_safe!(I8NonZero);
#[cfg_attr(not(kani), instrumented_impl)]
impl I8NonZero {
pub fn new(value: i8) -> Result<Self, ValidationError> {
if value != 0 {
Ok(Self(value))
} else {
Err(ValidationError::Zero)
}
}
pub fn get(&self) -> i8 {
self.0
}
pub fn into_inner(self) -> i8 {
self.0
}
}
crate::default_style!(I8NonZero => I8NonZeroStyle);
#[cfg_attr(not(kani), instrumented_impl)]
impl Prompt for I8NonZero {
fn prompt() -> Option<&'static str> {
Some("Please enter a non-zero number (!= 0):")
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl Elicitation for I8NonZero {
type Style = I8NonZeroStyle;
#[tracing::instrument(skip(communicator), fields(type_name = "I8NonZero"))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting I8NonZero");
loop {
let value = i8::elicit(communicator).await?;
match Self::new(value) {
Ok(non_zero) => {
tracing::debug!(value, "Valid I8NonZero constructed");
return Ok(non_zero);
}
Err(e) => {
tracing::warn!(value, error = %e, "Invalid I8NonZero, re-prompting");
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct I8Range<const MIN: i8, const MAX: i8>(i8);
impl<const MIN: i8, const MAX: i8> I8Range<MIN, MAX> {
pub fn new(value: i8) -> Result<Self, ValidationError> {
if value >= MIN && value <= MAX {
Ok(Self(value))
} else {
Err(ValidationError::OutOfRange {
value: value as i128,
min: MIN as i128,
max: MAX as i128,
})
}
}
pub fn get(&self) -> i8 {
self.0
}
pub fn into_inner(self) -> i8 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum I8RangeStyle {
#[default]
Default,
}
#[cfg_attr(not(kani), instrumented_impl)]
impl crate::Prompt for I8RangeStyle {
fn prompt() -> Option<&'static str> {
None }
}
#[cfg_attr(not(kani), instrumented_impl)]
impl crate::Elicitation for I8RangeStyle {
type Style = I8RangeStyle;
async fn elicit<C: crate::ElicitCommunicator>(_communicator: &C) -> crate::ElicitResult<Self> {
Ok(Self::Default)
}
}
impl<const MIN: i8, const MAX: i8> Prompt for I8Range<MIN, MAX> {
fn prompt() -> Option<&'static str> {
Some("Please enter a number within the specified range:")
}
}
impl<const MIN: i8, const MAX: i8> Elicitation for I8Range<MIN, MAX> {
type Style = I8RangeStyle;
#[tracing::instrument(skip(communicator), fields(type_name = "I8Range", min = MIN, max = MAX))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting I8Range<{}, {}> (i8 in range)", MIN, MAX);
loop {
let value = i8::elicit(communicator).await?;
match Self::new(value) {
Ok(ranged) => {
tracing::debug!(value, min = MIN, max = MAX, "Valid I8Range constructed");
return Ok(ranged);
}
Err(e) => {
tracing::warn!(value, error = %e, min = MIN, max = MAX, "Invalid I8Range, re-prompting");
}
}
}
}
}
#[cfg(test)]
mod i8_nonnegative_tests {
use super::*;
#[test]
fn i8_nonnegative_new_valid_positive() {
let result = I8NonNegative::new(42);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 42);
}
#[test]
fn i8_nonnegative_new_valid_zero() {
let result = I8NonNegative::new(0);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 0);
}
#[test]
fn i8_nonnegative_new_negative_invalid() {
let result = I8NonNegative::new(-1);
assert!(result.is_err());
}
#[test]
fn i8_nonnegative_into_inner() {
let non_neg = I8NonNegative::new(10).unwrap();
let value: i8 = non_neg.into_inner();
assert_eq!(value, 10);
}
}
#[cfg(test)]
mod i8_range_tests {
use super::*;
#[test]
fn i8_range_new_valid_within_range() {
let result = I8Range::<10, 20>::new(15);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 15);
}
#[test]
fn i8_range_new_valid_at_min() {
let result = I8Range::<10, 20>::new(10);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 10);
}
#[test]
fn i8_range_new_valid_at_max() {
let result = I8Range::<10, 20>::new(20);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 20);
}
#[test]
fn i8_range_new_below_min_invalid() {
let result = I8Range::<10, 20>::new(9);
assert!(result.is_err());
}
#[test]
fn i8_range_new_above_max_invalid() {
let result = I8Range::<10, 20>::new(21);
assert!(result.is_err());
}
#[test]
fn i8_range_into_inner() {
let ranged = I8Range::<10, 20>::new(15).unwrap();
let value: i8 = ranged.into_inner();
assert_eq!(value, 15);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct I16Positive(i16);
#[cfg_attr(not(kani), instrumented_impl)]
#[cfg_attr(not(kani), instrumented_impl)]
impl I16Positive {
pub fn new(value: i16) -> Result<Self, ValidationError> {
if value > 0 {
Ok(Self(value))
} else {
Err(ValidationError::NotPositive(value as i128))
}
}
pub fn get(&self) -> i16 {
self.0
}
pub fn into_inner(self) -> i16 {
self.0
}
}
crate::default_style!(I16Positive => I16PositiveStyle);
#[cfg_attr(not(kani), instrumented_impl)]
impl Prompt for I16Positive {
fn prompt() -> Option<&'static str> {
Some("Please enter a positive number (> 0):")
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl Elicitation for I16Positive {
type Style = I16PositiveStyle;
#[tracing::instrument(skip(communicator), fields(type_name = "I16Positive"))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting I16Positive (positive i16 value)");
loop {
let value = i16::elicit(communicator).await?;
match Self::new(value) {
Ok(positive) => {
tracing::debug!(value, "Valid I16Positive constructed");
return Ok(positive);
}
Err(e) => {
tracing::warn!(value, error = %e, "Invalid I16Positive, re-prompting");
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct I16NonNegative(i16);
#[cfg_attr(not(kani), instrumented_impl)]
#[cfg_attr(not(kani), instrumented_impl)]
impl I16NonNegative {
pub fn new(value: i16) -> Result<Self, ValidationError> {
if value >= 0 {
Ok(Self(value))
} else {
Err(ValidationError::Negative(value as i128))
}
}
pub fn get(&self) -> i16 {
self.0
}
pub fn into_inner(self) -> i16 {
self.0
}
}
crate::default_style!(I16NonNegative => I16NonNegativeStyle);
#[cfg_attr(not(kani), instrumented_impl)]
impl Prompt for I16NonNegative {
fn prompt() -> Option<&'static str> {
Some("Please enter a non-negative number (>= 0):")
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl Elicitation for I16NonNegative {
type Style = I16NonNegativeStyle;
#[tracing::instrument(skip(communicator), fields(type_name = "I16NonNegative"))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting I16NonNegative (non-negative i16 value)");
loop {
let value = i16::elicit(communicator).await?;
match Self::new(value) {
Ok(non_negative) => {
tracing::debug!(value, "Valid I16NonNegative constructed");
return Ok(non_negative);
}
Err(e) => {
tracing::warn!(value, error = %e, "Invalid I16NonNegative, re-prompting");
}
}
}
}
}
#[contract_type(requires = "value != 0", ensures = "result.get() != 0")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct I16NonZero(i16);
#[cfg_attr(not(kani), instrumented_impl)]
impl I16NonZero {
pub fn new(value: i16) -> Result<Self, ValidationError> {
if value != 0 {
Ok(Self(value))
} else {
Err(ValidationError::Zero)
}
}
pub fn get(&self) -> i16 {
self.0
}
pub fn into_inner(self) -> i16 {
self.0
}
}
crate::default_style!(I16NonZero => I16NonZeroStyle);
#[cfg_attr(not(kani), instrumented_impl)]
impl Prompt for I16NonZero {
fn prompt() -> Option<&'static str> {
Some("Please enter a non-zero number (!= 0):")
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl Elicitation for I16NonZero {
type Style = I16NonZeroStyle;
#[tracing::instrument(skip(communicator), fields(type_name = "I16NonZero"))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting I16NonZero");
loop {
let value = i16::elicit(communicator).await?;
match Self::new(value) {
Ok(non_zero) => {
tracing::debug!(value, "Valid I16NonZero constructed");
return Ok(non_zero);
}
Err(e) => {
tracing::warn!(value, error = %e, "Invalid I16NonZero, re-prompting");
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct I16Range<const MIN: i16, const MAX: i16>(i16);
impl<const MIN: i16, const MAX: i16> I16Range<MIN, MAX> {
pub fn new(value: i16) -> Result<Self, ValidationError> {
if value >= MIN && value <= MAX {
Ok(Self(value))
} else {
Err(ValidationError::OutOfRange {
value: value as i128,
min: MIN as i128,
max: MAX as i128,
})
}
}
pub fn get(&self) -> i16 {
self.0
}
pub fn into_inner(self) -> i16 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum I16RangeStyle {
#[default]
Default,
}
#[cfg_attr(not(kani), instrumented_impl)]
impl crate::Prompt for I16RangeStyle {
fn prompt() -> Option<&'static str> {
None
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl crate::Elicitation for I16RangeStyle {
type Style = I16RangeStyle;
async fn elicit<C: crate::ElicitCommunicator>(_communicator: &C) -> crate::ElicitResult<Self> {
Ok(Self::Default)
}
}
impl<const MIN: i16, const MAX: i16> Prompt for I16Range<MIN, MAX> {
fn prompt() -> Option<&'static str> {
Some("Please enter a number within the specified range:")
}
}
impl<const MIN: i16, const MAX: i16> Elicitation for I16Range<MIN, MAX> {
type Style = I16RangeStyle;
#[tracing::instrument(skip(communicator), fields(type_name = "I16Range", min = MIN, max = MAX))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting I16Range<{}, {}> (i16 in range)", MIN, MAX);
loop {
let value = i16::elicit(communicator).await?;
match Self::new(value) {
Ok(ranged) => {
tracing::debug!(value, min = MIN, max = MAX, "Valid I16Range constructed");
return Ok(ranged);
}
Err(e) => {
tracing::warn!(value, error = %e, min = MIN, max = MAX, "Invalid I16Range, re-prompting");
}
}
}
}
}
#[cfg(test)]
mod i16_positive_tests {
use super::*;
#[test]
fn i16_positive_new_valid() {
let result = I16Positive::new(1);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 1);
}
#[test]
fn i16_positive_new_zero_invalid() {
let result = I16Positive::new(0);
assert!(result.is_err());
}
#[test]
fn i16_positive_new_negative_invalid() {
let result = I16Positive::new(-1);
assert!(result.is_err());
}
#[test]
fn i16_positive_into_inner() {
let positive = I16Positive::new(1000).unwrap();
let value: i16 = positive.into_inner();
assert_eq!(value, 1000);
}
}
#[cfg(test)]
mod i16_nonnegative_tests {
use super::*;
#[test]
fn i16_nonnegative_new_valid_positive() {
let result = I16NonNegative::new(1000);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 1000);
}
#[test]
fn i16_nonnegative_new_valid_zero() {
let result = I16NonNegative::new(0);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 0);
}
#[test]
fn i16_nonnegative_new_negative_invalid() {
let result = I16NonNegative::new(-1);
assert!(result.is_err());
}
#[test]
fn i16_nonnegative_into_inner() {
let non_neg = I16NonNegative::new(500).unwrap();
let value: i16 = non_neg.into_inner();
assert_eq!(value, 500);
}
}
#[cfg(test)]
mod i16_range_tests {
use super::*;
#[test]
fn i16_range_new_valid_within_range() {
let result = I16Range::<100, 200>::new(150);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 150);
}
#[test]
fn i16_range_new_valid_at_min() {
let result = I16Range::<100, 200>::new(100);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 100);
}
#[test]
fn i16_range_new_valid_at_max() {
let result = I16Range::<100, 200>::new(200);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 200);
}
#[test]
fn i16_range_new_below_min_invalid() {
let result = I16Range::<100, 200>::new(99);
assert!(result.is_err());
}
#[test]
fn i16_range_new_above_max_invalid() {
let result = I16Range::<100, 200>::new(201);
assert!(result.is_err());
}
#[test]
fn i16_range_into_inner() {
let ranged = I16Range::<100, 200>::new(150).unwrap();
let value: i16 = ranged.into_inner();
assert_eq!(value, 150);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct U8NonZero(u8);
#[cfg_attr(not(kani), instrumented_impl)]
#[cfg_attr(not(kani), instrumented_impl)]
impl U8NonZero {
pub fn new(value: u8) -> Result<Self, ValidationError> {
if value != 0 {
Ok(Self(value))
} else {
Err(ValidationError::Zero)
}
}
pub fn get(&self) -> u8 {
self.0
}
pub fn into_inner(self) -> u8 {
self.0
}
}
crate::default_style!(U8NonZero => U8NonZeroStyle);
#[cfg_attr(not(kani), instrumented_impl)]
impl Prompt for U8NonZero {
fn prompt() -> Option<&'static str> {
Some("Please enter a non-zero number (!= 0):")
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl Elicitation for U8NonZero {
type Style = U8NonZeroStyle;
#[tracing::instrument(skip(communicator), fields(type_name = "U8NonZero"))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting U8NonZero (non-zero u8 value)");
loop {
let value = u8::elicit(communicator).await?;
match Self::new(value) {
Ok(non_zero) => {
tracing::debug!(value, "Valid U8NonZero constructed");
return Ok(non_zero);
}
Err(e) => {
tracing::warn!(value, error = %e, "Invalid U8NonZero, re-prompting");
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct U8Range<const MIN: u8, const MAX: u8>(u8);
impl<const MIN: u8, const MAX: u8> U8Range<MIN, MAX> {
pub fn new(value: u8) -> Result<Self, ValidationError> {
if value >= MIN && value <= MAX {
Ok(Self(value))
} else {
Err(ValidationError::OutOfRange {
value: value as i128,
min: MIN as i128,
max: MAX as i128,
})
}
}
pub fn get(&self) -> u8 {
self.0
}
pub fn into_inner(self) -> u8 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum U8RangeStyle {
#[default]
Default,
}
#[cfg_attr(not(kani), instrumented_impl)]
impl crate::Prompt for U8RangeStyle {
fn prompt() -> Option<&'static str> {
None
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl crate::Elicitation for U8RangeStyle {
type Style = U8RangeStyle;
async fn elicit<C: crate::ElicitCommunicator>(_communicator: &C) -> crate::ElicitResult<Self> {
Ok(Self::Default)
}
}
impl<const MIN: u8, const MAX: u8> Prompt for U8Range<MIN, MAX> {
fn prompt() -> Option<&'static str> {
Some("Please enter a number within the specified range:")
}
}
impl<const MIN: u8, const MAX: u8> Elicitation for U8Range<MIN, MAX> {
type Style = U8RangeStyle;
#[tracing::instrument(skip(communicator), fields(type_name = "U8Range", min = MIN, max = MAX))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting U8Range<{}, {}> (u8 in range)", MIN, MAX);
loop {
let value = u8::elicit(communicator).await?;
match Self::new(value) {
Ok(ranged) => {
tracing::debug!(value, min = MIN, max = MAX, "Valid U8Range constructed");
return Ok(ranged);
}
Err(e) => {
tracing::warn!(value, error = %e, min = MIN, max = MAX, "Invalid U8Range, re-prompting");
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct U16NonZero(u16);
#[cfg_attr(not(kani), instrumented_impl)]
#[cfg_attr(not(kani), instrumented_impl)]
impl U16NonZero {
pub fn new(value: u16) -> Result<Self, ValidationError> {
if value != 0 {
Ok(Self(value))
} else {
Err(ValidationError::Zero)
}
}
pub fn get(&self) -> u16 {
self.0
}
pub fn into_inner(self) -> u16 {
self.0
}
}
crate::default_style!(U16NonZero => U16NonZeroStyle);
#[cfg_attr(not(kani), instrumented_impl)]
impl Prompt for U16NonZero {
fn prompt() -> Option<&'static str> {
Some("Please enter a non-zero number (!= 0):")
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl Elicitation for U16NonZero {
type Style = U16NonZeroStyle;
#[tracing::instrument(skip(communicator), fields(type_name = "U16NonZero"))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting U16NonZero (non-zero u16 value)");
loop {
let value = u16::elicit(communicator).await?;
match Self::new(value) {
Ok(non_zero) => {
tracing::debug!(value, "Valid U16NonZero constructed");
return Ok(non_zero);
}
Err(e) => {
tracing::warn!(value, error = %e, "Invalid U16NonZero, re-prompting");
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct U16Range<const MIN: u16, const MAX: u16>(u16);
impl<const MIN: u16, const MAX: u16> U16Range<MIN, MAX> {
pub fn new(value: u16) -> Result<Self, ValidationError> {
if value >= MIN && value <= MAX {
Ok(Self(value))
} else {
Err(ValidationError::OutOfRange {
value: value as i128,
min: MIN as i128,
max: MAX as i128,
})
}
}
pub fn get(&self) -> u16 {
self.0
}
pub fn into_inner(self) -> u16 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum U16RangeStyle {
#[default]
Default,
}
#[cfg_attr(not(kani), instrumented_impl)]
impl crate::Prompt for U16RangeStyle {
fn prompt() -> Option<&'static str> {
None
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl crate::Elicitation for U16RangeStyle {
type Style = U16RangeStyle;
async fn elicit<C: crate::ElicitCommunicator>(_communicator: &C) -> crate::ElicitResult<Self> {
Ok(Self::Default)
}
}
impl<const MIN: u16, const MAX: u16> Prompt for U16Range<MIN, MAX> {
fn prompt() -> Option<&'static str> {
Some("Please enter a number within the specified range:")
}
}
impl<const MIN: u16, const MAX: u16> Elicitation for U16Range<MIN, MAX> {
type Style = U16RangeStyle;
#[tracing::instrument(skip(communicator), fields(type_name = "U16Range", min = MIN, max = MAX))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting U16Range<{}, {}> (u16 in range)", MIN, MAX);
loop {
let value = u16::elicit(communicator).await?;
match Self::new(value) {
Ok(ranged) => {
tracing::debug!(value, min = MIN, max = MAX, "Valid U16Range constructed");
return Ok(ranged);
}
Err(e) => {
tracing::warn!(value, error = %e, min = MIN, max = MAX, "Invalid U16Range, re-prompting");
}
}
}
}
}
#[cfg(test)]
mod u8_nonzero_tests {
use super::*;
#[test]
fn u8_nonzero_new_valid() {
let result = U8NonZero::new(1);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 1);
}
#[test]
fn u8_nonzero_new_zero_invalid() {
let result = U8NonZero::new(0);
assert!(result.is_err());
}
#[test]
fn u8_nonzero_into_inner() {
let non_zero = U8NonZero::new(255).unwrap();
let value: u8 = non_zero.into_inner();
assert_eq!(value, 255);
}
}
#[cfg(test)]
mod u8_range_tests {
use super::*;
#[test]
fn u8_range_new_valid_within_range() {
let result = U8Range::<10, 20>::new(15);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 15);
}
#[test]
fn u8_range_new_valid_at_min() {
let result = U8Range::<10, 20>::new(10);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 10);
}
#[test]
fn u8_range_new_valid_at_max() {
let result = U8Range::<10, 20>::new(20);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 20);
}
#[test]
fn u8_range_new_below_min_invalid() {
let result = U8Range::<10, 20>::new(9);
assert!(result.is_err());
}
#[test]
fn u8_range_new_above_max_invalid() {
let result = U8Range::<10, 20>::new(21);
assert!(result.is_err());
}
#[test]
fn u8_range_into_inner() {
let ranged = U8Range::<10, 20>::new(15).unwrap();
let value: u8 = ranged.into_inner();
assert_eq!(value, 15);
}
}
#[cfg(test)]
mod u16_nonzero_tests {
use super::*;
#[test]
fn u16_nonzero_new_valid() {
let result = U16NonZero::new(1000);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 1000);
}
#[test]
fn u16_nonzero_new_zero_invalid() {
let result = U16NonZero::new(0);
assert!(result.is_err());
}
#[test]
fn u16_nonzero_into_inner() {
let non_zero = U16NonZero::new(65535).unwrap();
let value: u16 = non_zero.into_inner();
assert_eq!(value, 65535);
}
}
#[cfg(test)]
mod u16_range_tests {
use super::*;
#[test]
fn u16_range_new_valid_within_range() {
let result = U16Range::<1000, 2000>::new(1500);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 1500);
}
#[test]
fn u16_range_new_valid_at_min() {
let result = U16Range::<1000, 2000>::new(1000);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 1000);
}
#[test]
fn u16_range_new_valid_at_max() {
let result = U16Range::<1000, 2000>::new(2000);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 2000);
}
#[test]
fn u16_range_new_below_min_invalid() {
let result = U16Range::<1000, 2000>::new(999);
assert!(result.is_err());
}
#[test]
fn u16_range_new_above_max_invalid() {
let result = U16Range::<1000, 2000>::new(2001);
assert!(result.is_err());
}
#[test]
fn u16_range_into_inner() {
let ranged = U16Range::<1000, 2000>::new(1500).unwrap();
let value: u16 = ranged.into_inner();
assert_eq!(value, 1500);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct U8Positive(u8);
#[elicitation_macros::instrumented_impl]
impl U8Positive {
pub fn new(value: u8) -> Result<Self, ValidationError> {
if value > 0 {
Ok(Self(value))
} else {
Err(ValidationError::Zero)
}
}
pub fn get(&self) -> u8 {
self.0
}
pub fn into_inner(self) -> u8 {
self.0
}
}
crate::default_style!(U8Positive => U8PositiveStyle);
#[cfg_attr(not(kani), instrumented_impl)]
impl Prompt for U8Positive {
fn prompt() -> Option<&'static str> {
Some("Please enter a positive number (> 0):")
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl Elicitation for U8Positive {
type Style = U8PositiveStyle;
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
loop {
if let Ok(v) = Self::new(u8::elicit(communicator).await?) {
return Ok(v);
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct U16Positive(u16);
#[elicitation_macros::instrumented_impl]
impl U16Positive {
pub fn new(value: u16) -> Result<Self, ValidationError> {
if value > 0 {
Ok(Self(value))
} else {
Err(ValidationError::Zero)
}
}
pub fn get(&self) -> u16 {
self.0
}
pub fn into_inner(self) -> u16 {
self.0
}
}
crate::default_style!(U16Positive => U16PositiveStyle);
#[cfg_attr(not(kani), instrumented_impl)]
impl Prompt for U16Positive {
fn prompt() -> Option<&'static str> {
Some("Please enter a positive number (> 0):")
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl Elicitation for U16Positive {
type Style = U16PositiveStyle;
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
loop {
if let Ok(v) = Self::new(u16::elicit(communicator).await?) {
return Ok(v);
}
}
}
}
macro_rules! impl_signed_contracts {
($base:ty, $positive:ident, $nonnegative:ident, $range:ident, $range_style:ident, $test_pos_value:expr, $test_nonneg_value:expr, $test_range_min:expr, $test_range_max:expr, $test_range_value:expr) => {
#[doc = concat!("Contract type for positive ", stringify!($base), " values (> 0).")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct $positive($base);
impl $positive {
pub fn new(value: $base) -> Result<Self, ValidationError> {
if value > 0 {
Ok(Self(value))
} else {
Err(ValidationError::NotPositive(value as i128))
}
}
pub fn get(&self) -> $base {
self.0
}
pub fn into_inner(self) -> $base {
self.0
}
}
paste::paste! {
crate::default_style!($positive => [<$positive Style>]);
impl Prompt for $positive {
fn prompt() -> Option<&'static str> {
Some("Please enter a positive number (> 0):")
}
}
impl Elicitation for $positive {
type Style = [<$positive Style>];
#[tracing::instrument(skip(communicator), fields(type_name = stringify!($positive)))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!(concat!("Eliciting ", stringify!($positive), " (positive ", stringify!($base), " value)"));
loop {
let value = <$base>::elicit(communicator).await?;
match Self::new(value) {
Ok(positive) => {
tracing::debug!(value, concat!("Valid ", stringify!($positive), " constructed"));
return Ok(positive);
}
Err(e) => {
tracing::warn!(value, error = %e, concat!("Invalid ", stringify!($positive), ", re-prompting"));
}
}
}
}
}
}
#[doc = concat!("Contract type for non-negative ", stringify!($base), " values (>= 0).")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct $nonnegative($base);
impl $nonnegative {
pub fn new(value: $base) -> Result<Self, ValidationError> {
if value >= 0 {
Ok(Self(value))
} else {
Err(ValidationError::Negative(value as i128))
}
}
pub fn get(&self) -> $base {
self.0
}
pub fn into_inner(self) -> $base {
self.0
}
}
paste::paste! {
crate::default_style!($nonnegative => [<$nonnegative Style>]);
impl Prompt for $nonnegative {
fn prompt() -> Option<&'static str> {
Some("Please enter a non-negative number (>= 0):")
}
}
impl Elicitation for $nonnegative {
type Style = [<$nonnegative Style>];
#[tracing::instrument(skip(communicator), fields(type_name = stringify!($nonnegative)))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!(concat!("Eliciting ", stringify!($nonnegative), " (non-negative ", stringify!($base), " value)"));
loop {
let value = <$base>::elicit(communicator).await?;
match Self::new(value) {
Ok(non_negative) => {
tracing::debug!(value, concat!("Valid ", stringify!($nonnegative), " constructed"));
return Ok(non_negative);
}
Err(e) => {
tracing::warn!(value, error = %e, concat!("Invalid ", stringify!($nonnegative), ", re-prompting"));
}
}
}
}
}
}
#[doc = concat!("Contract type for ", stringify!($base), " values within a specified range [MIN, MAX].")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct $range<const MIN: $base, const MAX: $base>($base);
impl<const MIN: $base, const MAX: $base> $range<MIN, MAX> {
pub fn new(value: $base) -> Result<Self, ValidationError> {
if value >= MIN && value <= MAX {
Ok(Self(value))
} else {
Err(ValidationError::OutOfRange {
value: value as i128,
min: MIN as i128,
max: MAX as i128,
})
}
}
pub fn get(&self) -> $base {
self.0
}
pub fn into_inner(self) -> $base {
self.0
}
}
#[doc = concat!("Default-only style enum for ", stringify!($range), ".")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum $range_style {
#[default]
Default,
}
impl crate::Prompt for $range_style {
fn prompt() -> Option<&'static str> {
None
}
}
impl crate::Elicitation for $range_style {
type Style = $range_style;
async fn elicit<C: crate::ElicitCommunicator>(_communicator: &C) -> crate::ElicitResult<Self> {
Ok(Self::Default)
}
}
impl<const MIN: $base, const MAX: $base> Prompt for $range<MIN, MAX> {
fn prompt() -> Option<&'static str> {
Some("Please enter a number within the specified range:")
}
}
impl<const MIN: $base, const MAX: $base> Elicitation for $range<MIN, MAX> {
type Style = $range_style;
#[tracing::instrument(skip(communicator), fields(type_name = stringify!($range), min = MIN, max = MAX))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!(concat!("Eliciting ", stringify!($range), "<{}, {}> (", stringify!($base), " in range)"), MIN, MAX);
loop {
let value = <$base>::elicit(communicator).await?;
match Self::new(value) {
Ok(ranged) => {
tracing::debug!(value, min = MIN, max = MAX, concat!("Valid ", stringify!($range), " constructed"));
return Ok(ranged);
}
Err(e) => {
tracing::warn!(value, error = %e, min = MIN, max = MAX, concat!("Invalid ", stringify!($range), ", re-prompting"));
}
}
}
}
}
paste::paste! {
#[cfg(test)]
mod [<$positive:snake _tests>] {
use super::*;
#[test]
fn [<$positive:snake _new_valid>]() {
let result = $positive::new($test_pos_value);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), $test_pos_value);
}
#[test]
fn [<$positive:snake _new_zero_invalid>]() {
let result = $positive::new(0);
assert!(result.is_err());
}
#[test]
fn [<$positive:snake _new_negative_invalid>]() {
let result = $positive::new(-1);
assert!(result.is_err());
}
#[test]
fn [<$positive:snake _into_inner>]() {
let positive = $positive::new($test_pos_value).unwrap();
let value: $base = positive.into_inner();
assert_eq!(value, $test_pos_value);
}
}
#[cfg(test)]
mod [<$nonnegative:snake _tests>] {
use super::*;
#[test]
fn [<$nonnegative:snake _new_valid_positive>]() {
let result = $nonnegative::new($test_nonneg_value);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), $test_nonneg_value);
}
#[test]
fn [<$nonnegative:snake _new_valid_zero>]() {
let result = $nonnegative::new(0);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), 0);
}
#[test]
fn [<$nonnegative:snake _new_negative_invalid>]() {
let result = $nonnegative::new(-1);
assert!(result.is_err());
}
#[test]
fn [<$nonnegative:snake _into_inner>]() {
let non_neg = $nonnegative::new($test_nonneg_value).unwrap();
let value: $base = non_neg.into_inner();
assert_eq!(value, $test_nonneg_value);
}
}
#[cfg(test)]
mod [<$range:snake _tests>] {
use super::*;
#[test]
fn [<$range:snake _new_valid_within_range>]() {
let result = $range::<$test_range_min, $test_range_max>::new($test_range_value);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), $test_range_value);
}
#[test]
fn [<$range:snake _new_valid_at_min>]() {
let result = $range::<$test_range_min, $test_range_max>::new($test_range_min);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), $test_range_min);
}
#[test]
fn [<$range:snake _new_valid_at_max>]() {
let result = $range::<$test_range_min, $test_range_max>::new($test_range_max);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), $test_range_max);
}
#[test]
fn [<$range:snake _new_below_min_invalid>]() {
let result = $range::<$test_range_min, $test_range_max>::new($test_range_min - 1);
assert!(result.is_err());
}
#[test]
fn [<$range:snake _new_above_max_invalid>]() {
let result = $range::<$test_range_min, $test_range_max>::new($test_range_max + 1);
assert!(result.is_err());
}
#[test]
fn [<$range:snake _into_inner>]() {
let ranged = $range::<$test_range_min, $test_range_max>::new($test_range_value).unwrap();
let value: $base = ranged.into_inner();
assert_eq!(value, $test_range_value);
}
}
}
};
}
macro_rules! impl_unsigned_contracts {
($base:ty, $nonzero:ident, $range:ident, $range_style:ident, $test_nonzero_value:expr, $test_range_min:expr, $test_range_max:expr, $test_range_value:expr) => {
#[doc = concat!("Contract type for non-zero ", stringify!($base), " values (!= 0).")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct $nonzero($base);
impl $nonzero {
pub fn new(value: $base) -> Result<Self, ValidationError> {
if value != 0 {
Ok(Self(value))
} else {
Err(ValidationError::Zero)
}
}
pub fn get(&self) -> $base {
self.0
}
pub fn into_inner(self) -> $base {
self.0
}
}
paste::paste! {
crate::default_style!($nonzero => [<$nonzero Style>]);
impl Prompt for $nonzero {
fn prompt() -> Option<&'static str> {
Some("Please enter a non-zero number (!= 0):")
}
}
impl Elicitation for $nonzero {
type Style = [<$nonzero Style>];
#[tracing::instrument(skip(communicator), fields(type_name = stringify!($nonzero)))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!(concat!("Eliciting ", stringify!($nonzero), " (non-zero ", stringify!($base), " value)"));
loop {
let value = <$base>::elicit(communicator).await?;
match Self::new(value) {
Ok(non_zero) => {
tracing::debug!(value, concat!("Valid ", stringify!($nonzero), " constructed"));
return Ok(non_zero);
}
Err(e) => {
tracing::warn!(value, error = %e, concat!("Invalid ", stringify!($nonzero), ", re-prompting"));
}
}
}
}
}
}
#[doc = concat!("Contract type for ", stringify!($base), " values within a specified range [MIN, MAX].")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct $range<const MIN: $base, const MAX: $base>($base);
impl<const MIN: $base, const MAX: $base> $range<MIN, MAX> {
pub fn new(value: $base) -> Result<Self, ValidationError> {
if value >= MIN && value <= MAX {
Ok(Self(value))
} else {
Err(ValidationError::OutOfRange {
value: value as i128,
min: MIN as i128,
max: MAX as i128,
})
}
}
pub fn get(&self) -> $base {
self.0
}
pub fn into_inner(self) -> $base {
self.0
}
}
#[doc = concat!("Default-only style enum for ", stringify!($range), ".")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum $range_style {
#[default]
Default,
}
impl crate::Prompt for $range_style {
fn prompt() -> Option<&'static str> {
None
}
}
impl crate::Elicitation for $range_style {
type Style = $range_style;
async fn elicit<C: crate::ElicitCommunicator>(_communicator: &C) -> crate::ElicitResult<Self> {
Ok(Self::Default)
}
}
impl<const MIN: $base, const MAX: $base> Prompt for $range<MIN, MAX> {
fn prompt() -> Option<&'static str> {
Some("Please enter a number within the specified range:")
}
}
impl<const MIN: $base, const MAX: $base> Elicitation for $range<MIN, MAX> {
type Style = $range_style;
#[tracing::instrument(skip(communicator), fields(type_name = stringify!($range), min = MIN, max = MAX))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!(concat!("Eliciting ", stringify!($range), "<{}, {}> (", stringify!($base), " in range)"), MIN, MAX);
loop {
let value = <$base>::elicit(communicator).await?;
match Self::new(value) {
Ok(ranged) => {
tracing::debug!(value, min = MIN, max = MAX, concat!("Valid ", stringify!($range), " constructed"));
return Ok(ranged);
}
Err(e) => {
tracing::warn!(value, error = %e, min = MIN, max = MAX, concat!("Invalid ", stringify!($range), ", re-prompting"));
}
}
}
}
}
paste::paste! {
#[cfg(test)]
mod [<$nonzero:snake _tests>] {
use super::*;
#[test]
fn [<$nonzero:snake _new_valid>]() {
let result = $nonzero::new($test_nonzero_value);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), $test_nonzero_value);
}
#[test]
fn [<$nonzero:snake _new_zero_invalid>]() {
let result = $nonzero::new(0);
assert!(result.is_err());
}
#[test]
fn [<$nonzero:snake _into_inner>]() {
let non_zero = $nonzero::new($test_nonzero_value).unwrap();
let value: $base = non_zero.into_inner();
assert_eq!(value, $test_nonzero_value);
}
}
#[cfg(test)]
mod [<$range:snake _tests>] {
use super::*;
#[test]
fn [<$range:snake _new_valid_within_range>]() {
let result = $range::<$test_range_min, $test_range_max>::new($test_range_value);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), $test_range_value);
}
#[test]
fn [<$range:snake _new_valid_at_min>]() {
let result = $range::<$test_range_min, $test_range_max>::new($test_range_min);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), $test_range_min);
}
#[test]
fn [<$range:snake _new_valid_at_max>]() {
let result = $range::<$test_range_min, $test_range_max>::new($test_range_max);
assert!(result.is_ok());
assert_eq!(result.unwrap().get(), $test_range_max);
}
#[test]
fn [<$range:snake _new_below_min_invalid>]() {
let result = $range::<$test_range_min, $test_range_max>::new($test_range_min - 1);
assert!(result.is_err());
}
#[test]
fn [<$range:snake _new_above_max_invalid>]() {
let result = $range::<$test_range_min, $test_range_max>::new($test_range_max + 1);
assert!(result.is_err());
}
#[test]
fn [<$range:snake _into_inner>]() {
let ranged = $range::<$test_range_min, $test_range_max>::new($test_range_value).unwrap();
let value: $base = ranged.into_inner();
assert_eq!(value, $test_range_value);
}
}
}
};
}
impl_signed_contracts!(
i32,
I32Positive,
I32NonNegative,
I32Range,
I32RangeStyle,
42,
100,
10,
100,
50
);
impl_unsigned_contracts!(u32, U32NonZero, U32Range, U32RangeStyle, 42, 10, 100, 50);
impl_integer_default_wrapper!(i8, I8Default, i8::MIN as i64, i8::MAX as i64);
impl_integer_default_wrapper!(i16, I16Default, i16::MIN as i64, i16::MAX as i64);
impl_integer_default_wrapper!(i32, I32Default, i32::MIN as i64, i32::MAX as i64);
impl_integer_default_wrapper!(i64, I64Default, i64::MIN, i64::MAX);
impl_integer_default_wrapper!(i128, I128Default, i64::MIN, i64::MAX); impl_integer_default_wrapper!(isize, IsizeDefault, isize::MIN as i64, isize::MAX as i64);
impl_integer_default_wrapper!(u8, U8Default, 0, u8::MAX as i64);
impl_integer_default_wrapper!(u16, U16Default, 0, u16::MAX as i64);
impl_integer_default_wrapper!(u32, U32Default, 0, u32::MAX as i64);
impl_integer_default_wrapper!(u64, U64Default, 0, i64::MAX); impl_integer_default_wrapper!(u128, U128Default, 0, i64::MAX); impl_integer_default_wrapper!(usize, UsizeDefault, 0, isize::MAX as i64);
impl_signed_contracts!(
i64,
I64Positive,
I64NonNegative,
I64Range,
I64RangeStyle,
42,
100,
10,
100,
50
);
impl_unsigned_contracts!(u64, U64NonZero, U64Range, U64RangeStyle, 42, 10, 100, 50);
impl_signed_contracts!(
i128,
I128Positive,
I128NonNegative,
I128Range,
I128RangeStyle,
42,
100,
10,
100,
50
);
impl_unsigned_contracts!(
u128,
U128NonZero,
U128Range,
U128RangeStyle,
42,
10,
100,
50
);
impl_signed_contracts!(
isize,
IsizePositive,
IsizeNonNegative,
IsizeRange,
IsizeRangeStyle,
42,
100,
10,
100,
50
);
impl_unsigned_contracts!(
usize,
UsizeNonZero,
UsizeRange,
UsizeRangeStyle,
42,
10,
100,
50
);
macro_rules! impl_signed_nonzero {
($base:ty, $nonzero:ident, $test_value:expr) => {
#[doc = concat!("Contract type for non-zero ", stringify!($base), " values (!= 0).")]
#[contract_type(requires = "value != 0", ensures = "result.get() != 0")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct $nonzero($base);
impl $nonzero {
pub fn new(value: $base) -> Result<Self, ValidationError> {
if value != 0 {
Ok(Self(value))
} else {
Err(ValidationError::Zero)
}
}
pub fn get(&self) -> $base {
self.0
}
pub fn into_inner(self) -> $base {
self.0
}
}
paste::paste! {
crate::default_style!($nonzero => [<$nonzero Style>]);
impl Prompt for $nonzero {
fn prompt() -> Option<&'static str> {
Some("Please enter a non-zero number (!= 0):")
}
}
impl Elicitation for $nonzero {
type Style = [<$nonzero Style>];
#[tracing::instrument(skip(communicator), fields(type_name = stringify!($nonzero)))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
loop {
let value = <$base>::elicit(communicator).await?;
match Self::new(value) {
Ok(v) => return Ok(v),
Err(e) => tracing::warn!(error = %e, "Invalid, re-prompting"),
}
}
}
}
}
};
}
impl_signed_nonzero!(i32, I32NonZero, 42);
impl_signed_nonzero!(i64, I64NonZero, 42);
impl_signed_nonzero!(i128, I128NonZero, 42);
impl_signed_nonzero!(isize, IsizeNonZero, 42);
macro_rules! impl_unsigned_positive {
($base:ty, $positive:ident, $test_value:expr) => {
#[doc = concat!("Contract type for positive ", stringify!($base), " values (> 0).")]
#[contract_type(requires = "value > 0", ensures = "result.get() > 0")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct $positive($base);
impl $positive {
pub fn new(value: $base) -> Result<Self, ValidationError> {
if value > 0 {
Ok(Self(value))
} else {
Err(ValidationError::NotPositive(value as i128))
}
}
pub fn get(&self) -> $base {
self.0
}
pub fn into_inner(self) -> $base {
self.0
}
}
paste::paste! {
crate::default_style!($positive => [<$positive Style>]);
impl Prompt for $positive {
fn prompt() -> Option<&'static str> {
Some("Please enter a positive number (> 0):")
}
}
impl Elicitation for $positive {
type Style = [<$positive Style>];
#[tracing::instrument(skip(communicator), fields(type_name = stringify!($positive)))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
loop {
let value = <$base>::elicit(communicator).await?;
match Self::new(value) {
Ok(v) => return Ok(v),
Err(e) => tracing::warn!(error = %e, "Invalid, re-prompting"),
}
}
}
}
}
};
}
impl_unsigned_positive!(u32, U32Positive, 42);
impl_unsigned_positive!(u64, U64Positive, 42);
impl_unsigned_positive!(u128, U128Positive, 42);
impl_unsigned_positive!(usize, UsizePositive, 42);