use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ElementType {
Objects,
Arrays,
Strings,
Numbers,
Booleans,
Nulls,
Mixed,
Empty,
}
impl fmt::Display for ElementType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ElementType::Objects => write!(f, "objects"),
ElementType::Arrays => write!(f, "arrays"),
ElementType::Strings => write!(f, "strings"),
ElementType::Numbers => write!(f, "numbers"),
ElementType::Booleans => write!(f, "booleans"),
ElementType::Nulls => write!(f, "nulls"),
ElementType::Mixed => write!(f, "mixed"),
ElementType::Empty => write!(f, ""),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResultStats {
Array {
count: usize,
element_type: ElementType,
},
Object,
String,
Number,
Boolean,
Null,
Stream { count: usize },
}
impl fmt::Display for ResultStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ResultStats::Array {
count,
element_type,
} => match element_type {
ElementType::Empty => write!(f, "Array [0]"),
_ => write!(f, "Array [{} {}]", count, element_type),
},
ResultStats::Object => write!(f, "Object"),
ResultStats::String => write!(f, "String"),
ResultStats::Number => write!(f, "Number"),
ResultStats::Boolean => write!(f, "Boolean"),
ResultStats::Null => write!(f, "null"),
ResultStats::Stream { count } => write!(f, "Stream [{}]", count),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn test_element_type_display() {
assert_eq!(ElementType::Objects.to_string(), "objects");
assert_eq!(ElementType::Arrays.to_string(), "arrays");
assert_eq!(ElementType::Strings.to_string(), "strings");
assert_eq!(ElementType::Numbers.to_string(), "numbers");
assert_eq!(ElementType::Booleans.to_string(), "booleans");
assert_eq!(ElementType::Nulls.to_string(), "nulls");
assert_eq!(ElementType::Mixed.to_string(), "mixed");
assert_eq!(ElementType::Empty.to_string(), "");
}
#[test]
fn test_result_stats_array_display() {
let stats = ResultStats::Array {
count: 42,
element_type: ElementType::Objects,
};
assert_eq!(stats.to_string(), "Array [42 objects]");
let stats = ResultStats::Array {
count: 10,
element_type: ElementType::Arrays,
};
assert_eq!(stats.to_string(), "Array [10 arrays]");
let stats = ResultStats::Array {
count: 5,
element_type: ElementType::Strings,
};
assert_eq!(stats.to_string(), "Array [5 strings]");
let stats = ResultStats::Array {
count: 100,
element_type: ElementType::Numbers,
};
assert_eq!(stats.to_string(), "Array [100 numbers]");
let stats = ResultStats::Array {
count: 3,
element_type: ElementType::Booleans,
};
assert_eq!(stats.to_string(), "Array [3 booleans]");
let stats = ResultStats::Array {
count: 2,
element_type: ElementType::Nulls,
};
assert_eq!(stats.to_string(), "Array [2 nulls]");
let stats = ResultStats::Array {
count: 50,
element_type: ElementType::Mixed,
};
assert_eq!(stats.to_string(), "Array [50 mixed]");
let stats = ResultStats::Array {
count: 0,
element_type: ElementType::Empty,
};
assert_eq!(stats.to_string(), "Array [0]");
}
#[test]
fn test_result_stats_scalar_display() {
assert_eq!(ResultStats::Object.to_string(), "Object");
assert_eq!(ResultStats::String.to_string(), "String");
assert_eq!(ResultStats::Number.to_string(), "Number");
assert_eq!(ResultStats::Boolean.to_string(), "Boolean");
assert_eq!(ResultStats::Null.to_string(), "null");
}
#[test]
fn test_result_stats_stream_display() {
let stats = ResultStats::Stream { count: 3 };
assert_eq!(stats.to_string(), "Stream [3]");
}
fn arb_element_type() -> impl Strategy<Value = ElementType> {
prop_oneof![
Just(ElementType::Objects),
Just(ElementType::Arrays),
Just(ElementType::Strings),
Just(ElementType::Numbers),
Just(ElementType::Booleans),
Just(ElementType::Nulls),
Just(ElementType::Mixed),
Just(ElementType::Empty),
]
}
fn arb_result_stats() -> impl Strategy<Value = ResultStats> {
prop_oneof![
(0usize..10000, arb_element_type()).prop_map(|(count, element_type)| {
ResultStats::Array {
count,
element_type,
}
}),
Just(ResultStats::Object),
Just(ResultStats::String),
Just(ResultStats::Number),
Just(ResultStats::Boolean),
Just(ResultStats::Null),
(1usize..10000).prop_map(|count| ResultStats::Stream { count }),
]
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_type_display_consistency(stats in arb_result_stats()) {
let display = stats.to_string();
match &stats {
ResultStats::Array { count, element_type } => {
prop_assert!(
display.starts_with("Array ["),
"Array stats should start with 'Array [', got: {}",
display
);
prop_assert!(
display.ends_with(']'),
"Array stats should end with ']', got: {}",
display
);
if *element_type == ElementType::Empty {
prop_assert_eq!(display, "Array [0]");
} else {
prop_assert!(
display.contains(&count.to_string()),
"Array stats should contain count {}, got: {}",
count,
display
);
prop_assert!(
display.contains(&element_type.to_string()),
"Array stats should contain element type '{}', got: {}",
element_type,
display
);
}
}
ResultStats::Object => {
prop_assert_eq!(display, "Object");
}
ResultStats::String => {
prop_assert_eq!(display, "String");
}
ResultStats::Number => {
prop_assert_eq!(display, "Number");
}
ResultStats::Boolean => {
prop_assert_eq!(display, "Boolean");
}
ResultStats::Null => {
prop_assert_eq!(display, "null");
}
ResultStats::Stream { count } => {
prop_assert!(
display.starts_with("Stream ["),
"Stream stats should start with 'Stream [', got: {}",
display
);
prop_assert!(
display.ends_with(']'),
"Stream stats should end with ']', got: {}",
display
);
prop_assert!(
display.contains(&count.to_string()),
"Stream stats should contain count {}, got: {}",
count,
display
);
}
}
}
#[test]
fn prop_array_display_format_matches_requirements(
count in 0usize..10000,
element_type in arb_element_type()
) {
let stats = ResultStats::Array { count, element_type: element_type.clone() };
let display = stats.to_string();
match element_type {
ElementType::Empty => {
prop_assert_eq!(
display, "Array [0]",
"Empty array should display as 'Array [0]'"
);
}
_ => {
let expected = format!("Array [{} {}]", count, element_type);
prop_assert_eq!(
display, expected,
"Array display format mismatch"
);
}
}
}
#[test]
fn prop_stream_display_format_matches_requirements(count in 1usize..10000) {
let stats = ResultStats::Stream { count };
let display = stats.to_string();
let expected = format!("Stream [{}]", count);
prop_assert_eq!(
display, expected,
"Stream display format should be 'Stream [N]'"
);
}
}
}