1use super::ValidationError;
4use crate::{ElicitCommunicator, ElicitResult, Elicitation, Prompt};
5use anodized::spec;
6use elicitation_derive::contract_type;
7#[cfg(not(kani))]
8use elicitation_macros::instrumented_impl;
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11
12macro_rules! impl_integer_default_wrapper {
25 ($primitive:ty, $wrapper:ident, $min:expr, $max:expr) => {
26 #[doc = concat!("Default wrapper for ", stringify!($primitive), " (unconstrained).")]
27 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
31 #[schemars(description = concat!(stringify!($primitive), " value"))]
32 pub struct $wrapper(
33 #[schemars(range(min = $min, max = $max))]
34 $primitive
35 );
36
37 rmcp::elicit_safe!($wrapper);
38
39 impl $wrapper {
40 pub fn new(value: $primitive) -> Self {
42 Self(value)
43 }
44
45 pub fn get(&self) -> $primitive {
47 self.0
48 }
49
50 pub fn into_inner(self) -> $primitive {
52 self.0
53 }
54 }
55
56 paste::paste! {
57 crate::default_style!($wrapper => [<$wrapper Style>]);
58
59 impl Prompt for $wrapper {
60 fn prompt() -> Option<&'static str> {
61 Some(concat!("Please enter a ", stringify!($primitive), ":"))
62 }
63 }
64
65 impl Elicitation for $wrapper {
66 type Style = [<$wrapper Style>];
67
68 #[tracing::instrument(skip(communicator))]
69 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
70 let prompt = communicator
72 .style_context()
73 .prompt_for_type::<$primitive>(
74 "value",
75 stringify!($primitive),
76 &crate::style::PromptContext::new(0, 1),
77 )?
78 .unwrap_or_else(|| Self::prompt().unwrap().to_string());
79
80 tracing::debug!(
81 prompt = %prompt,
82 concat!("Eliciting ", stringify!($wrapper))
83 );
84
85 let response = communicator.send_prompt(&prompt).await?;
86
87 let value: $primitive = response.trim().parse().map_err(|e| {
89 crate::ElicitError::new(crate::ElicitErrorKind::ParseError(
90 format!("Failed to parse {}: {}", stringify!($primitive), e),
91 ))
92 })?;
93
94 Ok(Self::new(value))
95 }
96
97 fn kani_proof() -> proc_macro2::TokenStream {
98 crate::verification::proof_helpers::kani_integer_default(
99 stringify!($wrapper),
100 stringify!($primitive),
101 )
102 }
103
104 fn verus_proof() -> proc_macro2::TokenStream {
105 crate::verification::proof_helpers::verus_integer_default(
106 stringify!($wrapper),
107 stringify!($primitive),
108 )
109 }
110
111 fn creusot_proof() -> proc_macro2::TokenStream {
112 crate::verification::proof_helpers::creusot_integer_default(
113 stringify!($wrapper),
114 stringify!($primitive),
115 )
116 }
117}
118 }
119 };
120}
121
122macro_rules! impl_integer_tryfrom {
133 ($ty:ident, $prim:ty) => {
134 impl ::std::convert::TryFrom<$prim> for $ty {
135 type Error = super::ValidationError;
136 #[inline]
137 fn try_from(v: $prim) -> Result<Self, Self::Error> {
138 Self::new(v)
139 }
140 }
141 };
142}
143
144macro_rules! impl_integer_serde_bridge {
152 ($ty:ident, $prim:ty, $description:expr, { $($key:literal: $val:tt),* $(,)? }) => {
153 impl serde::Serialize for $ty {
154 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
155 self.0.serialize(s)
156 }
157 }
158 impl<'de> serde::Deserialize<'de> for $ty {
159 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
160 let v = <$prim>::deserialize(d)?;
161 Self::new(v).map_err(serde::de::Error::custom)
162 }
163 }
164 impl schemars::JsonSchema for $ty {
165 fn schema_name() -> ::std::borrow::Cow<'static, str> {
166 stringify!($ty).into()
167 }
168 fn json_schema(_gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
169 schemars::json_schema!({
170 "type": "integer",
171 "description": $description,
172 $($key: $val),*
173 })
174 }
175 }
176 };
177}
178
179macro_rules! impl_integer_range_serde_bridge {
182 ($ty:ident<$prim:ty>) => {
183 impl<const MIN: $prim, const MAX: $prim> serde::Serialize for $ty<MIN, MAX> {
184 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
185 self.0.serialize(s)
186 }
187 }
188 impl<'de, const MIN: $prim, const MAX: $prim> serde::Deserialize<'de> for $ty<MIN, MAX> {
189 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
190 let v = <$prim>::deserialize(d)?;
191 Self::new(v).map_err(serde::de::Error::custom)
192 }
193 }
194 impl<const MIN: $prim, const MAX: $prim> schemars::JsonSchema for $ty<MIN, MAX> {
195 fn schema_name() -> ::std::borrow::Cow<'static, str> {
196 format!(concat!(stringify!($ty), "<{},{}>"), MIN, MAX).into()
197 }
198 fn json_schema(_gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
199 let mut obj = serde_json::Map::new();
202 obj.insert("type".into(), serde_json::Value::String("integer".into()));
203 obj.insert("minimum".into(), serde_json::json!(MIN as i128));
204 obj.insert("maximum".into(), serde_json::json!(MAX as i128));
205 schemars::Schema::from(obj)
206 }
207 }
208 };
209}
210
211#[contract_type(requires = "value > 0", ensures = "result.get() > 0")]
239#[derive(
240 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
241)]
242#[serde(try_from = "i8")]
243#[schemars(description = "Positive integer value (> 0)")]
244pub struct I8Positive(#[schemars(range(min = 1))] i8);
245
246rmcp::elicit_safe!(I8Positive);
248impl_integer_tryfrom!(I8Positive, i8);
249
250#[cfg_attr(not(kani), instrumented_impl)]
251impl I8Positive {
252 #[spec(requires: [value > 0])]
258 pub fn new(value: i8) -> Result<Self, ValidationError> {
259 if value > 0 {
260 Ok(Self(value))
261 } else {
262 Err(ValidationError::NotPositive(value as i128))
263 }
264 }
265
266 pub fn get(&self) -> i8 {
268 self.0
269 }
270
271 pub fn into_inner(self) -> i8 {
275 self.0
276 }
277}
278
279crate::default_style!(I8Positive => I8PositiveStyle);
281
282#[cfg_attr(not(kani), instrumented_impl)]
283impl Prompt for I8Positive {
284 fn prompt() -> Option<&'static str> {
285 Some("Please enter a positive number (> 0):")
286 }
287}
288
289#[cfg_attr(not(kani), instrumented_impl)]
290impl Elicitation for I8Positive {
291 type Style = I8PositiveStyle;
292
293 #[tracing::instrument(skip(communicator), fields(type_name = "I8Positive"))]
294 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
295 tracing::debug!("Eliciting I8Positive (positive i8 value)");
296
297 loop {
298 let value = i8::elicit(communicator).await?;
300
301 match Self::new(value) {
303 Ok(positive) => {
304 tracing::debug!(value, "Valid I8Positive constructed");
305 return Ok(positive);
306 }
307 Err(e) => {
308 tracing::warn!(value, error = %e, "Invalid I8Positive, re-prompting");
309 }
311 }
312 }
313 }
314
315 fn kani_proof() -> proc_macro2::TokenStream {
316 crate::verification::proof_helpers::kani_numeric_positive("I8Positive", "i8")
317 }
318
319 fn verus_proof() -> proc_macro2::TokenStream {
320 crate::verification::proof_helpers::verus_numeric_positive("I8Positive", "i8")
321 }
322
323 fn creusot_proof() -> proc_macro2::TokenStream {
324 crate::verification::proof_helpers::creusot_numeric_positive("I8Positive", "i8")
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331
332 #[test]
333 fn i8_positive_new_valid() {
334 let result = I8Positive::new(1);
335 assert!(result.is_ok());
336 assert_eq!(result.unwrap().get(), 1);
337 }
338
339 #[test]
340 fn i8_positive_new_zero_invalid() {
341 let result = I8Positive::new(0);
342 assert!(result.is_err());
343 }
344
345 #[test]
346 fn i8_positive_new_negative_invalid() {
347 let result = I8Positive::new(-1);
348 assert!(result.is_err());
349 }
350
351 #[test]
352 fn i8_positive_into_inner() {
353 let positive = I8Positive::new(42).unwrap();
354 let value: i8 = positive.into_inner();
355 assert_eq!(value, 42);
356 }
357}
358
359#[derive(
367 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
368)]
369#[serde(try_from = "i8")]
370#[schemars(description = "Non-negative integer value (>= 0)")]
371pub struct I8NonNegative(#[schemars(range(min = 0))] i8);
372
373rmcp::elicit_safe!(I8NonNegative);
374impl_integer_tryfrom!(I8NonNegative, i8);
375
376#[cfg_attr(not(kani), instrumented_impl)]
377#[cfg_attr(not(kani), instrumented_impl)]
378#[cfg_attr(not(kani), instrumented_impl)]
379impl I8NonNegative {
380 #[spec(requires: [value >= 0])]
386 pub fn new(value: i8) -> Result<Self, ValidationError> {
387 if value >= 0 {
388 Ok(Self(value))
389 } else {
390 Err(ValidationError::Negative(value as i128))
391 }
392 }
393
394 pub fn get(&self) -> i8 {
396 self.0
397 }
398
399 pub fn into_inner(self) -> i8 {
401 self.0
402 }
403}
404
405crate::default_style!(I8NonNegative => I8NonNegativeStyle);
406
407#[cfg_attr(not(kani), instrumented_impl)]
408impl Prompt for I8NonNegative {
409 fn prompt() -> Option<&'static str> {
410 Some("Please enter a non-negative number (>= 0):")
411 }
412}
413
414#[cfg_attr(not(kani), instrumented_impl)]
415impl Elicitation for I8NonNegative {
416 type Style = I8NonNegativeStyle;
417
418 #[tracing::instrument(skip(communicator), fields(type_name = "I8NonNegative"))]
419 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
420 tracing::debug!("Eliciting I8NonNegative (non-negative i8 value)");
421
422 loop {
423 let value = i8::elicit(communicator).await?;
424
425 match Self::new(value) {
426 Ok(non_negative) => {
427 tracing::debug!(value, "Valid I8NonNegative constructed");
428 return Ok(non_negative);
429 }
430 Err(e) => {
431 tracing::warn!(value, error = %e, "Invalid I8NonNegative, re-prompting");
432 }
433 }
434 }
435 }
436
437 fn kani_proof() -> proc_macro2::TokenStream {
438 crate::verification::proof_helpers::kani_numeric_nonneg("I8NonNegative", "i8")
439 }
440
441 fn verus_proof() -> proc_macro2::TokenStream {
442 crate::verification::proof_helpers::verus_numeric_positive("I8NonNegative", "i8")
443 }
444
445 fn creusot_proof() -> proc_macro2::TokenStream {
446 crate::verification::proof_helpers::creusot_numeric_positive("I8NonNegative", "i8")
447 }
448}
449
450#[contract_type(requires = "value != 0", ensures = "result.get() != 0")]
458#[derive(
459 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
460)]
461#[serde(try_from = "i8")]
462#[schemars(description = "Non-zero integer value (!= 0)")]
463pub struct I8NonZero(i8);
464
465rmcp::elicit_safe!(I8NonZero);
466impl_integer_tryfrom!(I8NonZero, i8);
467
468#[cfg_attr(not(kani), instrumented_impl)]
469impl I8NonZero {
470 #[spec(requires: [value != 0])]
476 pub fn new(value: i8) -> Result<Self, ValidationError> {
477 if value != 0 {
478 Ok(Self(value))
479 } else {
480 Err(ValidationError::Zero)
481 }
482 }
483
484 pub fn get(&self) -> i8 {
486 self.0
487 }
488
489 pub fn into_inner(self) -> i8 {
491 self.0
492 }
493}
494
495crate::default_style!(I8NonZero => I8NonZeroStyle);
496
497#[cfg_attr(not(kani), instrumented_impl)]
498impl Prompt for I8NonZero {
499 fn prompt() -> Option<&'static str> {
500 Some("Please enter a non-zero number (!= 0):")
501 }
502}
503
504#[cfg_attr(not(kani), instrumented_impl)]
505impl Elicitation for I8NonZero {
506 type Style = I8NonZeroStyle;
507
508 #[tracing::instrument(skip(communicator), fields(type_name = "I8NonZero"))]
509 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
510 tracing::debug!("Eliciting I8NonZero");
511
512 loop {
513 let value = i8::elicit(communicator).await?;
514
515 match Self::new(value) {
516 Ok(non_zero) => {
517 tracing::debug!(value, "Valid I8NonZero constructed");
518 return Ok(non_zero);
519 }
520 Err(e) => {
521 tracing::warn!(value, error = %e, "Invalid I8NonZero, re-prompting");
522 }
523 }
524 }
525 }
526
527 fn kani_proof() -> proc_macro2::TokenStream {
528 crate::verification::proof_helpers::kani_numeric_nonzero("I8NonZero", "i8")
529 }
530
531 fn verus_proof() -> proc_macro2::TokenStream {
532 crate::verification::proof_helpers::verus_numeric_nonzero("I8NonZero", "i8")
533 }
534
535 fn creusot_proof() -> proc_macro2::TokenStream {
536 crate::verification::proof_helpers::creusot_numeric_nonzero("I8NonZero", "i8")
537 }
538}
539
540#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
548pub struct I8Range<const MIN: i8, const MAX: i8>(i8);
549
550impl<const MIN: i8, const MAX: i8> I8Range<MIN, MAX> {
551 #[spec(requires: [value >= MIN, value <= MAX])]
557 pub fn new(value: i8) -> Result<Self, ValidationError> {
558 if value >= MIN && value <= MAX {
559 Ok(Self(value))
560 } else {
561 Err(ValidationError::OutOfRange {
562 value: value as i128,
563 min: MIN as i128,
564 max: MAX as i128,
565 })
566 }
567 }
568
569 pub fn get(&self) -> i8 {
571 self.0
572 }
573
574 pub fn into_inner(self) -> i8 {
576 self.0
577 }
578}
579
580#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
583pub enum I8RangeStyle {
584 #[default]
586 Default,
587}
588
589#[cfg_attr(not(kani), instrumented_impl)]
590impl crate::Prompt for I8RangeStyle {
591 fn prompt() -> Option<&'static str> {
592 None }
594}
595
596#[cfg_attr(not(kani), instrumented_impl)]
597impl crate::Elicitation for I8RangeStyle {
598 type Style = I8RangeStyle;
599
600 async fn elicit<C: crate::ElicitCommunicator>(_communicator: &C) -> crate::ElicitResult<Self> {
601 Ok(Self::Default)
602 }
603
604 fn kani_proof() -> proc_macro2::TokenStream {
605 proc_macro2::TokenStream::new()
606 }
607
608 fn verus_proof() -> proc_macro2::TokenStream {
609 proc_macro2::TokenStream::new()
610 }
611
612 fn creusot_proof() -> proc_macro2::TokenStream {
613 proc_macro2::TokenStream::new()
614 }
615}
616
617impl crate::style::ElicitationStyle for I8RangeStyle {}
618
619impl_integer_range_serde_bridge!(I8Range<i8>);
620
621impl<const MIN: i8, const MAX: i8> Prompt for I8Range<MIN, MAX> {
622 fn prompt() -> Option<&'static str> {
623 Some("Please enter a number within the specified range:")
625 }
626}
627
628impl<const MIN: i8, const MAX: i8> Elicitation for I8Range<MIN, MAX> {
629 type Style = I8RangeStyle;
630
631 #[tracing::instrument(skip(communicator), fields(type_name = "I8Range", min = MIN, max = MAX))]
632 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
633 tracing::debug!("Eliciting I8Range<{}, {}> (i8 in range)", MIN, MAX);
634
635 loop {
636 let value = i8::elicit(communicator).await?;
637
638 match Self::new(value) {
639 Ok(ranged) => {
640 tracing::debug!(value, min = MIN, max = MAX, "Valid I8Range constructed");
641 return Ok(ranged);
642 }
643 Err(e) => {
644 tracing::warn!(value, error = %e, min = MIN, max = MAX, "Invalid I8Range, re-prompting");
645 }
646 }
647 }
648 }
649
650 fn kani_proof() -> proc_macro2::TokenStream {
651 proc_macro2::TokenStream::new()
652 }
653
654 fn verus_proof() -> proc_macro2::TokenStream {
655 proc_macro2::TokenStream::new()
656 }
657
658 fn creusot_proof() -> proc_macro2::TokenStream {
659 proc_macro2::TokenStream::new()
660 }
661}
662
663#[cfg(test)]
664mod i8_nonnegative_tests {
665 use super::*;
666
667 #[test]
668 fn i8_nonnegative_new_valid_positive() {
669 let result = I8NonNegative::new(42);
670 assert!(result.is_ok());
671 assert_eq!(result.unwrap().get(), 42);
672 }
673
674 #[test]
675 fn i8_nonnegative_new_valid_zero() {
676 let result = I8NonNegative::new(0);
677 assert!(result.is_ok());
678 assert_eq!(result.unwrap().get(), 0);
679 }
680
681 #[test]
682 fn i8_nonnegative_new_negative_invalid() {
683 let result = I8NonNegative::new(-1);
684 assert!(result.is_err());
685 }
686
687 #[test]
688 fn i8_nonnegative_into_inner() {
689 let non_neg = I8NonNegative::new(10).unwrap();
690 let value: i8 = non_neg.into_inner();
691 assert_eq!(value, 10);
692 }
693}
694
695#[cfg(test)]
696mod i8_range_tests {
697 use super::*;
698
699 #[test]
700 fn i8_range_new_valid_within_range() {
701 let result = I8Range::<10, 20>::new(15);
702 assert!(result.is_ok());
703 assert_eq!(result.unwrap().get(), 15);
704 }
705
706 #[test]
707 fn i8_range_new_valid_at_min() {
708 let result = I8Range::<10, 20>::new(10);
709 assert!(result.is_ok());
710 assert_eq!(result.unwrap().get(), 10);
711 }
712
713 #[test]
714 fn i8_range_new_valid_at_max() {
715 let result = I8Range::<10, 20>::new(20);
716 assert!(result.is_ok());
717 assert_eq!(result.unwrap().get(), 20);
718 }
719
720 #[test]
721 fn i8_range_new_below_min_invalid() {
722 let result = I8Range::<10, 20>::new(9);
723 assert!(result.is_err());
724 }
725
726 #[test]
727 fn i8_range_new_above_max_invalid() {
728 let result = I8Range::<10, 20>::new(21);
729 assert!(result.is_err());
730 }
731
732 #[test]
733 fn i8_range_into_inner() {
734 let ranged = I8Range::<10, 20>::new(15).unwrap();
735 let value: i8 = ranged.into_inner();
736 assert_eq!(value, 15);
737 }
738}
739
740#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
748pub struct I16Positive(i16);
749
750#[cfg_attr(not(kani), instrumented_impl)]
751#[cfg_attr(not(kani), instrumented_impl)]
752impl I16Positive {
753 #[spec(requires: [value > 0])]
759 pub fn new(value: i16) -> Result<Self, ValidationError> {
760 if value > 0 {
761 Ok(Self(value))
762 } else {
763 Err(ValidationError::NotPositive(value as i128))
764 }
765 }
766
767 pub fn get(&self) -> i16 {
769 self.0
770 }
771
772 pub fn into_inner(self) -> i16 {
774 self.0
775 }
776}
777
778crate::default_style!(I16Positive => I16PositiveStyle);
779impl_integer_serde_bridge!(I16Positive, i16, "Positive i16 value (> 0)", { "minimum": 1 });
780
781#[cfg_attr(not(kani), instrumented_impl)]
782impl Prompt for I16Positive {
783 fn prompt() -> Option<&'static str> {
784 Some("Please enter a positive number (> 0):")
785 }
786}
787
788#[cfg_attr(not(kani), instrumented_impl)]
789impl Elicitation for I16Positive {
790 type Style = I16PositiveStyle;
791
792 #[tracing::instrument(skip(communicator), fields(type_name = "I16Positive"))]
793 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
794 tracing::debug!("Eliciting I16Positive (positive i16 value)");
795
796 loop {
797 let value = i16::elicit(communicator).await?;
798
799 match Self::new(value) {
800 Ok(positive) => {
801 tracing::debug!(value, "Valid I16Positive constructed");
802 return Ok(positive);
803 }
804 Err(e) => {
805 tracing::warn!(value, error = %e, "Invalid I16Positive, re-prompting");
806 }
807 }
808 }
809 }
810
811 fn kani_proof() -> proc_macro2::TokenStream {
812 crate::verification::proof_helpers::kani_numeric_positive("I16Positive", "i16")
813 }
814
815 fn verus_proof() -> proc_macro2::TokenStream {
816 crate::verification::proof_helpers::verus_numeric_positive("I16Positive", "i16")
817 }
818
819 fn creusot_proof() -> proc_macro2::TokenStream {
820 crate::verification::proof_helpers::creusot_numeric_positive("I16Positive", "i16")
821 }
822}
823
824#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
832pub struct I16NonNegative(i16);
833
834#[cfg_attr(not(kani), instrumented_impl)]
835#[cfg_attr(not(kani), instrumented_impl)]
836impl I16NonNegative {
837 #[spec(requires: [value >= 0])]
843 pub fn new(value: i16) -> Result<Self, ValidationError> {
844 if value >= 0 {
845 Ok(Self(value))
846 } else {
847 Err(ValidationError::Negative(value as i128))
848 }
849 }
850
851 pub fn get(&self) -> i16 {
853 self.0
854 }
855
856 pub fn into_inner(self) -> i16 {
858 self.0
859 }
860}
861
862crate::default_style!(I16NonNegative => I16NonNegativeStyle);
863impl_integer_serde_bridge!(I16NonNegative, i16, "Non-negative i16 value (>= 0)", { "minimum": 0 });
864
865#[cfg_attr(not(kani), instrumented_impl)]
866impl Prompt for I16NonNegative {
867 fn prompt() -> Option<&'static str> {
868 Some("Please enter a non-negative number (>= 0):")
869 }
870}
871
872#[cfg_attr(not(kani), instrumented_impl)]
873impl Elicitation for I16NonNegative {
874 type Style = I16NonNegativeStyle;
875
876 #[tracing::instrument(skip(communicator), fields(type_name = "I16NonNegative"))]
877 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
878 tracing::debug!("Eliciting I16NonNegative (non-negative i16 value)");
879
880 loop {
881 let value = i16::elicit(communicator).await?;
882
883 match Self::new(value) {
884 Ok(non_negative) => {
885 tracing::debug!(value, "Valid I16NonNegative constructed");
886 return Ok(non_negative);
887 }
888 Err(e) => {
889 tracing::warn!(value, error = %e, "Invalid I16NonNegative, re-prompting");
890 }
891 }
892 }
893 }
894
895 fn kani_proof() -> proc_macro2::TokenStream {
896 crate::verification::proof_helpers::kani_numeric_nonneg("I16NonNegative", "i16")
897 }
898
899 fn verus_proof() -> proc_macro2::TokenStream {
900 crate::verification::proof_helpers::verus_numeric_positive("I16NonNegative", "i16")
901 }
902
903 fn creusot_proof() -> proc_macro2::TokenStream {
904 crate::verification::proof_helpers::creusot_numeric_positive("I16NonNegative", "i16")
905 }
906}
907
908#[contract_type(requires = "value != 0", ensures = "result.get() != 0")]
914#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
915pub struct I16NonZero(i16);
916
917#[cfg_attr(not(kani), instrumented_impl)]
918impl I16NonZero {
919 #[spec(requires: [value != 0])]
925 pub fn new(value: i16) -> Result<Self, ValidationError> {
926 if value != 0 {
927 Ok(Self(value))
928 } else {
929 Err(ValidationError::Zero)
930 }
931 }
932
933 pub fn get(&self) -> i16 {
935 self.0
936 }
937
938 pub fn into_inner(self) -> i16 {
940 self.0
941 }
942}
943
944crate::default_style!(I16NonZero => I16NonZeroStyle);
945impl_integer_serde_bridge!(I16NonZero, i16, "Non-zero i16 value (!= 0)", { "not": {"const": 0} });
946
947#[cfg_attr(not(kani), instrumented_impl)]
948impl Prompt for I16NonZero {
949 fn prompt() -> Option<&'static str> {
950 Some("Please enter a non-zero number (!= 0):")
951 }
952}
953
954#[cfg_attr(not(kani), instrumented_impl)]
955impl Elicitation for I16NonZero {
956 type Style = I16NonZeroStyle;
957
958 #[tracing::instrument(skip(communicator), fields(type_name = "I16NonZero"))]
959 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
960 tracing::debug!("Eliciting I16NonZero");
961
962 loop {
963 let value = i16::elicit(communicator).await?;
964
965 match Self::new(value) {
966 Ok(non_zero) => {
967 tracing::debug!(value, "Valid I16NonZero constructed");
968 return Ok(non_zero);
969 }
970 Err(e) => {
971 tracing::warn!(value, error = %e, "Invalid I16NonZero, re-prompting");
972 }
973 }
974 }
975 }
976
977 fn kani_proof() -> proc_macro2::TokenStream {
978 crate::verification::proof_helpers::kani_numeric_nonzero("I16NonZero", "i16")
979 }
980
981 fn verus_proof() -> proc_macro2::TokenStream {
982 crate::verification::proof_helpers::verus_numeric_nonzero("I16NonZero", "i16")
983 }
984
985 fn creusot_proof() -> proc_macro2::TokenStream {
986 crate::verification::proof_helpers::creusot_numeric_nonzero("I16NonZero", "i16")
987 }
988}
989
990#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
998pub struct I16Range<const MIN: i16, const MAX: i16>(i16);
999
1000impl<const MIN: i16, const MAX: i16> I16Range<MIN, MAX> {
1001 #[spec(requires: [value >= MIN, value <= MAX])]
1007 pub fn new(value: i16) -> Result<Self, ValidationError> {
1008 if value >= MIN && value <= MAX {
1009 Ok(Self(value))
1010 } else {
1011 Err(ValidationError::OutOfRange {
1012 value: value as i128,
1013 min: MIN as i128,
1014 max: MAX as i128,
1015 })
1016 }
1017 }
1018
1019 pub fn get(&self) -> i16 {
1021 self.0
1022 }
1023
1024 pub fn into_inner(self) -> i16 {
1026 self.0
1027 }
1028}
1029
1030#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
1032pub enum I16RangeStyle {
1033 #[default]
1035 Default,
1036}
1037
1038#[cfg_attr(not(kani), instrumented_impl)]
1039impl crate::Prompt for I16RangeStyle {
1040 fn prompt() -> Option<&'static str> {
1041 None
1042 }
1043}
1044
1045#[cfg_attr(not(kani), instrumented_impl)]
1046impl crate::Elicitation for I16RangeStyle {
1047 type Style = I16RangeStyle;
1048
1049 async fn elicit<C: crate::ElicitCommunicator>(_communicator: &C) -> crate::ElicitResult<Self> {
1050 Ok(Self::Default)
1051 }
1052
1053 fn kani_proof() -> proc_macro2::TokenStream {
1054 proc_macro2::TokenStream::new()
1055 }
1056
1057 fn verus_proof() -> proc_macro2::TokenStream {
1058 proc_macro2::TokenStream::new()
1059 }
1060
1061 fn creusot_proof() -> proc_macro2::TokenStream {
1062 proc_macro2::TokenStream::new()
1063 }
1064}
1065
1066impl crate::style::ElicitationStyle for I16RangeStyle {}
1067
1068impl_integer_range_serde_bridge!(I16Range<i16>);
1069
1070impl<const MIN: i16, const MAX: i16> Prompt for I16Range<MIN, MAX> {
1071 fn prompt() -> Option<&'static str> {
1072 Some("Please enter a number within the specified range:")
1073 }
1074}
1075
1076impl<const MIN: i16, const MAX: i16> Elicitation for I16Range<MIN, MAX> {
1077 type Style = I16RangeStyle;
1078
1079 #[tracing::instrument(skip(communicator), fields(type_name = "I16Range", min = MIN, max = MAX))]
1080 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
1081 tracing::debug!("Eliciting I16Range<{}, {}> (i16 in range)", MIN, MAX);
1082
1083 loop {
1084 let value = i16::elicit(communicator).await?;
1085
1086 match Self::new(value) {
1087 Ok(ranged) => {
1088 tracing::debug!(value, min = MIN, max = MAX, "Valid I16Range constructed");
1089 return Ok(ranged);
1090 }
1091 Err(e) => {
1092 tracing::warn!(value, error = %e, min = MIN, max = MAX, "Invalid I16Range, re-prompting");
1093 }
1094 }
1095 }
1096 }
1097
1098 fn kani_proof() -> proc_macro2::TokenStream {
1099 proc_macro2::TokenStream::new()
1100 }
1101
1102 fn verus_proof() -> proc_macro2::TokenStream {
1103 proc_macro2::TokenStream::new()
1104 }
1105
1106 fn creusot_proof() -> proc_macro2::TokenStream {
1107 proc_macro2::TokenStream::new()
1108 }
1109}
1110
1111#[cfg(test)]
1112mod i16_positive_tests {
1113 use super::*;
1114
1115 #[test]
1116 fn i16_positive_new_valid() {
1117 let result = I16Positive::new(1);
1118 assert!(result.is_ok());
1119 assert_eq!(result.unwrap().get(), 1);
1120 }
1121
1122 #[test]
1123 fn i16_positive_new_zero_invalid() {
1124 let result = I16Positive::new(0);
1125 assert!(result.is_err());
1126 }
1127
1128 #[test]
1129 fn i16_positive_new_negative_invalid() {
1130 let result = I16Positive::new(-1);
1131 assert!(result.is_err());
1132 }
1133
1134 #[test]
1135 fn i16_positive_into_inner() {
1136 let positive = I16Positive::new(1000).unwrap();
1137 let value: i16 = positive.into_inner();
1138 assert_eq!(value, 1000);
1139 }
1140}
1141
1142#[cfg(test)]
1143mod i16_nonnegative_tests {
1144 use super::*;
1145
1146 #[test]
1147 fn i16_nonnegative_new_valid_positive() {
1148 let result = I16NonNegative::new(1000);
1149 assert!(result.is_ok());
1150 assert_eq!(result.unwrap().get(), 1000);
1151 }
1152
1153 #[test]
1154 fn i16_nonnegative_new_valid_zero() {
1155 let result = I16NonNegative::new(0);
1156 assert!(result.is_ok());
1157 assert_eq!(result.unwrap().get(), 0);
1158 }
1159
1160 #[test]
1161 fn i16_nonnegative_new_negative_invalid() {
1162 let result = I16NonNegative::new(-1);
1163 assert!(result.is_err());
1164 }
1165
1166 #[test]
1167 fn i16_nonnegative_into_inner() {
1168 let non_neg = I16NonNegative::new(500).unwrap();
1169 let value: i16 = non_neg.into_inner();
1170 assert_eq!(value, 500);
1171 }
1172}
1173
1174#[cfg(test)]
1175mod i16_range_tests {
1176 use super::*;
1177
1178 #[test]
1179 fn i16_range_new_valid_within_range() {
1180 let result = I16Range::<100, 200>::new(150);
1181 assert!(result.is_ok());
1182 assert_eq!(result.unwrap().get(), 150);
1183 }
1184
1185 #[test]
1186 fn i16_range_new_valid_at_min() {
1187 let result = I16Range::<100, 200>::new(100);
1188 assert!(result.is_ok());
1189 assert_eq!(result.unwrap().get(), 100);
1190 }
1191
1192 #[test]
1193 fn i16_range_new_valid_at_max() {
1194 let result = I16Range::<100, 200>::new(200);
1195 assert!(result.is_ok());
1196 assert_eq!(result.unwrap().get(), 200);
1197 }
1198
1199 #[test]
1200 fn i16_range_new_below_min_invalid() {
1201 let result = I16Range::<100, 200>::new(99);
1202 assert!(result.is_err());
1203 }
1204
1205 #[test]
1206 fn i16_range_new_above_max_invalid() {
1207 let result = I16Range::<100, 200>::new(201);
1208 assert!(result.is_err());
1209 }
1210
1211 #[test]
1212 fn i16_range_into_inner() {
1213 let ranged = I16Range::<100, 200>::new(150).unwrap();
1214 let value: i16 = ranged.into_inner();
1215 assert_eq!(value, 150);
1216 }
1217}
1218
1219#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1227pub struct U8NonZero(u8);
1228
1229#[cfg_attr(not(kani), instrumented_impl)]
1230#[cfg_attr(not(kani), instrumented_impl)]
1231impl U8NonZero {
1232 #[spec(requires: [value != 0])]
1238 pub fn new(value: u8) -> Result<Self, ValidationError> {
1239 if value != 0 {
1240 Ok(Self(value))
1241 } else {
1242 Err(ValidationError::Zero)
1243 }
1244 }
1245
1246 pub fn get(&self) -> u8 {
1248 self.0
1249 }
1250
1251 pub fn into_inner(self) -> u8 {
1253 self.0
1254 }
1255}
1256
1257crate::default_style!(U8NonZero => U8NonZeroStyle);
1258impl_integer_serde_bridge!(U8NonZero, u8, "Non-zero u8 value (!= 0)", { "minimum": 1 });
1259
1260#[cfg_attr(not(kani), instrumented_impl)]
1261impl Prompt for U8NonZero {
1262 fn prompt() -> Option<&'static str> {
1263 Some("Please enter a non-zero number (!= 0):")
1264 }
1265}
1266
1267#[cfg_attr(not(kani), instrumented_impl)]
1268impl Elicitation for U8NonZero {
1269 type Style = U8NonZeroStyle;
1270
1271 #[tracing::instrument(skip(communicator), fields(type_name = "U8NonZero"))]
1272 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
1273 tracing::debug!("Eliciting U8NonZero (non-zero u8 value)");
1274
1275 loop {
1276 let value = u8::elicit(communicator).await?;
1277
1278 match Self::new(value) {
1279 Ok(non_zero) => {
1280 tracing::debug!(value, "Valid U8NonZero constructed");
1281 return Ok(non_zero);
1282 }
1283 Err(e) => {
1284 tracing::warn!(value, error = %e, "Invalid U8NonZero, re-prompting");
1285 }
1286 }
1287 }
1288 }
1289
1290 fn kani_proof() -> proc_macro2::TokenStream {
1291 crate::verification::proof_helpers::kani_numeric_nonzero("U8NonZero", "u8")
1292 }
1293
1294 fn verus_proof() -> proc_macro2::TokenStream {
1295 crate::verification::proof_helpers::verus_numeric_nonzero("U8NonZero", "u8")
1296 }
1297
1298 fn creusot_proof() -> proc_macro2::TokenStream {
1299 crate::verification::proof_helpers::creusot_numeric_nonzero("U8NonZero", "u8")
1300 }
1301}
1302
1303#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1311pub struct U8Range<const MIN: u8, const MAX: u8>(u8);
1312
1313impl<const MIN: u8, const MAX: u8> U8Range<MIN, MAX> {
1314 #[spec(requires: [value >= MIN, value <= MAX])]
1320 pub fn new(value: u8) -> Result<Self, ValidationError> {
1321 if value >= MIN && value <= MAX {
1322 Ok(Self(value))
1323 } else {
1324 Err(ValidationError::OutOfRange {
1325 value: value as i128,
1326 min: MIN as i128,
1327 max: MAX as i128,
1328 })
1329 }
1330 }
1331
1332 pub fn get(&self) -> u8 {
1334 self.0
1335 }
1336
1337 pub fn into_inner(self) -> u8 {
1339 self.0
1340 }
1341}
1342
1343#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
1345pub enum U8RangeStyle {
1346 #[default]
1348 Default,
1349}
1350
1351#[cfg_attr(not(kani), instrumented_impl)]
1352impl crate::Prompt for U8RangeStyle {
1353 fn prompt() -> Option<&'static str> {
1354 None
1355 }
1356}
1357
1358#[cfg_attr(not(kani), instrumented_impl)]
1359impl crate::Elicitation for U8RangeStyle {
1360 type Style = U8RangeStyle;
1361
1362 async fn elicit<C: crate::ElicitCommunicator>(_communicator: &C) -> crate::ElicitResult<Self> {
1363 Ok(Self::Default)
1364 }
1365
1366 fn kani_proof() -> proc_macro2::TokenStream {
1367 proc_macro2::TokenStream::new()
1368 }
1369
1370 fn verus_proof() -> proc_macro2::TokenStream {
1371 proc_macro2::TokenStream::new()
1372 }
1373
1374 fn creusot_proof() -> proc_macro2::TokenStream {
1375 proc_macro2::TokenStream::new()
1376 }
1377}
1378
1379impl crate::style::ElicitationStyle for U8RangeStyle {}
1380
1381impl_integer_range_serde_bridge!(U8Range<u8>);
1382
1383impl<const MIN: u8, const MAX: u8> Prompt for U8Range<MIN, MAX> {
1384 fn prompt() -> Option<&'static str> {
1385 Some("Please enter a number within the specified range:")
1386 }
1387}
1388
1389impl<const MIN: u8, const MAX: u8> Elicitation for U8Range<MIN, MAX> {
1390 type Style = U8RangeStyle;
1391
1392 #[tracing::instrument(skip(communicator), fields(type_name = "U8Range", min = MIN, max = MAX))]
1393 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
1394 tracing::debug!("Eliciting U8Range<{}, {}> (u8 in range)", MIN, MAX);
1395
1396 loop {
1397 let value = u8::elicit(communicator).await?;
1398
1399 match Self::new(value) {
1400 Ok(ranged) => {
1401 tracing::debug!(value, min = MIN, max = MAX, "Valid U8Range constructed");
1402 return Ok(ranged);
1403 }
1404 Err(e) => {
1405 tracing::warn!(value, error = %e, min = MIN, max = MAX, "Invalid U8Range, re-prompting");
1406 }
1407 }
1408 }
1409 }
1410
1411 fn kani_proof() -> proc_macro2::TokenStream {
1412 proc_macro2::TokenStream::new()
1413 }
1414
1415 fn verus_proof() -> proc_macro2::TokenStream {
1416 proc_macro2::TokenStream::new()
1417 }
1418
1419 fn creusot_proof() -> proc_macro2::TokenStream {
1420 proc_macro2::TokenStream::new()
1421 }
1422}
1423
1424#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1432pub struct U16NonZero(u16);
1433
1434#[cfg_attr(not(kani), instrumented_impl)]
1435#[cfg_attr(not(kani), instrumented_impl)]
1436impl U16NonZero {
1437 #[spec(requires: [value != 0])]
1443 pub fn new(value: u16) -> Result<Self, ValidationError> {
1444 if value != 0 {
1445 Ok(Self(value))
1446 } else {
1447 Err(ValidationError::Zero)
1448 }
1449 }
1450
1451 pub fn get(&self) -> u16 {
1453 self.0
1454 }
1455
1456 pub fn into_inner(self) -> u16 {
1458 self.0
1459 }
1460}
1461
1462crate::default_style!(U16NonZero => U16NonZeroStyle);
1463impl_integer_serde_bridge!(U16NonZero, u16, "Non-zero u16 value (!= 0)", { "minimum": 1 });
1464
1465#[cfg_attr(not(kani), instrumented_impl)]
1466impl Prompt for U16NonZero {
1467 fn prompt() -> Option<&'static str> {
1468 Some("Please enter a non-zero number (!= 0):")
1469 }
1470}
1471
1472#[cfg_attr(not(kani), instrumented_impl)]
1473impl Elicitation for U16NonZero {
1474 type Style = U16NonZeroStyle;
1475
1476 #[tracing::instrument(skip(communicator), fields(type_name = "U16NonZero"))]
1477 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
1478 tracing::debug!("Eliciting U16NonZero (non-zero u16 value)");
1479
1480 loop {
1481 let value = u16::elicit(communicator).await?;
1482
1483 match Self::new(value) {
1484 Ok(non_zero) => {
1485 tracing::debug!(value, "Valid U16NonZero constructed");
1486 return Ok(non_zero);
1487 }
1488 Err(e) => {
1489 tracing::warn!(value, error = %e, "Invalid U16NonZero, re-prompting");
1490 }
1491 }
1492 }
1493 }
1494
1495 fn kani_proof() -> proc_macro2::TokenStream {
1496 crate::verification::proof_helpers::kani_numeric_nonzero("U16NonZero", "u16")
1497 }
1498
1499 fn verus_proof() -> proc_macro2::TokenStream {
1500 crate::verification::proof_helpers::verus_numeric_nonzero("U16NonZero", "u16")
1501 }
1502
1503 fn creusot_proof() -> proc_macro2::TokenStream {
1504 crate::verification::proof_helpers::creusot_numeric_nonzero("U16NonZero", "u16")
1505 }
1506}
1507
1508#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1516pub struct U16Range<const MIN: u16, const MAX: u16>(u16);
1517
1518impl<const MIN: u16, const MAX: u16> U16Range<MIN, MAX> {
1519 #[spec(requires: [value >= MIN, value <= MAX])]
1525 pub fn new(value: u16) -> Result<Self, ValidationError> {
1526 if value >= MIN && value <= MAX {
1527 Ok(Self(value))
1528 } else {
1529 Err(ValidationError::OutOfRange {
1530 value: value as i128,
1531 min: MIN as i128,
1532 max: MAX as i128,
1533 })
1534 }
1535 }
1536
1537 pub fn get(&self) -> u16 {
1539 self.0
1540 }
1541
1542 pub fn into_inner(self) -> u16 {
1544 self.0
1545 }
1546}
1547
1548#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
1550pub enum U16RangeStyle {
1551 #[default]
1553 Default,
1554}
1555
1556#[cfg_attr(not(kani), instrumented_impl)]
1557impl crate::Prompt for U16RangeStyle {
1558 fn prompt() -> Option<&'static str> {
1559 None
1560 }
1561}
1562
1563#[cfg_attr(not(kani), instrumented_impl)]
1564impl crate::Elicitation for U16RangeStyle {
1565 type Style = U16RangeStyle;
1566
1567 async fn elicit<C: crate::ElicitCommunicator>(_communicator: &C) -> crate::ElicitResult<Self> {
1568 Ok(Self::Default)
1569 }
1570
1571 fn kani_proof() -> proc_macro2::TokenStream {
1572 proc_macro2::TokenStream::new()
1573 }
1574
1575 fn verus_proof() -> proc_macro2::TokenStream {
1576 proc_macro2::TokenStream::new()
1577 }
1578
1579 fn creusot_proof() -> proc_macro2::TokenStream {
1580 proc_macro2::TokenStream::new()
1581 }
1582}
1583
1584impl crate::style::ElicitationStyle for U16RangeStyle {}
1585
1586impl_integer_range_serde_bridge!(U16Range<u16>);
1587
1588impl<const MIN: u16, const MAX: u16> Prompt for U16Range<MIN, MAX> {
1589 fn prompt() -> Option<&'static str> {
1590 Some("Please enter a number within the specified range:")
1591 }
1592}
1593
1594impl<const MIN: u16, const MAX: u16> Elicitation for U16Range<MIN, MAX> {
1595 type Style = U16RangeStyle;
1596
1597 #[tracing::instrument(skip(communicator), fields(type_name = "U16Range", min = MIN, max = MAX))]
1598 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
1599 tracing::debug!("Eliciting U16Range<{}, {}> (u16 in range)", MIN, MAX);
1600
1601 loop {
1602 let value = u16::elicit(communicator).await?;
1603
1604 match Self::new(value) {
1605 Ok(ranged) => {
1606 tracing::debug!(value, min = MIN, max = MAX, "Valid U16Range constructed");
1607 return Ok(ranged);
1608 }
1609 Err(e) => {
1610 tracing::warn!(value, error = %e, min = MIN, max = MAX, "Invalid U16Range, re-prompting");
1611 }
1612 }
1613 }
1614 }
1615
1616 fn kani_proof() -> proc_macro2::TokenStream {
1617 proc_macro2::TokenStream::new()
1618 }
1619
1620 fn verus_proof() -> proc_macro2::TokenStream {
1621 proc_macro2::TokenStream::new()
1622 }
1623
1624 fn creusot_proof() -> proc_macro2::TokenStream {
1625 proc_macro2::TokenStream::new()
1626 }
1627}
1628
1629#[cfg(test)]
1630mod u8_nonzero_tests {
1631 use super::*;
1632
1633 #[test]
1634 fn u8_nonzero_new_valid() {
1635 let result = U8NonZero::new(1);
1636 assert!(result.is_ok());
1637 assert_eq!(result.unwrap().get(), 1);
1638 }
1639
1640 #[test]
1641 fn u8_nonzero_new_zero_invalid() {
1642 let result = U8NonZero::new(0);
1643 assert!(result.is_err());
1644 }
1645
1646 #[test]
1647 fn u8_nonzero_into_inner() {
1648 let non_zero = U8NonZero::new(255).unwrap();
1649 let value: u8 = non_zero.into_inner();
1650 assert_eq!(value, 255);
1651 }
1652}
1653
1654#[cfg(test)]
1655mod u8_range_tests {
1656 use super::*;
1657
1658 #[test]
1659 fn u8_range_new_valid_within_range() {
1660 let result = U8Range::<10, 20>::new(15);
1661 assert!(result.is_ok());
1662 assert_eq!(result.unwrap().get(), 15);
1663 }
1664
1665 #[test]
1666 fn u8_range_new_valid_at_min() {
1667 let result = U8Range::<10, 20>::new(10);
1668 assert!(result.is_ok());
1669 assert_eq!(result.unwrap().get(), 10);
1670 }
1671
1672 #[test]
1673 fn u8_range_new_valid_at_max() {
1674 let result = U8Range::<10, 20>::new(20);
1675 assert!(result.is_ok());
1676 assert_eq!(result.unwrap().get(), 20);
1677 }
1678
1679 #[test]
1680 fn u8_range_new_below_min_invalid() {
1681 let result = U8Range::<10, 20>::new(9);
1682 assert!(result.is_err());
1683 }
1684
1685 #[test]
1686 fn u8_range_new_above_max_invalid() {
1687 let result = U8Range::<10, 20>::new(21);
1688 assert!(result.is_err());
1689 }
1690
1691 #[test]
1692 fn u8_range_into_inner() {
1693 let ranged = U8Range::<10, 20>::new(15).unwrap();
1694 let value: u8 = ranged.into_inner();
1695 assert_eq!(value, 15);
1696 }
1697}
1698
1699#[cfg(test)]
1700mod u16_nonzero_tests {
1701 use super::*;
1702
1703 #[test]
1704 fn u16_nonzero_new_valid() {
1705 let result = U16NonZero::new(1000);
1706 assert!(result.is_ok());
1707 assert_eq!(result.unwrap().get(), 1000);
1708 }
1709
1710 #[test]
1711 fn u16_nonzero_new_zero_invalid() {
1712 let result = U16NonZero::new(0);
1713 assert!(result.is_err());
1714 }
1715
1716 #[test]
1717 fn u16_nonzero_into_inner() {
1718 let non_zero = U16NonZero::new(65535).unwrap();
1719 let value: u16 = non_zero.into_inner();
1720 assert_eq!(value, 65535);
1721 }
1722}
1723
1724#[cfg(test)]
1725mod u16_range_tests {
1726 use super::*;
1727
1728 #[test]
1729 fn u16_range_new_valid_within_range() {
1730 let result = U16Range::<1000, 2000>::new(1500);
1731 assert!(result.is_ok());
1732 assert_eq!(result.unwrap().get(), 1500);
1733 }
1734
1735 #[test]
1736 fn u16_range_new_valid_at_min() {
1737 let result = U16Range::<1000, 2000>::new(1000);
1738 assert!(result.is_ok());
1739 assert_eq!(result.unwrap().get(), 1000);
1740 }
1741
1742 #[test]
1743 fn u16_range_new_valid_at_max() {
1744 let result = U16Range::<1000, 2000>::new(2000);
1745 assert!(result.is_ok());
1746 assert_eq!(result.unwrap().get(), 2000);
1747 }
1748
1749 #[test]
1750 fn u16_range_new_below_min_invalid() {
1751 let result = U16Range::<1000, 2000>::new(999);
1752 assert!(result.is_err());
1753 }
1754
1755 #[test]
1756 fn u16_range_new_above_max_invalid() {
1757 let result = U16Range::<1000, 2000>::new(2001);
1758 assert!(result.is_err());
1759 }
1760
1761 #[test]
1762 fn u16_range_into_inner() {
1763 let ranged = U16Range::<1000, 2000>::new(1500).unwrap();
1764 let value: u16 = ranged.into_inner();
1765 assert_eq!(value, 1500);
1766 }
1767}
1768
1769#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1772pub struct U8Positive(u8);
1773
1774#[cfg_attr(not(kani), instrumented_impl)]
1775impl U8Positive {
1776 #[spec(requires: [value > 0])]
1778 pub fn new(value: u8) -> Result<Self, ValidationError> {
1779 if value > 0 {
1780 Ok(Self(value))
1781 } else {
1782 Err(ValidationError::Zero)
1783 }
1784 }
1785 pub fn get(&self) -> u8 {
1787 self.0
1788 }
1789 pub fn into_inner(self) -> u8 {
1791 self.0
1792 }
1793}
1794
1795crate::default_style!(U8Positive => U8PositiveStyle);
1796impl_integer_serde_bridge!(U8Positive, u8, "Positive u8 value (> 0)", { "minimum": 1 });
1797
1798#[cfg_attr(not(kani), instrumented_impl)]
1799impl Prompt for U8Positive {
1800 fn prompt() -> Option<&'static str> {
1801 Some("Please enter a positive number (> 0):")
1802 }
1803}
1804
1805#[cfg_attr(not(kani), instrumented_impl)]
1806impl Elicitation for U8Positive {
1807 type Style = U8PositiveStyle;
1808 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
1809 loop {
1810 if let Ok(v) = Self::new(u8::elicit(communicator).await?) {
1811 return Ok(v);
1812 }
1813 }
1814 }
1815
1816 fn kani_proof() -> proc_macro2::TokenStream {
1817 crate::verification::proof_helpers::kani_numeric_positive("U8Positive", "u8")
1818 }
1819
1820 fn verus_proof() -> proc_macro2::TokenStream {
1821 crate::verification::proof_helpers::verus_numeric_positive("U8Positive", "u8")
1822 }
1823
1824 fn creusot_proof() -> proc_macro2::TokenStream {
1825 crate::verification::proof_helpers::creusot_numeric_positive("U8Positive", "u8")
1826 }
1827}
1828
1829#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1832pub struct U16Positive(u16);
1833
1834#[cfg_attr(not(kani), instrumented_impl)]
1835impl U16Positive {
1836 #[spec(requires: [value > 0])]
1838 pub fn new(value: u16) -> Result<Self, ValidationError> {
1839 if value > 0 {
1840 Ok(Self(value))
1841 } else {
1842 Err(ValidationError::Zero)
1843 }
1844 }
1845 pub fn get(&self) -> u16 {
1847 self.0
1848 }
1849 pub fn into_inner(self) -> u16 {
1851 self.0
1852 }
1853}
1854
1855crate::default_style!(U16Positive => U16PositiveStyle);
1856impl_integer_serde_bridge!(U16Positive, u16, "Positive u16 value (> 0)", { "minimum": 1 });
1857
1858#[cfg_attr(not(kani), instrumented_impl)]
1859impl Prompt for U16Positive {
1860 fn prompt() -> Option<&'static str> {
1861 Some("Please enter a positive number (> 0):")
1862 }
1863}
1864
1865#[cfg_attr(not(kani), instrumented_impl)]
1866impl Elicitation for U16Positive {
1867 type Style = U16PositiveStyle;
1868 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
1869 loop {
1870 if let Ok(v) = Self::new(u16::elicit(communicator).await?) {
1871 return Ok(v);
1872 }
1873 }
1874 }
1875
1876 fn kani_proof() -> proc_macro2::TokenStream {
1877 crate::verification::proof_helpers::kani_numeric_positive("U16Positive", "u16")
1878 }
1879
1880 fn verus_proof() -> proc_macro2::TokenStream {
1881 crate::verification::proof_helpers::verus_numeric_positive("U16Positive", "u16")
1882 }
1883
1884 fn creusot_proof() -> proc_macro2::TokenStream {
1885 crate::verification::proof_helpers::creusot_numeric_positive("U16Positive", "u16")
1886 }
1887}
1888
1889macro_rules! impl_signed_contracts {
1894 ($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) => {
1895 #[doc = concat!("Contract type for positive ", stringify!($base), " values (> 0).")]
1897 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1900 pub struct $positive($base);
1901
1902 impl $positive {
1903 #[spec(requires: [value > 0])]
1909 pub fn new(value: $base) -> Result<Self, ValidationError> {
1910 if value > 0 {
1911 Ok(Self(value))
1912 } else {
1913 Err(ValidationError::NotPositive(value as i128))
1914 }
1915 }
1916
1917 pub fn get(&self) -> $base {
1919 self.0
1920 }
1921
1922 pub fn into_inner(self) -> $base {
1924 self.0
1925 }
1926 }
1927
1928 paste::paste! {
1929 crate::default_style!($positive => [<$positive Style>]);
1930
1931 impl Prompt for $positive {
1932 fn prompt() -> Option<&'static str> {
1933 Some("Please enter a positive number (> 0):")
1934 }
1935 }
1936
1937 impl Elicitation for $positive {
1938 type Style = [<$positive Style>];
1939
1940 #[tracing::instrument(skip(communicator), fields(type_name = stringify!($positive)))]
1941 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
1942 tracing::debug!(concat!("Eliciting ", stringify!($positive), " (positive ", stringify!($base), " value)"));
1943
1944 loop {
1945 let value = <$base>::elicit(communicator).await?;
1946
1947 match Self::new(value) {
1948 Ok(positive) => {
1949 tracing::debug!(value, concat!("Valid ", stringify!($positive), " constructed"));
1950 return Ok(positive);
1951 }
1952 Err(e) => {
1953 tracing::warn!(value, error = %e, concat!("Invalid ", stringify!($positive), ", re-prompting"));
1954 }
1955 }
1956 }
1957 }
1958
1959 fn kani_proof() -> proc_macro2::TokenStream {
1960 crate::verification::proof_helpers::kani_numeric_positive(stringify!($positive), stringify!($base))
1961 }
1962
1963 fn verus_proof() -> proc_macro2::TokenStream {
1964 crate::verification::proof_helpers::verus_numeric_positive(stringify!($positive), stringify!($base))
1965 }
1966
1967 fn creusot_proof() -> proc_macro2::TokenStream {
1968 crate::verification::proof_helpers::creusot_numeric_positive(stringify!($positive), stringify!($base))
1969 }
1970
1971 }
1972 }
1973
1974 #[doc = concat!("Contract type for non-negative ", stringify!($base), " values (>= 0).")]
1976 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1979 pub struct $nonnegative($base);
1980
1981 impl $nonnegative {
1982 #[spec(requires: [value >= 0])]
1988 pub fn new(value: $base) -> Result<Self, ValidationError> {
1989 if value >= 0 {
1990 Ok(Self(value))
1991 } else {
1992 Err(ValidationError::Negative(value as i128))
1993 }
1994 }
1995
1996 pub fn get(&self) -> $base {
1998 self.0
1999 }
2000
2001 pub fn into_inner(self) -> $base {
2003 self.0
2004 }
2005 }
2006
2007 paste::paste! {
2008 crate::default_style!($nonnegative => [<$nonnegative Style>]);
2009
2010 impl Prompt for $nonnegative {
2011 fn prompt() -> Option<&'static str> {
2012 Some("Please enter a non-negative number (>= 0):")
2013 }
2014 }
2015
2016 impl Elicitation for $nonnegative {
2017 type Style = [<$nonnegative Style>];
2018
2019 #[tracing::instrument(skip(communicator), fields(type_name = stringify!($nonnegative)))]
2020 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
2021 tracing::debug!(concat!("Eliciting ", stringify!($nonnegative), " (non-negative ", stringify!($base), " value)"));
2022
2023 loop {
2024 let value = <$base>::elicit(communicator).await?;
2025
2026 match Self::new(value) {
2027 Ok(non_negative) => {
2028 tracing::debug!(value, concat!("Valid ", stringify!($nonnegative), " constructed"));
2029 return Ok(non_negative);
2030 }
2031 Err(e) => {
2032 tracing::warn!(value, error = %e, concat!("Invalid ", stringify!($nonnegative), ", re-prompting"));
2033 }
2034 }
2035 }
2036 }
2037
2038 fn kani_proof() -> proc_macro2::TokenStream {
2039 crate::verification::proof_helpers::kani_numeric_nonneg(stringify!($nonnegative), stringify!($base))
2040 }
2041
2042 fn verus_proof() -> proc_macro2::TokenStream {
2043 crate::verification::proof_helpers::verus_numeric_positive(stringify!($nonnegative), stringify!($base))
2044 }
2045
2046 fn creusot_proof() -> proc_macro2::TokenStream {
2047 crate::verification::proof_helpers::creusot_numeric_positive(stringify!($nonnegative), stringify!($base))
2048 }
2049
2050 }
2051 }
2052
2053 #[doc = concat!("Contract type for ", stringify!($base), " values within a specified range [MIN, MAX].")]
2055 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
2058 pub struct $range<const MIN: $base, const MAX: $base>($base);
2059
2060 impl<const MIN: $base, const MAX: $base> $range<MIN, MAX> {
2061 #[spec(requires: [value >= MIN, value <= MAX])]
2067 pub fn new(value: $base) -> Result<Self, ValidationError> {
2068 if value >= MIN && value <= MAX {
2069 Ok(Self(value))
2070 } else {
2071 Err(ValidationError::OutOfRange {
2072 value: value as i128,
2073 min: MIN as i128,
2074 max: MAX as i128,
2075 })
2076 }
2077 }
2078
2079 pub fn get(&self) -> $base {
2081 self.0
2082 }
2083
2084 pub fn into_inner(self) -> $base {
2086 self.0
2087 }
2088 }
2089
2090 #[doc = concat!("Default-only style enum for ", stringify!($range), ".")]
2091 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
2092 pub enum $range_style {
2093 #[default]
2095 Default,
2096 }
2097
2098 impl crate::Prompt for $range_style {
2099 fn prompt() -> Option<&'static str> {
2100 None
2101 }
2102 }
2103
2104 impl crate::Elicitation for $range_style {
2105 type Style = $range_style;
2106
2107 async fn elicit<C: crate::ElicitCommunicator>(_communicator: &C) -> crate::ElicitResult<Self> {
2108 Ok(Self::Default)
2109 }
2110
2111 fn kani_proof() -> proc_macro2::TokenStream {
2112 proc_macro2::TokenStream::new()
2113 }
2114
2115 fn verus_proof() -> proc_macro2::TokenStream {
2116 proc_macro2::TokenStream::new()
2117 }
2118
2119 fn creusot_proof() -> proc_macro2::TokenStream {
2120 proc_macro2::TokenStream::new()
2121 }
2122}
2123
2124
2125 impl crate::style::ElicitationStyle for $range_style {}
2126 impl<const MIN: $base, const MAX: $base> Prompt for $range<MIN, MAX> {
2127 fn prompt() -> Option<&'static str> {
2128 Some("Please enter a number within the specified range:")
2129 }
2130 }
2131
2132 impl<const MIN: $base, const MAX: $base> Elicitation for $range<MIN, MAX> {
2133 type Style = $range_style;
2134
2135 #[tracing::instrument(skip(communicator), fields(type_name = stringify!($range), min = MIN, max = MAX))]
2136 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
2137 tracing::debug!(concat!("Eliciting ", stringify!($range), "<{}, {}> (", stringify!($base), " in range)"), MIN, MAX);
2138
2139 loop {
2140 let value = <$base>::elicit(communicator).await?;
2141
2142 match Self::new(value) {
2143 Ok(ranged) => {
2144 tracing::debug!(value, min = MIN, max = MAX, concat!("Valid ", stringify!($range), " constructed"));
2145 return Ok(ranged);
2146 }
2147 Err(e) => {
2148 tracing::warn!(value, error = %e, min = MIN, max = MAX, concat!("Invalid ", stringify!($range), ", re-prompting"));
2149 }
2150 }
2151 }
2152 }
2153
2154 fn kani_proof() -> proc_macro2::TokenStream {
2155 proc_macro2::TokenStream::new()
2156 }
2157
2158 fn verus_proof() -> proc_macro2::TokenStream {
2159 proc_macro2::TokenStream::new()
2160 }
2161
2162 fn creusot_proof() -> proc_macro2::TokenStream {
2163 proc_macro2::TokenStream::new()
2164 }
2165}
2166
2167 paste::paste! {
2169 #[cfg(test)]
2170 mod [<$positive:snake _tests>] {
2171 use super::*;
2172
2173 #[test]
2174 fn [<$positive:snake _new_valid>]() {
2175 let result = $positive::new($test_pos_value);
2176 assert!(result.is_ok());
2177 assert_eq!(result.unwrap().get(), $test_pos_value);
2178 }
2179
2180 #[test]
2181 fn [<$positive:snake _new_zero_invalid>]() {
2182 let result = $positive::new(0);
2183 assert!(result.is_err());
2184 }
2185
2186 #[test]
2187 fn [<$positive:snake _new_negative_invalid>]() {
2188 let result = $positive::new(-1);
2189 assert!(result.is_err());
2190 }
2191
2192 #[test]
2193 fn [<$positive:snake _into_inner>]() {
2194 let positive = $positive::new($test_pos_value).unwrap();
2195 let value: $base = positive.into_inner();
2196 assert_eq!(value, $test_pos_value);
2197 }
2198 }
2199
2200 #[cfg(test)]
2201 mod [<$nonnegative:snake _tests>] {
2202 use super::*;
2203
2204 #[test]
2205 fn [<$nonnegative:snake _new_valid_positive>]() {
2206 let result = $nonnegative::new($test_nonneg_value);
2207 assert!(result.is_ok());
2208 assert_eq!(result.unwrap().get(), $test_nonneg_value);
2209 }
2210
2211 #[test]
2212 fn [<$nonnegative:snake _new_valid_zero>]() {
2213 let result = $nonnegative::new(0);
2214 assert!(result.is_ok());
2215 assert_eq!(result.unwrap().get(), 0);
2216 }
2217
2218 #[test]
2219 fn [<$nonnegative:snake _new_negative_invalid>]() {
2220 let result = $nonnegative::new(-1);
2221 assert!(result.is_err());
2222 }
2223
2224 #[test]
2225 fn [<$nonnegative:snake _into_inner>]() {
2226 let non_neg = $nonnegative::new($test_nonneg_value).unwrap();
2227 let value: $base = non_neg.into_inner();
2228 assert_eq!(value, $test_nonneg_value);
2229 }
2230 }
2231
2232 #[cfg(test)]
2233 mod [<$range:snake _tests>] {
2234 use super::*;
2235
2236 #[test]
2237 fn [<$range:snake _new_valid_within_range>]() {
2238 let result = $range::<$test_range_min, $test_range_max>::new($test_range_value);
2239 assert!(result.is_ok());
2240 assert_eq!(result.unwrap().get(), $test_range_value);
2241 }
2242
2243 #[test]
2244 fn [<$range:snake _new_valid_at_min>]() {
2245 let result = $range::<$test_range_min, $test_range_max>::new($test_range_min);
2246 assert!(result.is_ok());
2247 assert_eq!(result.unwrap().get(), $test_range_min);
2248 }
2249
2250 #[test]
2251 fn [<$range:snake _new_valid_at_max>]() {
2252 let result = $range::<$test_range_min, $test_range_max>::new($test_range_max);
2253 assert!(result.is_ok());
2254 assert_eq!(result.unwrap().get(), $test_range_max);
2255 }
2256
2257 #[test]
2258 fn [<$range:snake _new_below_min_invalid>]() {
2259 let result = $range::<$test_range_min, $test_range_max>::new($test_range_min - 1);
2260 assert!(result.is_err());
2261 }
2262
2263 #[test]
2264 fn [<$range:snake _new_above_max_invalid>]() {
2265 let result = $range::<$test_range_min, $test_range_max>::new($test_range_max + 1);
2266 assert!(result.is_err());
2267 }
2268
2269 #[test]
2270 fn [<$range:snake _into_inner>]() {
2271 let ranged = $range::<$test_range_min, $test_range_max>::new($test_range_value).unwrap();
2272 let value: $base = ranged.into_inner();
2273 assert_eq!(value, $test_range_value);
2274 }
2275 }
2276 }
2277 };
2278}
2279
2280macro_rules! impl_unsigned_contracts {
2285 ($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) => {
2286 #[doc = concat!("Contract type for non-zero ", stringify!($base), " values (!= 0).")]
2288 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
2291 pub struct $nonzero($base);
2292
2293 impl $nonzero {
2294 #[spec(requires: [value != 0])]
2300 pub fn new(value: $base) -> Result<Self, ValidationError> {
2301 if value != 0 {
2302 Ok(Self(value))
2303 } else {
2304 Err(ValidationError::Zero)
2305 }
2306 }
2307
2308 pub fn get(&self) -> $base {
2310 self.0
2311 }
2312
2313 pub fn into_inner(self) -> $base {
2315 self.0
2316 }
2317 }
2318
2319 paste::paste! {
2320 crate::default_style!($nonzero => [<$nonzero Style>]);
2321
2322 impl Prompt for $nonzero {
2323 fn prompt() -> Option<&'static str> {
2324 Some("Please enter a non-zero number (!= 0):")
2325 }
2326 }
2327
2328 impl Elicitation for $nonzero {
2329 type Style = [<$nonzero Style>];
2330
2331 #[tracing::instrument(skip(communicator), fields(type_name = stringify!($nonzero)))]
2332 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
2333 tracing::debug!(concat!("Eliciting ", stringify!($nonzero), " (non-zero ", stringify!($base), " value)"));
2334
2335 loop {
2336 let value = <$base>::elicit(communicator).await?;
2337
2338 match Self::new(value) {
2339 Ok(non_zero) => {
2340 tracing::debug!(value, concat!("Valid ", stringify!($nonzero), " constructed"));
2341 return Ok(non_zero);
2342 }
2343 Err(e) => {
2344 tracing::warn!(value, error = %e, concat!("Invalid ", stringify!($nonzero), ", re-prompting"));
2345 }
2346 }
2347 }
2348 }
2349
2350 fn kani_proof() -> proc_macro2::TokenStream {
2351 crate::verification::proof_helpers::kani_numeric_nonzero(stringify!($nonzero), stringify!($base))
2352 }
2353
2354 fn verus_proof() -> proc_macro2::TokenStream {
2355 crate::verification::proof_helpers::verus_numeric_nonzero(stringify!($nonzero), stringify!($base))
2356 }
2357
2358 fn creusot_proof() -> proc_macro2::TokenStream {
2359 crate::verification::proof_helpers::creusot_numeric_nonzero(stringify!($nonzero), stringify!($base))
2360 }
2361
2362 }
2363 }
2364
2365 #[doc = concat!("Contract type for ", stringify!($base), " values within a specified range [MIN, MAX].")]
2367 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
2370 pub struct $range<const MIN: $base, const MAX: $base>($base);
2371
2372 impl<const MIN: $base, const MAX: $base> $range<MIN, MAX> {
2373 #[spec(requires: [value >= MIN, value <= MAX])]
2379 pub fn new(value: $base) -> Result<Self, ValidationError> {
2380 if value >= MIN && value <= MAX {
2381 Ok(Self(value))
2382 } else {
2383 Err(ValidationError::OutOfRange {
2384 value: value as i128,
2385 min: MIN as i128,
2386 max: MAX as i128,
2387 })
2388 }
2389 }
2390
2391 pub fn get(&self) -> $base {
2393 self.0
2394 }
2395
2396 pub fn into_inner(self) -> $base {
2398 self.0
2399 }
2400 }
2401
2402 #[doc = concat!("Default-only style enum for ", stringify!($range), ".")]
2403 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
2404 pub enum $range_style {
2405 #[default]
2407 Default,
2408 }
2409
2410 impl crate::Prompt for $range_style {
2411 fn prompt() -> Option<&'static str> {
2412 None
2413 }
2414 }
2415
2416 impl crate::Elicitation for $range_style {
2417 type Style = $range_style;
2418
2419 async fn elicit<C: crate::ElicitCommunicator>(_communicator: &C) -> crate::ElicitResult<Self> {
2420 Ok(Self::Default)
2421 }
2422
2423 fn kani_proof() -> proc_macro2::TokenStream {
2424 proc_macro2::TokenStream::new()
2425 }
2426
2427 fn verus_proof() -> proc_macro2::TokenStream {
2428 proc_macro2::TokenStream::new()
2429 }
2430
2431 fn creusot_proof() -> proc_macro2::TokenStream {
2432 proc_macro2::TokenStream::new()
2433 }
2434}
2435
2436
2437 impl crate::style::ElicitationStyle for $range_style {}
2438 impl<const MIN: $base, const MAX: $base> Prompt for $range<MIN, MAX> {
2439 fn prompt() -> Option<&'static str> {
2440 Some("Please enter a number within the specified range:")
2441 }
2442 }
2443
2444 impl<const MIN: $base, const MAX: $base> Elicitation for $range<MIN, MAX> {
2445 type Style = $range_style;
2446
2447 #[tracing::instrument(skip(communicator), fields(type_name = stringify!($range), min = MIN, max = MAX))]
2448 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
2449 tracing::debug!(concat!("Eliciting ", stringify!($range), "<{}, {}> (", stringify!($base), " in range)"), MIN, MAX);
2450
2451 loop {
2452 let value = <$base>::elicit(communicator).await?;
2453
2454 match Self::new(value) {
2455 Ok(ranged) => {
2456 tracing::debug!(value, min = MIN, max = MAX, concat!("Valid ", stringify!($range), " constructed"));
2457 return Ok(ranged);
2458 }
2459 Err(e) => {
2460 tracing::warn!(value, error = %e, min = MIN, max = MAX, concat!("Invalid ", stringify!($range), ", re-prompting"));
2461 }
2462 }
2463 }
2464 }
2465
2466 fn kani_proof() -> proc_macro2::TokenStream {
2467 proc_macro2::TokenStream::new()
2468 }
2469
2470 fn verus_proof() -> proc_macro2::TokenStream {
2471 proc_macro2::TokenStream::new()
2472 }
2473
2474 fn creusot_proof() -> proc_macro2::TokenStream {
2475 proc_macro2::TokenStream::new()
2476 }
2477}
2478
2479 paste::paste! {
2481 #[cfg(test)]
2482 mod [<$nonzero:snake _tests>] {
2483 use super::*;
2484
2485 #[test]
2486 fn [<$nonzero:snake _new_valid>]() {
2487 let result = $nonzero::new($test_nonzero_value);
2488 assert!(result.is_ok());
2489 assert_eq!(result.unwrap().get(), $test_nonzero_value);
2490 }
2491
2492 #[test]
2493 fn [<$nonzero:snake _new_zero_invalid>]() {
2494 let result = $nonzero::new(0);
2495 assert!(result.is_err());
2496 }
2497
2498 #[test]
2499 fn [<$nonzero:snake _into_inner>]() {
2500 let non_zero = $nonzero::new($test_nonzero_value).unwrap();
2501 let value: $base = non_zero.into_inner();
2502 assert_eq!(value, $test_nonzero_value);
2503 }
2504 }
2505
2506 #[cfg(test)]
2507 mod [<$range:snake _tests>] {
2508 use super::*;
2509
2510 #[test]
2511 fn [<$range:snake _new_valid_within_range>]() {
2512 let result = $range::<$test_range_min, $test_range_max>::new($test_range_value);
2513 assert!(result.is_ok());
2514 assert_eq!(result.unwrap().get(), $test_range_value);
2515 }
2516
2517 #[test]
2518 fn [<$range:snake _new_valid_at_min>]() {
2519 let result = $range::<$test_range_min, $test_range_max>::new($test_range_min);
2520 assert!(result.is_ok());
2521 assert_eq!(result.unwrap().get(), $test_range_min);
2522 }
2523
2524 #[test]
2525 fn [<$range:snake _new_valid_at_max>]() {
2526 let result = $range::<$test_range_min, $test_range_max>::new($test_range_max);
2527 assert!(result.is_ok());
2528 assert_eq!(result.unwrap().get(), $test_range_max);
2529 }
2530
2531 #[test]
2532 fn [<$range:snake _new_below_min_invalid>]() {
2533 let result = $range::<$test_range_min, $test_range_max>::new($test_range_min - 1);
2534 assert!(result.is_err());
2535 }
2536
2537 #[test]
2538 fn [<$range:snake _new_above_max_invalid>]() {
2539 let result = $range::<$test_range_min, $test_range_max>::new($test_range_max + 1);
2540 assert!(result.is_err());
2541 }
2542
2543 #[test]
2544 fn [<$range:snake _into_inner>]() {
2545 let ranged = $range::<$test_range_min, $test_range_max>::new($test_range_value).unwrap();
2546 let value: $base = ranged.into_inner();
2547 assert_eq!(value, $test_range_value);
2548 }
2549 }
2550 }
2551 };
2552}
2553
2554impl_signed_contracts!(
2560 i32,
2561 I32Positive,
2562 I32NonNegative,
2563 I32Range,
2564 I32RangeStyle,
2565 42,
2566 100,
2567 10,
2568 100,
2569 50
2570);
2571
2572impl_unsigned_contracts!(u32, U32NonZero, U32Range, U32RangeStyle, 42, 10, 100, 50);
2574
2575impl_integer_serde_bridge!(I32Positive, i32, "Positive i32 value (> 0)", { "minimum": 1 });
2577impl_integer_serde_bridge!(I32NonNegative, i32, "Non-negative i32 value (>= 0)", { "minimum": 0 });
2578impl_integer_range_serde_bridge!(I32Range<i32>);
2579impl_integer_serde_bridge!(I32NonZero, i32, "Non-zero i32 value (!= 0)", { "not": {"const": 0} });
2580impl_integer_serde_bridge!(U32NonZero, u32, "Non-zero u32 value (!= 0)", { "minimum": 1 });
2581impl_integer_range_serde_bridge!(U32Range<u32>);
2582
2583impl_integer_default_wrapper!(i8, I8Default, i8::MIN as i64, i8::MAX as i64);
2589impl_integer_default_wrapper!(i16, I16Default, i16::MIN as i64, i16::MAX as i64);
2590impl_integer_default_wrapper!(i32, I32Default, i32::MIN as i64, i32::MAX as i64);
2591impl_integer_default_wrapper!(i64, I64Default, i64::MIN, i64::MAX);
2592impl_integer_default_wrapper!(i128, I128Default, i64::MIN, i64::MAX); impl_integer_default_wrapper!(isize, IsizeDefault, isize::MIN as i64, isize::MAX as i64);
2594
2595impl_integer_default_wrapper!(u8, U8Default, 0, u8::MAX as i64);
2597impl_integer_default_wrapper!(u16, U16Default, 0, u16::MAX as i64);
2598impl_integer_default_wrapper!(u32, U32Default, 0, u32::MAX as i64);
2599impl_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);
2602
2603impl_signed_contracts!(
2609 i64,
2610 I64Positive,
2611 I64NonNegative,
2612 I64Range,
2613 I64RangeStyle,
2614 42,
2615 100,
2616 10,
2617 100,
2618 50
2619);
2620
2621impl_unsigned_contracts!(u64, U64NonZero, U64Range, U64RangeStyle, 42, 10, 100, 50);
2623
2624impl_integer_serde_bridge!(I64Positive, i64, "Positive i64 value (> 0)", { "minimum": 1 });
2626impl_integer_serde_bridge!(I64NonNegative, i64, "Non-negative i64 value (>= 0)", { "minimum": 0 });
2627impl_integer_range_serde_bridge!(I64Range<i64>);
2628impl_integer_serde_bridge!(I64NonZero, i64, "Non-zero i64 value (!= 0)", { "not": {"const": 0} });
2629impl_integer_serde_bridge!(U64NonZero, u64, "Non-zero u64 value (!= 0)", { "minimum": 1 });
2630impl_integer_range_serde_bridge!(U64Range<u64>);
2631
2632impl_signed_contracts!(
2634 i128,
2635 I128Positive,
2636 I128NonNegative,
2637 I128Range,
2638 I128RangeStyle,
2639 42,
2640 100,
2641 10,
2642 100,
2643 50
2644);
2645
2646impl_unsigned_contracts!(
2648 u128,
2649 U128NonZero,
2650 U128Range,
2651 U128RangeStyle,
2652 42,
2653 10,
2654 100,
2655 50
2656);
2657
2658impl_integer_serde_bridge!(I128Positive, i128, "Positive i128 value (> 0)", { "minimum": 1 });
2660impl_integer_serde_bridge!(I128NonNegative, i128, "Non-negative i128 value (>= 0)", { "minimum": 0 });
2661impl_integer_range_serde_bridge!(I128Range<i128>);
2662impl_integer_serde_bridge!(I128NonZero, i128, "Non-zero i128 value (!= 0)", { "not": {"const": 0} });
2663impl_integer_serde_bridge!(U128NonZero, u128, "Non-zero u128 value (!= 0)", { "minimum": 1 });
2664impl_integer_range_serde_bridge!(U128Range<u128>);
2665
2666impl_signed_contracts!(
2668 isize,
2669 IsizePositive,
2670 IsizeNonNegative,
2671 IsizeRange,
2672 IsizeRangeStyle,
2673 42,
2674 100,
2675 10,
2676 100,
2677 50
2678);
2679
2680impl_unsigned_contracts!(
2682 usize,
2683 UsizeNonZero,
2684 UsizeRange,
2685 UsizeRangeStyle,
2686 42,
2687 10,
2688 100,
2689 50
2690);
2691
2692impl_integer_serde_bridge!(IsizePositive, isize, "Positive isize value (> 0)", { "minimum": 1 });
2694impl_integer_serde_bridge!(IsizeNonNegative, isize, "Non-negative isize value (>= 0)", { "minimum": 0 });
2695impl_integer_range_serde_bridge!(IsizeRange<isize>);
2696impl_integer_serde_bridge!(IsizeNonZero, isize, "Non-zero isize value (!= 0)", { "not": {"const": 0} });
2697impl_integer_serde_bridge!(UsizeNonZero, usize, "Non-zero usize value (!= 0)", { "minimum": 1 });
2698impl_integer_range_serde_bridge!(UsizeRange<usize>);
2699
2700macro_rules! impl_signed_nonzero {
2705 ($base:ty, $nonzero:ident, $test_value:expr) => {
2706 #[doc = concat!("Contract type for non-zero ", stringify!($base), " values (!= 0).")]
2707 #[contract_type(requires = "value != 0", ensures = "result.get() != 0")]
2708 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
2709 pub struct $nonzero($base);
2710
2711 impl $nonzero {
2712 #[spec(requires: [value != 0])]
2718 pub fn new(value: $base) -> Result<Self, ValidationError> {
2719 if value != 0 {
2720 Ok(Self(value))
2721 } else {
2722 Err(ValidationError::Zero)
2723 }
2724 }
2725
2726 pub fn get(&self) -> $base {
2728 self.0
2729 }
2730
2731 pub fn into_inner(self) -> $base {
2733 self.0
2734 }
2735 }
2736
2737 paste::paste! {
2738 crate::default_style!($nonzero => [<$nonzero Style>]);
2739
2740 impl Prompt for $nonzero {
2741 fn prompt() -> Option<&'static str> {
2742 Some("Please enter a non-zero number (!= 0):")
2743 }
2744 }
2745
2746 impl Elicitation for $nonzero {
2747 type Style = [<$nonzero Style>];
2748
2749 #[tracing::instrument(skip(communicator), fields(type_name = stringify!($nonzero)))]
2750 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
2751 loop {
2752 let value = <$base>::elicit(communicator).await?;
2753 match Self::new(value) {
2754 Ok(v) => return Ok(v),
2755 Err(e) => tracing::warn!(error = %e, "Invalid, re-prompting"),
2756 }
2757 }
2758 }
2759
2760 fn kani_proof() -> proc_macro2::TokenStream {
2761 crate::verification::proof_helpers::kani_numeric_nonzero(stringify!($nonzero), stringify!($base))
2762 }
2763
2764 fn verus_proof() -> proc_macro2::TokenStream {
2765 crate::verification::proof_helpers::verus_numeric_nonzero(stringify!($nonzero), stringify!($base))
2766 }
2767
2768 fn creusot_proof() -> proc_macro2::TokenStream {
2769 crate::verification::proof_helpers::creusot_numeric_nonzero(stringify!($nonzero), stringify!($base))
2770 }
2771
2772 }
2773 }
2774 };
2775}
2776
2777impl_signed_nonzero!(i32, I32NonZero, 42);
2779impl_signed_nonzero!(i64, I64NonZero, 42);
2780impl_signed_nonzero!(i128, I128NonZero, 42);
2781impl_signed_nonzero!(isize, IsizeNonZero, 42);
2782
2783macro_rules! impl_unsigned_positive {
2788 ($base:ty, $positive:ident, $test_value:expr) => {
2789 #[doc = concat!("Contract type for positive ", stringify!($base), " values (> 0).")]
2790 #[contract_type(requires = "value > 0", ensures = "result.get() > 0")]
2791 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
2792 pub struct $positive($base);
2793
2794 impl $positive {
2795 #[spec(requires: [value > 0])]
2801 pub fn new(value: $base) -> Result<Self, ValidationError> {
2802 if value > 0 {
2803 Ok(Self(value))
2804 } else {
2805 Err(ValidationError::NotPositive(value as i128))
2806 }
2807 }
2808
2809 pub fn get(&self) -> $base {
2811 self.0
2812 }
2813
2814 pub fn into_inner(self) -> $base {
2816 self.0
2817 }
2818 }
2819
2820 paste::paste! {
2821 crate::default_style!($positive => [<$positive Style>]);
2822
2823 impl Prompt for $positive {
2824 fn prompt() -> Option<&'static str> {
2825 Some("Please enter a positive number (> 0):")
2826 }
2827 }
2828
2829 impl Elicitation for $positive {
2830 type Style = [<$positive Style>];
2831
2832 #[tracing::instrument(skip(communicator), fields(type_name = stringify!($positive)))]
2833 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
2834 loop {
2835 let value = <$base>::elicit(communicator).await?;
2836 match Self::new(value) {
2837 Ok(v) => return Ok(v),
2838 Err(e) => tracing::warn!(error = %e, "Invalid, re-prompting"),
2839 }
2840 }
2841 }
2842
2843 fn kani_proof() -> proc_macro2::TokenStream {
2844 crate::verification::proof_helpers::kani_numeric_positive(stringify!($positive), stringify!($base))
2845 }
2846
2847 fn verus_proof() -> proc_macro2::TokenStream {
2848 crate::verification::proof_helpers::verus_numeric_positive(stringify!($positive), stringify!($base))
2849 }
2850
2851 fn creusot_proof() -> proc_macro2::TokenStream {
2852 crate::verification::proof_helpers::creusot_numeric_positive(stringify!($positive), stringify!($base))
2853 }
2854
2855 }
2856 }
2857 };
2858}
2859
2860impl_unsigned_positive!(u32, U32Positive, 42);
2862impl_unsigned_positive!(u64, U64Positive, 42);
2863impl_unsigned_positive!(u128, U128Positive, 42);
2864impl_unsigned_positive!(usize, UsizePositive, 42);
2865
2866impl_integer_serde_bridge!(U32Positive, u32, "Positive u32 value (> 0)", { "minimum": 1 });
2867impl_integer_serde_bridge!(U64Positive, u64, "Positive u64 value (> 0)", { "minimum": 1 });
2868impl_integer_serde_bridge!(U128Positive, u128, "Positive u128 value (> 0)", { "minimum": 1 });
2869impl_integer_serde_bridge!(UsizePositive, usize, "Positive usize value (> 0)", { "minimum": 1 });
2870
2871mod emit_impls {
2878 use super::*;
2879 use crate::emit_code::ToCodeLiteral;
2880 use proc_macro2::TokenStream;
2881
2882 macro_rules! impl_to_code_literal_int {
2884 ($T:ident) => {
2885 impl ToCodeLiteral for $T {
2886 fn to_code_literal(&self) -> TokenStream {
2887 let v = self.get();
2888 let msg = concat!("valid ", stringify!($T));
2889 let type_path: TokenStream = concat!("elicitation::", stringify!($T))
2890 .parse()
2891 .expect("valid type path");
2892 quote::quote! { #type_path::new(#v).expect(#msg) }
2893 }
2894 }
2895 };
2896 }
2897
2898 macro_rules! impl_to_code_literal_range {
2900 ($T:ident, $prim:ty) => {
2901 impl<const MIN: $prim, const MAX: $prim> ToCodeLiteral for $T<MIN, MAX> {
2902 fn to_code_literal(&self) -> TokenStream {
2903 let v = self.get();
2904 let msg = concat!("valid ", stringify!($T));
2905 let type_path: TokenStream = concat!("elicitation::", stringify!($T))
2906 .parse()
2907 .expect("valid type path");
2908 quote::quote! { #type_path::<#MIN, #MAX>::new(#v).expect(#msg) }
2909 }
2910 }
2911 };
2912 }
2913
2914 impl_to_code_literal_int!(I8Positive);
2916 impl_to_code_literal_int!(I8NonNegative);
2917 impl_to_code_literal_int!(I8NonZero);
2918 impl_to_code_literal_range!(I8Range, i8);
2919 impl_to_code_literal_int!(I16Positive);
2921 impl_to_code_literal_int!(I16NonNegative);
2922 impl_to_code_literal_int!(I16NonZero);
2923 impl_to_code_literal_range!(I16Range, i16);
2924 impl_to_code_literal_int!(I32Positive);
2926 impl_to_code_literal_int!(I32NonNegative);
2927 impl_to_code_literal_int!(I32NonZero);
2928 impl_to_code_literal_range!(I32Range, i32);
2929 impl_to_code_literal_int!(I64Positive);
2931 impl_to_code_literal_int!(I64NonNegative);
2932 impl_to_code_literal_int!(I64NonZero);
2933 impl_to_code_literal_range!(I64Range, i64);
2934 impl_to_code_literal_int!(I128Positive);
2936 impl_to_code_literal_int!(I128NonNegative);
2937 impl_to_code_literal_int!(I128NonZero);
2938 impl_to_code_literal_range!(I128Range, i128);
2939 impl_to_code_literal_int!(IsizePositive);
2941 impl_to_code_literal_int!(IsizeNonNegative);
2942 impl_to_code_literal_int!(IsizeNonZero);
2943 impl_to_code_literal_range!(IsizeRange, isize);
2944 impl_to_code_literal_int!(U8NonZero);
2946 impl_to_code_literal_int!(U8Positive);
2947 impl_to_code_literal_range!(U8Range, u8);
2948 impl_to_code_literal_int!(U16NonZero);
2950 impl_to_code_literal_int!(U16Positive);
2951 impl_to_code_literal_range!(U16Range, u16);
2952 impl_to_code_literal_int!(U32NonZero);
2954 impl_to_code_literal_int!(U32Positive);
2955 impl_to_code_literal_range!(U32Range, u32);
2956 impl_to_code_literal_int!(U64NonZero);
2958 impl_to_code_literal_int!(U64Positive);
2959 impl_to_code_literal_range!(U64Range, u64);
2960 impl_to_code_literal_int!(U128NonZero);
2962 impl_to_code_literal_int!(U128Positive);
2963 impl_to_code_literal_range!(U128Range, u128);
2964 impl_to_code_literal_int!(UsizeNonZero);
2966 impl_to_code_literal_int!(UsizePositive);
2967 impl_to_code_literal_range!(UsizeRange, usize);
2968}
2969
2970macro_rules! impl_primitive_introspect {
2973 ($($ty:ty => $name:literal),+ $(,)?) => {
2974 $(
2975 impl crate::ElicitIntrospect for $ty {
2976 fn pattern() -> crate::ElicitationPattern {
2977 crate::ElicitationPattern::Primitive
2978 }
2979 fn metadata() -> crate::TypeMetadata {
2980 crate::TypeMetadata {
2981 type_name: $name,
2982 description: <$ty as crate::Prompt>::prompt(),
2983 details: crate::PatternDetails::Primitive,
2984 }
2985 }
2986 }
2987 )+
2988 };
2989}
2990
2991impl_primitive_introspect!(
2992 I8Positive => "I8Positive",
2993 I8NonNegative => "I8NonNegative",
2994 I8NonZero => "I8NonZero",
2995 I16Positive => "I16Positive",
2996 I16NonNegative => "I16NonNegative",
2997 I16NonZero => "I16NonZero",
2998 I32Positive => "I32Positive",
2999 I32NonNegative => "I32NonNegative",
3000 I32NonZero => "I32NonZero",
3001 I64Positive => "I64Positive",
3002 I64NonNegative => "I64NonNegative",
3003 I64NonZero => "I64NonZero",
3004 I128Positive => "I128Positive",
3005 I128NonNegative => "I128NonNegative",
3006 I128NonZero => "I128NonZero",
3007 IsizePositive => "IsizePositive",
3008 IsizeNonNegative => "IsizeNonNegative",
3009 IsizeNonZero => "IsizeNonZero",
3010 U8NonZero => "U8NonZero",
3011 U8Positive => "U8Positive",
3012 U16NonZero => "U16NonZero",
3013 U16Positive => "U16Positive",
3014 U32NonZero => "U32NonZero",
3015 U32Positive => "U32Positive",
3016 U64NonZero => "U64NonZero",
3017 U64Positive => "U64Positive",
3018 U128NonZero => "U128NonZero",
3019 U128Positive => "U128Positive",
3020 UsizeNonZero => "UsizeNonZero",
3021 UsizePositive => "UsizePositive",
3022);