use nodedb_types::error::{NodeDbError, NodeDbResult};
use nodedb_types::value::Value;
pub(crate) fn value_as_u64(v: &Value) -> NodeDbResult<u64> {
match v {
Value::Integer(i) => u64::try_from(*i)
.map_err(|_| NodeDbError::storage(format!("expected u64 column, got negative {i}"))),
Value::Decimal(d) => Value::decimal_as_wide_u64(d)
.ok_or_else(|| NodeDbError::storage(format!("expected u64 column, got decimal {d}"))),
Value::String(s) => s
.parse::<u64>()
.map_err(|e| NodeDbError::storage(format!("parse u64 from '{s}': {e}"))),
other => Err(NodeDbError::storage(format!(
"expected integer for u64 column, got {other:?}"
))),
}
}
pub(crate) fn value_as_string(v: &Value) -> NodeDbResult<String> {
match v {
Value::String(s) => Ok(s.clone()),
Value::Null => Ok(String::new()),
other => Err(NodeDbError::storage(format!(
"expected string column, got {other:?}"
))),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn value_as_u64_accepts_integer() {
assert_eq!(value_as_u64(&Value::Integer(42)).unwrap(), 42u64);
}
#[test]
fn value_as_u64_rejects_a_negative_integer() {
let err = value_as_u64(&Value::Integer(-1)).unwrap_err();
assert!(err.to_string().contains("negative -1"), "{err}");
}
#[test]
fn value_as_u64_accepts_a_wide_uint64_decimal() {
let wide = Value::from_u64(u64::MAX);
assert!(matches!(wide, Value::Decimal(_)));
assert_eq!(value_as_u64(&wide).unwrap(), u64::MAX);
}
#[test]
fn value_as_u64_rejects_a_scaled_decimal() {
let scaled = Value::Decimal(rust_decimal::Decimal::new(15, 1));
let err = value_as_u64(&scaled).unwrap_err();
assert!(err.to_string().contains("decimal 1.5"), "{err}");
}
#[test]
fn value_as_u64_accepts_numeric_string() {
assert_eq!(value_as_u64(&Value::String("17".into())).unwrap(), 17u64);
}
#[test]
fn value_as_u64_rejects_non_numeric_string() {
let err = value_as_u64(&Value::String("not-a-number".into())).unwrap_err();
assert!(err.to_string().contains("parse u64"));
}
#[test]
fn value_as_u64_rejects_unsupported_variants() {
assert!(value_as_u64(&Value::Bool(true)).is_err());
assert!(value_as_u64(&Value::Null).is_err());
}
#[test]
fn value_as_string_decodes_string_and_null() {
assert_eq!(value_as_string(&Value::String("ok".into())).unwrap(), "ok");
assert_eq!(value_as_string(&Value::Null).unwrap(), "");
}
#[test]
fn value_as_string_rejects_non_string_variants() {
assert!(value_as_string(&Value::Integer(1)).is_err());
}
}