hottabych

EnumOrString<T>

Aug 11th, 2025
501
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Dart 6.31 KB | Source Code | 0 0
  1. /// A generic class that can hold either an enum value of type [T] or a raw [String].
  2. ///
  3. /// This class is designed to handle deserialization where a string input
  4. /// might correspond to a known enum value or be an arbitrary string.
  5. /// If the input string matches an enum's name, the enum value is stored;
  6. /// otherwise, the raw string is stored.
  7. class EnumValueOrString<T extends Enum> {
  8.   final T? _enumValue;
  9.   final String? _stringValue;
  10.  
  11.   /// Private constructor for internal use, ensuring that exactly one of
  12.   /// [_enumValue] or [_stringValue] is non-null.
  13.   EnumValueOrString._({
  14.     T? enumValue,
  15.     String? stringValue,
  16.   })  : _enumValue = enumValue,
  17.         _stringValue = stringValue {
  18.     assert(
  19.       (enumValue != null && stringValue == null) ||
  20.           (enumValue == null && stringValue != null),
  21.       'EnumValueOrString must hold either an enum value or a string, not both or neither.',
  22.     );
  23.   }
  24.  
  25.   /// Creates an [EnumValueOrString] instance holding an enum value.
  26.   factory EnumValueOrString.fromEnum(T value) {
  27.     return EnumValueOrString<T>._(enumValue: value);
  28.   }
  29.  
  30.   /// Creates an [EnumValueOrString] instance holding a raw string.
  31.   factory EnumValueOrString.fromString(String value) {
  32.     return EnumValueOrString<T>._(stringValue: value);
  33.   }
  34.  
  35.   /// Deserializes a [rawValue] into an [EnumValueOrString].
  36.   ///
  37.   /// It attempts to match the [rawValue] against the [name] property of
  38.   /// each enum value in [enumValues].
  39.   /// If a match is found, an instance holding the enum value is returned.
  40.   /// Otherwise, an instance holding the original [rawValue] as a string is returned.
  41.   factory EnumValueOrString.deserialize(String rawValue, List<T> enumValues) {
  42.     for (var enumValue in enumValues) {
  43.       if (enumValue.name == rawValue) {
  44.         return EnumValueOrString<T>.fromEnum(enumValue);
  45.       }
  46.     }
  47.     // If no matching enum value is found, store the raw string.
  48.     return EnumValueOrString<T>.fromString(rawValue);
  49.   }
  50.  
  51.   /// Returns `true` if this instance holds an enum value.
  52.   bool get isEnum => _enumValue != null;
  53.  
  54.   /// Returns `true` if this instance holds a raw string.
  55.   bool get isString => _stringValue != null;
  56.  
  57.   /// Returns the enum value held by this instance.
  58.   ///
  59.   /// Throws a [StateError] if this instance holds a string instead of an enum.
  60.   T get enumValue {
  61.     if (_enumValue == null) {
  62.       throw StateError(
  63.           'This EnumValueOrString instance holds a String, not an Enum.');
  64.     }
  65.     return _enumValue!;
  66.   }
  67.  
  68.   /// Returns the raw string held by this instance.
  69.   ///
  70.   /// Throws a [StateError] if this instance holds an enum instead of a string.
  71.   String get stringValue {
  72.     if (_stringValue == null) {
  73.       throw StateError(
  74.           'This EnumValueOrString instance holds an Enum, not a String.');
  75.     }
  76.     return _stringValue!;
  77.   }
  78.  
  79.   @override
  80.   String toString() {
  81.     if (isEnum) {
  82.       return 'EnumValueOrString.enum(${_enumValue!.name})';
  83.     } else {
  84.       return 'EnumValueOrString.string("$_stringValue")';
  85.     }
  86.   }
  87.  
  88.   @override
  89.   bool operator ==(Object other) {
  90.     if (identical(this, other)) return true;
  91.     return other is EnumValueOrString<T> &&
  92.         _enumValue == other._enumValue &&
  93.         _stringValue == other._stringValue;
  94.   }
  95.  
  96.   @override
  97.   int get hashCode => Object.hash(_enumValue, _stringValue);
  98. }
  99.  
  100. /// An example enum to demonstrate the usage of [EnumValueOrString].
  101. enum MyStatus {
  102.   active,
  103.   inactive,
  104.   pending,
  105.   completed,
  106.   cancelled,
  107. }
  108.  
  109. void main() {
  110.   print('--- Deserialization Examples ---');
  111.  
  112.   // Case 1: Deserializing a string that matches an enum value.
  113.   final knownStatus =
  114.       EnumValueOrString.deserialize('active', MyStatus.values);
  115.   print('Input: "active"');
  116.   print('Is Enum? ${knownStatus.isEnum}, Is String? ${knownStatus.isString}');
  117.   if (knownStatus.isEnum) {
  118.     print('Value: ${knownStatus.enumValue} (Type: ${knownStatus.enumValue.runtimeType})');
  119.   } else {
  120.     print('Value: ${knownStatus.stringValue} (Type: ${knownStatus.stringValue.runtimeType})');
  121.   }
  122.   print('');
  123.  
  124.   // Case 2: Deserializing a string that does NOT match any enum value.
  125.   final unknownStatus =
  126.       EnumValueOrString.deserialize('on_hold', MyStatus.values);
  127.   print('Input: "on_hold"');
  128.   print('Is Enum? ${unknownStatus.isEnum}, Is String? ${unknownStatus.isString}');
  129.   if (unknownStatus.isEnum) {
  130.     print('Value: ${unknownStatus.enumValue} (Type: ${unknownStatus.enumValue.runtimeType})');
  131.   } else {
  132.     print('Value: ${unknownStatus.stringValue} (Type: ${unknownStatus.stringValue.runtimeType})');
  133.   }
  134.   print('');
  135.  
  136.   print('--- Direct Creation Examples ---');
  137.  
  138.   // Case 3: Creating an instance directly from an enum value.
  139.   final directEnum = EnumValueOrString.fromEnum(MyStatus.completed);
  140.   print('Direct Enum: MyStatus.completed');
  141.   print('Is Enum? ${directEnum.isEnum}, Is String? ${directEnum.isString}');
  142.   print('Value: ${directEnum.enumValue}');
  143.   print('');
  144.  
  145.   // Case 4: Creating an instance directly from a raw string.
  146.   final directString = EnumValueOrString.fromString('archived_item');
  147.   print('Direct String: "archived_item"');
  148.   print('Is Enum? ${directString.isEnum}, Is String? ${directString.isString}');
  149.   print('Value: ${directString.stringValue}');
  150.   print('');
  151.  
  152.   print('--- Error Handling Examples ---');
  153.  
  154.   // Attempting to access stringValue on an enum-holding instance.
  155.   try {
  156.     print('Attempting to access stringValue on knownStatus:');
  157.     print(knownStatus.stringValue);
  158.   } catch (e) {
  159.     print('Caught error: $e');
  160.   }
  161.   print('');
  162.  
  163.   // Attempting to access enumValue on a string-holding instance.
  164.   try {
  165.     print('Attempting to access enumValue on unknownStatus:');
  166.     print(unknownStatus.enumValue);
  167.   } catch (e) {
  168.     print('Caught error: $e');
  169.   }
  170.   print('');
  171.  
  172.   print('--- Equality Examples ---');
  173.   final anotherKnownStatus =
  174.       EnumValueOrString.deserialize('active', MyStatus.values);
  175.   final anotherUnknownStatus =
  176.       EnumValueOrString.fromString('on_hold');
  177.  
  178.   print('knownStatus == anotherKnownStatus: ${knownStatus == anotherKnownStatus}'); // Expected: true
  179.   print('unknownStatus == anotherUnknownStatus: ${unknownStatus == anotherUnknownStatus}'); // Expected: true
  180.   print('knownStatus == unknownStatus: ${knownStatus == unknownStatus}'); // Expected: false
  181. }
Tags: Snippet
Advertisement
Add Comment
Please, Sign In to add comment