use super::ValidationError;
use crate::{ElicitCommunicator, ElicitResult, Elicitation, Prompt};
use std::rc::Rc;
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VecNonEmpty<T>(Vec<T>);
impl<T> VecNonEmpty<T> {
pub fn new(vec: Vec<T>) -> Result<Self, ValidationError> {
if vec.is_empty() {
Err(ValidationError::EmptyCollection)
} else {
Ok(Self(vec))
}
}
pub fn get(&self) -> &Vec<T> {
&self.0
}
pub fn into_inner(self) -> Vec<T> {
self.0
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
false
}
}
impl<T: Elicitation + Send> Prompt for VecNonEmpty<T> {
fn prompt() -> Option<&'static str> {
Some("Please provide a non-empty list:")
}
}
impl<T: Elicitation + Send> Elicitation for VecNonEmpty<T> {
type Style = <Vec<T> as Elicitation>::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting VecNonEmpty");
loop {
let vec = Vec::<T>::elicit(communicator).await?;
match Self::new(vec) {
Ok(valid) => {
tracing::debug!(count = valid.len(), "Valid non-empty vec");
return Ok(valid);
}
Err(e) => {
tracing::warn!(error = %e, "Vec is empty, re-prompting");
continue;
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VecAllSatisfy<C>(Vec<C>);
impl<C> VecAllSatisfy<C> {
pub fn new(elements: Vec<C>) -> Self {
Self(elements)
}
pub fn get(&self) -> &Vec<C> {
&self.0
}
pub fn into_inner(self) -> Vec<C> {
self.0
}
}
impl<C: Elicitation + Send> Prompt for VecAllSatisfy<C> {
fn prompt() -> Option<&'static str> {
Some("Please provide a list:")
}
}
impl<C: Elicitation + Send> Elicitation for VecAllSatisfy<C> {
type Style = <Vec<C> as Elicitation>::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting VecAllSatisfy");
let elements = Vec::<C>::elicit(communicator).await?;
tracing::debug!(count = elements.len(), "All elements satisfy contract");
Ok(Self::new(elements)) }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct OptionSome<T>(T);
impl<T> OptionSome<T> {
pub fn new(opt: Option<T>) -> Result<Self, ValidationError> {
match opt {
Some(value) => Ok(Self(value)),
None => Err(ValidationError::OptionIsNone),
}
}
pub fn from_value(value: T) -> Self {
Self(value)
}
pub fn get(&self) -> &T {
&self.0
}
pub fn into_inner(self) -> T {
self.0
}
}
impl<T: Elicitation + Send> Prompt for OptionSome<T> {
fn prompt() -> Option<&'static str> {
Some("Please provide a value (required, not optional):")
}
}
impl<T: Elicitation + Send> Elicitation for OptionSome<T> {
type Style = <Option<T> as Elicitation>::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting OptionSome");
loop {
let opt = Option::<T>::elicit(communicator).await?;
match Self::new(opt) {
Ok(valid) => {
tracing::debug!("Valid Some value");
return Ok(valid);
}
Err(e) => {
tracing::warn!(error = %e, "Option is None, re-prompting");
continue;
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ResultOk<T>(T);
impl<T> ResultOk<T> {
pub fn new<E>(result: Result<T, E>) -> Result<Self, ValidationError> {
match result {
Ok(value) => Ok(Self(value)),
Err(_) => Err(ValidationError::ResultIsErr),
}
}
pub fn from_value(value: T) -> Self {
Self(value)
}
pub fn get(&self) -> &T {
&self.0
}
pub fn into_inner(self) -> T {
self.0
}
}
impl<T: Elicitation + Send> Prompt for ResultOk<T> {
fn prompt() -> Option<&'static str> {
Some("Please provide a successful value:")
}
}
impl<T: Elicitation + Send> Elicitation for ResultOk<T> {
type Style = <T as Elicitation>::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting ResultOk");
let value = T::elicit(communicator).await?;
Ok(Self::from_value(value))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct BoxSatisfies<C>(Box<C>);
impl<C> BoxSatisfies<C> {
pub fn new(value: C) -> Self {
Self(Box::new(value))
}
pub fn get(&self) -> &C {
&self.0
}
pub fn into_inner(self) -> Box<C> {
self.0
}
}
impl<C: Elicitation + Send> Prompt for BoxSatisfies<C> {
fn prompt() -> Option<&'static str> {
C::prompt()
}
}
impl<C: Elicitation + Send> Elicitation for BoxSatisfies<C> {
type Style = C::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting BoxSatisfies");
let value = C::elicit(communicator).await?; Ok(Self::new(value))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ArcSatisfies<C>(Arc<C>);
impl<C> ArcSatisfies<C> {
pub fn new(value: C) -> Self {
Self(Arc::new(value))
}
pub fn get(&self) -> &C {
&self.0
}
pub fn into_inner(self) -> Arc<C> {
self.0
}
}
impl<C: Elicitation + Send> Prompt for ArcSatisfies<C> {
fn prompt() -> Option<&'static str> {
C::prompt()
}
}
impl<C: Elicitation + Send> Elicitation for ArcSatisfies<C> {
type Style = C::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting ArcSatisfies");
let value = C::elicit(communicator).await?; Ok(Self::new(value))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RcSatisfies<C>(Rc<C>);
impl<C> RcSatisfies<C> {
pub fn new(value: C) -> Self {
Self(Rc::new(value))
}
pub fn get(&self) -> &C {
&self.0
}
pub fn into_inner(self) -> Rc<C> {
self.0
}
}
impl<C: Elicitation + Send> Prompt for RcSatisfies<C> {
fn prompt() -> Option<&'static str> {
C::prompt()
}
}
impl<C: Elicitation + Send> Elicitation for RcSatisfies<C> {
type Style = C::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting RcSatisfies");
let value = C::elicit(communicator).await?; Ok(Self::new(value))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::verification::types::{I8Positive, StringNonEmpty};
#[test]
fn test_vec_non_empty_valid() {
let vec = vec![1, 2, 3];
let result = VecNonEmpty::new(vec);
assert!(result.is_ok());
assert_eq!(result.unwrap().len(), 3);
}
#[test]
fn test_vec_non_empty_empty() {
let vec: Vec<i32> = vec![];
let result = VecNonEmpty::new(vec);
assert!(result.is_err());
}
#[test]
fn test_vec_all_satisfy() {
let elements = vec![
I8Positive::new(1).unwrap(),
I8Positive::new(2).unwrap(),
I8Positive::new(3).unwrap(),
];
let vec = VecAllSatisfy::new(elements);
assert_eq!(vec.get().len(), 3);
}
#[test]
fn test_option_some_valid() {
let opt = Some(42);
let result = OptionSome::new(opt);
assert!(result.is_ok());
assert_eq!(*result.unwrap().get(), 42);
}
#[test]
fn test_option_some_none() {
let opt: Option<i32> = None;
let result = OptionSome::new(opt);
assert!(result.is_err());
}
#[test]
fn test_result_ok_valid() {
let res: Result<i32, String> = Ok(42);
let result = ResultOk::new(res);
assert!(result.is_ok());
assert_eq!(*result.unwrap().get(), 42);
}
#[test]
fn test_result_ok_err() {
let res: Result<i32, String> = Err("failed".to_string());
let result = ResultOk::<i32>::new(res);
assert!(result.is_err());
}
#[test]
fn test_box_satisfies() {
let value = I8Positive::new(5).unwrap();
let boxed = BoxSatisfies::new(value);
assert_eq!(boxed.get().get(), 5);
}
#[test]
fn test_arc_satisfies() {
let value: StringNonEmpty = StringNonEmpty::new("test".to_string()).unwrap();
let arc = ArcSatisfies::new(value);
assert_eq!(arc.get().get(), "test");
}
#[test]
fn test_rc_satisfies() {
let value = I8Positive::new(10).unwrap();
let rc = RcSatisfies::new(value);
assert_eq!(rc.get().get(), 10);
}
}
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, LinkedList, VecDeque};
use std::hash::Hash;
#[derive(Debug, Clone)]
#[cfg(not(kani))]
pub struct HashMapNonEmpty<K, V>(HashMap<K, V>);
#[cfg(kani)]
pub struct HashMapNonEmpty<K, V>(std::marker::PhantomData<(K, V)>);
impl<K, V> HashMapNonEmpty<K, V> {
#[cfg(not(kani))]
pub fn new(map: HashMap<K, V>) -> Result<Self, ValidationError> {
if map.is_empty() {
Err(ValidationError::EmptyCollection)
} else {
Ok(Self(map))
}
}
#[cfg(kani)]
pub fn new(_map: HashMap<K, V>) -> Result<Self, ValidationError> {
let is_empty: bool = kani::any();
if is_empty {
Err(ValidationError::EmptyCollection)
} else {
Ok(Self(std::marker::PhantomData))
}
}
#[cfg(not(kani))]
pub fn get(&self) -> &HashMap<K, V> {
&self.0
}
#[cfg(kani)]
pub fn get(&self) -> &HashMap<K, V> {
panic!("get() not supported in Kani verification")
}
#[cfg(not(kani))]
pub fn into_inner(self) -> HashMap<K, V> {
self.0
}
#[cfg(kani)]
pub fn into_inner(self) -> HashMap<K, V> {
panic!("into_inner() not supported in Kani verification")
}
#[cfg(not(kani))]
pub fn len(&self) -> usize {
self.0.len()
}
#[cfg(not(kani))]
pub fn is_empty(&self) -> bool {
false
}
#[cfg(kani)]
pub fn len(&self) -> usize {
panic!("len() not supported in Kani verification")
}
#[cfg(kani)]
pub fn is_empty(&self) -> bool {
panic!("is_empty() not supported in Kani verification")
}
}
impl<K, V> Prompt for HashMapNonEmpty<K, V>
where
K: Elicitation + Send,
V: Elicitation + Send,
{
fn prompt() -> Option<&'static str> {
Some("Please provide a non-empty map:")
}
}
impl<K, V> Elicitation for HashMapNonEmpty<K, V>
where
K: Elicitation + Eq + Hash + Send,
V: Elicitation + Send,
{
type Style = <HashMap<K, V> as Elicitation>::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting HashMapNonEmpty");
loop {
let map = HashMap::<K, V>::elicit(communicator).await?;
match Self::new(map) {
Ok(valid) => {
tracing::debug!(count = valid.len(), "Valid non-empty map");
return Ok(valid);
}
Err(e) => {
tracing::warn!(error = %e, "HashMap is empty, re-prompting");
continue;
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg(not(kani))]
pub struct BTreeMapNonEmpty<K, V>(BTreeMap<K, V>);
#[cfg(kani)]
pub struct BTreeMapNonEmpty<K, V>(std::marker::PhantomData<(K, V)>);
impl<K, V> BTreeMapNonEmpty<K, V> {
#[cfg(not(kani))]
pub fn new(map: BTreeMap<K, V>) -> Result<Self, ValidationError> {
if map.is_empty() {
Err(ValidationError::EmptyCollection)
} else {
Ok(Self(map))
}
}
#[cfg(kani)]
pub fn new(_map: BTreeMap<K, V>) -> Result<Self, ValidationError> {
let is_empty: bool = kani::any();
if is_empty {
Err(ValidationError::EmptyCollection)
} else {
Ok(Self(std::marker::PhantomData))
}
}
#[cfg(not(kani))]
pub fn get(&self) -> &BTreeMap<K, V> {
&self.0
}
#[cfg(kani)]
pub fn get(&self) -> &BTreeMap<K, V> {
panic!("get() not supported in Kani verification")
}
#[cfg(not(kani))]
pub fn into_inner(self) -> BTreeMap<K, V> {
self.0
}
#[cfg(kani)]
pub fn into_inner(self) -> BTreeMap<K, V> {
panic!("into_inner() not supported in Kani verification")
}
#[cfg(not(kani))]
pub fn len(&self) -> usize {
self.0.len()
}
#[cfg(not(kani))]
pub fn is_empty(&self) -> bool {
false
}
#[cfg(kani)]
pub fn len(&self) -> usize {
panic!("len() not supported in Kani verification")
}
#[cfg(kani)]
pub fn is_empty(&self) -> bool {
panic!("is_empty() not supported in Kani verification")
}
}
impl<K, V> Prompt for BTreeMapNonEmpty<K, V>
where
K: Elicitation + Send,
V: Elicitation + Send,
{
fn prompt() -> Option<&'static str> {
Some("Please provide a non-empty map:")
}
}
impl<K, V> Elicitation for BTreeMapNonEmpty<K, V>
where
K: Elicitation + Ord + Send,
V: Elicitation + Send,
{
type Style = <BTreeMap<K, V> as Elicitation>::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting BTreeMapNonEmpty");
loop {
let map = BTreeMap::<K, V>::elicit(communicator).await?;
match Self::new(map) {
Ok(valid) => {
tracing::debug!(count = valid.len(), "Valid non-empty btree map");
return Ok(valid);
}
Err(e) => {
tracing::warn!(error = %e, "BTreeMap is empty, re-prompting");
continue;
}
}
}
}
}
#[derive(Debug, Clone)]
#[cfg(not(kani))]
pub struct HashSetNonEmpty<T>(HashSet<T>);
#[cfg(kani)]
pub struct HashSetNonEmpty<T>(std::marker::PhantomData<T>);
impl<T> HashSetNonEmpty<T> {
#[cfg(not(kani))]
pub fn new(set: HashSet<T>) -> Result<Self, ValidationError> {
if set.is_empty() {
Err(ValidationError::EmptyCollection)
} else {
Ok(Self(set))
}
}
#[cfg(kani)]
pub fn new(_set: HashSet<T>) -> Result<Self, ValidationError> {
let is_empty: bool = kani::any();
if is_empty {
Err(ValidationError::EmptyCollection)
} else {
Ok(Self(std::marker::PhantomData))
}
}
#[cfg(not(kani))]
pub fn get(&self) -> &HashSet<T> {
&self.0
}
#[cfg(kani)]
pub fn get(&self) -> &HashSet<T> {
panic!("get() not supported in Kani verification")
}
#[cfg(not(kani))]
pub fn into_inner(self) -> HashSet<T> {
self.0
}
#[cfg(kani)]
pub fn into_inner(self) -> HashSet<T> {
panic!("into_inner() not supported in Kani verification")
}
#[cfg(not(kani))]
pub fn len(&self) -> usize {
self.0.len()
}
#[cfg(not(kani))]
pub fn is_empty(&self) -> bool {
false
}
#[cfg(kani)]
pub fn len(&self) -> usize {
panic!("len() not supported in Kani verification")
}
#[cfg(kani)]
pub fn is_empty(&self) -> bool {
panic!("is_empty() not supported in Kani verification")
}
}
impl<T> Prompt for HashSetNonEmpty<T>
where
T: Elicitation + Send,
{
fn prompt() -> Option<&'static str> {
Some("Please provide a non-empty set:")
}
}
impl<T> Elicitation for HashSetNonEmpty<T>
where
T: Elicitation + Eq + Hash + Send,
{
type Style = <HashSet<T> as Elicitation>::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting HashSetNonEmpty");
loop {
let set = HashSet::<T>::elicit(communicator).await?;
match Self::new(set) {
Ok(valid) => {
tracing::debug!(count = valid.len(), "Valid non-empty set");
return Ok(valid);
}
Err(e) => {
tracing::warn!(error = %e, "HashSet is empty, re-prompting");
continue;
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg(not(kani))]
pub struct BTreeSetNonEmpty<T>(BTreeSet<T>);
#[cfg(kani)]
pub struct BTreeSetNonEmpty<T>(std::marker::PhantomData<T>);
impl<T> BTreeSetNonEmpty<T> {
#[cfg(not(kani))]
pub fn new(set: BTreeSet<T>) -> Result<Self, ValidationError> {
if set.is_empty() {
Err(ValidationError::EmptyCollection)
} else {
Ok(Self(set))
}
}
#[cfg(kani)]
pub fn new(_set: BTreeSet<T>) -> Result<Self, ValidationError> {
let is_empty: bool = kani::any();
if is_empty {
Err(ValidationError::EmptyCollection)
} else {
Ok(Self(std::marker::PhantomData))
}
}
#[cfg(not(kani))]
pub fn get(&self) -> &BTreeSet<T> {
&self.0
}
#[cfg(kani)]
pub fn get(&self) -> &BTreeSet<T> {
panic!("get() not supported in Kani verification")
}
#[cfg(not(kani))]
pub fn into_inner(self) -> BTreeSet<T> {
self.0
}
#[cfg(kani)]
pub fn into_inner(self) -> BTreeSet<T> {
panic!("into_inner() not supported in Kani verification")
}
#[cfg(not(kani))]
pub fn len(&self) -> usize {
self.0.len()
}
#[cfg(not(kani))]
pub fn is_empty(&self) -> bool {
false
}
#[cfg(kani)]
pub fn len(&self) -> usize {
panic!("len() not supported in Kani verification")
}
#[cfg(kani)]
pub fn is_empty(&self) -> bool {
panic!("is_empty() not supported in Kani verification")
}
}
impl<T> Prompt for BTreeSetNonEmpty<T>
where
T: Elicitation + Send,
{
fn prompt() -> Option<&'static str> {
Some("Please provide a non-empty set:")
}
}
impl<T> Elicitation for BTreeSetNonEmpty<T>
where
T: Elicitation + Ord + Send,
{
type Style = <BTreeSet<T> as Elicitation>::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting BTreeSetNonEmpty");
loop {
let set = BTreeSet::<T>::elicit(communicator).await?;
match Self::new(set) {
Ok(valid) => {
tracing::debug!(count = valid.len(), "Valid non-empty btree set");
return Ok(valid);
}
Err(e) => {
tracing::warn!(error = %e, "BTreeSet is empty, re-prompting");
continue;
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg(not(kani))]
pub struct VecDequeNonEmpty<T>(VecDeque<T>);
#[cfg(kani)]
pub struct VecDequeNonEmpty<T>(std::marker::PhantomData<T>);
impl<T> VecDequeNonEmpty<T> {
#[cfg(not(kani))]
pub fn new(deque: VecDeque<T>) -> Result<Self, ValidationError> {
if deque.is_empty() {
Err(ValidationError::EmptyCollection)
} else {
Ok(Self(deque))
}
}
#[cfg(kani)]
pub fn new(_deque: VecDeque<T>) -> Result<Self, ValidationError> {
let is_empty: bool = kani::any();
if is_empty {
Err(ValidationError::EmptyCollection)
} else {
Ok(Self(std::marker::PhantomData))
}
}
#[cfg(not(kani))]
pub fn get(&self) -> &VecDeque<T> {
&self.0
}
#[cfg(kani)]
pub fn get(&self) -> &VecDeque<T> {
panic!("get() not supported in Kani verification")
}
#[cfg(not(kani))]
pub fn into_inner(self) -> VecDeque<T> {
self.0
}
#[cfg(kani)]
pub fn into_inner(self) -> VecDeque<T> {
panic!("into_inner() not supported in Kani verification")
}
#[cfg(not(kani))]
pub fn len(&self) -> usize {
self.0.len()
}
#[cfg(not(kani))]
pub fn is_empty(&self) -> bool {
false
}
#[cfg(kani)]
pub fn len(&self) -> usize {
panic!("len() not supported in Kani verification")
}
#[cfg(kani)]
pub fn is_empty(&self) -> bool {
panic!("is_empty() not supported in Kani verification")
}
}
impl<T> Prompt for VecDequeNonEmpty<T>
where
T: Elicitation + Send,
{
fn prompt() -> Option<&'static str> {
Some("Please provide a non-empty deque:")
}
}
impl<T> Elicitation for VecDequeNonEmpty<T>
where
T: Elicitation + Send,
{
type Style = <VecDeque<T> as Elicitation>::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting VecDequeNonEmpty");
loop {
let deque = VecDeque::<T>::elicit(communicator).await?;
match Self::new(deque) {
Ok(valid) => {
tracing::debug!(count = valid.len(), "Valid non-empty deque");
return Ok(valid);
}
Err(e) => {
tracing::warn!(error = %e, "VecDeque is empty, re-prompting");
continue;
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg(not(kani))]
pub struct LinkedListNonEmpty<T>(LinkedList<T>);
#[cfg(kani)]
pub struct LinkedListNonEmpty<T>(std::marker::PhantomData<T>);
impl<T> LinkedListNonEmpty<T> {
#[cfg(not(kani))]
pub fn new(list: LinkedList<T>) -> Result<Self, ValidationError> {
if list.is_empty() {
Err(ValidationError::EmptyCollection)
} else {
Ok(Self(list))
}
}
#[cfg(kani)]
pub fn new(_list: LinkedList<T>) -> Result<Self, ValidationError> {
let is_empty: bool = kani::any();
if is_empty {
Err(ValidationError::EmptyCollection)
} else {
Ok(Self(std::marker::PhantomData))
}
}
#[cfg(not(kani))]
pub fn get(&self) -> &LinkedList<T> {
&self.0
}
#[cfg(kani)]
pub fn get(&self) -> &LinkedList<T> {
panic!("get() not supported in Kani verification")
}
#[cfg(not(kani))]
pub fn into_inner(self) -> LinkedList<T> {
self.0
}
#[cfg(kani)]
pub fn into_inner(self) -> LinkedList<T> {
panic!("into_inner() not supported in Kani verification")
}
#[cfg(not(kani))]
pub fn len(&self) -> usize {
self.0.len()
}
#[cfg(not(kani))]
pub fn is_empty(&self) -> bool {
false
}
#[cfg(kani)]
pub fn len(&self) -> usize {
panic!("len() not supported in Kani verification")
}
#[cfg(kani)]
pub fn is_empty(&self) -> bool {
panic!("is_empty() not supported in Kani verification")
}
}
impl<T> Prompt for LinkedListNonEmpty<T>
where
T: Elicitation + Send,
{
fn prompt() -> Option<&'static str> {
Some("Please provide a non-empty list:")
}
}
impl<T> Elicitation for LinkedListNonEmpty<T>
where
T: Elicitation + Send,
{
type Style = <LinkedList<T> as Elicitation>::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting LinkedListNonEmpty");
loop {
let list = LinkedList::<T>::elicit(communicator).await?;
match Self::new(list) {
Ok(valid) => {
tracing::debug!(count = valid.len(), "Valid non-empty linked list");
return Ok(valid);
}
Err(e) => {
tracing::warn!(error = %e, "LinkedList is empty, re-prompting");
continue;
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ArrayAllSatisfy<C, const N: usize>([C; N]);
impl<C, const N: usize> ArrayAllSatisfy<C, N> {
pub fn new(elements: [C; N]) -> Self {
Self(elements)
}
pub fn get(&self) -> &[C; N] {
&self.0
}
pub fn into_inner(self) -> [C; N] {
self.0
}
}
impl<C, const N: usize> Prompt for ArrayAllSatisfy<C, N>
where
C: Elicitation + Send,
{
fn prompt() -> Option<&'static str> {
Some("Please provide array elements:")
}
}
impl<C, const N: usize> Elicitation for ArrayAllSatisfy<C, N>
where
C: Elicitation + Send,
{
type Style = <[C; N] as Elicitation>::Style;
#[tracing::instrument(skip(communicator), fields(array_size = N))]
async fn elicit<Comm: ElicitCommunicator>(communicator: &Comm) -> ElicitResult<Self> {
tracing::debug!("Eliciting ArrayAllSatisfy");
let elements = <[C; N]>::elicit(communicator).await?;
tracing::debug!(size = N, "All array elements satisfy contract");
Ok(Self::new(elements))
}
}
#[cfg(test)]
mod extended_tests {
use super::*;
use crate::verification::types::I8Positive;
#[test]
fn test_hashmap_non_empty_valid() {
let mut map = HashMap::new();
map.insert("key", 42);
let result = HashMapNonEmpty::new(map);
assert!(result.is_ok());
}
#[test]
fn test_hashmap_non_empty_empty() {
let map: HashMap<&str, i32> = HashMap::new();
let result = HashMapNonEmpty::new(map);
assert!(result.is_err());
}
#[test]
fn test_hashset_non_empty_valid() {
let mut set = HashSet::new();
set.insert(1);
let result = HashSetNonEmpty::new(set);
assert!(result.is_ok());
}
#[test]
fn test_btreemap_non_empty_valid() {
let mut map = BTreeMap::new();
map.insert(1, "value");
let result = BTreeMapNonEmpty::new(map);
assert!(result.is_ok());
}
#[test]
fn test_btreeset_non_empty_valid() {
let mut set = BTreeSet::new();
set.insert(1);
let result = BTreeSetNonEmpty::new(set);
assert!(result.is_ok());
}
#[test]
fn test_vecdeque_non_empty_valid() {
let mut deque = VecDeque::new();
deque.push_back(1);
let result = VecDequeNonEmpty::new(deque);
assert!(result.is_ok());
}
#[test]
fn test_linkedlist_non_empty_valid() {
let mut list = LinkedList::new();
list.push_back(1);
let result = LinkedListNonEmpty::new(list);
assert!(result.is_ok());
}
#[test]
fn test_array_all_satisfy() {
let elements = [
I8Positive::new(1).unwrap(),
I8Positive::new(2).unwrap(),
I8Positive::new(3).unwrap(),
];
let array = ArrayAllSatisfy::new(elements);
assert_eq!(array.get().len(), 3);
}
}
#[derive(Debug, Clone)]
pub struct BoxNonNull<T>(Box<T>);
impl<T> BoxNonNull<T> {
pub fn new(value: Box<T>) -> Result<Self, super::ValidationError> {
Ok(Self(value))
}
pub fn get(&self) -> &T {
&self.0
}
pub fn into_inner(self) -> Box<T> {
self.0
}
}
#[derive(Debug, Clone)]
pub struct ArcNonNull<T>(std::sync::Arc<T>);
impl<T> ArcNonNull<T> {
pub fn new(value: std::sync::Arc<T>) -> Result<Self, super::ValidationError> {
Ok(Self(value))
}
pub fn get(&self) -> &T {
&self.0
}
pub fn into_inner(self) -> std::sync::Arc<T> {
self.0
}
}
#[derive(Debug, Clone)]
pub struct RcNonNull<T>(std::rc::Rc<T>);
impl<T> RcNonNull<T> {
pub fn new(value: std::rc::Rc<T>) -> Result<Self, super::ValidationError> {
Ok(Self(value))
}
pub fn get(&self) -> &T {
&self.0
}
pub fn into_inner(self) -> std::rc::Rc<T> {
self.0
}
}