walkAsync<R> method

Future<R?> walkAsync<R>(
  1. Iterable<T> from, {
  2. required GraphWalkNodeProviderAsync<T> nodeProvider,
  3. required GraphWalkOutputsProviderAsync<T> outputsProvider,
  4. required GraphWalkNodeProcessorAsync<T, R> process,
  5. int? maxDepth,
})

A generic walk algorithm.

Implementation

Future<R?> walkAsync<R>(
  Iterable<T> from, {
  required GraphWalkNodeProviderAsync<T> nodeProvider,
  required GraphWalkOutputsProviderAsync<T> outputsProvider,
  required GraphWalkNodeProcessorAsync<T, R> process,
  int? maxDepth,
}) async {
  maxDepth ??= defaultMaxDepth;

  final processed = _processedNodes;
  final queue = ListQueue<GraphNodeStep<T>>();

  for (var rootValue in from) {
    var rootStepValue = GraphValueStep.root(this, rootValue);

    final rootNode = await nodeProvider(rootStepValue, rootValue) ??
        (throw StateError("Can't find root node: $rootValue"));

    var rootStep = GraphNodeStep.root(this, rootNode);
    queue.add(rootStep);
  }

  while (queue.isNotEmpty) {
    final step = queue.removeFirst();
    final node = step.node;
    final nodeValue = step.nodeValue;

    var processCount = processed.increment(node);

    if (processCount <= maxExpansion && step.depth <= maxDepth) {
      if (processRoots || step.isNotRoot) {
        var ret = await process(step);

        if (ret != null) {
          if (ret is GraphWalkingInstructionResult) {
            return ret.result as R?;
          } else if (ret is GraphWalkingInstructionOperation) {
            switch (ret.operation) {
              case GraphWalkingOperation.stop:
                return null;
              case GraphWalkingOperation.next:
                continue;
            }
          } else if (ret is GraphWalkingInstructionSetExpansionCounter) {
            processed[node] = ret.count;
          } else if (ret is GraphWalkingInstructionSetNodesExpansionCounter) {
            processed.addCounter(ret.counter);
          } else {
            return _resolveWalkProcessReturn<T, R>(node, ret);
          }
        }
      }

      if (stopMatcher.matchesNode(node)) {
        return _resolveWalkProcessReturn<T, R>(node, true);
      }

      var outputs = await outputsProvider(step, nodeValue);
      if (outputs != null) {
        var outputsNodes = outputs.map((v) {
          var nodeStepValue = GraphValueStep.subNode(step, v);
          return nodeProvider(nodeStepValue, v);
        });

        queue.addAllToScanQueue(
            await outputsNodes.toGraphNodeStepAsync(step), bfs);
      }
    }
  }

  return _resolveWalkProcessReturn<T, R>(null, false);
}