Skip to content

Commit

Permalink
Add generation of enums, use an enum for ProtocolEncoding. (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmorgan authored Sep 10, 2024
1 parent b93426a commit 0c22dbf
Show file tree
Hide file tree
Showing 14 changed files with 107 additions and 54 deletions.
2 changes: 1 addition & 1 deletion pkgs/_analyzer_macros/test/analyzer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ void main() {
AnalysisContextCollection(includedPaths: [directory.path]);
analysisContext = contextCollection.contexts.first;
injected.macroImplementation = await AnalyzerMacroImplementation.start(
protocol: Protocol(encoding: 'binary'),
protocol: Protocol(encoding: ProtocolEncoding.binary),
packageConfig: Isolate.packageConfigSync!);
});

Expand Down
2 changes: 1 addition & 1 deletion pkgs/_analyzer_macros/test/golden_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ void main() {
AnalysisContextCollection(includedPaths: [directory.path]);
analysisContext = contextCollection.contexts.first;
injected.macroImplementation = await AnalyzerMacroImplementation.start(
protocol: Protocol(encoding: 'binary'),
protocol: Protocol(encoding: ProtocolEncoding.binary),
packageConfig: Isolate.packageConfigSync!);
});

Expand Down
2 changes: 1 addition & 1 deletion pkgs/_cfe_macros/test/cfe_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void main() {

// Inject test macro implementation.
injected.macroImplementation = await CfeMacroImplementation.start(
protocol: Protocol(encoding: 'json'),
protocol: Protocol(encoding: ProtocolEncoding.json),
packageConfig: Isolate.packageConfigSync!);
});

Expand Down
2 changes: 1 addition & 1 deletion pkgs/_cfe_macros/test/golden_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void main() {

// Inject test macro implementation.
injected.macroImplementation = await CfeMacroImplementation.start(
protocol: Protocol(encoding: 'json'),
protocol: Protocol(encoding: ProtocolEncoding.json),
packageConfig: Isolate.packageConfigSync!);
});

Expand Down
4 changes: 2 additions & 2 deletions pkgs/_macro_client/test/macro_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import 'package:test/test.dart';

