decodeData static method

DecodedPreferences decodeData(
  1. String contents
)

Retains explicit types on JavaScript, where 1 and 1.0 share a number type.

Implementation

static DecodedPreferences decodeData(String contents) {
  final decoded = jsonDecode(contents);
  if (decoded is! Map<String, dynamic>) {
    throw const FormatException('Preferences must contain a JSON object.');
  }
  if (decoded['format'] is! String || decoded['values'] is! Map) {
    final values = copyPreferences(decoded);
    return DecodedPreferences(
      values,
      values.map(
        (key, value) => MapEntry(key, switch (value) {
          String() => 'string',
          bool() => 'bool',
          int() => 'int',
          double() => 'double',
          List<String>() => 'list',
          _ => throw const FormatException('Unsupported preference type.'),
        }),
      ),
    );
  }
  if (decoded['format'] != _format) {
    throw const FormatException('Unsupported preferences format version.');
  }
  final result = <String, Object?>{};
  final types = <String, String>{};
  for (final entry in (decoded['values'] as Map<String, dynamic>).entries) {
    final item = entry.value;
    if (item is! List || item.length != 2) {
      throw const FormatException('Malformed preference value.');
    }
    final Object? value = item[1];
    if (item[0] is! String) {
      throw const FormatException('Malformed preference type.');
    }
    types[entry.key] = item[0] as String;
    result[entry.key] = switch (item[0]) {
      'string' when value is String => value,
      'bool' when value is bool => value,
      'int' when value is String => int.parse(value),
      'double' when value is String => double.parse(value),
      'list'
          when value is List &&
              value.every((Object? item) => item is String) =>
        List<String>.from(value),
      _ => throw const FormatException('Malformed preference type.'),
    };
  }
  return DecodedPreferences(copyPreferences(result), types);
}