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; }