w4c-workflows-api/Services/Nodes/Executors/MergeNodeExecutor.cs
2026-09-12 01:02:46 +03:00

72 lines
2.3 KiB
C#

using System.Text.Json.Nodes;
using w4c_workflows.Models.Nodes;
namespace w4c_workflows.Services.Nodes.Executors;
/// <summary>
/// Merge node. Combines two input ports into one:
/// - <c>append</c> — input 0 items then input 1 items;
/// - <c>chooseBranch</c> — the items of the first non-empty input;
/// - <c>combineByPosition</c> — pair items by index and shallow-merge their
/// fields (input 1 overrides input 0); items past the shorter input pass
/// through unchanged.
/// Runs in all-items mode because it has more than one input port.
/// </summary>
public sealed class MergeNodeExecutor : INodeExecutor
{
public string Type => "core.merge";
public Task<NodeExecutionOutcome> RunAsync(NodeExecutionContext context, CancellationToken ct)
{
var first = context.Input(0);
var second = context.Input(1);
var mode = context.Parameters["mode"] is JsonValue value && value.TryGetValue<string>(out var text)
? text
: "append";
var items = mode switch
{
"chooseBranch" => first.Count > 0 ? first.ToList() : second.ToList(),
"combineByPosition" => CombineByPosition(first, second),
_ => first.Concat(second).ToList(),
};
return Task.FromResult(NodeExecutionOutcome.Single(items));
}
private static List<FlowItem> CombineByPosition(
IReadOnlyList<FlowItem> first, IReadOnlyList<FlowItem> second)
{
var merged = new List<FlowItem>(Math.Max(first.Count, second.Count));
for (var i = 0; i < first.Count || i < second.Count; i++)
{
var left = i < first.Count ? first[i] : null;
var right = i < second.Count ? second[i] : null;
if (left == null)
{
merged.Add(right!);
continue;
}
if (right == null)
{
merged.Add(left);
continue;
}
var json = (JsonObject)left.Json.DeepClone();
foreach (var (name, node) in right.Json)
json[name] = node?.DeepClone();
merged.Add(new FlowItem
{
Json = json,
Binary = left.Binary ?? right.Binary,
Origin = left.Origin ?? right.Origin,
});
}
return merged;
}
}