| // Copyright 2019 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:convert' hide json; |
| import 'dart:convert' as convert show json; |
| |
| import 'package:json_annotation/json_annotation.dart'; |
| |
| /// A converter for tags. |
| /// |
| /// The JSON format is: |
| /// |
| /// ```json |
| /// [ |
| /// { |
| /// "key": "tag_key", |
| /// "value": "tag_value" |
| /// } |
| /// ] |
| /// ``` |
| /// |
| /// Which is flattened out as a `Map<String, List<String>>`. |
| class TagsConverter implements JsonConverter<Map<String?, List<String?>>?, List<dynamic>?> { |
| const TagsConverter(); |
| |
| @override |
| Map<String?, List<String?>>? fromJson(List<dynamic>? json) { |
| if (json == null) { |
| return null; |
| } |
| final Map<String?, List<String?>> result = <String?, List<String?>>{}; |
| for (Map<String, dynamic> tag in json.cast<Map<String, dynamic>>()) { |
| final String? key = tag['key'] as String?; |
| result[key] ??= <String?>[]; |
| result[key]!.add(tag['value'] as String?); |
| } |
| return result; |
| } |
| |
| @override |
| List<Map<String, dynamic>>? toJson(Map<String?, List<String?>>? object) { |
| if (object == null) { |
| return null; |
| } |
| if (object.isEmpty) { |
| return const <Map<String, List<String>>>[]; |
| } |
| final List<Map<String, String>> result = <Map<String, String>>[]; |
| for (String? key in object.keys) { |
| if (key == null) { |
| continue; |
| } |
| final List<String?>? values = object[key]; |
| if (values == null) { |
| continue; |
| } |
| for (String? value in values) { |
| if (value == null) { |
| continue; |
| } |
| result.add(<String, String>{ |
| 'key': key, |
| 'value': value, |
| }); |
| } |
| } |
| return result; |
| } |
| } |
| |
| /// A converter for a "binary" JSON field. |
| /// |
| /// Encodes and decodes a String to and from base64. |
| class Base64Converter implements JsonConverter<String, String> { |
| const Base64Converter(); |
| |
| @override |
| String fromJson(String json) { |
| return utf8.decode(base64.decode(json)); |
| } |
| |
| @override |
| String toJson(String object) { |
| return base64.encode(utf8.encode(object)); |
| } |
| } |
| |
| /// A converter for "timestamp" fields encoded as microseconds since epoch. |
| class MicrosecondsSinceEpochConverter implements JsonConverter<DateTime?, String?> { |
| const MicrosecondsSinceEpochConverter(); |
| |
| @override |
| DateTime? fromJson(String? json) { |
| if (json == null) { |
| return null; |
| } |
| return DateTime.fromMicrosecondsSinceEpoch(int.parse(json)); |
| } |
| |
| @override |
| String? toJson(DateTime? object) { |
| if (object == null) { |
| return null; |
| } |
| return object.microsecondsSinceEpoch.toString(); |
| } |
| } |
| |
| /// A converter for "timestamp" fields encoded as seconds since epoch. |
| class SecondsSinceEpochConverter implements JsonConverter<DateTime?, String?> { |
| const SecondsSinceEpochConverter(); |
| |
| @override |
| DateTime? fromJson(String? json) { |
| if (json == null) { |
| return null; |
| } |
| return DateTime.fromMillisecondsSinceEpoch(int.parse(json) * 1000); |
| } |
| |
| @override |
| String? toJson(DateTime? dateTime) { |
| if (dateTime == null) { |
| return null; |
| } |
| final int secondsSinceEpoch = dateTime.millisecondsSinceEpoch ~/ 1000; |
| return secondsSinceEpoch.toString(); |
| } |
| } |
| |
| /// A converter for boolean fields encoded as strings. |
| class BoolConverter implements JsonConverter<bool?, String?> { |
| const BoolConverter(); |
| |
| @override |
| bool? fromJson(String? json) { |
| if (json == null) { |
| return null; |
| } |
| return json.toLowerCase() == 'true'; |
| } |
| |
| @override |
| String? toJson(bool? value) { |
| if (value == null) { |
| return null; |
| } |
| return '$value'; |
| } |
| } |
| |
| /// A converter for fields with nested JSON objects in String format. |
| class NestedJsonConverter implements JsonConverter<Map<String, dynamic>?, String?> { |
| const NestedJsonConverter(); |
| |
| @override |
| Map<String, dynamic>? fromJson(String? json) { |
| if (json == null) { |
| return null; |
| } |
| return convert.json.decode(json) as Map<String, dynamic>?; |
| } |
| |
| @override |
| String? toJson(Map<String, dynamic>? object) { |
| if (object == null) { |
| return null; |
| } |
| return convert.json.encode(object); |
| } |
| } |
| |
| const Map<String, int> _months = <String, int>{ |
| 'Jan': 1, |
| 'Feb': 2, |
| 'Mar': 3, |
| 'Apr': 4, |
| 'May': 5, |
| 'Jun': 6, |
| 'Jul': 7, |
| 'Aug': 8, |
| 'Sep': 9, |
| 'Oct': 10, |
| 'Nov': 11, |
| 'Dec': 12, |
| }; |
| |
| /// Convert a DateTime format from Gerrit to [DateTime]. |
| /// |
| /// Example format is "Wed Jun 07 22:54:06 2023 +0000" |
| class GerritDateTimeConverter implements JsonConverter<DateTime?, String?> { |
| const GerritDateTimeConverter(); |
| |
| @override |
| DateTime? fromJson(String? json) { |
| if (json == null) { |
| return null; |
| } |
| |
| final DateTime? date = DateTime.tryParse(json); |
| if (date != null) { |
| return date; |
| } |
| |
| json = json.substring(4); // Trim day of the week |
| final List<String> parts = json.split(' '); |
| final int month = _months[parts[0]]!; |
| final int year = int.parse(parts[3]); |
| final int day = int.parse(parts[1]); |
| final List<String> time = parts[2].split(':'); |
| final int hours = int.parse(time[0]); |
| final int minutes = int.parse(time[1]); |
| final int seconds = int.parse(time[2]); |
| |
| return DateTime(year, month, day, hours, minutes, seconds); |
| } |
| |
| @override |
| String? toJson(DateTime? object) { |
| return object?.toIso8601String(); |
| } |
| } |