Skip to content

Commit

Permalink
Add more documentation about JsonEnum (#1209)
Browse files Browse the repository at this point in the history
Also added some features to the readme builder, which will be useful
later
  • Loading branch information
kevmoo committed Sep 27, 2022
1 parent c844625 commit 89d75b9
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 27 deletions.
4 changes: 4 additions & 0 deletions json_serializable/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 6.4.1-dev

- Added more documentation `@JsonEnum`.

## 6.4.0

- Add support for `JsonEnum.valueField` which allows specifying a field in an
Expand Down
29 changes: 25 additions & 4 deletions json_serializable/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,34 @@ Annotate `enum` types with [`JsonEnum`] (new in `json_annotation` 4.2.0) to:
Annotate `enum` values with [`JsonValue`] to specify the encoded value to map
to target `enum` entries. Values can be of type [`String`] or [`int`].

<!-- TODO: hoist out to source code! -->

```dart
enum StatusCode {
@JsonValue(200)
success,
@JsonValue('500')
weird,
@JsonValue(301)
movedPermanently,
@JsonValue(302)
found,
@JsonValue(500)
internalServerError,
}
```

If you are annotating an
[enhanced enum](https://dart.dev/guides/language/language-tour#declaring-enhanced-enums),
you can use [`JsonEnum.valueField`] to specify the field to use for the
serialized value.

```dart
@JsonEnum(valueField: 'code')
enum StatusCodeEnhanced {
success(200),
movedPermanently(301),
found(302),
internalServerError(500);
const StatusCodeEnhanced(this.code);
final int code;
}
```

Expand Down Expand Up @@ -201,6 +221,7 @@ targets:
[`int`]: https://api.dart.dev/stable/dart-core/int-class.html
[`Iterable`]: https://api.dart.dev/stable/dart-core/Iterable-class.html
[`JsonConverter`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonConverter-class.html
[`JsonEnum.valueField`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonEnum/valueField.html
[`JsonEnum`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonEnum-class.html
[`JsonKey.fromJson`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonKey/fromJson.html
[`JsonKey.toJson`]: https://pub.dev/documentation/json_annotation/4.7.0/json_annotation/JsonKey/toJson.html
Expand Down
2 changes: 1 addition & 1 deletion json_serializable/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: json_serializable
version: 6.4.0
version: 6.4.1-dev
description: >-
Automatically generate code for converting to and from JSON by annotating
Dart classes.
Expand Down
25 changes: 25 additions & 0 deletions json_serializable/tool/readme/enum_example.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:json_annotation/json_annotation.dart';

// # simple_example
enum StatusCode {
@JsonValue(200)
success,
@JsonValue(301)
movedPermanently,
@JsonValue(302)
found,
@JsonValue(500)
internalServerError,
}

// # enhanced_example
@JsonEnum(valueField: 'code')
enum StatusCodeEnhanced {
success(200),
movedPermanently(301),
found(302),
internalServerError(500);

const StatusCodeEnhanced(this.code);
final int code;
}
20 changes: 9 additions & 11 deletions json_serializable/tool/readme/readme_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ To configure your project for the latest released version of
Given a library `example.dart` with an `Person` class annotated with
`ja:JsonSerializable`:

<!-- REPLACE example.dart -->
<!-- REPLACE example/example.dart -->

Building creates the corresponding part `example.g.dart`:

<!-- REPLACE example.g.dart -->
<!-- REPLACE example/example.g.dart -->

# Running the code generator

Expand Down Expand Up @@ -75,16 +75,14 @@ Annotate `enum` types with `ja:JsonEnum` (new in `json_annotation` 4.2.0) to:
Annotate `enum` values with `ja:JsonValue` to specify the encoded value to map
to target `enum` entries. Values can be of type `core:String` or `core:int`.

<!-- TODO: hoist out to source code! -->
<!-- REPLACE tool/readme/enum_example.dart-simple_example -->

```dart
enum StatusCode {
@JsonValue(200)
success,
@JsonValue('500')
weird,
}
```
If you are annotating an
[enhanced enum](https://dart.dev/guides/language/language-tour#declaring-enhanced-enums),
you can use `ja:JsonEnum.valueField` to specify the field to use for the
serialized value.

<!-- REPLACE tool/readme/enum_example.dart-enhanced_example -->

# Supported types

Expand Down
92 changes: 81 additions & 11 deletions json_serializable/tool/readme_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import 'dart:convert';

import 'package:build/build.dart';
import 'package:collection/collection.dart';
import 'package:path/path.dart' as p;
import 'package:pub_semver/pub_semver.dart';
import 'package:yaml/yaml.dart';

Expand All @@ -23,8 +22,9 @@ class _ReadmeBuilder extends Builder {
buildStep.assetIdForInputPackage('tool/readme/readme_template.md');

final replacements = {
'example.dart': await buildStep.getExampleContent('example.dart'),
'example.g.dart': await buildStep.getExampleContent('example.g.dart'),
...await buildStep.getExampleContent('example/example.dart'),
...await buildStep.getExampleContent('example/example.g.dart'),
...await buildStep.getExampleContent('tool/readme/enum_example.dart'),
'supported_types': _classCleanAndSort(supportedTypes()),
'collection_types': _classCleanAndSort(collectionTypes()),
'map_key_types': _classCleanAndSort(mapKeyTypes),
Expand All @@ -44,7 +44,7 @@ class _ReadmeBuilder extends Builder {
final foundClasses = SplayTreeMap<String, String>(compareAsciiLowerCase);

final theMap = <Pattern, String Function(Match)>{
RegExp(r'<!-- REPLACE ([\w\d\.]+) -->'): (match) {
RegExp(r'<!-- REPLACE ([\/\w\d\._-]+) -->'): (match) {
final replacementKey = match.group(1)!;
availableKeys.remove(replacementKey);
return (replacements[replacementKey] ?? '*MISSING! `$replacementKey`*')
Expand Down Expand Up @@ -174,9 +174,9 @@ extension on BuildStep {
return targetVersion;
}

Future<String> getExampleContent(String fileName) async {
Future<Map<String, String>> getExampleContent(String fileName) async {
final content = await readAsString(
assetIdForInputPackage(p.join('example', fileName)),
assetIdForInputPackage(fileName),
);

final lines = LineSplitter.split(content);
Expand All @@ -185,7 +185,12 @@ extension on BuildStep {

// All lines with content, except those starting with `/`.
// Also exclude blank lines that follow other blank lines
final cleanedSource = lines.where((l) {
final cleanedSourceLines = lines.where((l) {
if (l.isBlockLine) {
// This is a block!
return true;
}

if (l.startsWith(r'/')) {
return false;
}
Expand All @@ -201,11 +206,76 @@ extension on BuildStep {
}

return false;
}).join('\n');
});

if (cleanedSourceLines.any((element) => element.isBlockLine)) {
final result = <String, String>{};

String? currentBlock;
final builder = <String>[];

void finishBlock() {
result[currentBlock!] = builder.join('\n').trim();
builder.clear();
currentBlock = null;
}

for (var line in cleanedSourceLines) {
if (currentBlock == null) {
if (!line.isBlockLine) {
// have not got to a block yet – skip!
} else {
// this is our first block!
currentBlock = line.blockName;
if (result.containsKey(currentBlock)) {
throw StateError('Duplicate block "$currentBlock"');
}
}
continue;
}

if (line.isBlockLine) {
if (builder.isEmpty) {
log.warning('`$currentBlock` is empty');
} else {
finishBlock();
currentBlock = line.blockName;
if (result.containsKey(currentBlock)) {
throw StateError('Duplicate block "$currentBlock"');
}
}
} else {
builder.add(line);
}
}

return '''
if (currentBlock != null) finishBlock();

return result
.map((key, value) => MapEntry('$fileName-$key', value.dartBlock));
} else {
return {
fileName: cleanedSourceLines.join('\n').dartBlock,
};
}
}
}

extension on String {
bool get isBlockLine => trim().startsWith(_blockComment);

String get blockName {
assert(isBlockLine);
final index = indexOf(_blockComment);
assert(index >= 0);
final start = index + _blockComment.length;
return substring(start).trim();
}

String get dartBlock => '''
```dart
$cleanedSource
${trim()}
```''';
}

static const _blockComment = r'// # ';
}

0 comments on commit 89d75b9

Please sign in to comment.