void main() {
for (final protocol in [
Protocol(encoding: 'json'),
Protocol(encoding: 'binary')
Protocol(encoding: ProtocolEncoding.json),
Protocol(encoding: ProtocolEncoding.binary)
]) {
group('MacroClient using ${protocol.encoding}', () {
test('connects to service', () async {
Expand Down
4 changes: 2 additions & 2 deletions pkgs/_macro_host/test/macro_host_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import 'package:test/test.dart';

void main() {
for (final protocol in [
Protocol(encoding: 'json'),
Protocol(encoding: 'binary')
Protocol(encoding: ProtocolEncoding.json),
Protocol(encoding: ProtocolEncoding.binary)
]) {
group('MacroHost using ${protocol.encoding}', () {
test('hosts a macro, receives augmentations', () async {
Expand Down
4 changes: 2 additions & 2 deletions pkgs/_macro_runner/test/macro_runner_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import 'package:test/test.dart';

void main() {
for (final protocol in [
Protocol(encoding: 'json'),
Protocol(encoding: 'binary')
Protocol(encoding: ProtocolEncoding.json),
Protocol(encoding: ProtocolEncoding.binary)
]) {
group('MacroRunner with ${protocol.encoding}', () {
test('runs macros', () async {
Expand Down
4 changes: 2 additions & 2 deletions pkgs/_macro_server/test/macro_server_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import 'package:test/test.dart';

void main() {
for (final protocol in [
Protocol(encoding: 'json'),
Protocol(encoding: 'binary')
Protocol(encoding: ProtocolEncoding.json),
Protocol(encoding: ProtocolEncoding.binary)
]) {
group('MacroServer using ${protocol.encoding}', () {
test('serves a macro service', () async {
Expand Down
63 changes: 31 additions & 32 deletions pkgs/macro_service/lib/src/macro_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,29 +46,27 @@ int _nextRequestId = 0;
final _jsonConverter = json.fuse(utf8);

extension ProtocolExtension on Protocol {
bool get isJson => encoding == 'json';
bool get isBinary => encoding == 'binary';

/// Serializes [node] and sends it to [sink].
void send(void Function(Uint8List) sink, Map<String, Object?> node) {
if (isJson) {
sink(_jsonConverter.encode(node) as Uint8List);
sink(_utf8Newline);
} else if (isBinary) {
// Four byte message length followed by message.
// TODO(davidmorgan): variable length int encoding probably makes more
// sense than fixed four bytes.
final binary = node.serializeToBinary();
final length = binary.length;
sink(Uint8List.fromList([
(length >> 24) & 255,
(length >> 16) & 255,
(length >> 8) & 255,
length & 255,
]));
sink(binary);
} else {
throw StateError('Unsupported protocol: $this.');
switch (encoding) {
case ProtocolEncoding.json:
sink(_jsonConverter.encode(node) as Uint8List);
sink(_utf8Newline);
case ProtocolEncoding.binary:
// Four byte message length followed by message.
// TODO(davidmorgan): variable length int encoding probably makes more
// sense than fixed four bytes.
final binary = node.serializeToBinary();
final length = binary.length;
sink(Uint8List.fromList([
(length >> 24) & 255,
(length >> 16) & 255,
(length >> 8) & 255,
length & 255,
]));
sink(binary);
default:
throw StateError('Unsupported protocol: $this.');
}
}

Expand All @@ -77,17 +75,18 @@ extension ProtocolExtension on Protocol {
/// The data on `stream` can be arbitrarily split to `Uint8List` instances,
/// it does not have to be one list per message.
Stream<Map<String, Object?>> decode(Stream<Uint8List> stream) {
if (isJson) {
return const Utf8Decoder()
.bind(stream)
.transform(const LineSplitter())
.map((line) => json.decode(line) as Map<String, Object?>);
} else if (isBinary) {
return MessageGrouper(stream)
.messageStream
.map((message) => message.deserializeFromBinary());
} else {
throw StateError('Unsupported protocol: $this');
switch (encoding) {
case ProtocolEncoding.json:
return const Utf8Decoder()
.bind(stream)
.transform(const LineSplitter())
.map((line) => json.decode(line) as Map<String, Object?>);
case ProtocolEncoding.binary:
return MessageGrouper(stream)
.messageStream
.map((message) => message.deserializeFromBinary());
default:
throw StateError('Unsupported protocol: $this');
}
}
}
Expand Down
13 changes: 10 additions & 3 deletions pkgs/macro_service/lib/src/macro_service.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,20 @@ extension type MacroRequest.fromJson(Map<String, Object?> node)
/// The macro to host protocol version and encoding. TODO(davidmorgan): add the version.
extension type Protocol.fromJson(Map<String, Object?> node) implements Object {
Protocol({
String? encoding,
ProtocolEncoding? encoding,
}) : this.fromJson({
if (encoding != null) 'encoding': encoding,
});

/// The wire format: json or binary. TODO(davidmorgan): use an enum?
String get encoding => node['encoding'] as String;
/// The wire format: json or binary.
ProtocolEncoding get encoding => node['encoding'] as ProtocolEncoding;
}

/// The wire encoding used.
extension type const ProtocolEncoding.fromJson(String string)
implements Object {
static const ProtocolEncoding json = ProtocolEncoding.fromJson('json');
static const ProtocolEncoding binary = ProtocolEncoding.fromJson('binary');
}

/// Macro's query about the code it should augment.
Expand Down
4 changes: 2 additions & 2 deletions pkgs/macro_service/test/protocol_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import 'package:test/test.dart';

void main() {
for (final protocol in [
Protocol(encoding: 'json'),
Protocol(encoding: 'binary')
Protocol(encoding: ProtocolEncoding.json),
Protocol(encoding: ProtocolEncoding.binary)
]) {
group('Protocol using ${protocol.encoding}', () {
test('can round trip JSON data', () async {
Expand Down
8 changes: 6 additions & 2 deletions schemas/macro_service.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,15 @@
"description": "The macro to host protocol version and encoding. TODO(davidmorgan): add the version.",
"properties": {
"encoding": {
"type": "string",
"description": "The wire format: json or binary. TODO(davidmorgan): use an enum?"
"$comment": "The wire format: json or binary.",
"$ref": "#/$defs/ProtocolEncoding"
}
}
},
"ProtocolEncoding": {
"type": "string",
"description": "The wire encoding used."
},
"QueryRequest": {
"type": "object",
"description": "Macro's query about the code it should augment.",
Expand Down
7 changes: 4 additions & 3 deletions tool/dart_model_generator/lib/definitions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -332,10 +332,11 @@ final schemas = Schemas([
'TODO(davidmorgan): add the version.',
properties: [
Property('encoding',
type: 'String',
description: 'The wire format: json or binary. '
'TODO(davidmorgan): use an enum?'),
type: 'ProtocolEncoding',
description: 'The wire format: json or binary.'),
]),
Definition.$enum('ProtocolEncoding',
description: 'The wire encoding used.', values: ['json', 'binary']),
Definition.clazz('QueryRequest',
description: "Macro's query about the code it should augment.",
properties: [
Expand Down
42 changes: 42 additions & 0 deletions tool/dart_model_generator/lib/generate_dart_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ abstract class Definition {
required List<Property> properties,
bool createInBuffer}) = UnionTypeDefinition;

/// Defines an enum.
factory Definition.$enum(String name,
{required String description,
required List<String> values}) = EnumTypeDefinition;

/// Defines a named type represented in JSON as a string.
factory Definition.stringTypedef(String name, {required String description}) =
StringTypedefDefinition;
Expand Down Expand Up @@ -651,6 +656,43 @@ class UnionTypeDefinition implements Definition {
String get representationTypeName => 'Map<String, Object?>';
}

/// Definition of an enum type.
class EnumTypeDefinition implements Definition {
@override
final String name;
final String description;
final List<String> values;

EnumTypeDefinition(this.name,
{required this.description, required this.values});

@override
Map<String, Object?> generateSchema(GenerationContext context) => {
'type': 'string',
'description': description,
};

@override
String generateCode(GenerationContext context) {
final result = StringBuffer();

result.writeln('/// $description');
result.write('extension type const $name.fromJson(String string)'
' implements Object {');
for (final value in values) {
result.writeln("static const $name $value = $name.fromJson('$value');");
}
result.writeln('}');
return result.toString();
}

@override
Set<String> get allTypeNames => {};

@override
String get representationTypeName => 'String';
}

/// Definition of a named type that is actually a String.
class StringTypedefDefinition implements Definition {
@override
Expand Down

0 comments on commit 0c22dbf

Please sign in to comment.