Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions hive/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 2.20.0

- Adds `HiveConverter` for custom field conversion during TypeAdapter generation (similar to `json_serializable`'s `JsonConverter`)
- Adds `converters` parameter to `GenerateAdapters`

## 2.19.3

- IsolatedHive: Handles stale send ports on hot restart
Expand Down
1 change: 1 addition & 0 deletions hive/lib/hive.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export 'src/box_collection/box_collection_stub.dart'
export 'src/object/hive_object.dart' show HiveObject, HiveObjectMixin;

export 'src/annotations/generate_adapters.dart';
export 'src/annotations/hive_converter.dart';
export 'src/annotations/hive_field.dart';
export 'src/annotations/hive_type.dart';
export 'src/binary/binary_reader.dart';
Expand Down
8 changes: 8 additions & 0 deletions hive/lib/src/annotations/generate_adapters.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:hive_ce/src/annotations/hive_converter.dart';
import 'package:meta/meta.dart';

/// Annotation to generate TypeAdapters for the given [specs]
Expand All @@ -9,6 +10,7 @@ class GenerateAdapters {
this.specs, {
this.firstTypeId = 0,
this.reservedTypeIds = const {},
this.converters = const [],
});
// coverage:ignore-end

Expand All @@ -22,6 +24,12 @@ class GenerateAdapters {
///
/// These type ids will be skipped during generation
final Set<int> reservedTypeIds;

/// A list of [HiveConverter]s to apply when generating adapters for [specs]
///
/// A converter is selected by matching its type parameter [T] against each
/// field type. See [HiveConverter] for details.
final List<HiveConverter> converters;
}

/// Configuration that specifies the generation of a TypeAdapter
Expand Down
42 changes: 42 additions & 0 deletions hive/lib/src/annotations/hive_converter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'package:meta/meta.dart';

/// Implement this class to provide custom converters for a specific [Type].
///
/// [T] is the data type you'd like to convert to and from.
///
/// [S] is the type of the value stored in Hive. It must be a type Hive can
/// write natively (such as [String], [int], [List], [Set], or [Map]) or a type
/// with a registered [TypeAdapter].
///
/// Pass converter instances to [GenerateAdapters.converters]:
///
/// ```dart
/// class UriConverter implements HiveConverter<Uri, String> {
/// const UriConverter();
///
/// @override
/// Uri fromHive(String hive) => Uri.parse(hive);
///
/// @override
/// String toHive(Uri object) => object.toString();
/// }
///
/// @GenerateAdapters(
/// [AdapterSpec<Website>()],
/// converters: [UriConverter()],
/// )
/// class Website {
/// final Uri url;
/// }
/// ```
@immutable
abstract class HiveConverter<T, S> {
/// Constructor
const HiveConverter();

/// Convert a value read from Hive into [T].
T fromHive(S hive);

/// Convert a [T] value into a value Hive can write.
S toHive(T object);
}
2 changes: 1 addition & 1 deletion hive/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: hive_ce
description: Hive Community Edition - A spiritual continuation of Hive v2
version: 2.19.3
version: 2.20.0
homepage: https://github.com/IO-Design-Team/hive_ce/tree/main/hive

topics:
Expand Down
5 changes: 5 additions & 0 deletions hive_generator/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.12.0

- Adds support for `HiveConverter` via `GenerateAdapters.converters` (similar to `json_serializable`'s `JsonConverter`)
- Supports generic converters with inferred type arguments (e.g. `WrappedListConverter<String>`)

## 1.11.3

- Upgrades `analyzer` to `14.0.0`
Expand Down
38 changes: 31 additions & 7 deletions hive_generator/example/lib/hive/hive_adapters.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
import 'package:hive_ce/hive_ce.dart';
import 'package:meta/meta.dart';

@GenerateAdapters([
AdapterSpec<ClassSpec1>(),
AdapterSpec<ClassSpec2>(),
AdapterSpec<ClassSpec3>(),
AdapterSpec<ClassSpec4>(),
AdapterSpec<EnumSpec>(),
], firstTypeId: 50)
@GenerateAdapters(
[
AdapterSpec<ClassSpec1>(),
AdapterSpec<ClassSpec2>(),
AdapterSpec<ClassSpec3>(),
AdapterSpec<ClassSpec4>(),
AdapterSpec<EnumSpec>(),
AdapterSpec<ClassSpec5>(),
],
firstTypeId: 50,
converters: [UriConverter()],
)
part 'hive_adapters.g.dart';

/// Example converter matching json_serializable's JsonConverter pattern
class UriConverter implements HiveConverter<Uri, String> {
const UriConverter();

@override
Uri fromHive(String hive) => Uri.parse(hive);

@override
String toHive(Uri object) => object.toString();
}

@immutable
class ClassSpec1 {
final int value;
Expand Down Expand Up @@ -41,3 +57,11 @@ enum EnumSpec {

EnumSpec get getter => EnumSpec.value2;
}

@immutable
class ClassSpec5 {
final Uri url;
final Uri? optionalUrl;

const ClassSpec5(this.url, this.optionalUrl);
}
43 changes: 43 additions & 0 deletions hive_generator/example/lib/hive/hive_adapters.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion hive_generator/example/lib/hive/hive_adapters.g.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Generated by Hive CE
# Manual modifications may be necessary for certain migrations
# Check in to version control
nextTypeId: 55
nextTypeId: 56
types:
ClassSpec1:
typeId: 50
Expand Down Expand Up @@ -39,6 +39,14 @@ types:
fields:
value:
index: 0
ClassSpec5:
typeId: 55
nextIndex: 2
fields:
url:
index: 0
optionalUrl:
index: 1
ClassSpec4:
typeId: 54
nextIndex: 0
Expand Down
2 changes: 2 additions & 0 deletions hive_generator/example/lib/hive/hive_registrar.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extension HiveRegistrar on HiveInterface {
registerAdapter(ClassSpec2Adapter());
registerAdapter(ClassSpec3Adapter());
registerAdapter(ClassSpec4Adapter());
registerAdapter(ClassSpec5Adapter());
registerAdapter(ConstructorDefaultsAdapter());
registerAdapter(EmptyClassAdapter());
registerAdapter(Enum1Adapter());
Expand All @@ -34,6 +35,7 @@ extension IsolatedHiveRegistrar on IsolatedHiveInterface {
registerAdapter(ClassSpec2Adapter());
registerAdapter(ClassSpec3Adapter());
registerAdapter(ClassSpec4Adapter());
registerAdapter(ClassSpec5Adapter());
registerAdapter(ConstructorDefaultsAdapter());
registerAdapter(EmptyClassAdapter());
registerAdapter(Enum1Adapter());
Expand Down
8 changes: 6 additions & 2 deletions hive_generator/lib/src/adapter_builder/adapter_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,16 @@ abstract class AdapterBuilder {
/// TODO: Document this!
final List<AdapterField> setters;

/// Converters from [GenerateAdapters.converters]
final List<DartObject> converters;

/// TODO: Document this!
const AdapterBuilder(
this.cls,
this.getters, [
this.getters, {
this.setters = const <AdapterField>[],
]);
this.converters = const [],
});

/// TODO: Document this!
String buildRead();
Expand Down
69 changes: 64 additions & 5 deletions hive_generator/lib/src/adapter_builder/class_adapter_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,18 @@ import 'package:hive_ce_generator/src/adapter_builder/adapter_builder.dart';
import 'package:hive_ce_generator/src/helper/helper.dart';
import 'package:source_gen/source_gen.dart';

import 'package:hive_ce_generator/src/helper/hive_converter_helper.dart';
import 'package:hive_ce_generator/src/helper/type_helper.dart';

/// TODO: Document this!
class ClassAdapterBuilder extends AdapterBuilder {
/// TODO: Document this!
const ClassAdapterBuilder(
super.cls,
super.getters,
super.getters, {
super.setters,
);
super.converters,
});

/// [TypeChecker] for [HiveList].
final hiveListChecker =
Expand Down Expand Up @@ -121,6 +123,13 @@ class ClassAdapterBuilder extends AdapterBuilder {
}

String _cast(DartType type, String variable) {
final converter = _converterFor(type);
if (converter != null) {
final call =
'${converter.access}.fromHive($variable as ${converter.hiveType})';
return _nullSafe(type, variable, call);
}

final suffix = _suffixFromType(type);
if (hiveListChecker.isAssignableFromType(type)) {
return '($variable as HiveList$suffix)$suffix.castHiveList()';
Expand Down Expand Up @@ -155,7 +164,8 @@ class ClassAdapterBuilder extends AdapterBuilder {
final paramType = type as ParameterizedType;
final arg = paramType.typeArguments.first;
final suffix = _accessorSuffixFromType(type);
if (isMapOrIterable(arg) && !isUint8List(arg)) {
if (isMapOrIterable(arg) && !isUint8List(arg) ||
_converterFor(arg) != null) {
var cast = '';
// Using assignable because Set? is not exactly Set
if (setChecker.isAssignableFromType(type)) {
Expand All @@ -176,7 +186,10 @@ class ClassAdapterBuilder extends AdapterBuilder {
final arg1 = paramType.typeArguments[0];
final arg2 = paramType.typeArguments[1];
final suffix = _accessorSuffixFromType(type);
if (isMapOrIterable(arg1) || isMapOrIterable(arg2)) {
if (isMapOrIterable(arg1) ||
isMapOrIterable(arg2) ||
_converterFor(arg1) != null ||
_converterFor(arg2) != null) {
return '$suffix.map((dynamic k, dynamic v)=>'
'MapEntry(${_cast(arg1, 'k')},${_cast(arg2, 'v')}))';
} else {
Expand All @@ -195,12 +208,58 @@ class ClassAdapterBuilder extends AdapterBuilder {
for (final field in getters) {
code.writeln('''
..writeByte(${field.index})
..write(obj.${field.name})''');
..write(${_writeValue(field.type, 'obj.${field.name}')})''');
}
code.writeln(';');

return code.toString();
}

String _writeValue(DartType type, String expression) {
final converter = _converterFor(type);
if (converter != null) {
final nullable = type.nullabilitySuffix == NullabilitySuffix.question;
final value =
nullable ? '$expression as ${converter.fieldType}' : expression;
return _nullSafe(
type,
expression,
'${converter.access}.toHive($value)',
);
}

if (setChecker.isAssignableFromType(type) ||
(iterableChecker.isAssignableFromType(type) && !isUint8List(type))) {
final arg = (type as ParameterizedType).typeArguments.first;
final inner = _writeValue(arg, 'e');
if (inner == 'e') return expression;
final suffix = _accessorSuffixFromType(type);
final mapped = '$expression$suffix.map((e) => $inner)';
return setChecker.isAssignableFromType(type)
? '$mapped.toSet()'
: '$mapped.toList()';
}

if (mapChecker.isAssignableFromType(type)) {
final args = (type as ParameterizedType).typeArguments;
final key = _writeValue(args[0], 'k');
final value = _writeValue(args[1], 'v');
if (key == 'k' && value == 'v') return expression;
final suffix = _accessorSuffixFromType(type);
return '$expression$suffix.map((dynamic k, dynamic v) => '
'MapEntry($key, $value))';
}

return expression;
}

String _nullSafe(DartType type, String expression, String call) {
if (type.nullabilitySuffix != NullabilitySuffix.question) return call;
return '$expression == null ? null : $call';
}

HiveConverterMatch? _converterFor(DartType type) =>
findHiveConverter(type, converters);
}

/// Suffix to use when accessing a field in [type].
Expand Down
1 change: 1 addition & 0 deletions hive_generator/lib/src/generator/adapters_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class AdaptersGenerator extends GeneratorForAnnotation<GenerateAdapters> {
typeId: schemaType.typeId,
schema: schemaType,
ignoredFields: spec.ignoredFields,
converters: revived.converters,
);

content.write(result.content);
Expand Down
Loading
Loading