Add 'packages/serialize/' from commit 'be6a3669cca34cd83d189a1169edf6f381101cd8'
git-subtree-dir: packages/serialize git-subtree-mainline:f7c6ebf200
git-subtree-split:be6a3669cc
This commit is contained in:
commit
42a86be549
51 changed files with 4529 additions and 0 deletions
23
packages/serialize/.gitignore
vendored
Normal file
23
packages/serialize/.gitignore
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Created by .ignore support plugin (hsz.mobi)
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
.idea/**/mongoSettings.xml
|
||||
*.iws
|
||||
/out/
|
||||
.idea_modules/
|
||||
atlassian-ide-plugin.xml
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
.dart_tool
|
8
packages/serialize/.idea/modules.xml
Normal file
8
packages/serialize/.idea/modules.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/serialize.iml" filepath="$PROJECT_DIR$/.idea/serialize.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,8 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="tests in angel_serialize_generator" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true" nameIsGenerated="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/angel_serialize_generator" />
|
||||
<option name="scope" value="FOLDER" />
|
||||
<option name="testRunnerOptions" value="-j 4" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -0,0 +1,7 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="tests in enum_test.dart" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true" nameIsGenerated="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/angel_serialize_generator/test/enum_test.dart" />
|
||||
<option name="testRunnerOptions" value="-j4" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
19
packages/serialize/.idea/serialize.iml
Normal file
19
packages/serialize/.idea/serialize.iml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$/angel_serialize">
|
||||
<excludeFolder url="file://$MODULE_DIR$/angel_serialize/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/angel_serialize/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/angel_serialize/build" />
|
||||
</content>
|
||||
<content url="file://$MODULE_DIR$/angel_serialize_generator">
|
||||
<excludeFolder url="file://$MODULE_DIR$/angel_serialize_generator/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/angel_serialize_generator/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/angel_serialize_generator/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
</component>
|
||||
</module>
|
6
packages/serialize/.idea/vcs.xml
Normal file
6
packages/serialize/.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
4
packages/serialize/.travis.yml
Normal file
4
packages/serialize/.travis.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
language: dart
|
||||
script: bash tool/.travis.sh
|
||||
dart:
|
||||
- stable
|
427
packages/serialize/README.md
Normal file
427
packages/serialize/README.md
Normal file
|
@ -0,0 +1,427 @@
|
|||
# serialize
|
||||
|
||||
[![Pub](https://img.shields.io/pub/v/angel_serialize.svg)](https://pub.dartlang.org/packages/angel_serialize)
|
||||
[![build status](https://travis-ci.org/angel-dart/serialize.svg)](https://travis-ci.org/angel-dart/serialize)
|
||||
|
||||
Source-generated serialization for Dart objects. This package uses `package:source_gen` to eliminate
|
||||
the time you spend writing boilerplate serialization code for your models.
|
||||
`package:angel_serialize` also powers `package:angel_orm`.
|
||||
|
||||
- [Usage](#usage)
|
||||
- [Models](#models)
|
||||
- [Subclasses](#subclasses)
|
||||
- [Field Aliases](#aliases)
|
||||
- [Excluding Keys](#excluding-keys)
|
||||
- [Required Fields](#required-fields)
|
||||
- [Adding Annotations to Generated Classes](#adding-annotations-to-generated-classes)
|
||||
- [Custom Serializers](#custom-serializers)
|
||||
- [Serialization](#serializaition)
|
||||
- [Nesting](#nesting)
|
||||
- [ID and Date Fields](#id-and-dates)
|
||||
- [Binary Data](#binary-data)
|
||||
- [TypeScript Definition Generator](#typescript-definitions)
|
||||
- [Constructor Parameters](#constructor-parameters)
|
||||
|
||||
# Usage
|
||||
|
||||
In your `pubspec.yaml`, you need to install the following dependencies:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
angel_model: ^1.0.0
|
||||
angel_serialize: ^2.0.0
|
||||
dev_dependencies:
|
||||
angel_serialize_generator: ^2.0.0
|
||||
build_runner: ^1.0.0
|
||||
```
|
||||
|
||||
With the recent updates to `package:build_runner`, you can build models automatically,
|
||||
anywhere in your project structure,
|
||||
by running `pub run build_runner build`.
|
||||
|
||||
To tweak this:
|
||||
https://pub.dartlang.org/packages/build_config
|
||||
|
||||
If you want to watch for file changes and re-build when necessary, replace the `build` call
|
||||
with a call to `watch`. They take the same parameters.
|
||||
|
||||
# Models
|
||||
|
||||
There are a few changes opposed to normal Model classes. You need to add a `@serializable` annotation to your model
|
||||
class to have it serialized, and a serializable model class's name should also start
|
||||
with a leading underscore.
|
||||
|
||||
In addition, you may consider using an `abstract` class to ensure immutability
|
||||
of models.
|
||||
|
||||
Rather you writing the public class, `angel_serialize` does it for you. This means that the main class can have
|
||||
its constructors automatically generated, in addition into serialization functions.
|
||||
|
||||
For example, say we have a `Book` model. Create a class named `_Book`:
|
||||
|
||||
```dart
|
||||
library angel_serialize.test.models.book;
|
||||
|
||||
import 'package:angel_model/angel_model.dart';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
part 'book.g.dart';
|
||||
|
||||
@serializable
|
||||
abstract class _Book extends Model {
|
||||
String get author;
|
||||
|
||||
@SerializableField(defaultValue: '[Untitled]')
|
||||
String get title;
|
||||
|
||||
String get description;
|
||||
|
||||
int get pageCount;
|
||||
|
||||
BookType get type;
|
||||
}
|
||||
|
||||
/// It even supports enums!
|
||||
enum BookType {
|
||||
fiction,
|
||||
nonFiction
|
||||
}
|
||||
```
|
||||
|
||||
The following file will be generated:
|
||||
|
||||
- `book.g.dart`
|
||||
|
||||
Producing these classes:
|
||||
|
||||
- `Book`: Extends or implements `_Book`; may be `const`-enabled.
|
||||
- `BookSerializer`: static functionality for serializing `Book` models.
|
||||
- `BookFields`: The names of all fields from the `Book` model, statically-available.
|
||||
- `BookEncoder`: Allows `BookSerializer` to extend `Codec<Book, Map>`.
|
||||
- `BookDecoder`: Also allows `BookSerializer` to extend `Codec<Book, Map>`.
|
||||
|
||||
And the following other features:
|
||||
- `bookSerializer`: A top-level, `const` instance of `BookSerializer`.
|
||||
- `Book.toString`: Prints out all of a `Book` instance's fields.
|
||||
|
||||
# Serialization
|
||||
|
||||
You can use the generated files as follows:
|
||||
|
||||
```dart
|
||||
myFunction() {
|
||||
var warAndPeace = new Book(
|
||||
author: 'Leo Tolstoy',
|
||||
title: 'War and Peace',
|
||||
description: 'You will cry after reading this.',
|
||||
pageCount: 1225
|
||||
);
|
||||
|
||||
// Easily serialize models into Maps
|
||||
var map = BookSerializer.toMap(warAndPeace);
|
||||
|
||||
// Also deserialize from Maps
|
||||
var book = BookSerializer.fromMap(map);
|
||||
print(book.title); // 'War and Peace'
|
||||
|
||||
// For compatibility with `JSON.encode`, a `toJson` method
|
||||
// is included that forwards to `BookSerializer.toMap`:
|
||||
expect(book.toJson(), map);
|
||||
|
||||
// Generated classes act as value types, and thus can be compared.
|
||||
expect(BookSerializer.fromMap(map), equals(warAndPeace));
|
||||
}
|
||||
```
|
||||
|
||||
As of `2.0.2`, the generated output also includes information
|
||||
about the serialized names of keys on your model class.
|
||||
|
||||
```dart
|
||||
myOtherFunction() {
|
||||
// Relying on the serialized key of a field? No worries.
|
||||
map[BookFields.author] = 'Zora Neale Hurston';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Customizing Serialization
|
||||
|
||||
Currently, these serialization methods are supported:
|
||||
|
||||
- to `Map`
|
||||
- to JSON
|
||||
- to TypeScript definitions
|
||||
|
||||
You can customize these by means of `serializers`:
|
||||
|
||||
```dart
|
||||
@Serializable(serializers: const [Serializers.map, Serializers.json])
|
||||
class _MyClass extends Model {}
|
||||
```
|
||||
|
||||
## Subclasses
|
||||
`angel_serialize` pulls in fields from parent classes, as well as
|
||||
implemented interfaces, so it is extremely easy to share attributes among
|
||||
model classes:
|
||||
|
||||
```dart
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
part 'subclass.g.dart';
|
||||
|
||||
@serializable
|
||||
class _Animal {
|
||||
@notNull
|
||||
String genus;
|
||||
@notNull
|
||||
String species;
|
||||
}
|
||||
|
||||
@serializable
|
||||
class _Bird extends _Animal {
|
||||
@DefaultsTo(false)
|
||||
bool isSparrow;
|
||||
}
|
||||
|
||||
var saxaulSparrow = Bird(
|
||||
genus: 'Passer',
|
||||
species: 'ammodendri',
|
||||
isSparrow: true,
|
||||
);
|
||||
```
|
||||
|
||||
## Aliases
|
||||
|
||||
Whereas Dart fields conventionally are camelCased, most database columns
|
||||
tend to be snake_cased. This is not a problem, because we can define an alias
|
||||
for a field.
|
||||
|
||||
By default `angel_serialize` will transform keys into snake case. Use `alias` to
|
||||
provide a custom name, or pass `autoSnakeCaseNames`: `false` to the builder;
|
||||
|
||||
```dart
|
||||
@serializable
|
||||
abstract class _Spy extends Model {
|
||||
/// Will show up as 'agency_id' in serialized JSON.
|
||||
///
|
||||
/// When deserializing JSON, instead of searching for an 'agencyId' key,
|
||||
/// it will use 'agency_id'.
|
||||
///
|
||||
/// Hooray!
|
||||
String agencyId;
|
||||
|
||||
@SerializableField(alias: 'foo')
|
||||
String someOtherField;
|
||||
}
|
||||
```
|
||||
|
||||
You can also override `autoSnakeCaseNames` per model:
|
||||
|
||||
```dart
|
||||
@Serializable(autoSnakeCaseNames: false)
|
||||
abstract class _OtherCasing extends Model {
|
||||
String camelCasedField;
|
||||
}
|
||||
```
|
||||
|
||||
## Excluding Keys
|
||||
|
||||
In pratice, there may keys that you want to exclude from JSON.
|
||||
To accomplish this, simply annotate them with `@exclude`:
|
||||
|
||||
```dart
|
||||
@serializable
|
||||
abstract class _Whisper extends Model {
|
||||
/// Will never be serialized to JSON
|
||||
@SerializableField(exclude: true)
|
||||
String secret;
|
||||
}
|
||||
```
|
||||
|
||||
There are times, however, when you want to only exclude either serialization
|
||||
or deserialization, but not both. For example, you might want to deserialize
|
||||
passwords from a database without sending them to users as JSON.
|
||||
|
||||
In this case, use `canSerialize` or `canDeserialize`:
|
||||
|
||||
```dart
|
||||
@serializable
|
||||
abstract class _Whisper extends Model {
|
||||
/// Will never be serialized to JSON
|
||||
///
|
||||
/// ... But it can be deserialized
|
||||
@SerializableField(exclude: true, canDeserialize: true)
|
||||
String secret;
|
||||
}
|
||||
```
|
||||
|
||||
## Required Fields
|
||||
|
||||
It is easy to mark a field as required:
|
||||
|
||||
```dart
|
||||
@serializable
|
||||
abstract class _Foo extends Model {
|
||||
@SerializableField(isNullable: false)
|
||||
int myRequiredInt;
|
||||
|
||||
@SerializableField(isNullable: false, errorMessage: 'Custom message')
|
||||
int myOtherRequiredInt;
|
||||
}
|
||||
```
|
||||
|
||||
The given field will be marked as `@required` in the
|
||||
generated constructor, and serializers will check for its
|
||||
presence, throwing a `FormatException` if it is missing.
|
||||
|
||||
## Adding Annotations to Generated Classes
|
||||
There are times when you need the generated class to have annotations affixed to it:
|
||||
|
||||
```dart
|
||||
@Serializable(
|
||||
includeAnnotations: [
|
||||
Deprecated('blah blah blah'),
|
||||
pragma('something...'),
|
||||
]
|
||||
)
|
||||
abstract class _Foo extends Model {}
|
||||
```
|
||||
|
||||
## Custom Serializers
|
||||
`package:angel_serialize` does not cover every known Dart data type; you can add support for your own.
|
||||
Provide `serializer` and `deserializer` arguments to `@SerializableField()` as you see fit.
|
||||
|
||||
They are typically used together. Note that the argument to `deserializer` will always be
|
||||
`dynamic`, while `serializer` can receive the data type in question.
|
||||
|
||||
In such a case, you might want to also provide a `serializesTo` argument.
|
||||
This lets the generator, as well as the ORM, apply the correct (de)serialization rules
|
||||
and validations.
|
||||
|
||||
```dart
|
||||
DateTime _dateFromString(s) => s is String ? HttpDate.parse(s) : null;
|
||||
String _dateToString(DateTime v) => v == null ? null : HttpDate.format(v);
|
||||
|
||||
@serializable
|
||||
abstract class _HttpRequest {
|
||||
@SerializableField(
|
||||
serializer: #_dateToString,
|
||||
deserializer: #_dateFromString,
|
||||
serializesTo: String)
|
||||
DateTime date;
|
||||
}
|
||||
```
|
||||
|
||||
# Nesting
|
||||
|
||||
`angel_serialize` also supports a few types of nesting of `@serializable` classes:
|
||||
|
||||
- As a class member, ex. `Book myField`
|
||||
- As the type argument to a `List`, ex. `List<Book>`
|
||||
- As the second type argument to a `Map`, ex. `Map<String, Book>`
|
||||
|
||||
In other words, the following are all legal, and will be serialized/deserialized.
|
||||
You can use either the underscored name of a child class (ex. `_Book`), or the
|
||||
generated class name (ex `Book`):
|
||||
|
||||
```dart
|
||||
@serializable
|
||||
abstract class _Author extends Model {
|
||||
List<Book> books;
|
||||
Book newestBook;
|
||||
Map<String, Book> booksByIsbn;
|
||||
}
|
||||
```
|
||||
|
||||
If your model (`Author`) depends on a model defined in another file (`Book`),
|
||||
then you will need to generate `book.g.dart` before, `author.g.dart`,
|
||||
**in a separate build action**. This way, the analyzer can resolve the `Book` type.
|
||||
|
||||
# ID and Dates
|
||||
|
||||
This package will automatically generate `id`, `createdAt`, and `updatedAt` fields for you,
|
||||
in the style of an Angel `Model`. This will automatically be generated, **only** for classes
|
||||
extending `Model`.
|
||||
|
||||
# Binary Data
|
||||
|
||||
`package:angel_serialize` also handles `Uint8List` fields, by means of serialization to
|
||||
and from `base64` encoding.
|
||||
|
||||
# TypeScript Definitions
|
||||
|
||||
It is quite common to build frontends with JavaScript and/or TypeScript,
|
||||
so why not generate typings as well?
|
||||
|
||||
To accomplish this, add `Serializers.typescript` to your `@Serializable()` declaration:
|
||||
|
||||
```dart
|
||||
@Serializable(serializers: const [Serializers.map, Serializers.json, Serializers.typescript])
|
||||
class _Foo extends Model {}
|
||||
```
|
||||
|
||||
The aforementioned `_Author` class will generate the following in `author.d.ts`:
|
||||
|
||||
```typescript
|
||||
interface Author {
|
||||
id: string;
|
||||
name: string;
|
||||
age: number;
|
||||
books: Book[];
|
||||
newest_book: Book;
|
||||
created_at: any;
|
||||
updated_at: any;
|
||||
}
|
||||
interface Library {
|
||||
id: string;
|
||||
collection: BookCollection;
|
||||
created_at: any;
|
||||
updated_at: any;
|
||||
}
|
||||
interface BookCollection {
|
||||
[key: string]: Book;
|
||||
}
|
||||
```
|
||||
|
||||
Fields with an `@Exclude()` that specifies `canSerialize: false` will not be present in the
|
||||
TypeScript definition. The rationale for this is that if a field (i.e. `password`) will
|
||||
never be sent to the client, the client shouldn't even know the field exists.
|
||||
|
||||
# Constructor Parameters
|
||||
|
||||
Sometimes, you may need to have custom constructor parameters, for example, when
|
||||
using depedency injection frameworks. For these cases, `angel_serialize` can forward
|
||||
custom constructor parameters.
|
||||
|
||||
The following:
|
||||
|
||||
```dart
|
||||
@serializable
|
||||
abstract class _Bookmark extends _BookmarkBase {
|
||||
@SerializableField(exclude: true)
|
||||
final Book book;
|
||||
|
||||
int get page;
|
||||
String get comment;
|
||||
|
||||
_Bookmark(this.book);
|
||||
}
|
||||
```
|
||||
|
||||
Generates:
|
||||
|
||||
```dart
|
||||
class Bookmark extends _Bookmark {
|
||||
Bookmark(Book book,
|
||||
{this.id,
|
||||
this.page,
|
||||
this.comment,
|
||||
this.createdAt,
|
||||
this.updatedAt})
|
||||
: super(book);
|
||||
|
||||
@override
|
||||
final String id;
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
57
packages/serialize/angel_serialize/.gitignore
vendored
Normal file
57
packages/serialize/angel_serialize/.gitignore
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
../.packages
|
||||
.packages
|
||||
.pub/
|
||||
build/
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
47
packages/serialize/angel_serialize/CHANGELOG.md
Normal file
47
packages/serialize/angel_serialize/CHANGELOG.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
# 2.2.3+3
|
||||
* Add `exclude: true` to `super` call in `Exclude` constructor.
|
||||
|
||||
# 2.2.3+2
|
||||
* Apply `package:pedantic`.
|
||||
|
||||
# 2.2.3+1
|
||||
* Export `json`, `Codec`, and `Converter` from `dart:convert`.
|
||||
|
||||
# 2.2.3
|
||||
* `isNullable` defaults to `true`, and will not change.
|
||||
* Deprecate `@nullable`.
|
||||
* Add `@notNull`.
|
||||
|
||||
# 2.2.2+1
|
||||
* Export commonly-used packages, for the sake of convenience.
|
||||
|
||||
# 2.2.2
|
||||
* Add `HasAlias`, `DefaultsTo`, `nullable` idioms.
|
||||
* `isNullable` defaults to `false` now.
|
||||
|
||||
# 2.2.1
|
||||
* Add `serializesTo`.
|
||||
|
||||
# 2.2.0
|
||||
* Add `@SerializableField`.
|
||||
|
||||
# 2.1.0
|
||||
* Export `hashObjects`.
|
||||
|
||||
# 2.0.4+1
|
||||
* Allow Dart 1 for this annotation-only package.
|
||||
|
||||
# 2.0.4
|
||||
* Added `generatedSerializable`.
|
||||
|
||||
# 2.0.3
|
||||
* Increased the upper SDK boundary.
|
||||
|
||||
# 2.0.2
|
||||
* Added `DefaultValue`.
|
||||
|
||||
# 2.0.1
|
||||
* Added `Serializers.typescript`.
|
||||
|
||||
# 2.0.0
|
||||
* Dart 2+ constraint
|
21
packages/serialize/angel_serialize/LICENSE
Normal file
21
packages/serialize/angel_serialize/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 The Angel Framework
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
5
packages/serialize/angel_serialize/README.md
Normal file
5
packages/serialize/angel_serialize/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# angel_serialize
|
||||
The frontend for Angel model serialization.
|
||||
See documentation in the main project repo:
|
||||
|
||||
https://github.com/angel-dart/serialize
|
4
packages/serialize/angel_serialize/analysis_options.yaml
Normal file
4
packages/serialize/angel_serialize/analysis_options.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
8
packages/serialize/angel_serialize/example/main.dart
Normal file
8
packages/serialize/angel_serialize/example/main.dart
Normal file
|
@ -0,0 +1,8 @@
|
|||
// ignore_for_file: unused_element
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
|
||||
@serializable
|
||||
class _Todo {
|
||||
String text;
|
||||
bool completed;
|
||||
}
|
148
packages/serialize/angel_serialize/lib/angel_serialize.dart
Normal file
148
packages/serialize/angel_serialize/lib/angel_serialize.dart
Normal file
|
@ -0,0 +1,148 @@
|
|||
export 'dart:convert' show json, Codec, Converter;
|
||||
export 'package:angel_model/angel_model.dart';
|
||||
export 'package:collection/collection.dart';
|
||||
export 'package:meta/meta.dart' show required, Required;
|
||||
export 'package:quiver_hashcode/hashcode.dart' show hashObjects;
|
||||
|
||||
/// Excludes a field from being excluded.
|
||||
class Exclude extends SerializableField {
|
||||
const Exclude({bool canDeserialize = false, bool canSerialize = false})
|
||||
: super(
|
||||
exclude: true,
|
||||
canDeserialize: canDeserialize,
|
||||
canSerialize: canSerialize);
|
||||
}
|
||||
|
||||
/// No longer necessary, as this is the default.
|
||||
@deprecated
|
||||
const SerializableField nullable = SerializableField(isNullable: true);
|
||||
|
||||
/// Marks a field as not accepting `null` values.
|
||||
const SerializableField notNull = SerializableField(isNullable: false);
|
||||
|
||||
const Exclude exclude = Exclude();
|
||||
|
||||
/// Shorthand for [SerializableField].
|
||||
class DefaultsTo extends SerializableField {
|
||||
const DefaultsTo(value) : super(defaultValue: value);
|
||||
}
|
||||
|
||||
/// Shorthand for [SerializableField].
|
||||
class HasAlias extends SerializableField {
|
||||
const HasAlias(String name) : super(alias: name);
|
||||
}
|
||||
|
||||
/// Attaches options to a field.
|
||||
class SerializableField {
|
||||
/// An alternative name for this field.
|
||||
final String alias;
|
||||
|
||||
/// A default for this field.
|
||||
final defaultValue;
|
||||
|
||||
/// A custom serializer for this field.
|
||||
final Symbol serializer;
|
||||
|
||||
/// A custom serializer for this field.
|
||||
final Symbol deserializer;
|
||||
|
||||
/// An error message to be printed when the provided value is invalid.
|
||||
final String errorMessage;
|
||||
|
||||
/// Whether this field can be set to `null`.
|
||||
final bool isNullable;
|
||||
|
||||
/// Whether to exclude this field from serialization. Defaults to `false`.
|
||||
final bool exclude;
|
||||
|
||||
/// Whether this field can be serialized, if [exclude] is `true`. Defaults to `false`.
|
||||
final bool canDeserialize;
|
||||
|
||||
/// Whether this field can be serialized, if [exclude] is `true`. Defaults to `false`.
|
||||
final bool canSerialize;
|
||||
|
||||
/// May be used with [serializer] and [deserializer].
|
||||
///
|
||||
/// Specifies the [Type] that this field serializes to.
|
||||
///
|
||||
/// Ex. If you have a field that serializes to a JSON string,
|
||||
/// specify `serializesTo: String`.
|
||||
final Type serializesTo;
|
||||
|
||||
const SerializableField(
|
||||
{this.alias,
|
||||
this.defaultValue,
|
||||
this.serializer,
|
||||
this.deserializer,
|
||||
this.errorMessage,
|
||||
this.isNullable = true,
|
||||
this.exclude = false,
|
||||
this.canDeserialize = false,
|
||||
this.canSerialize = false,
|
||||
this.serializesTo});
|
||||
}
|
||||
|
||||
/// Marks a class as eligible for serialization.
|
||||
class Serializable {
|
||||
const Serializable(
|
||||
{this.serializers = const [Serializers.map, Serializers.json],
|
||||
this.autoSnakeCaseNames = true,
|
||||
// ignore: deprecated_member_use_from_same_package
|
||||
@deprecated this.autoIdAndDateFields = true,
|
||||
this.includeAnnotations = const []});
|
||||
|
||||
/// A list of enabled serialization modes.
|
||||
///
|
||||
/// See [Serializers].
|
||||
final List<int> serializers;
|
||||
|
||||
/// Overrides the setting in `SerializerGenerator`.
|
||||
final bool autoSnakeCaseNames;
|
||||
|
||||
/// Overrides the setting in `JsonModelGenerator`.
|
||||
@deprecated
|
||||
final bool autoIdAndDateFields;
|
||||
|
||||
/// A list of constant members to affix to the generated class.
|
||||
final List includeAnnotations;
|
||||
}
|
||||
|
||||
const Serializable serializable = Serializable();
|
||||
|
||||
/// Used by `package:angel_serialize_generator` to reliably identify generated models.
|
||||
class GeneratedSerializable {
|
||||
const GeneratedSerializable();
|
||||
}
|
||||
|
||||
const GeneratedSerializable generatedSerializable = GeneratedSerializable();
|
||||
|
||||
/// The supported serialization types.
|
||||
abstract class Serializers {
|
||||
/// All supported serialization types.
|
||||
static const List<int> all = [map, json, typescript];
|
||||
|
||||
/// Enable `fromMap` and `toMap` methods on the model class.
|
||||
static const int map = 0;
|
||||
|
||||
/// Enable a `toJson` method on the model class.
|
||||
static const int json = 1;
|
||||
|
||||
/// Generate a TypeScript definition file (`.d.ts`) for use on the client-side.
|
||||
static const int typescript = 2;
|
||||
}
|
||||
|
||||
@deprecated
|
||||
class DefaultValue {
|
||||
final value;
|
||||
|
||||
const DefaultValue(this.value);
|
||||
}
|
||||
|
||||
@deprecated
|
||||
|
||||
/// Prefer [SerializableField] instead.
|
||||
class Alias {
|
||||
final String name;
|
||||
|
||||
const Alias(this.name);
|
||||
}
|
0
packages/serialize/angel_serialize/mono_pkg.yaml
Normal file
0
packages/serialize/angel_serialize/mono_pkg.yaml
Normal file
13
packages/serialize/angel_serialize/pubspec.yaml
Normal file
13
packages/serialize/angel_serialize/pubspec.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
name: angel_serialize
|
||||
version: 2.2.3+3
|
||||
description: Static annotations powering Angel model serialization. Combine with angel_serialize_generator for flexible modeling.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/serialize
|
||||
environment:
|
||||
sdk: '>=2.0.0-dev <3.0.0'
|
||||
dependencies:
|
||||
angel_model: ^1.0.0
|
||||
collection: ^1.0.0
|
||||
meta: ^1.0.0
|
||||
pedantic: ^1.0.0
|
||||
quiver_hashcode: ^2.0.0
|
58
packages/serialize/angel_serialize_generator/.gitignore
vendored
Normal file
58
packages/serialize/angel_serialize_generator/.gitignore
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.packages
|
||||
.pub/
|
||||
build/
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
*.g.part
|
134
packages/serialize/angel_serialize_generator/CHANGELOG.md
Normal file
134
packages/serialize/angel_serialize_generator/CHANGELOG.md
Normal file
|
@ -0,0 +1,134 @@
|
|||
# 2.5.0
|
||||
* Support mutable models (again).
|
||||
* Use `whereType()` instead of chaining `where()` and `cast()`.
|
||||
* Support pulling fields from parent classes and interfaces.
|
||||
* Only generate `const` constructors if *all*
|
||||
fields lack a setter.
|
||||
* Don't type-annotate initializing formals.
|
||||
|
||||
# 2.4.4
|
||||
* Remove unnecessary `new` and `const`.
|
||||
|
||||
# 2.4.3
|
||||
* Generate `Codec` and `Converter` classes.
|
||||
* Generate `toString` methods.
|
||||
* Include original documentation comments from the model.
|
||||
|
||||
# 2.4.2
|
||||
* Fix bug where enums didn't support default values.
|
||||
* Stop emitting `@required` on items with default values.
|
||||
* Create default `@SerializableField` for fields without them.
|
||||
|
||||
# 2.4.1+1
|
||||
* Change `as Iterable<Map>` to `.cast<Map>`.
|
||||
|
||||
# 2.4.1
|
||||
* Support `serializesTo`.
|
||||
* Don't emit `@required` if there is a default value.
|
||||
* Deprecate `autoIdAndDateFields`.
|
||||
|
||||
# 2.4.0
|
||||
* Introduce `@SerializableField`, and say goodbye to annotation hell.
|
||||
* Support custom (de)serializers.
|
||||
* Allow passing of annotations to the generated class.
|
||||
* Fixted TypeScript `ref` generator.
|
||||
|
||||
# 2.3.0
|
||||
* Add `@DefaultValue` support.
|
||||
|
||||
# 2.2.2
|
||||
* Split out TS def builder, to emit to source.
|
||||
|
||||
# 2.2.1
|
||||
* Explicit changes for assisting `angel_orm_generator`.
|
||||
|
||||
# 2.2.0
|
||||
* Build to `cache`.
|
||||
* Only generate one `.g.dart` file.
|
||||
* Support for `Uint8List`.
|
||||
* Use `.cast()` for `List`s and `Map`s of *non-`Model`* types.
|
||||
|
||||
# 2.1.2
|
||||
* Add `declare module` to generated TypeScript files.
|
||||
|
||||
# 2.1.1
|
||||
* Generate `hashCode`.
|
||||
|
||||
# 2.1.0
|
||||
* Removed dependency on `package:id`.
|
||||
* Update dependencies for Dart2Stable.
|
||||
* `jsonModelBuilder` now uses `SharedPartBuilder`, rather than
|
||||
`PartBuilder`.
|
||||
|
||||
# 2.0.10
|
||||
* Generate `XFields.allFields` constant.
|
||||
* No longer breaks in cases where `dynamic` is present.
|
||||
* Call `toJson` in `toMap` on nested models.
|
||||
* Never generate named parameters from private fields.
|
||||
* Use the new `@generatedSerializable` to *always* find generated
|
||||
models.
|
||||
|
||||
# 2.0.9+4
|
||||
* Remove `defaults` in `build.yaml`.
|
||||
|
||||
# 2.0.9+3
|
||||
* Fix a cast error when self-referencing nested list expressions.
|
||||
|
||||
# 2.0.9+2
|
||||
* Fix previously unseen cast errors with enums.
|
||||
|
||||
# 2.0.9+1
|
||||
* Fix a cast error when deserializing nested model classes.
|
||||
|
||||
# 2.0.9
|
||||
* Upgrade to `source_gen@^0.8.0`.
|
||||
|
||||
# 2.0.8+3
|
||||
* Don't fail on `null` in `toMap`.
|
||||
* Support self-referencing via `toJson()`.
|
||||
|
||||
# 2.0.8+2
|
||||
* Better discern when custom methods disqualify classes
|
||||
from `const` protection.
|
||||
|
||||
# 2.0.8+1
|
||||
* Fix generation of `const` constructors with iterables.
|
||||
|
||||
# 2.0.8
|
||||
* Now supports de/serialization of `enum` types.
|
||||
* Generate `const` constructors when possible.
|
||||
* Remove `whereType`, perform manual coercion.
|
||||
* Generate a `fromMap` with typecasting, for Dart 2's sake.
|
||||
|
||||
# 2.0.7
|
||||
* Create unmodifiable Lists and Maps.
|
||||
* Support `@required` on fields.
|
||||
* Affix an `@immutable` annotation to classes, if
|
||||
`package:meta` is imported.
|
||||
* Add `/// <reference path="..." />` to TypeScript models.
|
||||
|
||||
# 2.0.6
|
||||
* Support for using `abstract` to create immutable model classes.
|
||||
* Add support for custom constructor parameters.
|
||||
* Closed [#21](https://github.com/angel-dart/serialize/issues/21) - better naming
|
||||
of `Map` types.
|
||||
* Added overridden `==` operators.
|
||||
|
||||
# 2.0.5
|
||||
* Deserialization now supports un-serialized `DateTime`.
|
||||
* Better support for regular typed Lists and Maps in TypeScript.
|
||||
|
||||
# 2.0.4
|
||||
* Fields in TypeScript definitions are now nullable by default.
|
||||
|
||||
# 2.0.3
|
||||
* Added a `TypeScriptDefinitionBuilder`.
|
||||
|
||||
# 2.0.2
|
||||
* Generates an `XFields` class with the serialized names of
|
||||
all fields in a model class `X`.
|
||||
* Removed unnecessary named parameters from `XSerializer.fromMap`.
|
||||
|
||||
# 2.0.1
|
||||
* Ensured that `List` is only transformed if
|
||||
it generically references a `Model`.
|
21
packages/serialize/angel_serialize_generator/LICENSE
Normal file
21
packages/serialize/angel_serialize_generator/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 The Angel Framework
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
5
packages/serialize/angel_serialize_generator/README.md
Normal file
5
packages/serialize/angel_serialize_generator/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# angel_serialize_generator
|
||||
The builder for Angel's model serialization.
|
||||
|
||||
Find documentation in the main project repo:
|
||||
https://github.com/angel-dart/serialize
|
|
@ -0,0 +1,8 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
||||
linter:
|
||||
rules:
|
||||
- unnecessary_new
|
||||
- unnecessary_const
|
37
packages/serialize/angel_serialize_generator/build.yaml
Normal file
37
packages/serialize/angel_serialize_generator/build.yaml
Normal file
|
@ -0,0 +1,37 @@
|
|||
builders:
|
||||
angel_serialize:
|
||||
import: "package:angel_serialize_generator/angel_serialize_generator.dart"
|
||||
builder_factories:
|
||||
- jsonModelBuilder
|
||||
- serializerBuilder
|
||||
auto_apply: root_package
|
||||
build_to: cache
|
||||
build_extensions:
|
||||
.dart:
|
||||
- ".angel_serialize.g.part"
|
||||
- ".angel_serialize_serializer.g.part"
|
||||
applies_builders: ["source_gen|combining_builder", "source_gen|part_cleanup"]
|
||||
runs_before: ["angel_orm_generator|angel_orm"]
|
||||
typescript:
|
||||
import: "package:angel_serialize_generator/angel_serialize_generator.dart"
|
||||
builder_factories:
|
||||
- typescriptDefinitionBuilder
|
||||
auto_apply: root_package
|
||||
build_to: source
|
||||
build_extensions:
|
||||
.dart:
|
||||
- ".d.ts"
|
||||
# targets:
|
||||
# _book:
|
||||
# sources:
|
||||
# - "test/models/book.dart"
|
||||
# - "test/models/has_map.dart"
|
||||
# - "test/models/goat.dart"
|
||||
# - "test/models/game_pad_button.dart"
|
||||
# - "test/models/with_enum.dart"
|
||||
# $default:
|
||||
# dependencies:
|
||||
# - "angel_serialize_generator:_book"
|
||||
# sources:
|
||||
# - "test/models/author.dart"
|
||||
# - "test/models/game_pad.dart"
|
|
@ -0,0 +1,9 @@
|
|||
// ignore_for_file: unused_element
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
part 'main.g.dart';
|
||||
|
||||
@serializable
|
||||
class _Todo {
|
||||
String text;
|
||||
bool completed;
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'main.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonModelGenerator
|
||||
// **************************************************************************
|
||||
|
||||
@generatedSerializable
|
||||
class Todo extends _Todo {
|
||||
Todo({this.text, this.completed});
|
||||
|
||||
@override
|
||||
String text;
|
||||
|
||||
@override
|
||||
bool completed;
|
||||
|
||||
Todo copyWith({String text, bool completed}) {
|
||||
return Todo(
|
||||
text: text ?? this.text, completed: completed ?? this.completed);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _Todo && other.text == text && other.completed == completed;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([text, completed]);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "Todo(text=$text, completed=$completed)";
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return TodoSerializer.toMap(this);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// SerializerGenerator
|
||||
// **************************************************************************
|
||||
|
||||
const TodoSerializer todoSerializer = TodoSerializer();
|
||||
|
||||
class TodoEncoder extends Converter<Todo, Map> {
|
||||
const TodoEncoder();
|
||||
|
||||
@override
|
||||
Map convert(Todo model) => TodoSerializer.toMap(model);
|
||||
}
|
||||
|
||||
class TodoDecoder extends Converter<Map, Todo> {
|
||||
const TodoDecoder();
|
||||
|
||||
@override
|
||||
Todo convert(Map map) => TodoSerializer.fromMap(map);
|
||||
}
|
||||
|
||||
class TodoSerializer extends Codec<Todo, Map> {
|
||||
const TodoSerializer();
|
||||
|
||||
@override
|
||||
get encoder => const TodoEncoder();
|
||||
@override
|
||||
get decoder => const TodoDecoder();
|
||||
static Todo fromMap(Map map) {
|
||||
return Todo(
|
||||
text: map['text'] as String, completed: map['completed'] as bool);
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(_Todo model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
return {'text': model.text, 'completed': model.completed};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TodoFields {
|
||||
static const List<String> allFields = <String>[text, completed];
|
||||
|
||||
static const String text = 'text';
|
||||
|
||||
static const String completed = 'completed';
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
library angel_serialize_generator;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:mirrors';
|
||||
import 'dart:typed_data';
|
||||
import 'package:analyzer/dart/constant/value.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:angel_model/angel_model.dart';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:build/build.dart';
|
||||
import 'package:code_buffer/code_buffer.dart';
|
||||
import 'package:code_builder/code_builder.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:recase/recase.dart';
|
||||
import 'package:source_gen/source_gen.dart' hide LibraryBuilder;
|
||||
|
||||
import 'build_context.dart';
|
||||
import 'context.dart';
|
||||
|
||||
part 'model.dart';
|
||||
|
||||
part 'serialize.dart';
|
||||
|
||||
part 'typescript.dart';
|
||||
|
||||
Builder jsonModelBuilder(_) {
|
||||
return SharedPartBuilder(const [JsonModelGenerator()], 'angel_serialize');
|
||||
}
|
||||
|
||||
Builder serializerBuilder(_) {
|
||||
return SharedPartBuilder(
|
||||
const [SerializerGenerator()], 'angel_serialize_serializer');
|
||||
}
|
||||
|
||||
Builder typescriptDefinitionBuilder(_) {
|
||||
return TypeScriptDefinitionBuilder();
|
||||
}
|
||||
|
||||
/// Converts a [DartType] to a [TypeReference].
|
||||
TypeReference convertTypeReference(DartType t) {
|
||||
return TypeReference((b) {
|
||||
b..symbol = t.name;
|
||||
|
||||
if (t is InterfaceType) {
|
||||
b.types.addAll(t.typeArguments.map(convertTypeReference));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Expression convertObject(DartObject o) {
|
||||
if (o.isNull) return literalNull;
|
||||
if (o.toBoolValue() != null) return literalBool(o.toBoolValue());
|
||||
if (o.toIntValue() != null) return literalNum(o.toIntValue());
|
||||
if (o.toDoubleValue() != null) return literalNum(o.toDoubleValue());
|
||||
if (o.toSymbolValue() != null) {
|
||||
return CodeExpression(Code('#' + o.toSymbolValue()));
|
||||
}
|
||||
if (o.toStringValue() != null) return literalString(o.toStringValue());
|
||||
if (o.toTypeValue() != null) return convertTypeReference(o.toTypeValue());
|
||||
if (o.toListValue() != null) {
|
||||
return literalList(o.toListValue().map(convertObject));
|
||||
}
|
||||
if (o.toMapValue() != null) {
|
||||
return literalMap(o
|
||||
.toMapValue()
|
||||
.map((k, v) => MapEntry(convertObject(k), convertObject(v))));
|
||||
}
|
||||
|
||||
var rev = ConstantReader(o).revive();
|
||||
Expression target = convertTypeReference(o.type);
|
||||
target = rev.accessor.isEmpty ? target : target.property(rev.accessor);
|
||||
return target.call(rev.positionalArguments.map(convertObject),
|
||||
rev.namedArguments.map((k, v) => MapEntry(k, convertObject(v))));
|
||||
}
|
||||
|
||||
String dartObjectToString(DartObject v) {
|
||||
var type = v.type;
|
||||
if (v.isNull) return 'null';
|
||||
if (v.toBoolValue() != null) return v.toBoolValue().toString();
|
||||
if (v.toIntValue() != null) return v.toIntValue().toString();
|
||||
if (v.toDoubleValue() != null) return v.toDoubleValue().toString();
|
||||
if (v.toSymbolValue() != null) return '#' + v.toSymbolValue();
|
||||
if (v.toTypeValue() != null) return v.toTypeValue().name;
|
||||
if (v.toListValue() != null) {
|
||||
return 'const [' + v.toListValue().map(dartObjectToString).join(', ') + ']';
|
||||
}
|
||||
if (v.toMapValue() != null) {
|
||||
return 'const {' +
|
||||
v.toMapValue().entries.map((entry) {
|
||||
var k = dartObjectToString(entry.key);
|
||||
var v = dartObjectToString(entry.value);
|
||||
return '$k: $v';
|
||||
}).join(', ') +
|
||||
'}';
|
||||
}
|
||||
if (v.toStringValue() != null) {
|
||||
return literalString(v.toStringValue()).accept(DartEmitter()).toString();
|
||||
}
|
||||
if (type is InterfaceType && type.element.isEnum) {
|
||||
// Find the index of the enum, then find the member.
|
||||
for (var field in type.element.fields) {
|
||||
if (field.isEnumConstant && field.isStatic) {
|
||||
var value = type.element.getField(field.name).constantValue;
|
||||
if (value == v) {
|
||||
return '${type.name}.${field.name}';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw ArgumentError(v.toString());
|
||||
}
|
||||
|
||||
/// Determines if a type supports `package:angel_serialize`.
|
||||
bool isModelClass(DartType t) {
|
||||
if (t == null) return false;
|
||||
|
||||
if (serializableTypeChecker.hasAnnotationOf(t.element)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (generatedSerializableTypeChecker.hasAnnotationOf(t.element)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (const TypeChecker.fromRuntime(Model).isAssignableFromType(t)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (t is InterfaceType) {
|
||||
return isModelClass(t.superclass);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isListOrMapType(DartType t) {
|
||||
return (const TypeChecker.fromRuntime(List).isAssignableFromType(t) ||
|
||||
const TypeChecker.fromRuntime(Map).isAssignableFromType(t)) &&
|
||||
!const TypeChecker.fromRuntime(Uint8List).isAssignableFromType(t);
|
||||
}
|
||||
|
||||
bool isEnumType(DartType t) {
|
||||
if (t is InterfaceType) {
|
||||
return t.element.isEnum;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Determines if a [DartType] is a `List` with the first type argument being a `Model`.
|
||||
bool isListOfModelType(InterfaceType t) {
|
||||
return const TypeChecker.fromRuntime(List).isAssignableFromType(t) &&
|
||||
t.typeArguments.length == 1 &&
|
||||
isModelClass(t.typeArguments[0]);
|
||||
}
|
||||
|
||||
/// Determines if a [DartType] is a `Map` with the second type argument being a `Model`.
|
||||
bool isMapToModelType(InterfaceType t) {
|
||||
return const TypeChecker.fromRuntime(Map).isAssignableFromType(t) &&
|
||||
t.typeArguments.length == 2 &&
|
||||
isModelClass(t.typeArguments[1]);
|
||||
}
|
||||
|
||||
bool isAssignableToModel(DartType type) =>
|
||||
const TypeChecker.fromRuntime(Model).isAssignableFromType(type);
|
||||
|
||||
/// Compute a [String] representation of a [type].
|
||||
String typeToString(DartType type) {
|
||||
if (type is InterfaceType) {
|
||||
if (type.typeArguments.isEmpty) return type.name;
|
||||
return type.name +
|
||||
'<' +
|
||||
type.typeArguments.map(typeToString).join(', ') +
|
||||
'>';
|
||||
} else {
|
||||
return type.name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
import 'dart:async';
|
||||
import 'package:analyzer/dart/constant/value.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/src/dart/element/element.dart';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:build/build.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:recase/recase.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
import 'context.dart';
|
||||
|
||||
// ignore: deprecated_member_use
|
||||
const TypeChecker aliasTypeChecker = TypeChecker.fromRuntime(Alias);
|
||||
|
||||
const TypeChecker dateTimeTypeChecker = TypeChecker.fromRuntime(DateTime);
|
||||
|
||||
// ignore: deprecated_member_use
|
||||
const TypeChecker excludeTypeChecker = TypeChecker.fromRuntime(Exclude);
|
||||
|
||||
const TypeChecker serializableFieldTypeChecker =
|
||||
TypeChecker.fromRuntime(SerializableField);
|
||||
|
||||
const TypeChecker serializableTypeChecker =
|
||||
TypeChecker.fromRuntime(Serializable);
|
||||
|
||||
const TypeChecker generatedSerializableTypeChecker =
|
||||
TypeChecker.fromRuntime(GeneratedSerializable);
|
||||
|
||||
final Map<String, BuildContext> _cache = {};
|
||||
|
||||
/// Create a [BuildContext].
|
||||
Future<BuildContext> buildContext(ClassElement clazz, ConstantReader annotation,
|
||||
BuildStep buildStep, Resolver resolver, bool autoSnakeCaseNames,
|
||||
{bool heedExclude = true}) async {
|
||||
var id = clazz.location.components.join('-');
|
||||
if (_cache.containsKey(id)) {
|
||||
return _cache[id];
|
||||
}
|
||||
|
||||
// Check for autoIdAndDateFields, autoSnakeCaseNames
|
||||
autoSnakeCaseNames =
|
||||
annotation.peek('autoSnakeCaseNames')?.boolValue ?? autoSnakeCaseNames;
|
||||
|
||||
var ctx = BuildContext(
|
||||
annotation,
|
||||
clazz,
|
||||
originalClassName: clazz.name,
|
||||
sourceFilename: p.basename(buildStep.inputId.path),
|
||||
autoSnakeCaseNames: autoSnakeCaseNames,
|
||||
includeAnnotations:
|
||||
annotation.peek('includeAnnotations')?.listValue ?? <DartObject>[],
|
||||
);
|
||||
// var lib = await resolver.libraryFor(buildStep.inputId);
|
||||
List<String> fieldNames = [];
|
||||
var fields = <FieldElement>[];
|
||||
|
||||
// Crawl for classes from parent classes.
|
||||
void crawlClass(InterfaceType t) {
|
||||
while (t != null) {
|
||||
fields.insertAll(0, t.element.fields);
|
||||
t.interfaces.forEach(crawlClass);
|
||||
t = t.superclass;
|
||||
}
|
||||
}
|
||||
|
||||
crawlClass(clazz.type);
|
||||
|
||||
for (var field in fields) {
|
||||
// Skip private fields
|
||||
if (field.name.startsWith('_')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (field.getter != null &&
|
||||
(field.setter != null || field.getter.isAbstract)) {
|
||||
var el = field.setter == null ? field.getter : field;
|
||||
fieldNames.add(field.name);
|
||||
|
||||
// Check for @SerializableField
|
||||
var fieldAnn = serializableFieldTypeChecker.firstAnnotationOf(el);
|
||||
|
||||
void handleSerializableField(SerializableFieldMirror sField) {
|
||||
ctx.fieldInfo[field.name] = sField;
|
||||
|
||||
if (sField.defaultValue != null) {
|
||||
ctx.defaults[field.name] = sField.defaultValue;
|
||||
}
|
||||
|
||||
if (sField.alias != null) {
|
||||
ctx.aliases[field.name] = sField.alias;
|
||||
} else if (autoSnakeCaseNames != false) {
|
||||
ctx.aliases[field.name] = ReCase(field.name).snakeCase;
|
||||
}
|
||||
|
||||
if (sField.isNullable == false) {
|
||||
var reason = sField.errorMessage ??
|
||||
"Missing required field '${ctx.resolveFieldName(field.name)}' on ${ctx.modelClassName}.";
|
||||
ctx.requiredFields[field.name] = reason;
|
||||
}
|
||||
|
||||
if (sField.exclude) {
|
||||
// ignore: deprecated_member_use
|
||||
ctx.excluded[field.name] = Exclude(
|
||||
canSerialize: sField.canSerialize,
|
||||
canDeserialize: sField.canDeserialize,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldAnn != null) {
|
||||
var cr = ConstantReader(fieldAnn);
|
||||
var excluded = cr.peek('exclude')?.boolValue ?? false;
|
||||
var sField = SerializableFieldMirror(
|
||||
alias: cr.peek('alias')?.stringValue,
|
||||
defaultValue: cr.peek('defaultValue')?.objectValue,
|
||||
serializer: cr.peek('serializer')?.symbolValue,
|
||||
deserializer: cr.peek('deserializer')?.symbolValue,
|
||||
errorMessage: cr.peek('errorMessage')?.stringValue,
|
||||
isNullable: cr.peek('isNullable')?.boolValue ?? !excluded,
|
||||
canDeserialize: cr.peek('canDeserialize')?.boolValue ?? false,
|
||||
canSerialize: cr.peek('canSerialize')?.boolValue ?? false,
|
||||
exclude: excluded,
|
||||
serializesTo: cr.peek('serializesTo')?.typeValue,
|
||||
);
|
||||
|
||||
handleSerializableField(sField);
|
||||
|
||||
// Apply
|
||||
} else {
|
||||
var foundNone = true;
|
||||
// Skip if annotated with @exclude
|
||||
var excludeAnnotation = excludeTypeChecker.firstAnnotationOf(el);
|
||||
|
||||
if (excludeAnnotation != null) {
|
||||
var cr = ConstantReader(excludeAnnotation);
|
||||
foundNone = false;
|
||||
|
||||
// ignore: deprecated_member_use
|
||||
ctx.excluded[field.name] = Exclude(
|
||||
canSerialize: cr.read('canSerialize').boolValue,
|
||||
canDeserialize: cr.read('canDeserialize').boolValue,
|
||||
);
|
||||
}
|
||||
|
||||
// Check for @DefaultValue()
|
||||
var defAnn =
|
||||
// ignore: deprecated_member_use
|
||||
const TypeChecker.fromRuntime(DefaultValue).firstAnnotationOf(el);
|
||||
if (defAnn != null) {
|
||||
var rev = ConstantReader(defAnn).revive().positionalArguments[0];
|
||||
ctx.defaults[field.name] = rev;
|
||||
foundNone = false;
|
||||
}
|
||||
|
||||
// Check for alias
|
||||
// ignore: deprecated_member_use
|
||||
Alias alias;
|
||||
var aliasAnn = aliasTypeChecker.firstAnnotationOf(el);
|
||||
|
||||
if (aliasAnn != null) {
|
||||
// ignore: deprecated_member_use
|
||||
alias = Alias(aliasAnn.getField('name').toStringValue());
|
||||
foundNone = false;
|
||||
}
|
||||
|
||||
if (alias?.name?.isNotEmpty == true) {
|
||||
ctx.aliases[field.name] = alias.name;
|
||||
} else if (autoSnakeCaseNames != false) {
|
||||
ctx.aliases[field.name] = ReCase(field.name).snakeCase;
|
||||
}
|
||||
|
||||
// Check for @required
|
||||
var required =
|
||||
const TypeChecker.fromRuntime(Required).firstAnnotationOf(el);
|
||||
|
||||
if (required != null) {
|
||||
log.warning(
|
||||
'Using @required on fields (like ${clazz.name}.${field.name}) is now deprecated; use @SerializableField(isNullable: false) instead.');
|
||||
var cr = ConstantReader(required);
|
||||
var reason = cr.peek('reason')?.stringValue ??
|
||||
"Missing required field '${ctx.resolveFieldName(field.name)}' on ${ctx.modelClassName}.";
|
||||
ctx.requiredFields[field.name] = reason;
|
||||
foundNone = false;
|
||||
}
|
||||
|
||||
if (foundNone) {
|
||||
var f = SerializableField();
|
||||
var sField = SerializableFieldMirror(
|
||||
alias: f.alias,
|
||||
defaultValue: null,
|
||||
serializer: f.serializer,
|
||||
deserializer: f.deserializer,
|
||||
errorMessage: f.errorMessage,
|
||||
isNullable: f.isNullable,
|
||||
canDeserialize: f.canDeserialize,
|
||||
canSerialize: f.canSerialize,
|
||||
exclude: f.exclude,
|
||||
serializesTo: null,
|
||||
);
|
||||
handleSerializableField(sField);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.fields.add(field);
|
||||
}
|
||||
}
|
||||
|
||||
// ShimFields are no longer used.
|
||||
// if (const TypeChecker.fromRuntime(Model).isAssignableFromType(clazz.type)) {
|
||||
// if (!fieldNames.contains('id')) {
|
||||
// var idField = ShimFieldImpl('id', lib.context.typeProvider.stringType);
|
||||
// ctx.fields.insert(0, idField);
|
||||
// ctx.shimmed['id'] = true;
|
||||
// }
|
||||
|
||||
// DartType dateTime;
|
||||
// for (var key in ['createdAt', 'updatedAt']) {
|
||||
// if (!fieldNames.contains(key)) {
|
||||
// if (dateTime == null) {
|
||||
// var coreLib =
|
||||
// await resolver.libraries.singleWhere((lib) => lib.isDartCore);
|
||||
// var dt = coreLib.getType('DateTime');
|
||||
// dateTime = dt.type;
|
||||
// }
|
||||
|
||||
// var field = ShimFieldImpl(key, dateTime);
|
||||
// ctx.aliases[key] = ReCase(key).snakeCase;
|
||||
// ctx.fields.add(field);
|
||||
// ctx.shimmed[key] = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Get constructor params, if any
|
||||
ctx.constructorParameters.addAll(clazz.unnamedConstructor.parameters);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/// A manually-instantiated [FieldElement].
|
||||
class ShimFieldImpl extends FieldElementImpl {
|
||||
@override
|
||||
final DartType type;
|
||||
|
||||
ShimFieldImpl(String name, this.type) : super(name, -1);
|
||||
}
|
109
packages/serialize/angel_serialize_generator/lib/context.dart
Normal file
109
packages/serialize/angel_serialize_generator/lib/context.dart
Normal file
|
@ -0,0 +1,109 @@
|
|||
import 'package:analyzer/dart/constant/value.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:code_builder/code_builder.dart';
|
||||
import 'package:recase/recase.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
|
||||
/// A base context for building serializable classes.
|
||||
class BuildContext {
|
||||
ReCase _modelClassNameRecase;
|
||||
TypeReference _modelClassType;
|
||||
|
||||
/// A map of fields that are absolutely required, and error messages for when they are absent.
|
||||
final Map<String, String> requiredFields = {};
|
||||
|
||||
/// A map of field names to resolved names from `@Alias()` declarations.
|
||||
final Map<String, String> aliases = {};
|
||||
|
||||
/// A map of field names to their default values.
|
||||
final Map<String, DartObject> defaults = {};
|
||||
|
||||
/// A map of fields to their related information.
|
||||
final Map<String, SerializableFieldMirror> fieldInfo = {};
|
||||
|
||||
/// A map of fields that have been marked as to be excluded from serialization.
|
||||
// ignore: deprecated_member_use
|
||||
final Map<String, Exclude> excluded = {};
|
||||
|
||||
/// A map of "synthetic" fields, i.e. `id` and `created_at` injected automatically.
|
||||
final Map<String, bool> shimmed = {};
|
||||
|
||||
final bool autoIdAndDateFields, autoSnakeCaseNames;
|
||||
|
||||
final String originalClassName, sourceFilename;
|
||||
|
||||
/// The fields declared on the original class.
|
||||
final List<FieldElement> fields = [];
|
||||
|
||||
final List<ParameterElement> constructorParameters = [];
|
||||
|
||||
final ConstantReader annotation;
|
||||
|
||||
final ClassElement clazz;
|
||||
|
||||
/// Any annotations to include in the generated class.
|
||||
final List<DartObject> includeAnnotations;
|
||||
|
||||
/// The name of the field that identifies data of this model type.
|
||||
String primaryKeyName = 'id';
|
||||
|
||||
BuildContext(this.annotation, this.clazz,
|
||||
{this.originalClassName,
|
||||
this.sourceFilename,
|
||||
this.autoSnakeCaseNames,
|
||||
this.autoIdAndDateFields,
|
||||
this.includeAnnotations = const <DartObject>[]});
|
||||
|
||||
/// The name of the generated class.
|
||||
String get modelClassName => originalClassName.startsWith('_')
|
||||
? originalClassName.substring(1)
|
||||
: originalClassName;
|
||||
|
||||
/// A [ReCase] instance reflecting on the [modelClassName].
|
||||
ReCase get modelClassNameRecase =>
|
||||
_modelClassNameRecase ??= ReCase(modelClassName);
|
||||
|
||||
TypeReference get modelClassType =>
|
||||
_modelClassType ??= TypeReference((b) => b.symbol = modelClassName);
|
||||
|
||||
/// The [FieldElement] pointing to the primary key.
|
||||
FieldElement get primaryKeyField =>
|
||||
fields.firstWhere((f) => f.name == primaryKeyName);
|
||||
|
||||
bool get importsPackageMeta {
|
||||
return clazz.library.imports.any((i) => i.uri == 'package:meta/meta.dart');
|
||||
}
|
||||
|
||||
/// Get the aliased name (if one is defined) for a field.
|
||||
String resolveFieldName(String name) =>
|
||||
aliases.containsKey(name) ? aliases[name] : name;
|
||||
|
||||
/// Finds the type that the field [name] should serialize to.
|
||||
DartType resolveSerializedFieldType(String name) {
|
||||
return fieldInfo[name]?.serializesTo ??
|
||||
fields.firstWhere((f) => f.name == name).type;
|
||||
}
|
||||
}
|
||||
|
||||
class SerializableFieldMirror {
|
||||
final String alias;
|
||||
final DartObject defaultValue;
|
||||
final Symbol serializer, deserializer;
|
||||
final String errorMessage;
|
||||
final bool isNullable, canDeserialize, canSerialize, exclude;
|
||||
final DartType serializesTo;
|
||||
|
||||
SerializableFieldMirror(
|
||||
{this.alias,
|
||||
this.defaultValue,
|
||||
this.serializer,
|
||||
this.deserializer,
|
||||
this.errorMessage,
|
||||
this.isNullable,
|
||||
this.canDeserialize,
|
||||
this.canSerialize,
|
||||
this.exclude,
|
||||
this.serializesTo});
|
||||
}
|
294
packages/serialize/angel_serialize_generator/lib/model.dart
Normal file
294
packages/serialize/angel_serialize_generator/lib/model.dart
Normal file
|
@ -0,0 +1,294 @@
|
|||
part of angel_serialize_generator;
|
||||
|
||||
class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
|
||||
const JsonModelGenerator();
|
||||
|
||||
@override
|
||||
Future<String> generateForAnnotatedElement(
|
||||
Element element, ConstantReader annotation, BuildStep buildStep) async {
|
||||
if (element.kind != ElementKind.CLASS) {
|
||||
throw 'Only classes can be annotated with a @Serializable() annotation.';
|
||||
}
|
||||
|
||||
var ctx = await buildContext(element as ClassElement, annotation, buildStep,
|
||||
await buildStep.resolver, true);
|
||||
|
||||
var lib = Library((b) {
|
||||
generateClass(ctx, b, annotation);
|
||||
});
|
||||
|
||||
var buf = lib.accept(DartEmitter());
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/// Generate an extended model class.
|
||||
void generateClass(
|
||||
BuildContext ctx, LibraryBuilder file, ConstantReader annotation) {
|
||||
file.body.add(Class((clazz) {
|
||||
clazz
|
||||
..name = ctx.modelClassNameRecase.pascalCase
|
||||
..annotations.add(refer('generatedSerializable'));
|
||||
|
||||
for (var ann in ctx.includeAnnotations) {
|
||||
clazz.annotations.add(convertObject(ann));
|
||||
}
|
||||
|
||||
if (shouldBeConstant(ctx)) {
|
||||
clazz.implements.add(Reference(ctx.originalClassName));
|
||||
} else {
|
||||
clazz.extend = Reference(ctx.originalClassName);
|
||||
}
|
||||
|
||||
//if (ctx.importsPackageMeta)
|
||||
// clazz.annotations.add(CodeExpression(Code('immutable')));
|
||||
|
||||
for (var field in ctx.fields) {
|
||||
clazz.fields.add(Field((b) {
|
||||
b
|
||||
..name = field.name
|
||||
// ..modifier = FieldModifier.final$
|
||||
..annotations.add(CodeExpression(Code('override')))
|
||||
..type = convertTypeReference(field.type);
|
||||
|
||||
// Fields should only be forced-final if the original field has no setter.
|
||||
if (field.setter == null && field is! ShimFieldImpl) {
|
||||
b.modifier = FieldModifier.final$;
|
||||
}
|
||||
|
||||
for (var el in [field.getter, field]) {
|
||||
if (el?.documentationComment != null) {
|
||||
b.docs.addAll(el.documentationComment.split('\n'));
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
generateConstructor(ctx, clazz, file);
|
||||
generateCopyWithMethod(ctx, clazz, file);
|
||||
generateEqualsOperator(ctx, clazz, file);
|
||||
generateHashCode(ctx, clazz);
|
||||
generateToString(ctx, clazz);
|
||||
|
||||
// Generate toJson() method if necessary
|
||||
var serializers = annotation.peek('serializers')?.listValue ?? [];
|
||||
|
||||
if (serializers.any((o) => o.toIntValue() == Serializers.json)) {
|
||||
clazz.methods.add(Method((method) {
|
||||
method
|
||||
..name = 'toJson'
|
||||
..returns = Reference('Map<String, dynamic>')
|
||||
..body = Code('return ${clazz.name}Serializer.toMap(this);');
|
||||
}));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
bool shouldBeConstant(BuildContext ctx) {
|
||||
// Check if all fields are without a getter
|
||||
return !isAssignableToModel(ctx.clazz.type) &&
|
||||
ctx.clazz.fields.every((f) =>
|
||||
f.getter?.isAbstract != false && f.setter?.isAbstract != false);
|
||||
}
|
||||
|
||||
/// Generate a constructor with named parameters.
|
||||
void generateConstructor(
|
||||
BuildContext ctx, ClassBuilder clazz, LibraryBuilder file) {
|
||||
clazz.constructors.add(Constructor((constructor) {
|
||||
// Add all `super` params
|
||||
constructor.constant = (ctx.clazz.unnamedConstructor?.isConst == true ||
|
||||
shouldBeConstant(ctx)) &&
|
||||
ctx.fields.every((f) {
|
||||
return f.setter == null && f is! ShimFieldImpl;
|
||||
});
|
||||
|
||||
for (var param in ctx.constructorParameters) {
|
||||
constructor.requiredParameters.add(Parameter((b) => b
|
||||
..name = param.name
|
||||
..type = convertTypeReference(param.type)));
|
||||
}
|
||||
|
||||
for (var field in ctx.fields) {
|
||||
if (!shouldBeConstant(ctx) && isListOrMapType(field.type)) {
|
||||
String typeName = const TypeChecker.fromRuntime(List)
|
||||
.isAssignableFromType(field.type)
|
||||
? 'List'
|
||||
: 'Map';
|
||||
var defaultValue = typeName == 'List' ? '[]' : '{}';
|
||||
var existingDefault = ctx.defaults[field.name];
|
||||
|
||||
if (existingDefault != null) {
|
||||
defaultValue = dartObjectToString(existingDefault);
|
||||
}
|
||||
|
||||
constructor.initializers.add(Code('''
|
||||
this.${field.name} =
|
||||
$typeName.unmodifiable(${field.name} ?? $defaultValue)'''));
|
||||
}
|
||||
}
|
||||
|
||||
for (var field in ctx.fields) {
|
||||
constructor.optionalParameters.add(Parameter((b) {
|
||||
b
|
||||
..toThis = shouldBeConstant(ctx)
|
||||
..name = field.name
|
||||
..named = true;
|
||||
|
||||
var existingDefault = ctx.defaults[field.name];
|
||||
|
||||
if (existingDefault != null) {
|
||||
b.defaultTo = Code(dartObjectToString(existingDefault));
|
||||
}
|
||||
|
||||
if (!isListOrMapType(field.type)) {
|
||||
b.toThis = true;
|
||||
} else if (!b.toThis) {
|
||||
b.type = convertTypeReference(field.type);
|
||||
}
|
||||
|
||||
if (ctx.requiredFields.containsKey(field.name) &&
|
||||
b.defaultTo == null) {
|
||||
b.annotations.add(CodeExpression(Code('required')));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if (ctx.constructorParameters.isNotEmpty) {
|
||||
if (!shouldBeConstant(ctx) ||
|
||||
ctx.clazz.unnamedConstructor?.isConst == true) {
|
||||
constructor.initializers.add(Code(
|
||||
'super(${ctx.constructorParameters.map((p) => p.name).join(',')})'));
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/// Generate a `copyWith` method.
|
||||
void generateCopyWithMethod(
|
||||
BuildContext ctx, ClassBuilder clazz, LibraryBuilder file) {
|
||||
clazz.methods.add(Method((method) {
|
||||
method
|
||||
..name = 'copyWith'
|
||||
..returns = ctx.modelClassType;
|
||||
|
||||
// Add all `super` params
|
||||
if (ctx.constructorParameters.isNotEmpty) {
|
||||
for (var param in ctx.constructorParameters) {
|
||||
method.requiredParameters.add(Parameter((b) => b
|
||||
..name = param.name
|
||||
..type = convertTypeReference(param.type)));
|
||||
}
|
||||
}
|
||||
|
||||
var buf = StringBuffer('return ${ctx.modelClassName}(');
|
||||
int i = 0;
|
||||
|
||||
for (var param in ctx.constructorParameters) {
|
||||
if (i++ > 0) buf.write(', ');
|
||||
buf.write(param.name);
|
||||
}
|
||||
|
||||
// Add named parameters
|
||||
for (var field in ctx.fields) {
|
||||
method.optionalParameters.add(Parameter((b) {
|
||||
b
|
||||
..name = field.name
|
||||
..named = true
|
||||
..type = convertTypeReference(field.type);
|
||||
}));
|
||||
|
||||
if (i++ > 0) buf.write(', ');
|
||||
buf.write('${field.name}: ${field.name} ?? this.${field.name}');
|
||||
}
|
||||
|
||||
buf.write(');');
|
||||
method.body = Code(buf.toString());
|
||||
}));
|
||||
}
|
||||
|
||||
static String generateEquality(DartType type, [bool nullable = false]) {
|
||||
if (type is InterfaceType) {
|
||||
if (const TypeChecker.fromRuntime(List).isAssignableFromType(type)) {
|
||||
if (type.typeParameters.length == 1) {
|
||||
var eq = generateEquality(type.typeArguments[0]);
|
||||
return 'ListEquality<${type.typeArguments[0].name}>($eq)';
|
||||
} else {
|
||||
return 'ListEquality()';
|
||||
}
|
||||
} else if (const TypeChecker.fromRuntime(Map)
|
||||
.isAssignableFromType(type)) {
|
||||
if (type.typeParameters.length == 2) {
|
||||
var keq = generateEquality(type.typeArguments[0]),
|
||||
veq = generateEquality(type.typeArguments[1]);
|
||||
return 'MapEquality<${type.typeArguments[0].name}, ${type.typeArguments[1].name}>(keys: $keq, values: $veq)';
|
||||
} else {
|
||||
return 'MapEquality()';
|
||||
}
|
||||
}
|
||||
|
||||
return nullable ? null : 'DefaultEquality<${type.name}>()';
|
||||
} else {
|
||||
return 'DefaultEquality()';
|
||||
}
|
||||
}
|
||||
|
||||
static String Function(String, String) generateComparator(DartType type) {
|
||||
if (type is! InterfaceType || type.name == 'dynamic') {
|
||||
return (a, b) => '$a == $b';
|
||||
}
|
||||
var eq = generateEquality(type, true);
|
||||
if (eq == null) return (a, b) => '$a == $b';
|
||||
return (a, b) => '$eq.equals($a, $b)';
|
||||
}
|
||||
|
||||
void generateHashCode(BuildContext ctx, ClassBuilder clazz) {
|
||||
clazz
|
||||
..methods.add(Method((method) {
|
||||
method
|
||||
..name = 'hashCode'
|
||||
..type = MethodType.getter
|
||||
..returns = refer('int')
|
||||
..annotations.add(refer('override'))
|
||||
..body = refer('hashObjects')
|
||||
.call([literalList(ctx.fields.map((f) => refer(f.name)))])
|
||||
.returned
|
||||
.statement;
|
||||
}));
|
||||
}
|
||||
|
||||
void generateToString(BuildContext ctx, ClassBuilder clazz) {
|
||||
clazz.methods.add(Method((b) {
|
||||
b
|
||||
..name = 'toString'
|
||||
..returns = refer('String')
|
||||
..annotations.add(refer('override'))
|
||||
..body = Block((b) {
|
||||
var buf = StringBuffer('\"${ctx.modelClassName}(');
|
||||
var i = 0;
|
||||
for (var field in ctx.fields) {
|
||||
if (i++ > 0) buf.write(', ');
|
||||
buf.write('${field.name}=\$${field.name}');
|
||||
}
|
||||
buf.write(')\"');
|
||||
b.addExpression(CodeExpression(Code(buf.toString())).returned);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
void generateEqualsOperator(
|
||||
BuildContext ctx, ClassBuilder clazz, LibraryBuilder file) {
|
||||
clazz.methods.add(Method((method) {
|
||||
method
|
||||
..name = 'operator =='
|
||||
..returns = Reference('bool')
|
||||
..requiredParameters.add(Parameter((b) => b.name = 'other'));
|
||||
|
||||
var buf = ['other is ${ctx.originalClassName}'];
|
||||
|
||||
buf.addAll(ctx.fields.map((f) {
|
||||
return generateComparator(f.type)('other.${f.name}', f.name);
|
||||
}));
|
||||
|
||||
method.body = Code('return ${buf.join('&&')};');
|
||||
}));
|
||||
}
|
||||
}
|
394
packages/serialize/angel_serialize_generator/lib/serialize.dart
Normal file
394
packages/serialize/angel_serialize_generator/lib/serialize.dart
Normal file
|
@ -0,0 +1,394 @@
|
|||
part of angel_serialize_generator;
|
||||
|
||||
class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||
final bool autoSnakeCaseNames;
|
||||
|
||||
const SerializerGenerator({this.autoSnakeCaseNames = true});
|
||||
|
||||
@override
|
||||
Future<String> generateForAnnotatedElement(
|
||||
Element element, ConstantReader annotation, BuildStep buildStep) async {
|
||||
if (element.kind != ElementKind.CLASS) {
|
||||
throw 'Only classes can be annotated with a @Serializable() annotation.';
|
||||
}
|
||||
|
||||
var ctx = await buildContext(element as ClassElement, annotation, buildStep,
|
||||
await buildStep.resolver, autoSnakeCaseNames != false);
|
||||
|
||||
var serializers = annotation.peek('serializers')?.listValue ?? [];
|
||||
|
||||
if (serializers.isEmpty) return null;
|
||||
|
||||
// Check if any serializer is recognized
|
||||
if (!serializers.any((s) => Serializers.all.contains(s.toIntValue()))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var lib = Library((b) {
|
||||
generateClass(serializers.map((s) => s.toIntValue()).toList(), ctx, b);
|
||||
generateFieldsClass(ctx, b);
|
||||
});
|
||||
|
||||
var buf = lib.accept(DartEmitter());
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/// Generate a serializer class.
|
||||
void generateClass(
|
||||
List<int> serializers, BuildContext ctx, LibraryBuilder file) {
|
||||
// Generate canonical codecs, etc.
|
||||
var pascal = ctx.modelClassNameRecase.pascalCase,
|
||||
camel = ctx.modelClassNameRecase.camelCase;
|
||||
|
||||
if (ctx.constructorParameters.isEmpty) {
|
||||
file.body.add(Code('''
|
||||
const ${pascal}Serializer ${camel}Serializer = ${pascal}Serializer();
|
||||
|
||||
class ${pascal}Encoder extends Converter<${pascal}, Map> {
|
||||
const ${pascal}Encoder();
|
||||
|
||||
@override
|
||||
Map convert(${pascal} model) => ${pascal}Serializer.toMap(model);
|
||||
}
|
||||
|
||||
class ${pascal}Decoder extends Converter<Map, ${pascal}> {
|
||||
const ${pascal}Decoder();
|
||||
|
||||
@override
|
||||
${pascal} convert(Map map) => ${pascal}Serializer.fromMap(map);
|
||||
}
|
||||
'''));
|
||||
}
|
||||
|
||||
file.body.add(Class((clazz) {
|
||||
clazz..name = '${pascal}Serializer';
|
||||
if (ctx.constructorParameters.isEmpty) {
|
||||
clazz
|
||||
..extend = TypeReference((b) => b
|
||||
..symbol = 'Codec'
|
||||
..types.addAll([ctx.modelClassType, refer('Map')]));
|
||||
|
||||
// Add constructor, Codec impl, etc.
|
||||
clazz.constructors.add(Constructor((b) => b..constant = true));
|
||||
clazz.methods.add(Method((b) => b
|
||||
..name = 'encoder'
|
||||
..type = MethodType.getter
|
||||
..annotations.add(refer('override'))
|
||||
..body = refer('${pascal}Encoder').constInstance([]).code));
|
||||
clazz.methods.add(Method((b) => b
|
||||
..name = 'decoder'
|
||||
..type = MethodType.getter
|
||||
..annotations.add(refer('override'))
|
||||
..body = refer('${pascal}Decoder').constInstance([]).code));
|
||||
} else {
|
||||
clazz.abstract = true;
|
||||
}
|
||||
|
||||
if (serializers.contains(Serializers.map)) {
|
||||
generateFromMapMethod(clazz, ctx, file);
|
||||
}
|
||||
|
||||
if (serializers.contains(Serializers.map) ||
|
||||
serializers.contains(Serializers.json)) {
|
||||
generateToMapMethod(clazz, ctx, file);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
void generateToMapMethod(
|
||||
ClassBuilder clazz, BuildContext ctx, LibraryBuilder file) {
|
||||
clazz.methods.add(Method((method) {
|
||||
method
|
||||
..static = true
|
||||
..name = 'toMap'
|
||||
..returns = Reference('Map<String, dynamic>')
|
||||
..requiredParameters.add(Parameter((b) {
|
||||
b
|
||||
..name = 'model'
|
||||
..type = refer(ctx.originalClassName);
|
||||
}));
|
||||
|
||||
var buf = StringBuffer();
|
||||
|
||||
ctx.requiredFields.forEach((key, msg) {
|
||||
if (ctx.excluded[key]?.canSerialize == false) return;
|
||||
buf.writeln('''
|
||||
if (model.$key == null) {
|
||||
throw FormatException("$msg");
|
||||
}
|
||||
''');
|
||||
});
|
||||
|
||||
buf.writeln('return {');
|
||||
int i = 0;
|
||||
|
||||
// Add named parameters
|
||||
for (var field in ctx.fields) {
|
||||
var type = ctx.resolveSerializedFieldType(field.name);
|
||||
|
||||
// Skip excluded fields
|
||||
if (ctx.excluded[field.name]?.canSerialize == false) continue;
|
||||
|
||||
var alias = ctx.resolveFieldName(field.name);
|
||||
|
||||
if (i++ > 0) buf.write(', ');
|
||||
|
||||
String serializedRepresentation = 'model.${field.name}';
|
||||
|
||||
String serializerToMap(ReCase rc, String value) {
|
||||
// if (rc.pascalCase == ctx.modelClassName) {
|
||||
// return '($value)?.toJson()';
|
||||
// }
|
||||
return '${rc.pascalCase}Serializer.toMap($value)';
|
||||
}
|
||||
|
||||
if (ctx.fieldInfo[field.name]?.serializer != null) {
|
||||
var name = MirrorSystem.getName(ctx.fieldInfo[field.name].serializer);
|
||||
serializedRepresentation = '$name(model.${field.name})';
|
||||
}
|
||||
|
||||
// Serialize dates
|
||||
else if (dateTimeTypeChecker.isAssignableFromType(type)) {
|
||||
serializedRepresentation = 'model.${field.name}?.toIso8601String()';
|
||||
}
|
||||
|
||||
// Serialize model classes via `XSerializer.toMap`
|
||||
else if (isModelClass(type)) {
|
||||
var rc = ReCase(type.name);
|
||||
serializedRepresentation =
|
||||
'${serializerToMap(rc, 'model.${field.name}')}';
|
||||
} else if (type is InterfaceType) {
|
||||
if (isListOfModelType(type)) {
|
||||
var name = type.typeArguments[0].name;
|
||||
if (name.startsWith('_')) name = name.substring(1);
|
||||
var rc = ReCase(name);
|
||||
var m = serializerToMap(rc, 'm');
|
||||
serializedRepresentation = '''
|
||||
model.${field.name}
|
||||
?.map((m) => $m)
|
||||
?.toList()''';
|
||||
} else if (isMapToModelType(type)) {
|
||||
var rc = ReCase(type.typeArguments[1].name);
|
||||
serializedRepresentation =
|
||||
'''model.${field.name}.keys?.fold({}, (map, key) {
|
||||
return map..[key] =
|
||||
${serializerToMap(rc, 'model.${field.name}[key]')};
|
||||
})''';
|
||||
} else if (type.element.isEnum) {
|
||||
serializedRepresentation = '''
|
||||
model.${field.name} == null ?
|
||||
null
|
||||
: ${type.name}.values.indexOf(model.${field.name})
|
||||
''';
|
||||
} else if (const TypeChecker.fromRuntime(Uint8List)
|
||||
.isAssignableFromType(type)) {
|
||||
serializedRepresentation = '''
|
||||
model.${field.name} == null ?
|
||||
null
|
||||
: base64.encode(model.${field.name})
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
||||
buf.write("'$alias': $serializedRepresentation");
|
||||
}
|
||||
|
||||
buf.write('};');
|
||||
method.body = Block.of([
|
||||
Code('if (model == null) { return null; }'),
|
||||
Code(buf.toString()),
|
||||
]);
|
||||
}));
|
||||
}
|
||||
|
||||
void generateFromMapMethod(
|
||||
ClassBuilder clazz, BuildContext ctx, LibraryBuilder file) {
|
||||
clazz.methods.add(Method((method) {
|
||||
method
|
||||
..static = true
|
||||
..name = 'fromMap'
|
||||
..returns = ctx.modelClassType
|
||||
..requiredParameters.add(
|
||||
Parameter((b) => b
|
||||
..name = 'map'
|
||||
..type = Reference('Map')),
|
||||
);
|
||||
|
||||
// Add all `super` params
|
||||
if (ctx.constructorParameters.isNotEmpty) {
|
||||
for (var param in ctx.constructorParameters) {
|
||||
method.requiredParameters.add(Parameter((b) => b
|
||||
..name = param.name
|
||||
..type = convertTypeReference(param.type)));
|
||||
}
|
||||
}
|
||||
|
||||
var buf = StringBuffer();
|
||||
|
||||
ctx.requiredFields.forEach((key, msg) {
|
||||
if (ctx.excluded[key]?.canDeserialize == false) return;
|
||||
var name = ctx.resolveFieldName(key);
|
||||
buf.writeln('''
|
||||
if (map['$name'] == null) {
|
||||
throw FormatException("$msg");
|
||||
}
|
||||
''');
|
||||
});
|
||||
|
||||
buf.writeln('return ${ctx.modelClassName}(');
|
||||
int i = 0;
|
||||
|
||||
for (var param in ctx.constructorParameters) {
|
||||
if (i++ > 0) buf.write(', ');
|
||||
buf.write(param.name);
|
||||
}
|
||||
|
||||
for (var field in ctx.fields) {
|
||||
var type = ctx.resolveSerializedFieldType(field.name);
|
||||
|
||||
if (ctx.excluded[field.name]?.canDeserialize == false) continue;
|
||||
|
||||
var alias = ctx.resolveFieldName(field.name);
|
||||
|
||||
if (i++ > 0) buf.write(', ');
|
||||
|
||||
String deserializedRepresentation =
|
||||
"map['$alias'] as ${typeToString(type)}";
|
||||
|
||||
var defaultValue = 'null';
|
||||
var existingDefault = ctx.defaults[field.name];
|
||||
|
||||
if (existingDefault != null) {
|
||||
defaultValue = dartObjectToString(existingDefault);
|
||||
deserializedRepresentation =
|
||||
'$deserializedRepresentation ?? $defaultValue';
|
||||
}
|
||||
|
||||
if (ctx.fieldInfo[field.name]?.deserializer != null) {
|
||||
var name =
|
||||
MirrorSystem.getName(ctx.fieldInfo[field.name].deserializer);
|
||||
deserializedRepresentation = "$name(map['$alias'])";
|
||||
} else if (dateTimeTypeChecker.isAssignableFromType(type)) {
|
||||
deserializedRepresentation = "map['$alias'] != null ? "
|
||||
"(map['$alias'] is DateTime ? (map['$alias'] as DateTime) : DateTime.parse(map['$alias'].toString()))"
|
||||
" : $defaultValue";
|
||||
}
|
||||
|
||||
// Serialize model classes via `XSerializer.toMap`
|
||||
else if (isModelClass(type)) {
|
||||
var rc = ReCase(type.name);
|
||||
deserializedRepresentation = "map['$alias'] != null"
|
||||
" ? ${rc.pascalCase}Serializer.fromMap(map['$alias'] as Map)"
|
||||
" : $defaultValue";
|
||||
} else if (type is InterfaceType) {
|
||||
if (isListOfModelType(type)) {
|
||||
var rc = ReCase(type.typeArguments[0].name);
|
||||
deserializedRepresentation = "map['$alias'] is Iterable"
|
||||
" ? List.unmodifiable(((map['$alias'] as Iterable)"
|
||||
".whereType<Map>())"
|
||||
".map(${rc.pascalCase}Serializer.fromMap))"
|
||||
" : $defaultValue";
|
||||
} else if (isMapToModelType(type)) {
|
||||
var rc = ReCase(type.typeArguments[1].name);
|
||||
deserializedRepresentation = '''
|
||||
map['$alias'] is Map
|
||||
? Map.unmodifiable((map['$alias'] as Map).keys.fold({}, (out, key) {
|
||||
return out..[key] = ${rc.pascalCase}Serializer
|
||||
.fromMap(((map['$alias'] as Map)[key]) as Map);
|
||||
}))
|
||||
: $defaultValue
|
||||
''';
|
||||
} else if (type.element.isEnum) {
|
||||
deserializedRepresentation = '''
|
||||
map['$alias'] is ${type.name}
|
||||
? (map['$alias'] as ${type.name})
|
||||
:
|
||||
(
|
||||
map['$alias'] is int
|
||||
? ${type.name}.values[map['$alias'] as int]
|
||||
: $defaultValue
|
||||
)
|
||||
''';
|
||||
} else if (const TypeChecker.fromRuntime(List)
|
||||
.isAssignableFromType(type) &&
|
||||
type.typeArguments.length == 1) {
|
||||
var arg = convertTypeReference(type.typeArguments[0])
|
||||
.accept(DartEmitter());
|
||||
deserializedRepresentation = '''
|
||||
map['$alias'] is Iterable
|
||||
? (map['$alias'] as Iterable).cast<$arg>().toList()
|
||||
: $defaultValue
|
||||
''';
|
||||
} else if (const TypeChecker.fromRuntime(Map)
|
||||
.isAssignableFromType(type) &&
|
||||
type.typeArguments.length == 2) {
|
||||
var key = convertTypeReference(type.typeArguments[0])
|
||||
.accept(DartEmitter());
|
||||
var value = convertTypeReference(type.typeArguments[1])
|
||||
.accept(DartEmitter());
|
||||
deserializedRepresentation = '''
|
||||
map['$alias'] is Map
|
||||
? (map['$alias'] as Map).cast<$key, $value>()
|
||||
: $defaultValue
|
||||
''';
|
||||
} else if (const TypeChecker.fromRuntime(Uint8List)
|
||||
.isAssignableFromType(type)) {
|
||||
deserializedRepresentation = '''
|
||||
map['$alias'] is Uint8List
|
||||
? (map['$alias'] as Uint8List)
|
||||
:
|
||||
(
|
||||
map['$alias'] is Iterable<int>
|
||||
? Uint8List.fromList((map['$alias'] as Iterable<int>).toList())
|
||||
:
|
||||
(
|
||||
map['$alias'] is String
|
||||
? Uint8List.fromList(base64.decode(map['$alias'] as String))
|
||||
: $defaultValue
|
||||
)
|
||||
)
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
||||
buf.write('${field.name}: $deserializedRepresentation');
|
||||
}
|
||||
|
||||
buf.write(');');
|
||||
method.body = Code(buf.toString());
|
||||
}));
|
||||
}
|
||||
|
||||
void generateFieldsClass(BuildContext ctx, LibraryBuilder file) {
|
||||
file.body.add(Class((clazz) {
|
||||
clazz
|
||||
..abstract = true
|
||||
..name = '${ctx.modelClassNameRecase.pascalCase}Fields';
|
||||
|
||||
clazz.fields.add(Field((b) {
|
||||
b
|
||||
..static = true
|
||||
..modifier = FieldModifier.constant
|
||||
..type = TypeReference((b) => b
|
||||
..symbol = 'List'
|
||||
..types.add(refer('String')))
|
||||
..name = 'allFields'
|
||||
..assignment = literalConstList(
|
||||
ctx.fields.map((f) => refer(f.name)).toList(),
|
||||
refer('String'))
|
||||
.code;
|
||||
}));
|
||||
|
||||
for (var field in ctx.fields) {
|
||||
clazz.fields.add(Field((b) {
|
||||
b
|
||||
..static = true
|
||||
..modifier = FieldModifier.constant
|
||||
..type = Reference('String')
|
||||
..name = field.name
|
||||
..assignment = Code("'${ctx.resolveFieldName(field.name)}'");
|
||||
}));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
223
packages/serialize/angel_serialize_generator/lib/typescript.dart
Normal file
223
packages/serialize/angel_serialize_generator/lib/typescript.dart
Normal file
|
@ -0,0 +1,223 @@
|
|||
part of angel_serialize_generator;
|
||||
|
||||
class TypeScriptDefinitionBuilder implements Builder {
|
||||
final bool autoSnakeCaseNames;
|
||||
|
||||
const TypeScriptDefinitionBuilder({this.autoSnakeCaseNames = true});
|
||||
|
||||
@override
|
||||
Map<String, List<String>> get buildExtensions {
|
||||
return {
|
||||
'.dart': ['.d.ts']
|
||||
};
|
||||
}
|
||||
|
||||
Future<String> compileToTypeScriptType(
|
||||
BuildContext ctx,
|
||||
String fieldName,
|
||||
DartType type,
|
||||
List<String> refs,
|
||||
List<CodeBuffer> ext,
|
||||
BuildStep buildStep) async {
|
||||
String typeScriptType = 'any';
|
||||
|
||||
var types = const {
|
||||
num: 'number',
|
||||
bool: 'boolean',
|
||||
String: 'string',
|
||||
Symbol: 'Symbol',
|
||||
};
|
||||
|
||||
types.forEach((t, tsType) {
|
||||
if (TypeChecker.fromRuntime(t).isAssignableFromType(type)) {
|
||||
typeScriptType = tsType;
|
||||
}
|
||||
});
|
||||
|
||||
if (type is InterfaceType) {
|
||||
if (isListOfModelType(type)) {
|
||||
var arg = await compileToTypeScriptType(
|
||||
ctx, fieldName, type.typeArguments[0], refs, ext, buildStep);
|
||||
typeScriptType = '$arg[]';
|
||||
} else if (const TypeChecker.fromRuntime(Map)
|
||||
.isAssignableFromType(type) &&
|
||||
type.typeArguments.length == 2) {
|
||||
var key = await compileToTypeScriptType(
|
||||
ctx, fieldName, type.typeArguments[0], refs, ext, buildStep);
|
||||
var value = await compileToTypeScriptType(
|
||||
ctx, fieldName, type.typeArguments[1], refs, ext, buildStep);
|
||||
//var modelType = type.typeArguments[1];
|
||||
/*var innerCtx = await buildContext(
|
||||
modelType.element,
|
||||
ConstantReader(
|
||||
serializableTypeChecker.firstAnnotationOf(modelType.element)),
|
||||
buildStep,
|
||||
buildStep.resolver,
|
||||
autoSnakeCaseNames,
|
||||
true,
|
||||
);*/
|
||||
|
||||
typeScriptType =
|
||||
ctx.modelClassNameRecase.pascalCase + ReCase(fieldName).pascalCase;
|
||||
|
||||
ext.add(CodeBuffer()
|
||||
..writeln('interface $typeScriptType {')
|
||||
..indent()
|
||||
..writeln('[key: $key]: $value;')
|
||||
..outdent()
|
||||
..writeln('}'));
|
||||
} else if (const TypeChecker.fromRuntime(List)
|
||||
.isAssignableFromType(type)) {
|
||||
if (type.typeArguments.isEmpty) {
|
||||
typeScriptType = 'any[]';
|
||||
} else {
|
||||
var arg = await compileToTypeScriptType(
|
||||
ctx, fieldName, type.typeArguments[0], refs, ext, buildStep);
|
||||
typeScriptType = '$arg[]';
|
||||
}
|
||||
} else if (isModelClass(type)) {
|
||||
var sourcePath = buildStep.inputId.uri.toString();
|
||||
var targetPath = type.element.source.uri.toString();
|
||||
|
||||
if (!p.equals(sourcePath, targetPath)) {
|
||||
var relative = p.relative(targetPath, from: sourcePath);
|
||||
String ref;
|
||||
|
||||
if (type.element.source.uri.scheme == 'asset') {
|
||||
var id = AssetId.resolve(type.element.source.uri.toString());
|
||||
if (id.package != buildStep.inputId.package) {
|
||||
ref = '/// <reference types="${id.package}" />';
|
||||
}
|
||||
}
|
||||
|
||||
if (ref == null) {
|
||||
// var relative = (p.dirname(targetPath) == p.dirname(sourcePath))
|
||||
// ? p.basename(targetPath)
|
||||
// : p.relative(targetPath, from: sourcePath);
|
||||
var parent = p.dirname(relative);
|
||||
var filename =
|
||||
p.setExtension(p.basenameWithoutExtension(relative), '.d.ts');
|
||||
relative = p.joinAll(p.split(parent).toList()..add(filename));
|
||||
ref = '/// <reference path="$relative" />';
|
||||
}
|
||||
if (!refs.contains(ref)) refs.add(ref);
|
||||
}
|
||||
|
||||
var ctx = await buildContext(
|
||||
type.element,
|
||||
ConstantReader(
|
||||
serializableTypeChecker.firstAnnotationOf(type.element)),
|
||||
buildStep,
|
||||
buildStep.resolver,
|
||||
autoSnakeCaseNames,
|
||||
);
|
||||
typeScriptType = ctx.modelClassNameRecase.pascalCase;
|
||||
}
|
||||
}
|
||||
|
||||
return typeScriptType;
|
||||
}
|
||||
|
||||
@override
|
||||
Future build(BuildStep buildStep) async {
|
||||
var contexts = <BuildContext>[];
|
||||
LibraryReader lib;
|
||||
|
||||
try {
|
||||
lib = LibraryReader(await buildStep.inputLibrary);
|
||||
} catch (_) {
|
||||
return;
|
||||
}
|
||||
|
||||
var elements = <AnnotatedElement>[];
|
||||
|
||||
try {
|
||||
elements = lib
|
||||
.annotatedWith(const TypeChecker.fromRuntime(Serializable))
|
||||
.toList();
|
||||
} catch (_) {
|
||||
// Ignore error in source_gen/build_runner that has no explanation
|
||||
}
|
||||
|
||||
for (var element in elements) {
|
||||
if (element.element.kind != ElementKind.CLASS) {
|
||||
throw 'Only classes can be annotated with a @Serializable() annotation.';
|
||||
}
|
||||
|
||||
var annotation = element.annotation;
|
||||
|
||||
var serializers = annotation.peek('serializers')?.listValue ?? [];
|
||||
|
||||
if (serializers.isEmpty) continue;
|
||||
|
||||
// Check if TypeScript serializer is supported
|
||||
if (!serializers.any((s) => s.toIntValue() == Serializers.typescript)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
contexts.add(await buildContext(
|
||||
element.element as ClassElement,
|
||||
element.annotation,
|
||||
buildStep,
|
||||
await buildStep.resolver,
|
||||
autoSnakeCaseNames != false));
|
||||
}
|
||||
|
||||
if (contexts.isEmpty) return;
|
||||
|
||||
var refs = <String>[];
|
||||
var buf = CodeBuffer(
|
||||
trailingNewline: true,
|
||||
sourceUrl: buildStep.inputId.uri,
|
||||
);
|
||||
|
||||
buf.writeln('// GENERATED CODE - DO NOT MODIFY BY HAND');
|
||||
|
||||
// declare module `foo` {
|
||||
buf
|
||||
..writeln("declare module '${buildStep.inputId.package}' {")
|
||||
..indent();
|
||||
|
||||
for (var ctx in contexts) {
|
||||
// interface Bar { ... }
|
||||
buf
|
||||
..writeln('interface ${ctx.modelClassNameRecase.pascalCase} {')
|
||||
..indent();
|
||||
|
||||
var ext = <CodeBuffer>[];
|
||||
|
||||
for (var field in ctx.fields) {
|
||||
// Skip excluded fields
|
||||
if (ctx.excluded[field.name]?.canSerialize == false) continue;
|
||||
|
||||
var alias = ctx.resolveFieldName(field.name);
|
||||
var typeScriptType = await compileToTypeScriptType(ctx, field.name,
|
||||
ctx.resolveSerializedFieldType(field.name), refs, ext, buildStep);
|
||||
|
||||
// foo: string;
|
||||
if (!ctx.requiredFields.containsKey(field.name)) alias += '?';
|
||||
buf.writeln('$alias: $typeScriptType;');
|
||||
}
|
||||
|
||||
buf
|
||||
..outdent()
|
||||
..writeln('}');
|
||||
|
||||
for (var b in ext) {
|
||||
b.copyInto(buf);
|
||||
}
|
||||
}
|
||||
|
||||
buf
|
||||
..outdent()
|
||||
..writeln('}');
|
||||
var finalBuf = CodeBuffer();
|
||||
refs.forEach(finalBuf.writeln);
|
||||
buf.copyInto(finalBuf);
|
||||
|
||||
await buildStep.writeAsString(
|
||||
buildStep.inputId.changeExtension('.d.ts'),
|
||||
finalBuf.toString(),
|
||||
);
|
||||
}
|
||||
}
|
28
packages/serialize/angel_serialize_generator/pubspec.yaml
Normal file
28
packages/serialize/angel_serialize_generator/pubspec.yaml
Normal file
|
@ -0,0 +1,28 @@
|
|||
name: angel_serialize_generator
|
||||
version: 2.5.0
|
||||
description: Model serialization generators, designed for use with Angel. Combine with angel_serialize for flexible modeling.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/serialize
|
||||
environment:
|
||||
sdk: '>=2.0.0 <3.0.0'
|
||||
dependencies:
|
||||
analyzer: ">=0.27.1 <2.0.0"
|
||||
angel_model: ^1.0.0
|
||||
angel_serialize: ^2.2.0
|
||||
build: ">=0.12.0 <2.0.0"
|
||||
build_config: ">=0.3.0 <2.0.0"
|
||||
code_buffer: ^1.0.0
|
||||
code_builder: ^3.0.0
|
||||
meta: ^1.0.0
|
||||
path: ^1.0.0
|
||||
recase: ^2.0.0
|
||||
source_gen: ^0.9.0
|
||||
quiver: ^2.0.0
|
||||
dev_dependencies:
|
||||
build_runner: ^1.0.0
|
||||
collection: ^1.0.0
|
||||
pedantic: ^1.0.0
|
||||
test: ^1.0.0
|
||||
# dependency_overrides:
|
||||
# angel_serialize:
|
||||
# path: ../angel_serialize
|
161
packages/serialize/angel_serialize_generator/test/book_test.dart
Normal file
161
packages/serialize/angel_serialize_generator/test/book_test.dart
Normal file
|
@ -0,0 +1,161 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'models/book.dart';
|
||||
|
||||
const String deathlyHallowsIsbn = '0-545-01022-5';
|
||||
|
||||
main() {
|
||||
var deathlyHallows = Book(
|
||||
id: '0',
|
||||
author: 'J.K. Rowling',
|
||||
title: 'Harry Potter and the Deathly Hallows',
|
||||
description: 'The 7th book.',
|
||||
pageCount: 759,
|
||||
notModels: [1.0, 3.0],
|
||||
updatedAt: DateTime.now());
|
||||
var serializedDeathlyHallows = deathlyHallows.toJson();
|
||||
print('Deathly Hallows: $deathlyHallows');
|
||||
|
||||
var jkRowling = Author(
|
||||
id: '1',
|
||||
name: 'J.K. Rowling',
|
||||
age: 51,
|
||||
books: [deathlyHallows],
|
||||
newestBook: deathlyHallows);
|
||||
var serializedJkRowling = authorSerializer.encode(jkRowling);
|
||||
var deathlyHallowsMap = bookSerializer.encode(deathlyHallows);
|
||||
print('J.K. Rowling: $jkRowling');
|
||||
|
||||
var library = Library(collection: {deathlyHallowsIsbn: deathlyHallows});
|
||||
var serializedLibrary = LibrarySerializer.toMap(library);
|
||||
print('Library: $library');
|
||||
|
||||
group('serialization', () {
|
||||
test('serialization sets proper fields', () {
|
||||
expect(serializedDeathlyHallows['id'], deathlyHallows.id);
|
||||
expect(serializedDeathlyHallows['author'], deathlyHallows.author);
|
||||
expect(
|
||||
serializedDeathlyHallows['description'], deathlyHallows.description);
|
||||
expect(serializedDeathlyHallows['page_count'], deathlyHallows.pageCount);
|
||||
expect(serializedDeathlyHallows['created_at'], isNull);
|
||||
expect(serializedDeathlyHallows['updated_at'],
|
||||
deathlyHallows.updatedAt.toIso8601String());
|
||||
});
|
||||
|
||||
test('can be mutated', () {
|
||||
var b = deathlyHallows.copyWith();
|
||||
b.author = 'Hey';
|
||||
expect(b.author, 'Hey');
|
||||
expect(b.toJson()[BookFields.author], 'Hey');
|
||||
});
|
||||
|
||||
test('heeds @Alias', () {
|
||||
expect(serializedDeathlyHallows['page_count'], deathlyHallows.pageCount);
|
||||
expect(serializedDeathlyHallows.keys, isNot(contains('pageCount')));
|
||||
});
|
||||
|
||||
test('standard list', () {
|
||||
expect(serializedDeathlyHallows['not_models'], deathlyHallows.notModels);
|
||||
});
|
||||
|
||||
test('heeds @exclude', () {
|
||||
expect(serializedJkRowling.keys, isNot(contains('secret')));
|
||||
});
|
||||
|
||||
test('heeds canDeserialize', () {
|
||||
var map = Map.from(serializedJkRowling)..['obscured'] = 'foo';
|
||||
var author = authorSerializer.decode(map);
|
||||
expect(author.obscured, 'foo');
|
||||
});
|
||||
|
||||
test('heeds canSerialize', () {
|
||||
expect(serializedJkRowling.keys, isNot(contains('obscured')));
|
||||
});
|
||||
|
||||
test('nested @serializable class is serialized', () {
|
||||
expect(serializedJkRowling['newest_book'], deathlyHallowsMap);
|
||||
});
|
||||
|
||||
test('list of nested @serializable class is serialized', () {
|
||||
expect(serializedJkRowling['books'], [deathlyHallowsMap]);
|
||||
});
|
||||
|
||||
test('map with @serializable class as second key is serialized', () {
|
||||
expect(serializedLibrary['collection'],
|
||||
{deathlyHallowsIsbn: deathlyHallowsMap});
|
||||
});
|
||||
});
|
||||
|
||||
test('fields', () {
|
||||
expect(BookFields.author, 'author');
|
||||
expect(BookFields.notModels, 'not_models');
|
||||
expect(BookFields.camelCaseString, 'camelCase');
|
||||
});
|
||||
|
||||
test('equals', () {
|
||||
expect(jkRowling.copyWith(), jkRowling);
|
||||
expect(deathlyHallows.copyWith(), deathlyHallows);
|
||||
expect(library.copyWith(), library);
|
||||
});
|
||||
|
||||
test('custom method', () {
|
||||
expect(jkRowling.customMethod, 'hey!');
|
||||
});
|
||||
|
||||
test('required fields fromMap', () {
|
||||
expect(() => AuthorSerializer.fromMap({}), throwsFormatException);
|
||||
});
|
||||
|
||||
test('required fields toMap', () {
|
||||
var author = Author(name: null, age: 24);
|
||||
expect(() => author.toJson(), throwsFormatException);
|
||||
});
|
||||
|
||||
group('deserialization', () {
|
||||
test('deserialization sets proper fields', () {
|
||||
var book = BookSerializer.fromMap(deathlyHallowsMap);
|
||||
expect(book.id, deathlyHallows.id);
|
||||
expect(book.author, deathlyHallows.author);
|
||||
expect(book.description, deathlyHallows.description);
|
||||
expect(book.pageCount, deathlyHallows.pageCount);
|
||||
expect(book.notModels, deathlyHallows.notModels);
|
||||
expect(book.createdAt, isNull);
|
||||
expect(book.updatedAt, deathlyHallows.updatedAt);
|
||||
});
|
||||
|
||||
group('nested @serializable', () {
|
||||
var author = AuthorSerializer.fromMap(serializedJkRowling);
|
||||
|
||||
test('nested @serializable class is deserialized', () {
|
||||
var newestBook = author.newestBook;
|
||||
expect(newestBook, isNotNull);
|
||||
expect(newestBook.id, deathlyHallows.id);
|
||||
expect(newestBook.pageCount, deathlyHallows.pageCount);
|
||||
expect(newestBook.updatedAt, deathlyHallows.updatedAt);
|
||||
});
|
||||
|
||||
test('list of nested @serializable class is deserialized', () {
|
||||
expect(author.books, allOf(isList, isNotEmpty, hasLength(1)));
|
||||
var book = author.books.first;
|
||||
expect(book.id, deathlyHallows.id);
|
||||
expect(book.author, deathlyHallows.author);
|
||||
expect(book.description, deathlyHallows.description);
|
||||
expect(book.pageCount, deathlyHallows.pageCount);
|
||||
expect(book.createdAt, isNull);
|
||||
expect(book.updatedAt, deathlyHallows.updatedAt);
|
||||
});
|
||||
|
||||
test('map with @serializable class as second key is deserialized', () {
|
||||
var lib = LibrarySerializer.fromMap(serializedLibrary);
|
||||
expect(lib.collection, allOf(isNotEmpty, hasLength(1)));
|
||||
expect(lib.collection.keys.first, deathlyHallowsIsbn);
|
||||
var book = lib.collection[deathlyHallowsIsbn];
|
||||
expect(book.id, deathlyHallows.id);
|
||||
expect(book.author, deathlyHallows.author);
|
||||
expect(book.description, deathlyHallows.description);
|
||||
expect(book.pageCount, deathlyHallows.pageCount);
|
||||
expect(book.createdAt, isNull);
|
||||
expect(book.updatedAt, deathlyHallows.updatedAt);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'models/goat.dart';
|
||||
|
||||
void main() {
|
||||
group('constructor', () {
|
||||
test('int default', () {
|
||||
expect(Goat().integer, 34);
|
||||
});
|
||||
|
||||
test('list default', () {
|
||||
expect(Goat().list, [34, 35]);
|
||||
});
|
||||
});
|
||||
|
||||
group('from map', () {
|
||||
test('int default', () {
|
||||
expect(GoatSerializer.fromMap({}).integer, 34);
|
||||
});
|
||||
|
||||
test('list default', () {
|
||||
expect(GoatSerializer.fromMap({}).list, [34, 35]);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'models/with_enum.dart';
|
||||
|
||||
const WithEnum aWithEnum = WithEnum(type: WithEnumType.a);
|
||||
const WithEnum aWithEnum2 = WithEnum(type: WithEnumType.a);
|
||||
|
||||
void main() {
|
||||
test('enum serializes to int', () {
|
||||
var w = WithEnum(type: WithEnumType.b).toJson();
|
||||
expect(w[WithEnumFields.type], WithEnumType.values.indexOf(WithEnumType.b));
|
||||
});
|
||||
|
||||
test('enum serializes null if null', () {
|
||||
var w = WithEnum(type: null).toJson();
|
||||
expect(w[WithEnumFields.type], null);
|
||||
});
|
||||
|
||||
test('enum deserializes to default value from null', () {
|
||||
var map = {WithEnumFields.type: null};
|
||||
var w = WithEnumSerializer.fromMap(map);
|
||||
expect(w.type, WithEnumType.b);
|
||||
});
|
||||
|
||||
test('enum deserializes from int', () {
|
||||
var map = {
|
||||
WithEnumFields.type: WithEnumType.values.indexOf(WithEnumType.b)
|
||||
};
|
||||
var w = WithEnumSerializer.fromMap(map);
|
||||
expect(w.type, WithEnumType.b);
|
||||
});
|
||||
|
||||
test('enum deserializes from value', () {
|
||||
var map = {WithEnumFields.type: WithEnumType.c};
|
||||
var w = WithEnumSerializer.fromMap(map);
|
||||
expect(w.type, WithEnumType.c);
|
||||
});
|
||||
|
||||
test('equality', () {
|
||||
expect(WithEnum(type: WithEnumType.a), WithEnum(type: WithEnumType.a));
|
||||
expect(
|
||||
WithEnum(type: WithEnumType.a), isNot(WithEnum(type: WithEnumType.b)));
|
||||
});
|
||||
|
||||
test('const', () {
|
||||
expect(identical(aWithEnum, aWithEnum2), true);
|
||||
});
|
||||
|
||||
test('uint8list', () {
|
||||
var ee = WithEnum(
|
||||
imageBytes: Uint8List.fromList(List<int>.generate(1000, (i) => i)));
|
||||
var eeMap = ee.toJson();
|
||||
print(ee);
|
||||
var ef = WithEnumSerializer.fromMap(eeMap);
|
||||
expect(ee.copyWith(), ee);
|
||||
expect(ef, ee);
|
||||
});
|
||||
}
|
40
packages/serialize/angel_serialize_generator/test/models/book.d.ts
vendored
Normal file
40
packages/serialize/angel_serialize_generator/test/models/book.d.ts
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
declare module 'angel_serialize_generator' {
|
||||
interface Book {
|
||||
id?: string;
|
||||
created_at?: any;
|
||||
updated_at?: any;
|
||||
author?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
page_count?: number;
|
||||
not_models?: number[];
|
||||
camelCase?: string;
|
||||
}
|
||||
interface Author {
|
||||
id?: string;
|
||||
created_at?: any;
|
||||
updated_at?: any;
|
||||
name: string;
|
||||
age: number;
|
||||
books?: Book[];
|
||||
newest_book?: Book;
|
||||
}
|
||||
interface Library {
|
||||
id?: string;
|
||||
created_at?: any;
|
||||
updated_at?: any;
|
||||
collection?: LibraryCollection;
|
||||
}
|
||||
interface LibraryCollection {
|
||||
[key: string]: Book;
|
||||
}
|
||||
interface Bookmark {
|
||||
id?: string;
|
||||
created_at?: any;
|
||||
updated_at?: any;
|
||||
history?: number[];
|
||||
page: number;
|
||||
comment?: string;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
library angel_serialize.test.models.book;
|
||||
|
||||
import 'package:angel_model/angel_model.dart';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
part 'book.g.dart';
|
||||
|
||||
@Serializable(
|
||||
serializers: Serializers.all,
|
||||
includeAnnotations: [
|
||||
pragma('hello'),
|
||||
SerializableField(alias: 'omg'),
|
||||
],
|
||||
)
|
||||
abstract class _Book extends Model {
|
||||
String author, title, description;
|
||||
|
||||
/// The number of pages the book has.
|
||||
int pageCount;
|
||||
|
||||
List<double> notModels;
|
||||
|
||||
@SerializableField(alias: 'camelCase', isNullable: true)
|
||||
String camelCaseString;
|
||||
}
|
||||
|
||||
@Serializable(serializers: Serializers.all)
|
||||
abstract class _Author extends Model {
|
||||
@SerializableField(isNullable: false)
|
||||
String get name;
|
||||
|
||||
String get customMethod => 'hey!';
|
||||
|
||||
@SerializableField(
|
||||
isNullable: false, errorMessage: 'Custom message for missing `age`')
|
||||
int get age;
|
||||
|
||||
List<_Book> get books;
|
||||
|
||||
/// The newest book.
|
||||
_Book get newestBook;
|
||||
|
||||
@SerializableField(exclude: true, isNullable: true)
|
||||
String get secret;
|
||||
|
||||
@SerializableField(exclude: true, canDeserialize: true, isNullable: true)
|
||||
String get obscured;
|
||||
}
|
||||
|
||||
@Serializable(serializers: Serializers.all)
|
||||
abstract class _Library extends Model {
|
||||
Map<String, _Book> get collection;
|
||||
}
|
||||
|
||||
@Serializable(serializers: Serializers.all)
|
||||
abstract class _Bookmark extends Model {
|
||||
@SerializableField(exclude: true)
|
||||
final _Book book;
|
||||
|
||||
List<int> get history;
|
||||
|
||||
@SerializableField(isNullable: false)
|
||||
int get page;
|
||||
|
||||
String get comment;
|
||||
|
||||
_Bookmark(this.book);
|
||||
}
|
|
@ -0,0 +1,712 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of angel_serialize.test.models.book;
|
||||
|
||||
// **************************************************************************
|
||||
// JsonModelGenerator
|
||||
// **************************************************************************
|
||||
|
||||
@generatedSerializable
|
||||
@pragma('hello')
|
||||
@SerializableField(alias: 'omg')
|
||||
class Book extends _Book {
|
||||
Book(
|
||||
{this.id,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.author,
|
||||
this.title,
|
||||
this.description,
|
||||
this.pageCount,
|
||||
List<double> notModels,
|
||||
this.camelCaseString})
|
||||
: this.notModels = List.unmodifiable(notModels ?? []);
|
||||
|
||||
/// A unique identifier corresponding to this item.
|
||||
@override
|
||||
String id;
|
||||
|
||||
/// The time at which this item was created.
|
||||
@override
|
||||
DateTime createdAt;
|
||||
|
||||
/// The last time at which this item was updated.
|
||||
@override
|
||||
DateTime updatedAt;
|
||||
|
||||
@override
|
||||
String author;
|
||||
|
||||
@override
|
||||
String title;
|
||||
|
||||
@override
|
||||
String description;
|
||||
|
||||
/// The number of pages the book has.
|
||||
@override
|
||||
int pageCount;
|
||||
|
||||
@override
|
||||
List<double> notModels;
|
||||
|
||||
@override
|
||||
String camelCaseString;
|
||||
|
||||
Book copyWith(
|
||||
{String id,
|
||||
DateTime createdAt,
|
||||
DateTime updatedAt,
|
||||
String author,
|
||||
String title,
|
||||
String description,
|
||||
int pageCount,
|
||||
List<double> notModels,
|
||||
String camelCaseString}) {
|
||||
return Book(
|
||||
id: id ?? this.id,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
author: author ?? this.author,
|
||||
title: title ?? this.title,
|
||||
description: description ?? this.description,
|
||||
pageCount: pageCount ?? this.pageCount,
|
||||
notModels: notModels ?? this.notModels,
|
||||
camelCaseString: camelCaseString ?? this.camelCaseString);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _Book &&
|
||||
other.id == id &&
|
||||
other.createdAt == createdAt &&
|
||||
other.updatedAt == updatedAt &&
|
||||
other.author == author &&
|
||||
other.title == title &&
|
||||
other.description == description &&
|
||||
other.pageCount == pageCount &&
|
||||
ListEquality<double>(DefaultEquality<double>())
|
||||
.equals(other.notModels, notModels) &&
|
||||
other.camelCaseString == camelCaseString;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([
|
||||
id,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
author,
|
||||
title,
|
||||
description,
|
||||
pageCount,
|
||||
notModels,
|
||||
camelCaseString
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "Book(id=$id, createdAt=$createdAt, updatedAt=$updatedAt, author=$author, title=$title, description=$description, pageCount=$pageCount, notModels=$notModels, camelCaseString=$camelCaseString)";
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return BookSerializer.toMap(this);
|
||||
}
|
||||
}
|
||||
|
||||
@generatedSerializable
|
||||
class Author extends _Author {
|
||||
Author(
|
||||
{this.id,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
@required this.name,
|
||||
@required this.age,
|
||||
List<_Book> books,
|
||||
this.newestBook,
|
||||
this.secret,
|
||||
this.obscured})
|
||||
: this.books = List.unmodifiable(books ?? []);
|
||||
|
||||
/// A unique identifier corresponding to this item.
|
||||
@override
|
||||
String id;
|
||||
|
||||
/// The time at which this item was created.
|
||||
@override
|
||||
DateTime createdAt;
|
||||
|
||||
/// The last time at which this item was updated.
|
||||
@override
|
||||
DateTime updatedAt;
|
||||
|
||||
@override
|
||||
final String name;
|
||||
|
||||
@override
|
||||
final int age;
|
||||
|
||||
@override
|
||||
final List<_Book> books;
|
||||
|
||||
/// The newest book.
|
||||
@override
|
||||
final _Book newestBook;
|
||||
|
||||
@override
|
||||
final String secret;
|
||||
|
||||
@override
|
||||
final String obscured;
|
||||
|
||||
Author copyWith(
|
||||
{String id,
|
||||
DateTime createdAt,
|
||||
DateTime updatedAt,
|
||||
String name,
|
||||
int age,
|
||||
List<_Book> books,
|
||||
_Book newestBook,
|
||||
String secret,
|
||||
String obscured}) {
|
||||
return Author(
|
||||
id: id ?? this.id,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
name: name ?? this.name,
|
||||
age: age ?? this.age,
|
||||
books: books ?? this.books,
|
||||
newestBook: newestBook ?? this.newestBook,
|
||||
secret: secret ?? this.secret,
|
||||
obscured: obscured ?? this.obscured);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _Author &&
|
||||
other.id == id &&
|
||||
other.createdAt == createdAt &&
|
||||
other.updatedAt == updatedAt &&
|
||||
other.name == name &&
|
||||
other.age == age &&
|
||||
ListEquality<_Book>(DefaultEquality<_Book>())
|
||||
.equals(other.books, books) &&
|
||||
other.newestBook == newestBook &&
|
||||
other.secret == secret &&
|
||||
other.obscured == obscured;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([
|
||||
id,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
name,
|
||||
age,
|
||||
books,
|
||||
newestBook,
|
||||
secret,
|
||||
obscured
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "Author(id=$id, createdAt=$createdAt, updatedAt=$updatedAt, name=$name, age=$age, books=$books, newestBook=$newestBook, secret=$secret, obscured=$obscured)";
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return AuthorSerializer.toMap(this);
|
||||
}
|
||||
}
|
||||
|
||||
@generatedSerializable
|
||||
class Library extends _Library {
|
||||
Library(
|
||||
{this.id, this.createdAt, this.updatedAt, Map<String, _Book> collection})
|
||||
: this.collection = Map.unmodifiable(collection ?? {});
|
||||
|
||||
/// A unique identifier corresponding to this item.
|
||||
@override
|
||||
String id;
|
||||
|
||||
/// The time at which this item was created.
|
||||
@override
|
||||
DateTime createdAt;
|
||||
|
||||
/// The last time at which this item was updated.
|
||||
@override
|
||||
DateTime updatedAt;
|
||||
|
||||
@override
|
||||
final Map<String, _Book> collection;
|
||||
|
||||
Library copyWith(
|
||||
{String id,
|
||||
DateTime createdAt,
|
||||
DateTime updatedAt,
|
||||
Map<String, _Book> collection}) {
|
||||
return Library(
|
||||
id: id ?? this.id,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
collection: collection ?? this.collection);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _Library &&
|
||||
other.id == id &&
|
||||
other.createdAt == createdAt &&
|
||||
other.updatedAt == updatedAt &&
|
||||
MapEquality<String, _Book>(
|
||||
keys: DefaultEquality<String>(),
|
||||
values: DefaultEquality<_Book>())
|
||||
.equals(other.collection, collection);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([id, createdAt, updatedAt, collection]);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "Library(id=$id, createdAt=$createdAt, updatedAt=$updatedAt, collection=$collection)";
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return LibrarySerializer.toMap(this);
|
||||
}
|
||||
}
|
||||
|
||||
@generatedSerializable
|
||||
class Bookmark extends _Bookmark {
|
||||
Bookmark(_Book book,
|
||||
{this.id,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
List<int> history,
|
||||
@required this.page,
|
||||
this.comment})
|
||||
: this.history = List.unmodifiable(history ?? []),
|
||||
super(book);
|
||||
|
||||
/// A unique identifier corresponding to this item.
|
||||
@override
|
||||
String id;
|
||||
|
||||
/// The time at which this item was created.
|
||||
@override
|
||||
DateTime createdAt;
|
||||
|
||||
/// The last time at which this item was updated.
|
||||
@override
|
||||
DateTime updatedAt;
|
||||
|
||||
@override
|
||||
final List<int> history;
|
||||
|
||||
@override
|
||||
final int page;
|
||||
|
||||
@override
|
||||
final String comment;
|
||||
|
||||
Bookmark copyWith(_Book book,
|
||||
{String id,
|
||||
DateTime createdAt,
|
||||
DateTime updatedAt,
|
||||
List<int> history,
|
||||
int page,
|
||||
String comment}) {
|
||||
return Bookmark(book,
|
||||
id: id ?? this.id,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
history: history ?? this.history,
|
||||
page: page ?? this.page,
|
||||
comment: comment ?? this.comment);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _Bookmark &&
|
||||
other.id == id &&
|
||||
other.createdAt == createdAt &&
|
||||
other.updatedAt == updatedAt &&
|
||||
ListEquality<int>(DefaultEquality<int>())
|
||||
.equals(other.history, history) &&
|
||||
other.page == page &&
|
||||
other.comment == comment;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([id, createdAt, updatedAt, history, page, comment]);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "Bookmark(id=$id, createdAt=$createdAt, updatedAt=$updatedAt, history=$history, page=$page, comment=$comment)";
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return BookmarkSerializer.toMap(this);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// SerializerGenerator
|
||||
// **************************************************************************
|
||||
|
||||
const BookSerializer bookSerializer = BookSerializer();
|
||||
|
||||
class BookEncoder extends Converter<Book, Map> {
|
||||
const BookEncoder();
|
||||
|
||||
@override
|
||||
Map convert(Book model) => BookSerializer.toMap(model);
|
||||
}
|
||||
|
||||
class BookDecoder extends Converter<Map, Book> {
|
||||
const BookDecoder();
|
||||
|
||||
@override
|
||||
Book convert(Map map) => BookSerializer.fromMap(map);
|
||||
}
|
||||
|
||||
class BookSerializer extends Codec<Book, Map> {
|
||||
const BookSerializer();
|
||||
|
||||
@override
|
||||
get encoder => const BookEncoder();
|
||||
@override
|
||||
get decoder => const BookDecoder();
|
||||
static Book fromMap(Map map) {
|
||||
return Book(
|
||||
id: map['id'] as String,
|
||||
createdAt: map['created_at'] != null
|
||||
? (map['created_at'] is DateTime
|
||||
? (map['created_at'] as DateTime)
|
||||
: DateTime.parse(map['created_at'].toString()))
|
||||
: null,
|
||||
updatedAt: map['updated_at'] != null
|
||||
? (map['updated_at'] is DateTime
|
||||
? (map['updated_at'] as DateTime)
|
||||
: DateTime.parse(map['updated_at'].toString()))
|
||||
: null,
|
||||
author: map['author'] as String,
|
||||
title: map['title'] as String,
|
||||
description: map['description'] as String,
|
||||
pageCount: map['page_count'] as int,
|
||||
notModels: map['not_models'] is Iterable
|
||||
? (map['not_models'] as Iterable).cast<double>().toList()
|
||||
: null,
|
||||
camelCaseString: map['camelCase'] as String);
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(_Book model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
'id': model.id,
|
||||
'created_at': model.createdAt?.toIso8601String(),
|
||||
'updated_at': model.updatedAt?.toIso8601String(),
|
||||
'author': model.author,
|
||||
'title': model.title,
|
||||
'description': model.description,
|
||||
'page_count': model.pageCount,
|
||||
'not_models': model.notModels,
|
||||
'camelCase': model.camelCaseString
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BookFields {
|
||||
static const List<String> allFields = <String>[
|
||||
id,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
author,
|
||||
title,
|
||||
description,
|
||||
pageCount,
|
||||
notModels,
|
||||
camelCaseString
|
||||
];
|
||||
|
||||
static const String id = 'id';
|
||||
|
||||
static const String createdAt = 'created_at';
|
||||
|
||||
static const String updatedAt = 'updated_at';
|
||||
|
||||
static const String author = 'author';
|
||||
|
||||
static const String title = 'title';
|
||||
|
||||
static const String description = 'description';
|
||||
|
||||
static const String pageCount = 'page_count';
|
||||
|
||||
static const String notModels = 'not_models';
|
||||
|
||||
static const String camelCaseString = 'camelCase';
|
||||
}
|
||||
|
||||
const AuthorSerializer authorSerializer = AuthorSerializer();
|
||||
|
||||
class AuthorEncoder extends Converter<Author, Map> {
|
||||
const AuthorEncoder();
|
||||
|
||||
@override
|
||||
Map convert(Author model) => AuthorSerializer.toMap(model);
|
||||
}
|
||||
|
||||
class AuthorDecoder extends Converter<Map, Author> {
|
||||
const AuthorDecoder();
|
||||
|
||||
@override
|
||||
Author convert(Map map) => AuthorSerializer.fromMap(map);
|
||||
}
|
||||
|
||||
class AuthorSerializer extends Codec<Author, Map> {
|
||||
const AuthorSerializer();
|
||||
|
||||
@override
|
||||
get encoder => const AuthorEncoder();
|
||||
@override
|
||||
get decoder => const AuthorDecoder();
|
||||
static Author fromMap(Map map) {
|
||||
if (map['name'] == null) {
|
||||
throw FormatException("Missing required field 'name' on Author.");
|
||||
}
|
||||
|
||||
if (map['age'] == null) {
|
||||
throw FormatException("Custom message for missing `age`");
|
||||
}
|
||||
|
||||
return Author(
|
||||
id: map['id'] as String,
|
||||
createdAt: map['created_at'] != null
|
||||
? (map['created_at'] is DateTime
|
||||
? (map['created_at'] as DateTime)
|
||||
: DateTime.parse(map['created_at'].toString()))
|
||||
: null,
|
||||
updatedAt: map['updated_at'] != null
|
||||
? (map['updated_at'] is DateTime
|
||||
? (map['updated_at'] as DateTime)
|
||||
: DateTime.parse(map['updated_at'].toString()))
|
||||
: null,
|
||||
name: map['name'] as String,
|
||||
age: map['age'] as int,
|
||||
books: map['books'] is Iterable
|
||||
? List.unmodifiable(((map['books'] as Iterable).whereType<Map>())
|
||||
.map(BookSerializer.fromMap))
|
||||
: null,
|
||||
newestBook: map['newest_book'] != null
|
||||
? BookSerializer.fromMap(map['newest_book'] as Map)
|
||||
: null,
|
||||
obscured: map['obscured'] as String);
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(_Author model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
if (model.name == null) {
|
||||
throw FormatException("Missing required field 'name' on Author.");
|
||||
}
|
||||
|
||||
if (model.age == null) {
|
||||
throw FormatException("Custom message for missing `age`");
|
||||
}
|
||||
|
||||
return {
|
||||
'id': model.id,
|
||||
'created_at': model.createdAt?.toIso8601String(),
|
||||
'updated_at': model.updatedAt?.toIso8601String(),
|
||||
'name': model.name,
|
||||
'age': model.age,
|
||||
'books': model.books?.map((m) => BookSerializer.toMap(m))?.toList(),
|
||||
'newest_book': BookSerializer.toMap(model.newestBook)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AuthorFields {
|
||||
static const List<String> allFields = <String>[
|
||||
id,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
name,
|
||||
age,
|
||||
books,
|
||||
newestBook,
|
||||
secret,
|
||||
obscured
|
||||
];
|
||||
|
||||
static const String id = 'id';
|
||||
|
||||
static const String createdAt = 'created_at';
|
||||
|
||||
static const String updatedAt = 'updated_at';
|
||||
|
||||
static const String name = 'name';
|
||||
|
||||
static const String age = 'age';
|
||||
|
||||
static const String books = 'books';
|
||||
|
||||
static const String newestBook = 'newest_book';
|
||||
|
||||
static const String secret = 'secret';
|
||||
|
||||
static const String obscured = 'obscured';
|
||||
}
|
||||
|
||||
const LibrarySerializer librarySerializer = LibrarySerializer();
|
||||
|
||||
class LibraryEncoder extends Converter<Library, Map> {
|
||||
const LibraryEncoder();
|
||||
|
||||
@override
|
||||
Map convert(Library model) => LibrarySerializer.toMap(model);
|
||||
}
|
||||
|
||||
class LibraryDecoder extends Converter<Map, Library> {
|
||||
const LibraryDecoder();
|
||||
|
||||
@override
|
||||
Library convert(Map map) => LibrarySerializer.fromMap(map);
|
||||
}
|
||||
|
||||
class LibrarySerializer extends Codec<Library, Map> {
|
||||
const LibrarySerializer();
|
||||
|
||||
@override
|
||||
get encoder => const LibraryEncoder();
|
||||
@override
|
||||
get decoder => const LibraryDecoder();
|
||||
static Library fromMap(Map map) {
|
||||
return Library(
|
||||
id: map['id'] as String,
|
||||
createdAt: map['created_at'] != null
|
||||
? (map['created_at'] is DateTime
|
||||
? (map['created_at'] as DateTime)
|
||||
: DateTime.parse(map['created_at'].toString()))
|
||||
: null,
|
||||
updatedAt: map['updated_at'] != null
|
||||
? (map['updated_at'] is DateTime
|
||||
? (map['updated_at'] as DateTime)
|
||||
: DateTime.parse(map['updated_at'].toString()))
|
||||
: null,
|
||||
collection: map['collection'] is Map
|
||||
? Map.unmodifiable(
|
||||
(map['collection'] as Map).keys.fold({}, (out, key) {
|
||||
return out
|
||||
..[key] = BookSerializer.fromMap(
|
||||
((map['collection'] as Map)[key]) as Map);
|
||||
}))
|
||||
: null);
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(_Library model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
'id': model.id,
|
||||
'created_at': model.createdAt?.toIso8601String(),
|
||||
'updated_at': model.updatedAt?.toIso8601String(),
|
||||
'collection': model.collection.keys?.fold({}, (map, key) {
|
||||
return map..[key] = BookSerializer.toMap(model.collection[key]);
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class LibraryFields {
|
||||
static const List<String> allFields = <String>[
|
||||
id,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
collection
|
||||
];
|
||||
|
||||
static const String id = 'id';
|
||||
|
||||
static const String createdAt = 'created_at';
|
||||
|
||||
static const String updatedAt = 'updated_at';
|
||||
|
||||
static const String collection = 'collection';
|
||||
}
|
||||
|
||||
abstract class BookmarkSerializer {
|
||||
static Bookmark fromMap(Map map, _Book book) {
|
||||
if (map['page'] == null) {
|
||||
throw FormatException("Missing required field 'page' on Bookmark.");
|
||||
}
|
||||
|
||||
return Bookmark(book,
|
||||
id: map['id'] as String,
|
||||
createdAt: map['created_at'] != null
|
||||
? (map['created_at'] is DateTime
|
||||
? (map['created_at'] as DateTime)
|
||||
: DateTime.parse(map['created_at'].toString()))
|
||||
: null,
|
||||
updatedAt: map['updated_at'] != null
|
||||
? (map['updated_at'] is DateTime
|
||||
? (map['updated_at'] as DateTime)
|
||||
: DateTime.parse(map['updated_at'].toString()))
|
||||
: null,
|
||||
history: map['history'] is Iterable
|
||||
? (map['history'] as Iterable).cast<int>().toList()
|
||||
: null,
|
||||
page: map['page'] as int,
|
||||
comment: map['comment'] as String);
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(_Bookmark model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
if (model.page == null) {
|
||||
throw FormatException("Missing required field 'page' on Bookmark.");
|
||||
}
|
||||
|
||||
return {
|
||||
'id': model.id,
|
||||
'created_at': model.createdAt?.toIso8601String(),
|
||||
'updated_at': model.updatedAt?.toIso8601String(),
|
||||
'history': model.history,
|
||||
'page': model.page,
|
||||
'comment': model.comment
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BookmarkFields {
|
||||
static const List<String> allFields = <String>[
|
||||
id,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
history,
|
||||
page,
|
||||
comment
|
||||
];
|
||||
|
||||
static const String id = 'id';
|
||||
|
||||
static const String createdAt = 'created_at';
|
||||
|
||||
static const String updatedAt = 'updated_at';
|
||||
|
||||
static const String history = 'history';
|
||||
|
||||
static const String page = 'page';
|
||||
|
||||
static const String comment = 'comment';
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
part 'game_pad_button.g.dart';
|
||||
|
||||
@serializable
|
||||
abstract class _GamepadButton {
|
||||
String get name;
|
||||
int get radius;
|
||||
}
|
||||
|
||||
@serializable
|
||||
class _Gamepad {
|
||||
List<_GamepadButton> buttons;
|
||||
|
||||
Map<String, dynamic> dynamicMap;
|
||||
|
||||
// ignore: unused_field
|
||||
String _somethingPrivate;
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'game_pad_button.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonModelGenerator
|
||||
// **************************************************************************
|
||||
|
||||
@generatedSerializable
|
||||
class GamepadButton implements _GamepadButton {
|
||||
const GamepadButton({this.name, this.radius});
|
||||
|
||||
@override
|
||||
final String name;
|
||||
|
||||
@override
|
||||
final int radius;
|
||||
|
||||
GamepadButton copyWith({String name, int radius}) {
|
||||
return GamepadButton(
|
||||
name: name ?? this.name, radius: radius ?? this.radius);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _GamepadButton &&
|
||||
other.name == name &&
|
||||
other.radius == radius;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([name, radius]);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "GamepadButton(name=$name, radius=$radius)";
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return GamepadButtonSerializer.toMap(this);
|
||||
}
|
||||
}
|
||||
|
||||
@generatedSerializable
|
||||
class Gamepad extends _Gamepad {
|
||||
Gamepad({List<_GamepadButton> buttons, Map<String, dynamic> dynamicMap})
|
||||
: this.buttons = List.unmodifiable(buttons ?? []),
|
||||
this.dynamicMap = Map.unmodifiable(dynamicMap ?? {});
|
||||
|
||||
@override
|
||||
List<_GamepadButton> buttons;
|
||||
|
||||
@override
|
||||
Map<String, dynamic> dynamicMap;
|
||||
|
||||
Gamepad copyWith(
|
||||
{List<_GamepadButton> buttons, Map<String, dynamic> dynamicMap}) {
|
||||
return Gamepad(
|
||||
buttons: buttons ?? this.buttons,
|
||||
dynamicMap: dynamicMap ?? this.dynamicMap);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _Gamepad &&
|
||||
ListEquality<_GamepadButton>(DefaultEquality<_GamepadButton>())
|
||||
.equals(other.buttons, buttons) &&
|
||||
MapEquality<String, dynamic>(
|
||||
keys: DefaultEquality<String>(), values: DefaultEquality())
|
||||
.equals(other.dynamicMap, dynamicMap);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([buttons, dynamicMap]);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "Gamepad(buttons=$buttons, dynamicMap=$dynamicMap)";
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return GamepadSerializer.toMap(this);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// SerializerGenerator
|
||||
// **************************************************************************
|
||||
|
||||
const GamepadButtonSerializer gamepadButtonSerializer =
|
||||
GamepadButtonSerializer();
|
||||
|
||||
class GamepadButtonEncoder extends Converter<GamepadButton, Map> {
|
||||
const GamepadButtonEncoder();
|
||||
|
||||
@override
|
||||
Map convert(GamepadButton model) => GamepadButtonSerializer.toMap(model);
|
||||
}
|
||||
|
||||
class GamepadButtonDecoder extends Converter<Map, GamepadButton> {
|
||||
const GamepadButtonDecoder();
|
||||
|
||||
@override
|
||||
GamepadButton convert(Map map) => GamepadButtonSerializer.fromMap(map);
|
||||
}
|
||||
|
||||
class GamepadButtonSerializer extends Codec<GamepadButton, Map> {
|
||||
const GamepadButtonSerializer();
|
||||
|
||||
@override
|
||||
get encoder => const GamepadButtonEncoder();
|
||||
@override
|
||||
get decoder => const GamepadButtonDecoder();
|
||||
static GamepadButton fromMap(Map map) {
|
||||
return GamepadButton(
|
||||
name: map['name'] as String, radius: map['radius'] as int);
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(_GamepadButton model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
return {'name': model.name, 'radius': model.radius};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GamepadButtonFields {
|
||||
static const List<String> allFields = <String>[name, radius];
|
||||
|
||||
static const String name = 'name';
|
||||
|
||||
static const String radius = 'radius';
|
||||
}
|
||||
|
||||
const GamepadSerializer gamepadSerializer = GamepadSerializer();
|
||||
|
||||
class GamepadEncoder extends Converter<Gamepad, Map> {
|
||||
const GamepadEncoder();
|
||||
|
||||
@override
|
||||
Map convert(Gamepad model) => GamepadSerializer.toMap(model);
|
||||
}
|
||||
|
||||
class GamepadDecoder extends Converter<Map, Gamepad> {
|
||||
const GamepadDecoder();
|
||||
|
||||
@override
|
||||
Gamepad convert(Map map) => GamepadSerializer.fromMap(map);
|
||||
}
|
||||
|
||||
class GamepadSerializer extends Codec<Gamepad, Map> {
|
||||
const GamepadSerializer();
|
||||
|
||||
@override
|
||||
get encoder => const GamepadEncoder();
|
||||
@override
|
||||
get decoder => const GamepadDecoder();
|
||||
static Gamepad fromMap(Map map) {
|
||||
return Gamepad(
|
||||
buttons: map['buttons'] is Iterable
|
||||
? List.unmodifiable(((map['buttons'] as Iterable).whereType<Map>())
|
||||
.map(GamepadButtonSerializer.fromMap))
|
||||
: null,
|
||||
dynamicMap: map['dynamic_map'] is Map
|
||||
? (map['dynamic_map'] as Map).cast<String, dynamic>()
|
||||
: null);
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(_Gamepad model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
'buttons':
|
||||
model.buttons?.map((m) => GamepadButtonSerializer.toMap(m))?.toList(),
|
||||
'dynamic_map': model.dynamicMap
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GamepadFields {
|
||||
static const List<String> allFields = <String>[buttons, dynamicMap];
|
||||
|
||||
static const String buttons = 'buttons';
|
||||
|
||||
static const String dynamicMap = 'dynamic_map';
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
part 'goat.g.dart';
|
||||
|
||||
@serializable
|
||||
abstract class _Goat {
|
||||
@SerializableField(defaultValue: 34)
|
||||
int get integer;
|
||||
|
||||
@SerializableField(defaultValue: [34, 35])
|
||||
List<int> get list;
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'goat.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonModelGenerator
|
||||
// **************************************************************************
|
||||
|
||||
@generatedSerializable
|
||||
class Goat implements _Goat {
|
||||
const Goat({this.integer = 34, this.list = const [34, 35]});
|
||||
|
||||
@override
|
||||
final int integer;
|
||||
|
||||
@override
|
||||
final List<int> list;
|
||||
|
||||
Goat copyWith({int integer, List<int> list}) {
|
||||
return Goat(integer: integer ?? this.integer, list: list ?? this.list);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _Goat &&
|
||||
other.integer == integer &&
|
||||
ListEquality<int>(DefaultEquality<int>()).equals(other.list, list);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([integer, list]);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "Goat(integer=$integer, list=$list)";
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return GoatSerializer.toMap(this);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// SerializerGenerator
|
||||
// **************************************************************************
|
||||
|
||||
const GoatSerializer goatSerializer = GoatSerializer();
|
||||
|
||||
class GoatEncoder extends Converter<Goat, Map> {
|
||||
const GoatEncoder();
|
||||
|
||||
@override
|
||||
Map convert(Goat model) => GoatSerializer.toMap(model);
|
||||
}
|
||||
|
||||
class GoatDecoder extends Converter<Map, Goat> {
|
||||
const GoatDecoder();
|
||||
|
||||
@override
|
||||
Goat convert(Map map) => GoatSerializer.fromMap(map);
|
||||
}
|
||||
|
||||
class GoatSerializer extends Codec<Goat, Map> {
|
||||
const GoatSerializer();
|
||||
|
||||
@override
|
||||
get encoder => const GoatEncoder();
|
||||
@override
|
||||
get decoder => const GoatDecoder();
|
||||
static Goat fromMap(Map map) {
|
||||
return Goat(
|
||||
integer: map['integer'] as int ?? 34,
|
||||
list: map['list'] is Iterable
|
||||
? (map['list'] as Iterable).cast<int>().toList()
|
||||
: const [34, 35]);
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(_Goat model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
return {'integer': model.integer, 'list': model.list};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GoatFields {
|
||||
static const List<String> allFields = <String>[integer, list];
|
||||
|
||||
static const String integer = 'integer';
|
||||
|
||||
static const String list = 'list';
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import 'dart:convert';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
part 'has_map.g.dart';
|
||||
|
||||
Map _fromString(v) => json.decode(v.toString()) as Map;
|
||||
|
||||
String _toString(Map v) => json.encode(v);
|
||||
|
||||
@serializable
|
||||
abstract class _HasMap {
|
||||
@SerializableField(
|
||||
serializer: #_toString,
|
||||
deserializer: #_fromString,
|
||||
isNullable: false,
|
||||
serializesTo: String)
|
||||
Map get value;
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'has_map.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonModelGenerator
|
||||
// **************************************************************************
|
||||
|
||||
@generatedSerializable
|
||||
class HasMap implements _HasMap {
|
||||
const HasMap({@required this.value});
|
||||
|
||||
@override
|
||||
final Map<dynamic, dynamic> value;
|
||||
|
||||
HasMap copyWith({Map<dynamic, dynamic> value}) {
|
||||
return HasMap(value: value ?? this.value);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _HasMap &&
|
||||
MapEquality<dynamic, dynamic>(
|
||||
keys: DefaultEquality(), values: DefaultEquality())
|
||||
.equals(other.value, value);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([value]);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "HasMap(value=$value)";
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return HasMapSerializer.toMap(this);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// SerializerGenerator
|
||||
// **************************************************************************
|
||||
|
||||
const HasMapSerializer hasMapSerializer = HasMapSerializer();
|
||||
|
||||
class HasMapEncoder extends Converter<HasMap, Map> {
|
||||
const HasMapEncoder();
|
||||
|
||||
@override
|
||||
Map convert(HasMap model) => HasMapSerializer.toMap(model);
|
||||
}
|
||||
|
||||
class HasMapDecoder extends Converter<Map, HasMap> {
|
||||
const HasMapDecoder();
|
||||
|
||||
@override
|
||||
HasMap convert(Map map) => HasMapSerializer.fromMap(map);
|
||||
}
|
||||
|
||||
class HasMapSerializer extends Codec<HasMap, Map> {
|
||||
const HasMapSerializer();
|
||||
|
||||
@override
|
||||
get encoder => const HasMapEncoder();
|
||||
@override
|
||||
get decoder => const HasMapDecoder();
|
||||
static HasMap fromMap(Map map) {
|
||||
if (map['value'] == null) {
|
||||
throw FormatException("Missing required field 'value' on HasMap.");
|
||||
}
|
||||
|
||||
return HasMap(value: _fromString(map['value']));
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(_HasMap model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
if (model.value == null) {
|
||||
throw FormatException("Missing required field 'value' on HasMap.");
|
||||
}
|
||||
|
||||
return {'value': _toString(model.value)};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class HasMapFields {
|
||||
static const List<String> allFields = <String>[value];
|
||||
|
||||
static const String value = 'value';
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
part 'subclass.g.dart';
|
||||
|
||||
@serializable
|
||||
class _Animal {
|
||||
@notNull
|
||||
String genus;
|
||||
@notNull
|
||||
String species;
|
||||
}
|
||||
|
||||
@serializable
|
||||
class _Bird extends _Animal {
|
||||
@DefaultsTo(false)
|
||||
bool isSparrow;
|
||||
}
|
||||
|
||||
var saxaulSparrow = Bird(
|
||||
genus: 'Passer',
|
||||
species: 'ammodendri',
|
||||
isSparrow: true,
|
||||
);
|
|
@ -0,0 +1,214 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'subclass.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonModelGenerator
|
||||
// **************************************************************************
|
||||
|
||||
@generatedSerializable
|
||||
class Animal extends _Animal {
|
||||
Animal({@required this.genus, @required this.species});
|
||||
|
||||
@override
|
||||
String genus;
|
||||
|
||||
@override
|
||||
String species;
|
||||
|
||||
Animal copyWith({String genus, String species}) {
|
||||
return Animal(genus: genus ?? this.genus, species: species ?? this.species);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _Animal && other.genus == genus && other.species == species;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([genus, species]);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "Animal(genus=$genus, species=$species)";
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return AnimalSerializer.toMap(this);
|
||||
}
|
||||
}
|
||||
|
||||
@generatedSerializable
|
||||
class Bird extends _Bird {
|
||||
Bird({@required this.genus, @required this.species, this.isSparrow = false});
|
||||
|
||||
@override
|
||||
String genus;
|
||||
|
||||
@override
|
||||
String species;
|
||||
|
||||
@override
|
||||
bool isSparrow;
|
||||
|
||||
Bird copyWith({String genus, String species, bool isSparrow}) {
|
||||
return Bird(
|
||||
genus: genus ?? this.genus,
|
||||
species: species ?? this.species,
|
||||
isSparrow: isSparrow ?? this.isSparrow);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _Bird &&
|
||||
other.genus == genus &&
|
||||
other.species == species &&
|
||||
other.isSparrow == isSparrow;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([genus, species, isSparrow]);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "Bird(genus=$genus, species=$species, isSparrow=$isSparrow)";
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return BirdSerializer.toMap(this);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// SerializerGenerator
|
||||
// **************************************************************************
|
||||
|
||||
const AnimalSerializer animalSerializer = AnimalSerializer();
|
||||
|
||||
class AnimalEncoder extends Converter<Animal, Map> {
|
||||
const AnimalEncoder();
|
||||
|
||||
@override
|
||||
Map convert(Animal model) => AnimalSerializer.toMap(model);
|
||||
}
|
||||
|
||||
class AnimalDecoder extends Converter<Map, Animal> {
|
||||
const AnimalDecoder();
|
||||
|
||||
@override
|
||||
Animal convert(Map map) => AnimalSerializer.fromMap(map);
|
||||
}
|
||||
|
||||
class AnimalSerializer extends Codec<Animal, Map> {
|
||||
const AnimalSerializer();
|
||||
|
||||
@override
|
||||
get encoder => const AnimalEncoder();
|
||||
@override
|
||||
get decoder => const AnimalDecoder();
|
||||
static Animal fromMap(Map map) {
|
||||
if (map['genus'] == null) {
|
||||
throw FormatException("Missing required field 'genus' on Animal.");
|
||||
}
|
||||
|
||||
if (map['species'] == null) {
|
||||
throw FormatException("Missing required field 'species' on Animal.");
|
||||
}
|
||||
|
||||
return Animal(
|
||||
genus: map['genus'] as String, species: map['species'] as String);
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(_Animal model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
if (model.genus == null) {
|
||||
throw FormatException("Missing required field 'genus' on Animal.");
|
||||
}
|
||||
|
||||
if (model.species == null) {
|
||||
throw FormatException("Missing required field 'species' on Animal.");
|
||||
}
|
||||
|
||||
return {'genus': model.genus, 'species': model.species};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AnimalFields {
|
||||
static const List<String> allFields = <String>[genus, species];
|
||||
|
||||
static const String genus = 'genus';
|
||||
|
||||
static const String species = 'species';
|
||||
}
|
||||
|
||||
const BirdSerializer birdSerializer = BirdSerializer();
|
||||
|
||||
class BirdEncoder extends Converter<Bird, Map> {
|
||||
const BirdEncoder();
|
||||
|
||||
@override
|
||||
Map convert(Bird model) => BirdSerializer.toMap(model);
|
||||
}
|
||||
|
||||
class BirdDecoder extends Converter<Map, Bird> {
|
||||
const BirdDecoder();
|
||||
|
||||
@override
|
||||
Bird convert(Map map) => BirdSerializer.fromMap(map);
|
||||
}
|
||||
|
||||
class BirdSerializer extends Codec<Bird, Map> {
|
||||
const BirdSerializer();
|
||||
|
||||
@override
|
||||
get encoder => const BirdEncoder();
|
||||
@override
|
||||
get decoder => const BirdDecoder();
|
||||
static Bird fromMap(Map map) {
|
||||
if (map['genus'] == null) {
|
||||
throw FormatException("Missing required field 'genus' on Bird.");
|
||||
}
|
||||
|
||||
if (map['species'] == null) {
|
||||
throw FormatException("Missing required field 'species' on Bird.");
|
||||
}
|
||||
|
||||
return Bird(
|
||||
genus: map['genus'] as String,
|
||||
species: map['species'] as String,
|
||||
isSparrow: map['is_sparrow'] as bool ?? false);
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(_Bird model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
if (model.genus == null) {
|
||||
throw FormatException("Missing required field 'genus' on Bird.");
|
||||
}
|
||||
|
||||
if (model.species == null) {
|
||||
throw FormatException("Missing required field 'species' on Bird.");
|
||||
}
|
||||
|
||||
return {
|
||||
'genus': model.genus,
|
||||
'species': model.species,
|
||||
'is_sparrow': model.isSparrow
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BirdFields {
|
||||
static const List<String> allFields = <String>[genus, species, isSparrow];
|
||||
|
||||
static const String genus = 'genus';
|
||||
|
||||
static const String species = 'species';
|
||||
|
||||
static const String isSparrow = 'is_sparrow';
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
part 'with_enum.g.dart';
|
||||
|
||||
@serializable
|
||||
abstract class _WithEnum {
|
||||
@DefaultsTo(WithEnumType.b)
|
||||
WithEnumType get type;
|
||||
|
||||
List<int> get finalList;
|
||||
|
||||
Uint8List get imageBytes;
|
||||
}
|
||||
|
||||
enum WithEnumType { a, b, c }
|
|
@ -0,0 +1,123 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'with_enum.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonModelGenerator
|
||||
// **************************************************************************
|
||||
|
||||
@generatedSerializable
|
||||
class WithEnum implements _WithEnum {
|
||||
const WithEnum({this.type = WithEnumType.b, this.finalList, this.imageBytes});
|
||||
|
||||
@override
|
||||
final WithEnumType type;
|
||||
|
||||
@override
|
||||
final List<int> finalList;
|
||||
|
||||
@override
|
||||
final Uint8List imageBytes;
|
||||
|
||||
WithEnum copyWith(
|
||||
{WithEnumType type, List<int> finalList, Uint8List imageBytes}) {
|
||||
return WithEnum(
|
||||
type: type ?? this.type,
|
||||
finalList: finalList ?? this.finalList,
|
||||
imageBytes: imageBytes ?? this.imageBytes);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _WithEnum &&
|
||||
other.type == type &&
|
||||
ListEquality<int>(DefaultEquality<int>())
|
||||
.equals(other.finalList, finalList) &&
|
||||
ListEquality().equals(other.imageBytes, imageBytes);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([type, finalList, imageBytes]);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "WithEnum(type=$type, finalList=$finalList, imageBytes=$imageBytes)";
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return WithEnumSerializer.toMap(this);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// SerializerGenerator
|
||||
// **************************************************************************
|
||||
|
||||
const WithEnumSerializer withEnumSerializer = WithEnumSerializer();
|
||||
|
||||
class WithEnumEncoder extends Converter<WithEnum, Map> {
|
||||
const WithEnumEncoder();
|
||||
|
||||
@override
|
||||
Map convert(WithEnum model) => WithEnumSerializer.toMap(model);
|
||||
}
|
||||
|
||||
class WithEnumDecoder extends Converter<Map, WithEnum> {
|
||||
const WithEnumDecoder();
|
||||
|
||||
@override
|
||||
WithEnum convert(Map map) => WithEnumSerializer.fromMap(map);
|
||||
}
|
||||
|
||||
class WithEnumSerializer extends Codec<WithEnum, Map> {
|
||||
const WithEnumSerializer();
|
||||
|
||||
@override
|
||||
get encoder => const WithEnumEncoder();
|
||||
@override
|
||||
get decoder => const WithEnumDecoder();
|
||||
static WithEnum fromMap(Map map) {
|
||||
return WithEnum(
|
||||
type: map['type'] is WithEnumType
|
||||
? (map['type'] as WithEnumType)
|
||||
: (map['type'] is int
|
||||
? WithEnumType.values[map['type'] as int]
|
||||
: WithEnumType.b),
|
||||
finalList: map['final_list'] is Iterable
|
||||
? (map['final_list'] as Iterable).cast<int>().toList()
|
||||
: null,
|
||||
imageBytes: map['image_bytes'] is Uint8List
|
||||
? (map['image_bytes'] as Uint8List)
|
||||
: (map['image_bytes'] is Iterable<int>
|
||||
? Uint8List.fromList(
|
||||
(map['image_bytes'] as Iterable<int>).toList())
|
||||
: (map['image_bytes'] is String
|
||||
? Uint8List.fromList(
|
||||
base64.decode(map['image_bytes'] as String))
|
||||
: null)));
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(_WithEnum model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
'type':
|
||||
model.type == null ? null : WithEnumType.values.indexOf(model.type),
|
||||
'final_list': model.finalList,
|
||||
'image_bytes':
|
||||
model.imageBytes == null ? null : base64.encode(model.imageBytes)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class WithEnumFields {
|
||||
static const List<String> allFields = <String>[type, finalList, imageBytes];
|
||||
|
||||
static const String type = 'type';
|
||||
|
||||
static const String finalList = 'final_list';
|
||||
|
||||
static const String imageBytes = 'image_bytes';
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import 'dart:convert';
|
||||
import 'package:test/test.dart';
|
||||
import 'models/has_map.dart';
|
||||
|
||||
void main() {
|
||||
var m = HasMap(value: {'foo': 'bar'});
|
||||
print(json.encode(m));
|
||||
|
||||
test('json', () {
|
||||
expect(json.encode(m), r'{"value":"{\"foo\":\"bar\"}"}');
|
||||
});
|
||||
|
||||
test('decode', () {
|
||||
var mm = json.decode(r'{"value":"{\"foo\":\"bar\"}"}') as Map;
|
||||
var mmm = HasMapSerializer.fromMap(mm);
|
||||
expect(mmm, m);
|
||||
});
|
||||
}
|
4
packages/serialize/tool/.travis.sh
Normal file
4
packages/serialize/tool/.travis.sh
Normal file
|
@ -0,0 +1,4 @@
|
|||
cd angel_serialize_generator
|
||||
pub get
|
||||
pub run build_runner build --delete-conflicting-outputs
|
||||
pub run test
|
Loading…
Reference in a new issue