hydrate_data/
value.rs

1use crate::AssetId;
2use crate::{HashMap, Schema, SchemaFingerprint, SchemaNamedType, SchemaSet};
3use std::hash::{Hash, Hasher};
4use std::sync::Arc;
5
6use hydrate_schema::{DataSetError, DataSetResult, SchemaEnum};
7
8/// All the possible value types that can exist that do not potentially contain values within them.
9/// So excludes containers, nullable, records, etc.
10#[derive(Clone, Debug, PartialEq)]
11pub enum PropertyValue {
12    Boolean(bool),
13    I32(i32),
14    I64(i64),
15    U32(u32),
16    U64(u64),
17    F32(f32),
18    F64(f64),
19    Bytes(Arc<Vec<u8>>),
20    String(Arc<String>),
21    AssetRef(AssetId),
22    Enum(ValueEnum),
23}
24
25impl PropertyValue {
26    /// Convert to a value
27    pub fn as_value(&self) -> Value {
28        match self {
29            PropertyValue::Boolean(x) => Value::Boolean(*x),
30            PropertyValue::I32(x) => Value::I32(*x),
31            PropertyValue::I64(x) => Value::I64(*x),
32            PropertyValue::U32(x) => Value::U32(*x),
33            PropertyValue::U64(x) => Value::U64(*x),
34            PropertyValue::F32(x) => Value::F32(*x),
35            PropertyValue::F64(x) => Value::F64(*x),
36            PropertyValue::Bytes(x) => Value::Bytes(x.clone()),
37            PropertyValue::String(x) => Value::String(x.clone()),
38            PropertyValue::AssetRef(x) => Value::AssetRef(*x),
39            PropertyValue::Enum(x) => Value::Enum(x.clone()),
40        }
41    }
42
43    /// Validates if the values could be property values, and if they can if they would be matching
44    pub fn are_matching_property_values(
45        lhs: &Value,
46        rhs: &Value,
47    ) -> bool {
48        match (lhs, rhs) {
49            (Value::Boolean(lhs), Value::Boolean(rhs)) => *lhs == *rhs,
50            (Value::I32(lhs), Value::I32(rhs)) => *lhs == *rhs,
51            (Value::I64(lhs), Value::I64(rhs)) => *lhs == *rhs,
52            (Value::U32(lhs), Value::U32(rhs)) => *lhs == *rhs,
53            (Value::U64(lhs), Value::U64(rhs)) => *lhs == *rhs,
54            (Value::F32(lhs), Value::F32(rhs)) => *lhs == *rhs,
55            (Value::F64(lhs), Value::F64(rhs)) => *lhs == *rhs,
56            (Value::Bytes(lhs), Value::Bytes(rhs)) => *lhs == *rhs,
57            (Value::String(lhs), Value::String(rhs)) => *lhs == *rhs,
58            (Value::AssetRef(lhs), Value::AssetRef(rhs)) => *lhs == *rhs,
59            (Value::Enum(lhs), Value::Enum(rhs)) => *lhs == *rhs,
60            _ => false,
61        }
62    }
63}
64
65/// Hashmap-like container
66#[derive(Clone, Debug, Default)]
67pub struct ValueMap {
68    properties: HashMap<Value, Value>,
69}
70
71impl Hash for ValueMap {
72    fn hash<H: Hasher>(
73        &self,
74        state: &mut H,
75    ) {
76        let mut hash = 0;
77        for (k, v) in &self.properties {
78            let mut inner_hasher = siphasher::sip::SipHasher::default();
79            k.hash(&mut inner_hasher);
80            v.hash(&mut inner_hasher);
81            hash = hash ^ inner_hasher.finish();
82        }
83
84        hash.hash(state);
85    }
86}
87
88/// A struct-like container of properties
89#[derive(Clone, Debug, Default)]
90pub struct ValueRecord {
91    properties: HashMap<String, Value>,
92}
93
94impl Hash for ValueRecord {
95    fn hash<H: Hasher>(
96        &self,
97        state: &mut H,
98    ) {
99        let mut hash = 0;
100        for (k, v) in &self.properties {
101            let mut inner_hasher = siphasher::sip::SipHasher::default();
102            k.hash(&mut inner_hasher);
103            v.hash(&mut inner_hasher);
104            hash = hash ^ inner_hasher.finish();
105        }
106
107        hash.hash(state);
108    }
109}
110
111/// A value for an enum. Strings are used instead of numbers so that we can handle loading
112/// "broken" data.
113#[derive(Clone, Debug, Default, PartialEq, Hash)]
114pub struct ValueEnum {
115    symbol_name: String,
116}
117
118impl ValueEnum {
119    pub fn new(symbol_name: String) -> Self {
120        ValueEnum { symbol_name }
121    }
122
123    pub fn symbol_name(&self) -> &str {
124        &self.symbol_name
125    }
126}
127
128/// All the possible types that can be stored in a Value
129#[derive(Clone, Debug)]
130pub enum Value {
131    Nullable(Option<Box<Value>>),
132    Boolean(bool),
133    I32(i32),
134    I64(i64),
135    U32(u32),
136    U64(u64),
137    F32(f32),
138    F64(f64),
139    Bytes(Arc<Vec<u8>>),
140    // buffer value hash
141    String(Arc<String>),
142    StaticArray(Vec<Value>),
143    DynamicArray(Vec<Value>),
144    Map(ValueMap),
145    AssetRef(AssetId),
146    Record(ValueRecord),
147    Enum(ValueEnum),
148}
149
150impl Hash for Value {
151    fn hash<H: Hasher>(
152        &self,
153        state: &mut H,
154    ) {
155        match self {
156            Value::Nullable(x) => x.hash(state),
157            Value::Boolean(x) => x.hash(state),
158            Value::I32(x) => x.hash(state),
159            Value::I64(x) => x.hash(state),
160            Value::U32(x) => x.hash(state),
161            Value::U64(x) => x.hash(state),
162            Value::F32(x) => x.to_bits().hash(state),
163            Value::F64(x) => x.to_bits().hash(state),
164            Value::Bytes(x) => x.hash(state),
165            Value::String(x) => x.hash(state),
166            Value::StaticArray(x) => x.hash(state),
167            Value::DynamicArray(x) => x.hash(state),
168            Value::Map(x) => x.hash(state),
169            Value::AssetRef(x) => x.hash(state),
170            Value::Record(x) => x.hash(state),
171            Value::Enum(x) => x.hash(state),
172        }
173    }
174}
175
176const DEFAULT_VALUE_NULLABLE: Value = Value::Nullable(None);
177const DEFAULT_VALUE_BOOLEAN: Value = Value::Boolean(false);
178const DEFAULT_VALUE_I32: Value = Value::I32(0);
179const DEFAULT_VALUE_I64: Value = Value::I64(0);
180const DEFAULT_VALUE_U32: Value = Value::U32(0);
181const DEFAULT_VALUE_U64: Value = Value::U64(0);
182const DEFAULT_VALUE_F32: Value = Value::F32(0.0);
183const DEFAULT_VALUE_F64: Value = Value::F64(0.0);
184const DEFAULT_VALUE_ASSET_REF: Value = Value::AssetRef(AssetId::null());
185
186lazy_static::lazy_static! {
187    static ref DEFAULT_VALUE_BYTES: Value = Value::Bytes(Arc::new(Vec::default()));
188    static ref DEFAULT_VALUE_STRING: Value = Value::String(Arc::from(String::default()));
189    static ref DEFAULT_VALUE_STATIC_ARRAY: Value = Value::StaticArray(Default::default());
190    static ref DEFAULT_VALUE_DYNAMIC_ARRAY: Value = Value::DynamicArray(Default::default());
191    static ref DEFAULT_VALUE_MAP: Value = Value::Map(ValueMap::default());
192    static ref DEFAULT_VALUE_RECORD: Value = Value::Record(ValueRecord::default());
193    static ref DEFAULT_VALUE_ENUM: Value = Value::Enum(ValueEnum::default());
194}
195
196impl Value {
197    /// Produces a default value for the given schema. Because schemas may reference other schemas,
198    /// and a default value may have containers in it, we need to have access to all schemas that
199    /// may exist.
200    pub fn default_for_schema<'a>(
201        schema: &Schema,
202        schema_set: &'a SchemaSet,
203    ) -> &'a Self {
204        match schema {
205            Schema::Nullable(_) => &DEFAULT_VALUE_NULLABLE,
206            Schema::Boolean => &DEFAULT_VALUE_BOOLEAN,
207            Schema::I32 => &DEFAULT_VALUE_I32,
208            Schema::I64 => &DEFAULT_VALUE_I64,
209            Schema::U32 => &DEFAULT_VALUE_U32,
210            Schema::U64 => &DEFAULT_VALUE_U64,
211            Schema::F32 => &DEFAULT_VALUE_F32,
212            Schema::F64 => &DEFAULT_VALUE_F64,
213            Schema::Bytes => &DEFAULT_VALUE_BYTES,
214            Schema::String => &DEFAULT_VALUE_STRING,
215            Schema::StaticArray(_) => &DEFAULT_VALUE_STATIC_ARRAY,
216            Schema::DynamicArray(_) => &DEFAULT_VALUE_DYNAMIC_ARRAY,
217            Schema::Map(_) => &DEFAULT_VALUE_MAP,
218            Schema::AssetRef(_) => &DEFAULT_VALUE_ASSET_REF,
219            Schema::Record(_) => &DEFAULT_VALUE_RECORD,
220            Schema::Enum(named_type_id) => {
221                schema_set.default_value_for_enum(*named_type_id).unwrap()
222            }
223        }
224    }
225
226    /// Validates that the value matches the provided schema exactly. Even if this returns false,
227    /// it may still be possible to migrate the data into the given schema. This will recursively
228    /// descend through containers, records, etc.
229    pub fn matches_schema(
230        &self,
231        schema: &Schema,
232        named_types: &HashMap<SchemaFingerprint, SchemaNamedType>,
233    ) -> bool {
234        match self {
235            Value::Nullable(inner_value) => {
236                match schema {
237                    Schema::Nullable(inner_schema) => {
238                        if let Some(inner_value) = inner_value {
239                            // check inner value is the intended schema
240                            inner_value.matches_schema(inner_schema, named_types)
241                        } else {
242                            // value is null, that's allowed
243                            true
244                        }
245                    }
246                    _ => false,
247                }
248            }
249            Value::Boolean(_) => schema.is_boolean(),
250            Value::I32(_) => schema.is_i32(),
251            Value::I64(_) => schema.is_i64(),
252            Value::U32(_) => schema.is_u32(),
253            Value::U64(_) => schema.is_u64(),
254            Value::F32(_) => schema.is_f32(),
255            Value::F64(_) => schema.is_f64(),
256            Value::Bytes(_) => schema.is_bytes(),
257            Value::String(_) => schema.is_string(),
258            Value::StaticArray(inner_values) => match schema {
259                Schema::StaticArray(inner_schema) => {
260                    // We can be lazy about having the correct number of values in the Vec, which allows for an empty
261                    // static array to be represented by an empty vec
262                    // if inner_schema.length() != inner_values.len() {
263                    //     return false;
264                    // }
265
266                    for value in inner_values {
267                        if !value.matches_schema(&*inner_schema.item_type(), named_types) {
268                            return false;
269                        }
270                    }
271
272                    true
273                }
274                _ => false,
275            },
276            Value::DynamicArray(inner_values) => match schema {
277                Schema::DynamicArray(inner_schema) => {
278                    for inner_value in inner_values {
279                        if !inner_value.matches_schema(inner_schema.item_type(), named_types) {
280                            return false;
281                        }
282                    }
283
284                    true
285                }
286                _ => false,
287            },
288            Value::Map(inner_value) => match schema {
289                Schema::Map(inner_schema) => {
290                    for (k, v) in &inner_value.properties {
291                        if !k.matches_schema(inner_schema.key_type(), named_types) {
292                            return false;
293                        }
294
295                        if !v.matches_schema(inner_schema.value_type(), named_types) {
296                            return false;
297                        }
298                    }
299
300                    true
301                }
302                _ => false,
303            },
304            Value::AssetRef(_) => {
305                //TODO: Validate type
306                schema.is_asset_ref()
307            }
308            Value::Record(inner_value) => {
309                // All value properties must exist and match in the schema. However we allow the
310                // value to be missing properties in the schema
311                match schema {
312                    Schema::Record(named_type_id) => {
313                        let named_type = named_types.get(named_type_id).unwrap();
314                        match named_type {
315                            SchemaNamedType::Record(inner_schema) => {
316                                // Walk through all properties and make sure the field exists and type matches
317                                for (k, v) in &inner_value.properties {
318                                    let mut property_match_found = false;
319                                    for field in inner_schema.fields() {
320                                        if field.name() == k {
321                                            if v.matches_schema(field.field_schema(), named_types) {
322                                                property_match_found = true;
323                                                break;
324                                            } else {
325                                                return false;
326                                            }
327                                        }
328                                    }
329
330                                    if !property_match_found {
331                                        return false;
332                                    }
333                                }
334
335                                true
336                            }
337                            _ => panic!("A Schema::Record fingerprint is matching a named type that isn't a record"),
338                        }
339                    }
340                    _ => false,
341                }
342            }
343            Value::Enum(inner_value) => {
344                match schema {
345                    Schema::Enum(named_type_id) => {
346                        let named_type = named_types.get(named_type_id).unwrap();
347                        match named_type {
348                        SchemaNamedType::Enum(inner_schema) => {
349                            for option in inner_schema.symbols() {
350                                if option.name() == inner_value.symbol_name {
351                                    return true;
352                                }
353                            }
354
355                            false
356                        }
357                        _ => panic!("A Schema::Enum fingerprint is matching a named type that isn't a enum"),
358                    }
359                    }
360                    _ => false,
361                }
362            }
363        }
364    }
365
366    /// Returns the value as a property value, if possible. Some types cannot be stored as
367    /// PropertyValue
368    pub fn as_property_value(&self) -> Option<PropertyValue> {
369        match self {
370            Value::Boolean(x) => Some(PropertyValue::Boolean(*x)),
371            Value::I32(x) => Some(PropertyValue::I32(*x)),
372            Value::I64(x) => Some(PropertyValue::I64(*x)),
373            Value::U32(x) => Some(PropertyValue::U32(*x)),
374            Value::U64(x) => Some(PropertyValue::U64(*x)),
375            Value::F32(x) => Some(PropertyValue::F32(*x)),
376            Value::F64(x) => Some(PropertyValue::F64(*x)),
377            Value::Bytes(x) => Some(PropertyValue::Bytes(x.clone())),
378            Value::String(x) => Some(PropertyValue::String(x.clone())),
379            Value::AssetRef(x) => Some(PropertyValue::AssetRef(*x)),
380            Value::Enum(x) => Some(PropertyValue::Enum(x.clone())),
381            _ => None,
382        }
383    }
384
385    //
386    // Nullable
387    //
388    pub fn is_nullable(&self) -> bool {
389        match self {
390            Value::Nullable(_) => true,
391            _ => false,
392        }
393    }
394
395    pub fn is_null(&self) -> bool {
396        match self {
397            Value::Nullable(None) => true,
398            _ => false,
399        }
400    }
401
402    pub fn as_nullable(&self) -> DataSetResult<Option<&Value>> {
403        Ok(self.try_as_nullable().ok_or(DataSetError::InvalidSchema)?)
404    }
405
406    pub fn try_as_nullable(&self) -> Option<Option<&Value>> {
407        match self {
408            Value::Nullable(x) => Some(x.as_ref().map(|x| x.as_ref())),
409            _ => None,
410        }
411    }
412
413    pub fn set_nullable(
414        &mut self,
415        value: Option<Value>,
416    ) {
417        *self = Value::Nullable(value.map(|x| Box::new(x)))
418    }
419
420    //
421    // Boolean
422    //
423    pub fn is_boolean(&self) -> bool {
424        match self {
425            Value::Boolean(_) => true,
426            _ => false,
427        }
428    }
429
430    pub fn as_boolean(&self) -> DataSetResult<bool> {
431        Ok(self.try_as_boolean().ok_or(DataSetError::InvalidSchema)?)
432    }
433
434    pub fn try_as_boolean(&self) -> Option<bool> {
435        match self {
436            Value::Boolean(x) => Some(*x),
437            _ => None,
438        }
439    }
440
441    pub fn set_boolean(
442        &mut self,
443        value: bool,
444    ) {
445        *self = Value::Boolean(value);
446    }
447
448    //
449    // i32
450    //
451    pub fn is_i32(&self) -> bool {
452        match self {
453            Value::I32(_) => true,
454            _ => false,
455        }
456    }
457
458    pub fn as_i32(&self) -> DataSetResult<i32> {
459        Ok(self.try_as_i32().ok_or(DataSetError::InvalidSchema)?)
460    }
461
462    pub fn try_as_i32(&self) -> Option<i32> {
463        match self {
464            Value::I32(x) => Some(*x as i32),
465            Value::U32(x) => Some(*x as i32),
466            Value::I64(x) => Some(*x as i32),
467            Value::U64(x) => Some(*x as i32),
468            Value::F32(x) => Some(*x as i32),
469            Value::F64(x) => Some(*x as i32),
470            _ => None,
471        }
472    }
473
474    pub fn set_i32(
475        &mut self,
476        value: i32,
477    ) {
478        *self = Value::I32(value);
479    }
480
481    //
482    // u32
483    //
484    pub fn is_u32(&self) -> bool {
485        match self {
486            Value::U32(_) => true,
487            _ => false,
488        }
489    }
490
491    pub fn as_u32(&self) -> DataSetResult<u32> {
492        Ok(self.try_as_u32().ok_or(DataSetError::InvalidSchema)?)
493    }
494
495    pub fn try_as_u32(&self) -> Option<u32> {
496        match self {
497            Value::I32(x) => Some(*x as u32),
498            Value::U32(x) => Some(*x as u32),
499            Value::I64(x) => Some(*x as u32),
500            Value::U64(x) => Some(*x as u32),
501            Value::F32(x) => Some(*x as u32),
502            Value::F64(x) => Some(*x as u32),
503            _ => None,
504        }
505    }
506
507    pub fn set_u32(
508        &mut self,
509        value: u32,
510    ) {
511        *self = Value::U32(value);
512    }
513
514    //
515    // i64
516    //
517    pub fn is_i64(&self) -> bool {
518        match self {
519            Value::I64(_) => true,
520            _ => false,
521        }
522    }
523
524    pub fn as_i64(&self) -> DataSetResult<i64> {
525        Ok(self.try_as_i64().ok_or(DataSetError::InvalidSchema)?)
526    }
527
528    pub fn try_as_i64(&self) -> Option<i64> {
529        match self {
530            Value::I32(x) => Some(*x as i64),
531            Value::U32(x) => Some(*x as i64),
532            Value::I64(x) => Some(*x as i64),
533            Value::U64(x) => Some(*x as i64),
534            Value::F32(x) => Some(*x as i64),
535            Value::F64(x) => Some(*x as i64),
536            _ => None,
537        }
538    }
539
540    pub fn set_i64(
541        &mut self,
542        value: i64,
543    ) {
544        *self = Value::I64(value);
545    }
546
547    //
548    // u64
549    //
550    pub fn is_u64(&self) -> bool {
551        match self {
552            Value::U64(_) => true,
553            _ => false,
554        }
555    }
556
557    pub fn as_u64(&self) -> DataSetResult<u64> {
558        Ok(self.try_as_u64().ok_or(DataSetError::InvalidSchema)?)
559    }
560
561    pub fn try_as_u64(&self) -> Option<u64> {
562        match self {
563            Value::I32(x) => Some(*x as u64),
564            Value::U32(x) => Some(*x as u64),
565            Value::I64(x) => Some(*x as u64),
566            Value::U64(x) => Some(*x as u64),
567            Value::F32(x) => Some(*x as u64),
568            Value::F64(x) => Some(*x as u64),
569            _ => None,
570        }
571    }
572
573    pub fn set_u64(
574        &mut self,
575        value: u64,
576    ) {
577        *self = Value::U64(value);
578    }
579
580    //
581    // f32
582    //
583    pub fn is_f32(&self) -> bool {
584        match self {
585            Value::F32(_) => true,
586            _ => false,
587        }
588    }
589
590    pub fn as_f32(&self) -> DataSetResult<f32> {
591        Ok(self.try_as_f32().ok_or(DataSetError::InvalidSchema)?)
592    }
593
594    pub fn try_as_f32(&self) -> Option<f32> {
595        match self {
596            Value::I32(x) => Some(*x as f32),
597            Value::U32(x) => Some(*x as f32),
598            Value::I64(x) => Some(*x as f32),
599            Value::U64(x) => Some(*x as f32),
600            Value::F32(x) => Some(*x),
601            Value::F64(x) => Some(*x as f32),
602            _ => None,
603        }
604    }
605
606    pub fn set_f32(
607        &mut self,
608        value: f32,
609    ) {
610        *self = Value::F32(value);
611    }
612
613    //
614    // f64
615    //
616    pub fn is_f64(&self) -> bool {
617        match self {
618            Value::F64(_) => true,
619            _ => false,
620        }
621    }
622
623    pub fn as_f64(&self) -> DataSetResult<f64> {
624        Ok(self.try_as_f64().ok_or(DataSetError::InvalidSchema)?)
625    }
626
627    pub fn try_as_f64(&self) -> Option<f64> {
628        match self {
629            Value::I32(x) => Some(*x as f64),
630            Value::U32(x) => Some(*x as f64),
631            Value::I64(x) => Some(*x as f64),
632            Value::U64(x) => Some(*x as f64),
633            Value::F32(x) => Some(*x as f64),
634            Value::F64(x) => Some(*x),
635            _ => None,
636        }
637    }
638
639    pub fn set_f64(
640        &mut self,
641        value: f64,
642    ) {
643        *self = Value::F64(value);
644    }
645
646    //
647    // Bytes
648    //
649    pub fn is_bytes(&self) -> bool {
650        match self {
651            Value::Bytes(_) => true,
652            _ => false,
653        }
654    }
655
656    pub fn as_bytes(&self) -> DataSetResult<&Arc<Vec<u8>>> {
657        Ok(self.try_as_bytes().ok_or(DataSetError::InvalidSchema)?)
658    }
659
660    pub fn try_as_bytes(&self) -> Option<&Arc<Vec<u8>>> {
661        match self {
662            Value::Bytes(x) => Some(x),
663            _ => None,
664        }
665    }
666    pub fn set_bytes(
667        &mut self,
668        value: Arc<Vec<u8>>,
669    ) {
670        *self = Value::Bytes(value);
671    }
672
673    //
674    // String
675    //
676    pub fn is_string(&self) -> bool {
677        match self {
678            Value::String(_) => true,
679            _ => false,
680        }
681    }
682
683    pub fn as_string(&self) -> DataSetResult<&Arc<String>> {
684        Ok(self.try_as_string().ok_or(DataSetError::InvalidSchema)?)
685    }
686
687    pub fn try_as_string(&self) -> Option<&Arc<String>> {
688        match self {
689            Value::String(x) => Some(x),
690            _ => None,
691        }
692    }
693
694    pub fn set_string(
695        &mut self,
696        value: Arc<String>,
697    ) {
698        *self = Value::String(value);
699    }
700
701    //
702    // StaticArray
703    //
704
705    //
706    // DynamicArray
707    //
708
709    //
710    // Map
711    //
712
713    //
714    // AssetRef
715    //
716    pub fn is_asset_ref(&self) -> bool {
717        match self {
718            Value::AssetRef(_) => true,
719            _ => false,
720        }
721    }
722
723    pub fn as_asset_ref(&self) -> DataSetResult<AssetId> {
724        Ok(self.try_as_asset_ref().ok_or(DataSetError::InvalidSchema)?)
725    }
726
727    pub fn try_as_asset_ref(&self) -> Option<AssetId> {
728        match self {
729            Value::AssetRef(x) => Some(*x),
730            _ => None,
731        }
732    }
733
734    pub fn set_asset_ref(
735        &mut self,
736        value: AssetId,
737    ) {
738        *self = Value::AssetRef(value);
739    }
740
741    //
742    // Record
743    //
744    pub fn is_record(&self) -> bool {
745        match self {
746            Value::Record(_) => true,
747            _ => false,
748        }
749    }
750
751    pub fn as_record(&self) -> DataSetResult<&ValueRecord> {
752        Ok(self.try_as_record().ok_or(DataSetError::InvalidSchema)?)
753    }
754
755    pub fn try_as_record(&self) -> Option<&ValueRecord> {
756        match self {
757            Value::Record(x) => Some(&*x),
758            _ => None,
759        }
760    }
761
762    pub fn set_record(
763        &mut self,
764        value: ValueRecord,
765    ) {
766        *self = Value::Record(value);
767    }
768
769    //
770    // Enum
771    //
772    pub fn is_enum(&self) -> bool {
773        match self {
774            Value::Enum(_) => true,
775            _ => false,
776        }
777    }
778
779    pub fn as_enum(&self) -> DataSetResult<&ValueEnum> {
780        Ok(self.try_as_enum().ok_or(DataSetError::InvalidSchema)?)
781    }
782
783    pub fn try_as_enum(&self) -> Option<&ValueEnum> {
784        match self {
785            Value::Enum(x) => Some(&*x),
786            _ => None,
787        }
788    }
789
790    pub fn set_enum(
791        &mut self,
792        value: ValueEnum,
793    ) {
794        *self = Value::Enum(value);
795    }
796
797    /// Utility function to convert a string to an enum value. This handles potentially matching
798    /// a symbol alias and using the new symbol name instead. We generally expect an enum values
799    /// in memory to use the current symbol name, not an alias
800    pub fn enum_value_from_string(
801        schema_enum: &SchemaEnum,
802        name: &str,
803    ) -> Option<Value> {
804        schema_enum
805            .find_symbol_from_name(name)
806            .map(|x| Value::Enum(ValueEnum::new(x.name().to_string())))
807    }
808}