using System.Text.Json.Nodes;
using w4c_workflows.Models.Nodes;
namespace w4c_workflows.Services.Nodes.Executors;
///
/// Switch node. Evaluates an ordered rule list per item and routes the item to
/// the first matching rule's output port (port 0..3). Unmatched items go to the
/// fallback port (4) unless fallbackEnabled is false. Rules beyond the
/// blueprint's four rule ports are ignored.
///
public sealed class SwitchNodeExecutor : INodeExecutor
{
/// Number of rule output ports on the blueprint (fallback is the next one).
public const int RulePorts = 4;
private const int FallbackPort = RulePorts;
public string Type => "core.switch";
public Task RunAsync(NodeExecutionContext context, CancellationToken ct)
{
var item = context.Input(0).FirstOrDefault();
if (item == null)
return Task.FromResult(NodeExecutionOutcome.Empty);
var ports = new List>(RulePorts + 1);
for (var i = 0; i <= RulePorts; i++)
ports.Add(Array.Empty());
var rules = context.Parameters["rules"] as JsonArray ?? new JsonArray();
for (var i = 0; i < rules.Count && i < RulePorts; i++)
{
if (rules[i] is not JsonObject rule)
continue;
var op = rule["operator"] is JsonValue value && value.TryGetValue(out var text)
? text
: NodeConditionEvaluator.DefaultOperator;
var caseSensitive = rule["caseSensitive"] is JsonValue flag
&& flag.TryGetValue(out var cs) && cs;
bool matched;
try
{
matched = NodeConditionEvaluator.Evaluate(rule["left"], op, rule["right"], caseSensitive);
}
catch (ArgumentException ex)
{
return Task.FromResult(NodeExecutionOutcome.Failed(ex.Message, "invalid_condition"));
}
if (!matched)
continue;
ports[i] = new List { item };
return Task.FromResult(new NodeExecutionOutcome { Outputs = ports });
}
if (ReadBool(context, "fallbackEnabled", defaultValue: true))
ports[FallbackPort] = new List { item };
return Task.FromResult(new NodeExecutionOutcome { Outputs = ports });
}
private static bool ReadBool(NodeExecutionContext context, string name, bool defaultValue)
=> context.Parameters[name] is JsonValue value && value.TryGetValue(out var flag) ? flag : defaultValue;
}