use qubit_datatype::DataConversionError;
use qubit_datatype::DataType;
use qubit_datatype::InvalidValueReason;
use qubit_value::MultiValues;
use qubit_value::Value;
use qubit_value::ValueError;
use qubit_value::ValueMissingReason;
#[test]
fn test_multi_values_converters_convert_first_list_and_value() {
let values = MultiValues::String(vec!["1".to_string(), "2".to_string()]);
assert_eq!(values.to_first::<i32>().unwrap(), 1);
assert_eq!(values.to_list::<i32>().unwrap(), vec![1, 2]);
assert_eq!(values.first_value(), Value::String("1".to_string()));
}
#[test]
fn test_multi_values_converters_report_list_conversion_index() {
let values = MultiValues::String(vec!["1".to_string(), "bad".to_string()]);
let error = values.to_list::<i32>().unwrap_err();
assert!(matches!(
error,
ValueError::ListConversion(ref error)
if error.source_index() == 1
&& error.conversion_error() == &DataConversionError::invalid(
DataType::String,
DataType::Int32,
InvalidValueReason::InvalidSyntax {
expected: "integer",
},
)
));
}
#[test]
fn test_multi_values_empty_conversion_preserves_conversion_semantics() {
let values = MultiValues::String(Vec::new());
let error = values
.to_first::<i32>()
.expect_err("empty collection has no first converted value");
let ValueError::Missing(missing) = error else {
panic!("expected an empty collection conversion error");
};
assert_eq!(missing.reason(), ValueMissingReason::EmptyCollection);
assert_eq!(missing.source_type(), Some(DataType::String));
assert_eq!(missing.target_type(), Some(DataType::Int32));
}
macro_rules! assert_multi_values_identity_conversion {
($value:expr, $ty:ty, $first:expr, $list:expr) => {{
let values = $value;
assert_eq!(values.to_first::<$ty>().unwrap(), $first);
assert_eq!(values.to_list::<$ty>().unwrap(), $list);
}};
}
#[test]
fn test_multi_values_converters_cover_every_runtime_variant() {
assert_multi_values_identity_conversion!(MultiValues::Bool(vec![true, false]), bool, true, vec![true, false]);
assert_multi_values_identity_conversion!(MultiValues::Char(vec!['a', 'b']), char, 'a', vec!['a', 'b']);
assert_multi_values_identity_conversion!(MultiValues::Int8(vec![-1, 2]), i8, -1, vec![-1, 2]);
assert_multi_values_identity_conversion!(MultiValues::Int16(vec![-1, 2]), i16, -1, vec![-1, 2]);
assert_multi_values_identity_conversion!(MultiValues::Int32(vec![-1, 2]), i32, -1, vec![-1, 2]);
assert_multi_values_identity_conversion!(MultiValues::Int64(vec![-1, 2]), i64, -1, vec![-1, 2]);
assert_multi_values_identity_conversion!(MultiValues::Int128(vec![-1, 2]), i128, -1, vec![-1, 2]);
assert_multi_values_identity_conversion!(MultiValues::UInt8(vec![1, 2]), u8, 1, vec![1, 2]);
assert_multi_values_identity_conversion!(MultiValues::UInt16(vec![1, 2]), u16, 1, vec![1, 2]);
assert_multi_values_identity_conversion!(MultiValues::UInt32(vec![1, 2]), u32, 1, vec![1, 2]);
assert_multi_values_identity_conversion!(MultiValues::UInt64(vec![1, 2]), u64, 1, vec![1, 2]);
assert_multi_values_identity_conversion!(MultiValues::UInt128(vec![1, 2]), u128, 1, vec![1, 2]);
assert_multi_values_identity_conversion!(MultiValues::Float32(vec![1.5, 2.5]), f32, 1.5, vec![1.5, 2.5]);
assert_multi_values_identity_conversion!(MultiValues::Float64(vec![1.5, 2.5]), f64, 1.5, vec![1.5, 2.5]);
assert_multi_values_identity_conversion!(
MultiValues::String(vec!["a".to_string(), "b".to_string()]),
String,
"a".to_string(),
vec!["a".to_string(), "b".to_string()]
);
assert_multi_values_identity_conversion!(
MultiValues::Duration(vec![std::time::Duration::from_secs(1)]),
std::time::Duration,
std::time::Duration::from_secs(1),
vec![std::time::Duration::from_secs(1)]
);
#[cfg(feature = "chrono")]
{
let date = chrono::NaiveDate::from_ymd_opt(2025, 1, 1).unwrap();
assert_multi_values_identity_conversion!(MultiValues::Date(vec![date]), chrono::NaiveDate, date, vec![date]);
let time = chrono::NaiveTime::from_hms_opt(1, 2, 3).unwrap();
assert_multi_values_identity_conversion!(MultiValues::Time(vec![time]), chrono::NaiveTime, time, vec![time]);
let datetime = date.and_time(time);
assert_multi_values_identity_conversion!(
MultiValues::DateTime(vec![datetime]),
chrono::NaiveDateTime,
datetime,
vec![datetime]
);
let instant = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(datetime, chrono::Utc);
assert_multi_values_identity_conversion!(
MultiValues::Instant(vec![instant]),
chrono::DateTime<chrono::Utc>,
instant,
vec![instant]
);
}
#[cfg(feature = "big-integer")]
{
let value = num_bigint::BigInt::from(123);
assert_multi_values_identity_conversion!(
MultiValues::BigInteger(vec![value.clone()]),
num_bigint::BigInt,
value.clone(),
vec![value]
);
}
#[cfg(feature = "big-decimal")]
{
let value = "1.25".parse::<bigdecimal::BigDecimal>().unwrap();
assert_multi_values_identity_conversion!(
MultiValues::BigDecimal(vec![value.clone()]),
bigdecimal::BigDecimal,
value.clone(),
vec![value]
);
}
#[cfg(feature = "url")]
{
let value = url::Url::parse("https://example.com").unwrap();
assert_multi_values_identity_conversion!(
MultiValues::Url(vec![value.clone()]),
url::Url,
value.clone(),
vec![value]
);
}
let map = std::collections::HashMap::from([("key".to_string(), "value".to_string())]);
assert_multi_values_identity_conversion!(
MultiValues::StringMap(vec![map.clone()]),
std::collections::HashMap<String, String>,
map.clone(),
vec![map]
);
#[cfg(feature = "json")]
{
let value = serde_json::json!({"key": 1});
assert_multi_values_identity_conversion!(
MultiValues::Json(vec![value.clone()]),
serde_json::Value,
value.clone(),
vec![value]
);
}
}