1use std::collections::HashMap;
7use thiserror::Error;
8
9use crate::value::{Value, ValueType};
10
11#[derive(Error, Debug, Clone, PartialEq)]
13pub enum ValueConversionError {
14 #[error("type mismatch: expected {expected}, got {actual}")]
16 TypeMismatch {
17 expected: ValueType,
19 actual: ValueType,
21 },
22
23 #[error("missing field: {0}")]
25 MissingField(String),
26
27 #[error("invalid value: {0}")]
29 InvalidValue(String),
30
31 #[error("value out of range: {0}")]
33 OutOfRange(String),
34
35 #[error("{0}")]
37 Custom(String),
38}
39
40impl ValueConversionError {
41 pub fn type_mismatch(expected: ValueType, actual: ValueType) -> Self {
43 Self::TypeMismatch { expected, actual }
44 }
45
46 pub fn missing_field(field: impl Into<String>) -> Self {
48 Self::MissingField(field.into())
49 }
50
51 pub fn invalid_value(msg: impl Into<String>) -> Self {
53 Self::InvalidValue(msg.into())
54 }
55
56 pub fn out_of_range(msg: impl Into<String>) -> Self {
58 Self::OutOfRange(msg.into())
59 }
60
61 pub fn custom(msg: impl Into<String>) -> Self {
63 Self::Custom(msg.into())
64 }
65}
66
67pub trait FromValue: Sized {
69 fn from_value(value: Value) -> Result<Self, ValueConversionError>;
71
72 fn from_value_ref(value: &Value) -> Result<Self, ValueConversionError> {
74 Self::from_value(value.clone())
75 }
76}
77
78pub trait IntoValue {
80 fn into_value(self) -> Value;
82}
83
84impl<T: Into<Value>> IntoValue for T {
86 fn into_value(self) -> Value {
87 self.into()
88 }
89}
90
91impl FromValue for Value {
94 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
95 Ok(value)
96 }
97}
98
99impl FromValue for () {
100 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
101 match value {
102 Value::Null => Ok(()),
103 _ => Err(ValueConversionError::type_mismatch(
104 ValueType::Null,
105 value.value_type(),
106 )),
107 }
108 }
109}
110
111impl FromValue for bool {
112 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
113 match value {
114 Value::Bool(b) => Ok(b),
115 _ => Err(ValueConversionError::type_mismatch(
116 ValueType::Bool,
117 value.value_type(),
118 )),
119 }
120 }
121}
122
123impl FromValue for i64 {
124 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
125 match value {
126 Value::Int(i) => Ok(i),
127 _ => Err(ValueConversionError::type_mismatch(
128 ValueType::Int,
129 value.value_type(),
130 )),
131 }
132 }
133}
134
135impl FromValue for i32 {
136 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
137 match value {
138 Value::Int(i) => i.try_into().map_err(|_| {
139 ValueConversionError::out_of_range(format!(
140 "{} is out of range for i32",
141 i
142 ))
143 }),
144 _ => Err(ValueConversionError::type_mismatch(
145 ValueType::Int,
146 value.value_type(),
147 )),
148 }
149 }
150}
151
152impl FromValue for u64 {
153 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
154 match value {
155 Value::Int(i) => i.try_into().map_err(|_| {
156 ValueConversionError::out_of_range(format!(
157 "{} is out of range for u64",
158 i
159 ))
160 }),
161 _ => Err(ValueConversionError::type_mismatch(
162 ValueType::Int,
163 value.value_type(),
164 )),
165 }
166 }
167}
168
169impl FromValue for u32 {
170 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
171 match value {
172 Value::Int(i) => i.try_into().map_err(|_| {
173 ValueConversionError::out_of_range(format!(
174 "{} is out of range for u32",
175 i
176 ))
177 }),
178 _ => Err(ValueConversionError::type_mismatch(
179 ValueType::Int,
180 value.value_type(),
181 )),
182 }
183 }
184}
185
186impl FromValue for usize {
187 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
188 match value {
189 Value::Int(i) => i.try_into().map_err(|_| {
190 ValueConversionError::out_of_range(format!(
191 "{} is out of range for usize",
192 i
193 ))
194 }),
195 _ => Err(ValueConversionError::type_mismatch(
196 ValueType::Int,
197 value.value_type(),
198 )),
199 }
200 }
201}
202
203impl FromValue for f64 {
204 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
205 match value {
206 Value::Float(f) => Ok(f),
207 Value::Int(i) => Ok(i as f64),
208 _ => Err(ValueConversionError::type_mismatch(
209 ValueType::Float,
210 value.value_type(),
211 )),
212 }
213 }
214}
215
216impl FromValue for f32 {
217 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
218 match value {
219 Value::Float(f) => Ok(f as f32),
220 Value::Int(i) => Ok(i as f32),
221 _ => Err(ValueConversionError::type_mismatch(
222 ValueType::Float,
223 value.value_type(),
224 )),
225 }
226 }
227}
228
229impl FromValue for String {
230 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
231 match value {
232 Value::String(s) => Ok(s),
233 _ => Err(ValueConversionError::type_mismatch(
234 ValueType::String,
235 value.value_type(),
236 )),
237 }
238 }
239}
240
241impl<T: FromValue> FromValue for Vec<T> {
242 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
243 match value {
244 Value::List(list) => list.into_iter().map(T::from_value).collect(),
245 _ => Err(ValueConversionError::type_mismatch(
246 ValueType::List,
247 value.value_type(),
248 )),
249 }
250 }
251}
252
253impl<T: FromValue> FromValue for HashMap<String, T> {
254 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
255 match value {
256 Value::Map(map) => map
257 .into_iter()
258 .map(|(k, v)| T::from_value(v).map(|v| (k, v)))
259 .collect(),
260 _ => Err(ValueConversionError::type_mismatch(
261 ValueType::Map,
262 value.value_type(),
263 )),
264 }
265 }
266}
267
268impl<T: FromValue> FromValue for Option<T> {
269 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
270 match value {
271 Value::Null => Ok(None),
272 other => T::from_value(other).map(Some),
273 }
274 }
275}
276
277impl FromValue for Vec<u8> {
278 fn from_value(value: Value) -> Result<Self, ValueConversionError> {
279 match value {
280 Value::Bytes(b) => Ok(b),
281 _ => Err(ValueConversionError::type_mismatch(
282 ValueType::Bytes,
283 value.value_type(),
284 )),
285 }
286 }
287}
288
289impl From<()> for Value {
292 fn from(_: ()) -> Self {
293 Value::Null
294 }
295}
296
297impl From<usize> for Value {
298 fn from(u: usize) -> Self {
299 Value::Int(u as i64)
300 }
301}
302
303impl From<u64> for Value {
304 fn from(u: u64) -> Self {
305 Value::Int(u as i64)
306 }
307}
308
309impl From<u32> for Value {
310 fn from(u: u32) -> Self {
311 Value::Int(u as i64)
312 }
313}
314
315impl<T: IntoValue> From<Vec<T>> for Value {
316 fn from(v: Vec<T>) -> Self {
317 Value::List(v.into_iter().map(|x| x.into_value()).collect())
318 }
319}
320
321#[cfg(feature = "serde-support")]
323mod serde_support {
324 use super::*;
325 use serde::{de::DeserializeOwned, Serialize};
326 use serde_json;
327
328 pub fn value_to_json(value: &Value) -> serde_json::Value {
330 match value {
331 Value::Null => serde_json::Value::Null,
332 Value::Bool(b) => serde_json::Value::Bool(*b),
333 Value::Int(i) => serde_json::Value::Number((*i).into()),
334 Value::Float(f) => {
335 serde_json::Number::from_f64(*f)
336 .map(serde_json::Value::Number)
337 .unwrap_or(serde_json::Value::Null)
338 }
339 Value::String(s) => serde_json::Value::String(s.clone()),
340 Value::List(l) => {
341 serde_json::Value::Array(l.iter().map(value_to_json).collect())
342 }
343 Value::Map(m) => {
344 let obj: serde_json::Map<String, serde_json::Value> = m
345 .iter()
346 .map(|(k, v)| (k.clone(), value_to_json(v)))
347 .collect();
348 serde_json::Value::Object(obj)
349 }
350 Value::Function(_) => serde_json::Value::Null,
351 Value::Bytes(b) => {
352 use base64::Engine as _;
353 let encoded = base64::engine::general_purpose::STANDARD.encode(b);
354 serde_json::Value::String(encoded)
355 }
356 Value::Error(e) => {
357 let mut obj = serde_json::Map::new();
358 obj.insert("error".into(), serde_json::Value::String(e.clone()));
359 serde_json::Value::Object(obj)
360 }
361 }
362 }
363
364 pub fn json_to_value(json: serde_json::Value) -> Value {
366 match json {
367 serde_json::Value::Null => Value::Null,
368 serde_json::Value::Bool(b) => Value::Bool(b),
369 serde_json::Value::Number(n) => {
370 if let Some(i) = n.as_i64() {
371 Value::Int(i)
372 } else if let Some(f) = n.as_f64() {
373 Value::Float(f)
374 } else {
375 Value::Null
376 }
377 }
378 serde_json::Value::String(s) => Value::String(s),
379 serde_json::Value::Array(arr) => {
380 Value::List(arr.into_iter().map(json_to_value).collect())
381 }
382 serde_json::Value::Object(obj) => {
383 let map: HashMap<String, Value> = obj
384 .into_iter()
385 .map(|(k, v)| (k, json_to_value(v)))
386 .collect();
387 Value::Map(map)
388 }
389 }
390 }
391
392 pub fn from_value_serde<T: DeserializeOwned>(
394 value: Value,
395 ) -> Result<T, ValueConversionError> {
396 let json = value_to_json(&value);
397 serde_json::from_value(json)
398 .map_err(|e| ValueConversionError::custom(e.to_string()))
399 }
400
401 pub fn to_value_serde<T: Serialize>(value: &T) -> Result<Value, ValueConversionError> {
403 let json = serde_json::to_value(value)
404 .map_err(|e| ValueConversionError::custom(e.to_string()))?;
405 Ok(json_to_value(json))
406 }
407
408 impl Value {
409 pub fn deserialize<T: DeserializeOwned>(self) -> Result<T, ValueConversionError> {
411 from_value_serde(self)
412 }
413
414 pub fn to_json_string(&self) -> String {
416 let json = value_to_json(self);
417 serde_json::to_string(&json).unwrap_or_else(|_| "null".to_string())
418 }
419
420 pub fn to_json_string_pretty(&self) -> String {
422 let json = value_to_json(self);
423 serde_json::to_string_pretty(&json).unwrap_or_else(|_| "null".to_string())
424 }
425
426 pub fn from_json_str(s: &str) -> Result<Self, ValueConversionError> {
428 let json: serde_json::Value = serde_json::from_str(s)
429 .map_err(|e| ValueConversionError::custom(e.to_string()))?;
430 Ok(json_to_value(json))
431 }
432 }
433}
434
435#[cfg(feature = "serde-support")]
436pub use serde_support::*;
437
438#[macro_export]
440macro_rules! extract_field {
441 ($map:expr, $field:expr, $type:ty) => {{
442 let map = match $map {
443 $crate::Value::Map(m) => m,
444 other => {
445 return Err($crate::ValueConversionError::type_mismatch(
446 $crate::ValueType::Map,
447 other.value_type(),
448 ))
449 }
450 };
451 let value = map
452 .get($field)
453 .ok_or_else(|| $crate::ValueConversionError::missing_field($field))?
454 .clone();
455 <$type as $crate::FromValue>::from_value(value)?
456 }};
457}
458
459#[macro_export]
461macro_rules! extract_field_opt {
462 ($map:expr, $field:expr, $type:ty) => {{
463 let map = match $map {
464 $crate::Value::Map(ref m) => m,
465 other => {
466 return Err($crate::ValueConversionError::type_mismatch(
467 $crate::ValueType::Map,
468 other.value_type(),
469 ))
470 }
471 };
472 match map.get($field) {
473 Some(v) => Some(<$type as $crate::FromValue>::from_value(v.clone())?),
474 None => None,
475 }
476 }};
477}
478
479#[cfg(test)]
480mod tests {
481 use super::*;
482
483 #[test]
484 fn test_from_value_primitives() {
485 assert_eq!(bool::from_value(Value::Bool(true)).unwrap(), true);
486 assert_eq!(i64::from_value(Value::Int(42)).unwrap(), 42);
487 assert_eq!(f64::from_value(Value::Float(3.14)).unwrap(), 3.14);
488 assert_eq!(
489 String::from_value(Value::String("hello".into())).unwrap(),
490 "hello"
491 );
492 }
493
494 #[test]
495 fn test_from_value_type_mismatch() {
496 let err = bool::from_value(Value::Int(42)).unwrap_err();
497 assert!(matches!(err, ValueConversionError::TypeMismatch { .. }));
498 }
499
500 #[test]
501 fn test_from_value_collections() {
502 let list = Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
503 let vec: Vec<i64> = Vec::from_value(list).unwrap();
504 assert_eq!(vec, vec![1, 2, 3]);
505
506 let mut map = HashMap::new();
507 map.insert("a".into(), Value::Int(1));
508 map.insert("b".into(), Value::Int(2));
509 let value = Value::Map(map);
510 let result: HashMap<String, i64> = HashMap::from_value(value).unwrap();
511 assert_eq!(result.get("a"), Some(&1));
512 assert_eq!(result.get("b"), Some(&2));
513 }
514
515 #[test]
516 fn test_from_value_option() {
517 let opt: Option<i64> = Option::from_value(Value::Null).unwrap();
518 assert_eq!(opt, None);
519
520 let opt: Option<i64> = Option::from_value(Value::Int(42)).unwrap();
521 assert_eq!(opt, Some(42));
522 }
523
524 #[test]
525 fn test_numeric_range() {
526 let err = i32::from_value(Value::Int(i64::MAX)).unwrap_err();
527 assert!(matches!(err, ValueConversionError::OutOfRange(_)));
528 }
529
530 #[cfg(feature = "serde-support")]
531 mod serde_tests {
532 use super::*;
533 use serde::{Deserialize, Serialize};
534
535 #[derive(Debug, PartialEq, Serialize, Deserialize)]
536 struct TestStruct {
537 name: String,
538 value: i32,
539 optional: Option<String>,
540 }
541
542 #[test]
543 fn test_serde_roundtrip() {
544 let original = TestStruct {
545 name: "test".into(),
546 value: 42,
547 optional: Some("opt".into()),
548 };
549
550 let value = to_value_serde(&original).unwrap();
551 let restored: TestStruct = from_value_serde(value).unwrap();
552 assert_eq!(original, restored);
553 }
554
555 #[test]
556 fn test_json_conversion() {
557 let value = Value::Map({
558 let mut m = HashMap::new();
559 m.insert("key".into(), Value::String("value".into()));
560 m.insert("number".into(), Value::Int(42));
561 m
562 });
563
564 let json_str = value.to_json_string();
565 let parsed = Value::from_json_str(&json_str).unwrap();
566
567 let parsed_map = parsed.as_map().unwrap();
569 assert_eq!(
570 parsed_map.get("key"),
571 Some(&Value::String("value".into()))
572 );
573 assert_eq!(parsed_map.get("number"), Some(&Value::Int(42)));
574 }
575 }
576}