Skip to content

Commit

Permalink
feat(luthor_generator): Add support for dart_mappable classes (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
exaby73 authored Oct 28, 2024
1 parent ee5fae7 commit 584f3b1
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 10 deletions.
2 changes: 0 additions & 2 deletions packages/luthor_generator/example/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,3 @@ analyzer:
errors:
invalid_annotation_target: ignore
prefer_single_quotes: true
exclude:
- lib/*.g.dart
22 changes: 22 additions & 0 deletions packages/luthor_generator/example/lib/without_freezed.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import 'package:dart_mappable/dart_mappable.dart';
import 'package:luthor/luthor.dart';

part 'without_freezed.mapper.dart';

part 'without_freezed.g.dart';

@luthor
Expand All @@ -18,4 +21,23 @@ class WithoutFreezed {
age: json['age'] as int,
);
}

Map<String, dynamic> toJson() {
return {
'name': name,
'age': age,
};
}
}

@luthor
@MappableClass()
class WithDartMappable with WithDartMappableMappable {
final String email;
final String password;

const WithDartMappable({
@isEmail required this.email,
@HasMin(8) required this.password,
});
}
15 changes: 15 additions & 0 deletions packages/luthor_generator/example/lib/without_freezed.g.dart

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

122 changes: 122 additions & 0 deletions packages/luthor_generator/example/lib/without_freezed.mapper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter

part of 'without_freezed.dart';

class WithDartMappableMapper extends ClassMapperBase<WithDartMappable> {
WithDartMappableMapper._();

static WithDartMappableMapper? _instance;
static WithDartMappableMapper ensureInitialized() {
if (_instance == null) {
MapperContainer.globals.use(_instance = WithDartMappableMapper._());
}
return _instance!;
}

@override
final String id = 'WithDartMappable';

static String _$email(WithDartMappable v) => v.email;
static const Field<WithDartMappable, String> _f$email =
Field('email', _$email);
static String _$password(WithDartMappable v) => v.password;
static const Field<WithDartMappable, String> _f$password =
Field('password', _$password);

@override
final MappableFields<WithDartMappable> fields = const {
#email: _f$email,
#password: _f$password,
};

static WithDartMappable _instantiate(DecodingData data) {
return WithDartMappable(
email: data.dec(_f$email), password: data.dec(_f$password));
}

@override
final Function instantiate = _instantiate;

static WithDartMappable fromMap(Map<String, dynamic> map) {
return ensureInitialized().decodeMap<WithDartMappable>(map);
}

static WithDartMappable fromJson(String json) {
return ensureInitialized().decodeJson<WithDartMappable>(json);
}
}

mixin WithDartMappableMappable {
String toJson() {
return WithDartMappableMapper.ensureInitialized()
.encodeJson<WithDartMappable>(this as WithDartMappable);
}

Map<String, dynamic> toMap() {
return WithDartMappableMapper.ensureInitialized()
.encodeMap<WithDartMappable>(this as WithDartMappable);
}

WithDartMappableCopyWith<WithDartMappable, WithDartMappable, WithDartMappable>
get copyWith => _WithDartMappableCopyWithImpl(
this as WithDartMappable, $identity, $identity);
@override
String toString() {
return WithDartMappableMapper.ensureInitialized()
.stringifyValue(this as WithDartMappable);
}

@override
bool operator ==(Object other) {
return WithDartMappableMapper.ensureInitialized()
.equalsValue(this as WithDartMappable, other);
}

@override
int get hashCode {
return WithDartMappableMapper.ensureInitialized()
.hashValue(this as WithDartMappable);
}
}

extension WithDartMappableValueCopy<$R, $Out>
on ObjectCopyWith<$R, WithDartMappable, $Out> {
WithDartMappableCopyWith<$R, WithDartMappable, $Out>
get $asWithDartMappable =>
$base.as((v, t, t2) => _WithDartMappableCopyWithImpl(v, t, t2));
}

abstract class WithDartMappableCopyWith<$R, $In extends WithDartMappable, $Out>
implements ClassCopyWith<$R, $In, $Out> {
$R call({String? email, String? password});
WithDartMappableCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(
Then<$Out2, $R2> t);
}

class _WithDartMappableCopyWithImpl<$R, $Out>
extends ClassCopyWithBase<$R, WithDartMappable, $Out>
implements WithDartMappableCopyWith<$R, WithDartMappable, $Out> {
_WithDartMappableCopyWithImpl(super.value, super.then, super.then2);

@override
late final ClassMapperBase<WithDartMappable> $mapper =
WithDartMappableMapper.ensureInitialized();
@override
$R call({String? email, String? password}) => $apply(FieldCopyWithData({
if (email != null) #email: email,
if (password != null) #password: password
}));
@override
WithDartMappable $make(CopyWithData data) => WithDartMappable(
email: data.get(#email, or: $value.email),
password: data.get(#password, or: $value.password));

@override
WithDartMappableCopyWith<$R2, WithDartMappable, $Out2> $chain<$R2, $Out2>(
Then<$Out2, $R2> t) =>
_WithDartMappableCopyWithImpl($value, $cast, t);
}
2 changes: 2 additions & 0 deletions packages/luthor_generator/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ environment:
sdk: ">=3.0.0 <4.0.0"

dependencies:
dart_mappable: ^4.3.0
freezed_annotation: ^2.4.1
json_annotation: ^4.9.0
luthor: any
Expand All @@ -23,3 +24,4 @@ dev_dependencies:
build_runner: ^2.4.11
freezed: ^2.5.3
json_serializable: ^6.8.0
dart_mappable_builder: ^4.3.0
2 changes: 2 additions & 0 deletions packages/luthor_generator/lib/checkers.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'package:dart_mappable/dart_mappable.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:luthor/luthor.dart';
import 'package:source_gen/source_gen.dart';

const luthorChecker = TypeChecker.fromRuntime(Luthor);
const jsonKeyChecker = TypeChecker.fromRuntime(JsonKey);
const defaultChecker = TypeChecker.fromRuntime(Default);
const dartMappableChecker = TypeChecker.fromRuntime(MappableClass);

const isEmailChecker = TypeChecker.fromRuntime(IsEmail);
const isDateTimeChecker = TypeChecker.fromRuntime(IsDateTime);
Expand Down
36 changes: 29 additions & 7 deletions packages/luthor_generator/lib/generators/luthor_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ class LuthorGenerator extends GeneratorForAnnotation<Luthor> {
final name = element.name;
final constructor = element.constructors.first;

final isDartMappableClass =
getAnnotation(dartMappableChecker, element) != null;
final hasFromJsonCtor = element.constructors.any(
(element) => element.isFactory && element.name == 'fromJson',
);
if (!hasFromJsonCtor) {
if (!hasFromJsonCtor && !isDartMappableClass) {
throw InvalidGenerationSourceError(
'Luthor can only be applied to classes with a factory fromJson constructor',
element: element,
Expand Down Expand Up @@ -61,24 +63,44 @@ class LuthorGenerator extends GeneratorForAnnotation<Luthor> {

buffer.write('});\n\n');

_writeValidateMethod(buffer, name);
_writeValidateMethod(buffer, name, isDartMappable: isDartMappableClass);

_writeExtension(buffer, name);
_writeExtension(buffer, name, isDartMappable: isDartMappableClass);

return buffer.toString();
}

void _writeValidateMethod(StringBuffer buffer, String name) {
void _writeValidateMethod(
StringBuffer buffer,
String name, {
required bool isDartMappable,
}) {
late final String fromJsonString;
if (isDartMappable) {
fromJsonString = '${name}Mapper.fromMap';
} else {
fromJsonString = '$name.fromJson';
}
buffer.write(
'SchemaValidationResult<$name> \$${name}Validate(Map<String, dynamic> json) => '
'\$${name}Schema.validateSchema(json, fromJson: $name.fromJson);',
'\$${name}Schema.validateSchema(json, fromJson: $fromJsonString);',
);
}

void _writeExtension(StringBuffer buffer, String name) {
void _writeExtension(
StringBuffer buffer,
String name, {
required bool isDartMappable,
}) {
late final String toJsonString;
if (isDartMappable) {
toJsonString = 'toMap()';
} else {
toJsonString = 'toJson()';
}
buffer.write(
'\n\nextension ${name}ValidationExtension on $name {\n'
' SchemaValidationResult<$name> validateSelf() => \$${name}Validate(toJson());\n'
' SchemaValidationResult<$name> validateSelf() => \$${name}Validate($toJsonString);\n'
'}\n',
);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/luthor_generator/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: luthor_generator
description: Generate luthor schemas with Freezed
description: Generate luthor schemas
version: 0.4.6
repository: https://github.com/exaby73/luthor/tree/main/packages/luthor_generator
homepage: https://luthor.netlify.app
Expand All @@ -11,6 +11,7 @@ dependencies:
analyzer: ^6.3.0
build: ^2.4.1
collection: ^1.18.0
dart_mappable: ^4.3.0
freezed_annotation: ^2.4.3
json_annotation: ^4.8.1
luthor: ^0.4.3
Expand Down

0 comments on commit 584f3b1

Please sign in to comment.