blob: f10d6ac2679858dd31614a2a288d80546e95c813 [file] [log] [blame]
// 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();
}
}