Skip to main content

typebox/value/
cast.rs

1//! Value coercion to match schemas.
2
3use crate::error::CastError;
4use crate::schema::{LiteralValue, Schema, SchemaKind};
5use crate::value::Value;
6use indexmap::IndexMap;
7
8/// Coerce a value to conform to a schema.
9///
10/// Performs type conversions (string to int, int to bool, etc.),
11/// clamps numeric values to bounds, and fills in missing object fields.
12pub fn cast(schema: &Schema, value: &Value) -> Result<Value, CastError> {
13    match (&schema.kind, value) {
14        (SchemaKind::Null, Value::Null) => Ok(Value::Null),
15        (SchemaKind::Null, _) => Ok(Value::Null),
16
17        (SchemaKind::Bool, Value::Bool(b)) => Ok(Value::Bool(*b)),
18        (SchemaKind::Bool, v) => coerce_to_bool(v),
19
20        (SchemaKind::Int8 { minimum, maximum }, Value::Int64(n)) => {
21            let val = clamp_i64_to_i8(*n);
22            let min = minimum.unwrap_or(i8::MIN);
23            let max = maximum.unwrap_or(i8::MAX);
24            Ok(Value::Int64(clamp(val, min, max) as i64))
25        }
26        (SchemaKind::Int8 { minimum, maximum }, v) => {
27            let n = coerce_to_i64(v)?;
28            let val = clamp_i64_to_i8(n);
29            let min = minimum.unwrap_or(i8::MIN);
30            let max = maximum.unwrap_or(i8::MAX);
31            Ok(Value::Int64(clamp(val, min, max) as i64))
32        }
33
34        (SchemaKind::Int16 { minimum, maximum }, Value::Int64(n)) => {
35            let val = clamp_i64_to_i16(*n);
36            let min = minimum.unwrap_or(i16::MIN);
37            let max = maximum.unwrap_or(i16::MAX);
38            Ok(Value::Int64(clamp(val, min, max) as i64))
39        }
40        (SchemaKind::Int16 { minimum, maximum }, v) => {
41            let n = coerce_to_i64(v)?;
42            let val = clamp_i64_to_i16(n);
43            let min = minimum.unwrap_or(i16::MIN);
44            let max = maximum.unwrap_or(i16::MAX);
45            Ok(Value::Int64(clamp(val, min, max) as i64))
46        }
47
48        (SchemaKind::Int32 { minimum, maximum }, Value::Int64(n)) => {
49            let val = clamp_i64_to_i32(*n);
50            let min = minimum.unwrap_or(i32::MIN);
51            let max = maximum.unwrap_or(i32::MAX);
52            Ok(Value::Int64(clamp(val, min, max) as i64))
53        }
54        (SchemaKind::Int32 { minimum, maximum }, v) => {
55            let n = coerce_to_i64(v)?;
56            let val = clamp_i64_to_i32(n);
57            let min = minimum.unwrap_or(i32::MIN);
58            let max = maximum.unwrap_or(i32::MAX);
59            Ok(Value::Int64(clamp(val, min, max) as i64))
60        }
61
62        (SchemaKind::Int64 { minimum, maximum }, Value::Int64(n)) => {
63            let min = minimum.unwrap_or(i64::MIN);
64            let max = maximum.unwrap_or(i64::MAX);
65            Ok(Value::Int64(clamp(*n, min, max)))
66        }
67        (SchemaKind::Int64 { minimum, maximum }, v) => {
68            let n = coerce_to_i64(v)?;
69            let min = minimum.unwrap_or(i64::MIN);
70            let max = maximum.unwrap_or(i64::MAX);
71            Ok(Value::Int64(clamp(n, min, max)))
72        }
73
74        (SchemaKind::UInt8 { minimum, maximum }, Value::Int64(n)) => {
75            let val = clamp_i64_to_u8(*n);
76            let min = minimum.unwrap_or(u8::MIN);
77            let max = maximum.unwrap_or(u8::MAX);
78            Ok(Value::Int64(clamp(val, min, max) as i64))
79        }
80        (SchemaKind::UInt8 { minimum, maximum }, v) => {
81            let n = coerce_to_i64(v)?;
82            let val = clamp_i64_to_u8(n);
83            let min = minimum.unwrap_or(u8::MIN);
84            let max = maximum.unwrap_or(u8::MAX);
85            Ok(Value::Int64(clamp(val, min, max) as i64))
86        }
87
88        (SchemaKind::UInt16 { minimum, maximum }, Value::Int64(n)) => {
89            let val = clamp_i64_to_u16(*n);
90            let min = minimum.unwrap_or(u16::MIN);
91            let max = maximum.unwrap_or(u16::MAX);
92            Ok(Value::Int64(clamp(val, min, max) as i64))
93        }
94        (SchemaKind::UInt16 { minimum, maximum }, v) => {
95            let n = coerce_to_i64(v)?;
96            let val = clamp_i64_to_u16(n);
97            let min = minimum.unwrap_or(u16::MIN);
98            let max = maximum.unwrap_or(u16::MAX);
99            Ok(Value::Int64(clamp(val, min, max) as i64))
100        }
101
102        (SchemaKind::UInt32 { minimum, maximum }, Value::Int64(n)) => {
103            let val = clamp_i64_to_u32(*n);
104            let min = minimum.unwrap_or(u32::MIN);
105            let max = maximum.unwrap_or(u32::MAX);
106            Ok(Value::Int64(clamp(val, min, max) as i64))
107        }
108        (SchemaKind::UInt32 { minimum, maximum }, v) => {
109            let n = coerce_to_i64(v)?;
110            let val = clamp_i64_to_u32(n);
111            let min = minimum.unwrap_or(u32::MIN);
112            let max = maximum.unwrap_or(u32::MAX);
113            Ok(Value::Int64(clamp(val, min, max) as i64))
114        }
115
116        (SchemaKind::UInt64 { minimum, maximum }, Value::Int64(n)) => {
117            let val = (*n).max(0);
118            let min = minimum.unwrap_or(u64::MIN);
119            let max = maximum.unwrap_or(u64::MAX);
120            Ok(Value::Int64(clamp(val as u64, min, max) as i64))
121        }
122        (SchemaKind::UInt64 { minimum, maximum }, v) => {
123            let n = coerce_to_i64(v)?;
124            let val = n.max(0) as u64;
125            let min = minimum.unwrap_or(u64::MIN);
126            let max = maximum.unwrap_or(u64::MAX);
127            Ok(Value::Int64(clamp(val, min, max) as i64))
128        }
129
130        (SchemaKind::Float32 { minimum, maximum }, Value::Float64(f)) => {
131            let val = *f as f32;
132            let min = minimum.unwrap_or(f32::MIN);
133            let max = maximum.unwrap_or(f32::MAX);
134            Ok(Value::Float64(clamp_f32(val, min, max) as f64))
135        }
136        (SchemaKind::Float32 { minimum, maximum }, Value::Int64(n)) => {
137            let val = *n as f32;
138            let min = minimum.unwrap_or(f32::MIN);
139            let max = maximum.unwrap_or(f32::MAX);
140            Ok(Value::Float64(clamp_f32(val, min, max) as f64))
141        }
142        (SchemaKind::Float32 { minimum, maximum }, v) => {
143            let f = coerce_to_f64(v)?;
144            let val = f as f32;
145            let min = minimum.unwrap_or(f32::MIN);
146            let max = maximum.unwrap_or(f32::MAX);
147            Ok(Value::Float64(clamp_f32(val, min, max) as f64))
148        }
149
150        (SchemaKind::Float64 { minimum, maximum }, Value::Float64(f)) => {
151            let min = minimum.unwrap_or(f64::MIN);
152            let max = maximum.unwrap_or(f64::MAX);
153            Ok(Value::Float64(clamp_f64(*f, min, max)))
154        }
155        (SchemaKind::Float64 { minimum, maximum }, Value::Int64(n)) => {
156            let val = *n as f64;
157            let min = minimum.unwrap_or(f64::MIN);
158            let max = maximum.unwrap_or(f64::MAX);
159            Ok(Value::Float64(clamp_f64(val, min, max)))
160        }
161        (SchemaKind::Float64 { minimum, maximum }, v) => {
162            let f = coerce_to_f64(v)?;
163            let min = minimum.unwrap_or(f64::MIN);
164            let max = maximum.unwrap_or(f64::MAX);
165            Ok(Value::Float64(clamp_f64(f, min, max)))
166        }
167
168        (SchemaKind::String { .. }, Value::String(s)) => Ok(Value::String(s.clone())),
169        (SchemaKind::String { .. }, v) => Ok(Value::String(value_to_string(v))),
170
171        (SchemaKind::Bytes { .. }, Value::Bytes(b)) => Ok(Value::Bytes(b.clone())),
172        (SchemaKind::Bytes { .. }, Value::UInt8Array(b)) => Ok(Value::Bytes(b.clone())),
173        (SchemaKind::Bytes { .. }, Value::String(s)) => Ok(Value::Bytes(s.as_bytes().to_vec())),
174        (SchemaKind::Bytes { .. }, v) => Err(CastError::CannotCast(format!(
175            "cannot cast {:?} to bytes",
176            v.kind()
177        ))),
178
179        (
180            SchemaKind::Array {
181                items,
182                min_items,
183                max_items,
184                ..
185            },
186            Value::Array(arr),
187        ) => {
188            let min = min_items.unwrap_or(0);
189            let max = max_items.unwrap_or(usize::MAX);
190
191            let mut result: Vec<Value> = arr
192                .iter()
193                .take(max)
194                .map(|v| cast(items, v))
195                .collect::<Result<Vec<_>, _>>()?;
196
197            while result.len() < min {
198                result.push(super::create::create(items).map_err(|e| {
199                    CastError::CannotCast(format!("cannot create default item: {}", e))
200                })?);
201            }
202
203            Ok(Value::Array(result))
204        }
205        (
206            SchemaKind::Array {
207                items, min_items, ..
208            },
209            v,
210        ) => {
211            let min = min_items.unwrap_or(0);
212            let single = cast(items, v)?;
213            let mut arr = vec![single];
214            while arr.len() < min {
215                arr.push(super::create::create(items).map_err(|e| {
216                    CastError::CannotCast(format!("cannot create default item: {}", e))
217                })?);
218            }
219            Ok(Value::Array(arr))
220        }
221
222        (
223            SchemaKind::Object {
224                properties,
225                required,
226                additional_properties,
227            },
228            Value::Object(map),
229        ) => {
230            let mut result = IndexMap::new();
231
232            for field_name in required {
233                if let Some(field_schema) = properties.get(field_name) {
234                    if let Some(val) = map.get(field_name) {
235                        result.insert(field_name.clone(), cast(field_schema, val)?);
236                    } else {
237                        result.insert(
238                            field_name.clone(),
239                            super::create::create(field_schema).map_err(|e| {
240                                CastError::CannotCast(format!("cannot create default field: {}", e))
241                            })?,
242                        );
243                    }
244                }
245            }
246
247            if let Some(additional_schema) = additional_properties {
248                for (key, val) in map {
249                    if !properties.contains_key(key) {
250                        result.insert(key.clone(), cast(additional_schema, val)?);
251                    }
252                }
253            }
254
255            Ok(Value::Object(result))
256        }
257        (
258            SchemaKind::Object {
259                properties,
260                required,
261                ..
262            },
263            _,
264        ) => {
265            let mut result = IndexMap::new();
266            for field_name in required {
267                if let Some(field_schema) = properties.get(field_name) {
268                    result.insert(
269                        field_name.clone(),
270                        super::create::create(field_schema).map_err(|e| {
271                            CastError::CannotCast(format!("cannot create default field: {}", e))
272                        })?,
273                    );
274                }
275            }
276            Ok(Value::Object(result))
277        }
278
279        (SchemaKind::Tuple { items }, Value::Array(arr)) => {
280            let mut result = Vec::with_capacity(items.len());
281            for (i, item_schema) in items.iter().enumerate() {
282                if let Some(val) = arr.get(i) {
283                    result.push(cast(item_schema, val)?);
284                } else {
285                    result.push(super::create::create(item_schema).map_err(|e| {
286                        CastError::CannotCast(format!("cannot create tuple element: {}", e))
287                    })?);
288                }
289            }
290            Ok(Value::Array(result))
291        }
292        (SchemaKind::Tuple { items }, _) => {
293            let mut result = Vec::with_capacity(items.len());
294            for item_schema in items {
295                result.push(super::create::create(item_schema).map_err(|e| {
296                    CastError::CannotCast(format!("cannot create tuple element: {}", e))
297                })?);
298            }
299            Ok(Value::Array(result))
300        }
301
302        (SchemaKind::Union { any_of }, value) => {
303            for variant in any_of {
304                let casted = cast(variant, value)?;
305                if super::check::check(variant, &casted) {
306                    return Ok(casted);
307                }
308            }
309            if let Some(first) = any_of.first() {
310                return cast(first, value);
311            }
312            Err(CastError::CannotCast("empty union".to_string()))
313        }
314
315        (SchemaKind::Literal { value: lit }, val) => {
316            let matches = match (lit, val) {
317                (LiteralValue::Null, Value::Null) => true,
318                (LiteralValue::Boolean(b), Value::Bool(v)) => *b == *v,
319                (LiteralValue::Number(n), Value::Int64(v)) => *n == *v,
320                (LiteralValue::Float(f), Value::Float64(v)) => (f - v).abs() < f64::EPSILON,
321                (LiteralValue::String(s), Value::String(v)) => s == v,
322                _ => false,
323            };
324            if matches {
325                Ok(val.clone())
326            } else {
327                match lit {
328                    LiteralValue::Null => Ok(Value::Null),
329                    LiteralValue::Boolean(b) => Ok(Value::Bool(*b)),
330                    LiteralValue::Number(n) => Ok(Value::Int64(*n)),
331                    LiteralValue::Float(f) => Ok(Value::Float64(*f)),
332                    LiteralValue::String(s) => Ok(Value::String(s.clone())),
333                }
334            }
335        }
336
337        (SchemaKind::Enum { values }, Value::String(s)) => {
338            if values.contains(s) {
339                Ok(Value::String(s.clone()))
340            } else if let Some(first) = values.first() {
341                Ok(Value::String(first.clone()))
342            } else {
343                Err(CastError::CannotCast("empty enum".to_string()))
344            }
345        }
346        (SchemaKind::Enum { values }, _) => {
347            if let Some(first) = values.first() {
348                Ok(Value::String(first.clone()))
349            } else {
350                Err(CastError::CannotCast("empty enum".to_string()))
351            }
352        }
353
354        (SchemaKind::Ref { reference }, _) => Err(CastError::CannotCast(format!(
355            "unresolved ref: {}",
356            reference
357        ))),
358
359        (SchemaKind::Named { schema, .. }, value) => cast(schema, value),
360
361        (SchemaKind::Function { .. }, val) => Ok(val.clone()),
362        (SchemaKind::Void, _) => Ok(Value::Null),
363        (SchemaKind::Never, _) => Err(CastError::CannotCast("never type".to_string())),
364        (SchemaKind::Any, val) => Ok(val.clone()),
365        (SchemaKind::Unknown, val) => Ok(val.clone()),
366        (SchemaKind::Undefined, _) => Ok(Value::Null),
367        (SchemaKind::Recursive { schema }, value) => cast(schema, value),
368        (SchemaKind::Intersect { all_of }, value) => {
369            let mut result = value.clone();
370            for s in all_of {
371                result = cast(s, &result)?;
372            }
373            Ok(result)
374        }
375    }
376}
377
378fn coerce_to_bool(value: &Value) -> Result<Value, CastError> {
379    match value {
380        Value::Bool(b) => Ok(Value::Bool(*b)),
381        Value::Int64(n) => Ok(Value::Bool(*n != 0)),
382        Value::Float64(f) => Ok(Value::Bool(*f != 0.0)),
383        Value::String(s) => {
384            let lower = s.to_lowercase();
385            Ok(Value::Bool(
386                lower == "true" || lower == "1" || lower == "yes",
387            ))
388        }
389        Value::Null => Ok(Value::Bool(false)),
390        _ => Err(CastError::CannotCast(format!(
391            "cannot cast {:?} to bool",
392            value.kind()
393        ))),
394    }
395}
396
397fn coerce_to_i64(value: &Value) -> Result<i64, CastError> {
398    match value {
399        Value::Int64(n) => Ok(*n),
400        Value::Float64(f) => Ok(*f as i64),
401        Value::Bool(b) => Ok(if *b { 1 } else { 0 }),
402        Value::String(s) => s
403            .parse::<i64>()
404            .or_else(|_| s.parse::<f64>().map(|f| f as i64))
405            .map_err(|_| CastError::CannotCast(format!("cannot parse '{}' as number", s))),
406        _ => Err(CastError::CannotCast(format!(
407            "cannot cast {:?} to integer",
408            value.kind()
409        ))),
410    }
411}
412
413fn coerce_to_f64(value: &Value) -> Result<f64, CastError> {
414    match value {
415        Value::Float64(f) => Ok(*f),
416        Value::Int64(n) => Ok(*n as f64),
417        Value::Bool(b) => Ok(if *b { 1.0 } else { 0.0 }),
418        Value::String(s) => s
419            .parse::<f64>()
420            .map_err(|_| CastError::CannotCast(format!("cannot parse '{}' as float", s))),
421        _ => Err(CastError::CannotCast(format!(
422            "cannot cast {:?} to float",
423            value.kind()
424        ))),
425    }
426}
427
428fn value_to_string(value: &Value) -> String {
429    match value {
430        Value::Null => "null".to_string(),
431        Value::Bool(b) => b.to_string(),
432        Value::Int64(n) => n.to_string(),
433        Value::Float64(f) => f.to_string(),
434        Value::String(s) => s.clone(),
435        Value::Bytes(b) => String::from_utf8_lossy(b).to_string(),
436        Value::Array(arr) => {
437            let items: Vec<String> = arr.iter().map(value_to_string).collect();
438            format!("[{}]", items.join(", "))
439        }
440        Value::Object(map) => {
441            let items: Vec<String> = map
442                .iter()
443                .map(|(k, v)| format!("{}: {}", k, value_to_string(v)))
444                .collect();
445            format!("{{{}}}", items.join(", "))
446        }
447        _ => value.kind().to_string(),
448    }
449}
450
451fn clamp<T: Ord>(value: T, min: T, max: T) -> T {
452    if value < min {
453        min
454    } else if value > max {
455        max
456    } else {
457        value
458    }
459}
460
461fn clamp_f32(value: f32, min: f32, max: f32) -> f32 {
462    if value < min {
463        min
464    } else if value > max {
465        max
466    } else {
467        value
468    }
469}
470
471fn clamp_f64(value: f64, min: f64, max: f64) -> f64 {
472    if value < min {
473        min
474    } else if value > max {
475        max
476    } else {
477        value
478    }
479}
480
481fn clamp_i64_to_i8(n: i64) -> i8 {
482    clamp(n, i8::MIN as i64, i8::MAX as i64) as i8
483}
484
485fn clamp_i64_to_i16(n: i64) -> i16 {
486    clamp(n, i16::MIN as i64, i16::MAX as i64) as i16
487}
488
489fn clamp_i64_to_i32(n: i64) -> i32 {
490    clamp(n, i32::MIN as i64, i32::MAX as i64) as i32
491}
492
493fn clamp_i64_to_u8(n: i64) -> u8 {
494    clamp(n, 0, u8::MAX as i64) as u8
495}
496
497fn clamp_i64_to_u16(n: i64) -> u16 {
498    clamp(n, 0, u16::MAX as i64) as u16
499}
500
501fn clamp_i64_to_u32(n: i64) -> u32 {
502    clamp(n, 0, u32::MAX as i64) as u32
503}
504
505#[cfg(test)]
506mod tests {
507    use super::*;
508    use crate::builder::SchemaBuilder;
509    use crate::schema::{Schema, SchemaKind};
510
511    #[test]
512    fn test_cast_null() {
513        assert_eq!(
514            cast(&Schema::new(SchemaKind::Null), &Value::Null).unwrap(),
515            Value::Null
516        );
517        assert_eq!(
518            cast(&Schema::new(SchemaKind::Null), &Value::Int64(42)).unwrap(),
519            Value::Null
520        );
521    }
522
523    #[test]
524    fn test_cast_bool() {
525        assert_eq!(
526            cast(&Schema::new(SchemaKind::Bool), &Value::Bool(true)).unwrap(),
527            Value::Bool(true)
528        );
529        assert_eq!(
530            cast(&Schema::new(SchemaKind::Bool), &Value::Int64(1)).unwrap(),
531            Value::Bool(true)
532        );
533        assert_eq!(
534            cast(&Schema::new(SchemaKind::Bool), &Value::Int64(0)).unwrap(),
535            Value::Bool(false)
536        );
537        assert_eq!(
538            cast(
539                &Schema::new(SchemaKind::Bool),
540                &Value::String("true".to_string())
541            )
542            .unwrap(),
543            Value::Bool(true)
544        );
545        assert_eq!(
546            cast(&Schema::new(SchemaKind::Bool), &Value::Null).unwrap(),
547            Value::Bool(false)
548        );
549    }
550
551    #[test]
552    fn test_cast_int_with_bounds() {
553        let schema = Schema::new(SchemaKind::Int64 {
554            minimum: Some(10),
555            maximum: Some(20),
556        });
557        assert_eq!(cast(&schema, &Value::Int64(15)).unwrap(), Value::Int64(15));
558        assert_eq!(cast(&schema, &Value::Int64(5)).unwrap(), Value::Int64(10));
559        assert_eq!(cast(&schema, &Value::Int64(25)).unwrap(), Value::Int64(20));
560    }
561
562    #[test]
563    fn test_cast_string_to_int() {
564        assert_eq!(
565            cast(&SchemaBuilder::int64(), &Value::String("42".to_string())).unwrap(),
566            Value::Int64(42)
567        );
568        assert!(cast(
569            &SchemaBuilder::int64(),
570            &Value::String("not a number".to_string())
571        )
572        .is_err());
573    }
574
575    #[test]
576    fn test_cast_to_string() {
577        assert_eq!(
578            cast(&SchemaBuilder::string().build(), &Value::Int64(42)).unwrap(),
579            Value::String("42".to_string())
580        );
581        assert_eq!(
582            cast(&SchemaBuilder::string().build(), &Value::Bool(true)).unwrap(),
583            Value::String("true".to_string())
584        );
585    }
586
587    #[test]
588    fn test_cast_array() {
589        let schema = SchemaBuilder::array(SchemaBuilder::int64())
590            .min_items(2)
591            .max_items(3)
592            .build();
593
594        let arr = cast(&schema, &Value::Array(vec![Value::Int64(1)])).unwrap();
595        if let Value::Array(items) = arr {
596            assert_eq!(items.len(), 2);
597            assert_eq!(items[0], Value::Int64(1));
598            assert_eq!(items[1], Value::Int64(0));
599        } else {
600            panic!("Expected array");
601        }
602    }
603
604    #[test]
605    fn test_cast_object() {
606        let schema = SchemaBuilder::object()
607            .field("id", SchemaBuilder::int64())
608            .field("name", SchemaBuilder::string().build())
609            .build();
610
611        let input = Value::object()
612            .field("id", Value::String("42".to_string()))
613            .build();
614
615        let result = cast(&schema, &input).unwrap();
616        if let Value::Object(map) = result {
617            assert_eq!(map.get("id"), Some(&Value::Int64(42)));
618            assert_eq!(map.get("name"), Some(&Value::String(String::new())));
619        } else {
620            panic!("Expected object");
621        }
622    }
623
624    #[test]
625    fn test_cast_tuple() {
626        let schema = Schema::new(SchemaKind::Tuple {
627            items: vec![SchemaBuilder::int64(), SchemaBuilder::string().build()],
628        });
629
630        let input = Value::Array(vec![Value::String("42".to_string())]);
631        let result = cast(&schema, &input).unwrap();
632        assert_eq!(
633            result,
634            Value::Array(vec![Value::Int64(42), Value::String(String::new())])
635        );
636    }
637
638    #[test]
639    fn test_cast_union() {
640        let schema = SchemaBuilder::union(vec![
641            SchemaBuilder::int64(),
642            SchemaBuilder::string().build(),
643        ]);
644
645        let result = cast(&schema, &Value::Int64(42)).unwrap();
646        assert_eq!(result, Value::Int64(42));
647
648        let schema_string_first = SchemaBuilder::union(vec![
649            SchemaBuilder::string().build(),
650            SchemaBuilder::int64(),
651        ]);
652
653        let result2 = cast(&schema_string_first, &Value::Int64(42)).unwrap();
654        assert_eq!(result2, Value::String("42".to_string()));
655    }
656
657    #[test]
658    fn test_cast_literal() {
659        let schema = Schema::new(SchemaKind::Literal {
660            value: LiteralValue::String("hello".to_string()),
661        });
662
663        assert_eq!(
664            cast(&schema, &Value::String("hello".to_string())).unwrap(),
665            Value::String("hello".to_string())
666        );
667        assert_eq!(
668            cast(&schema, &Value::String("world".to_string())).unwrap(),
669            Value::String("hello".to_string())
670        );
671    }
672
673    #[test]
674    fn test_cast_enum() {
675        let schema = Schema::new(SchemaKind::Enum {
676            values: vec!["one".to_string(), "two".to_string()],
677        });
678
679        assert_eq!(
680            cast(&schema, &Value::String("one".to_string())).unwrap(),
681            Value::String("one".to_string())
682        );
683        assert_eq!(
684            cast(&schema, &Value::String("other".to_string())).unwrap(),
685            Value::String("one".to_string())
686        );
687    }
688
689    #[test]
690    fn test_cast_float_truncation() {
691        assert_eq!(
692            cast(&SchemaBuilder::int64(), &Value::Float64(3.7)).unwrap(),
693            Value::Int64(3)
694        );
695    }
696}