dmxServeMacros function

Future<void> dmxServeMacros(
  1. List<DmxMacro> macros, {
  2. String name = 'macros',
  3. String version = DmxPackage.version,
})

Serves macros over the worker protocol until the driver closes stdin dartmacros.protocol. This is the whole of main for a macro worker.

The frames are drained by a listener rather than an await for loop: an expansion that awaits a render dartmacros.render is waiting on a frame that has not arrived yet, and a loop suspended inside its own body would never read it.

name and version are this worker's own identity in the handshake, not dmx's — the driver reads neither, and they exist so a worker can say what it is in a log or a --verbose trace. version defaults to the version of the dmx package the worker was built against, which is generated from pubspec.yaml rather than written down release.version.

Implementation

Future<void> dmxServeMacros(
  List<DmxMacro> macros, {
  String name = 'macros',
  String version = DmxPackage.version,
}) async {
  final byName = {for (final macro in macros) macro.name: macro};
  final driver = _Driver();
  final closed = Completer<void>();
  // Expansions answer in the order they were asked for, whatever each one
  // awaits along the way [dartmacros.pipeline].
  var queue = Future<void>.value();
  final frames = stdin.transform(utf8.decoder).transform(const LineSplitter());
  frames.listen(
    (frame) {
      final Object? message = jsonDecode(frame);
      if (message is! Map<String, Object?>) {
        return;
      }
      switch (message['op']) {
        case 'hello':
          _reply({
            'v': 1,
            'name': name,
            'version': version,
            'contextVersion': 1,
            'ops': ['expand'],
            'macros': byName.keys.toList(),
          });
        case 'expand':
          queue = queue.then(
            (_) async => _reply(await _expand(byName, message, driver)),
          );
        default:
          driver.settle(message);
      }
    },
    onDone: () {
      if (!closed.isCompleted) {
        closed.complete();
      }
    },
  );
  await closed.future;
  await queue;
}