replayScenarioStream function

Stream<Msg> replayScenarioStream(
  1. List<ReplayAction> actions, {
  2. required bool loop,
  3. required bool keepOpen,
  4. required double speed,
  5. ReplayEventHook? eventHook,
})

Builds a replay message stream from a list of replay actions.

Implementation

Stream<Msg> replayScenarioStream(
  List<ReplayAction> actions, {
  required bool loop,
  required bool keepOpen,
  required double speed,
  ReplayEventHook? eventHook,
}) async* {
  if (actions.isEmpty) {
    if (!keepOpen && !loop) yield const QuitMsg();
    return;
  }

  Duration pendingDelay = Duration.zero;

  Duration scaled(Duration input) {
    final micros = (input.inMicroseconds / speed).round();
    return Duration(microseconds: math.max(0, micros));
  }

  do {
    for (final action in actions) {
      if (action.type == 'sleep') {
        pendingDelay += scaled(Duration(milliseconds: action.ms));
        continue;
      }

      if (action.type == 'event') {
        if (pendingDelay > Duration.zero) {
          await Future<void>.delayed(pendingDelay);
          pendingDelay = Duration.zero;
        }

        final event = action.customEvent;
        if (event == null) {
          continue;
        }

        final directive =
            await eventHook?.call(event) ??
            ReplayEventDirective.emit(<Msg>[ReplayEventMsg(event)]);

        final hookDelay = scaled(directive.delay);
        if (hookDelay > Duration.zero) {
          await Future<void>.delayed(hookDelay);
        }
        for (final msg in directive.messages) {
          yield msg;
        }
        if (directive.control == ReplayEventControl.quit) {
          yield const QuitMsg();
          return;
        }
        if (directive.control == ReplayEventControl.stop) {
          return;
        }
        continue;
      }

      final events = action.toMessages();
      for (final msg in events) {
        if (pendingDelay > Duration.zero) {
          await Future<void>.delayed(pendingDelay);
          pendingDelay = Duration.zero;
        }
        yield msg;
      }
    }

    if (pendingDelay > Duration.zero) {
      await Future<void>.delayed(pendingDelay);
      pendingDelay = Duration.zero;
    }
  } while (loop);

  if (!keepOpen) {
    yield const QuitMsg();
  }
}