namespace StellaOps.PolicyDsl;
///
/// Provides signal values for policy evaluation.
///
public sealed class SignalContext
{
private readonly Dictionary _signals;
///
/// Creates an empty signal context.
///
public SignalContext()
{
_signals = new Dictionary(StringComparer.Ordinal);
}
///
/// Creates a signal context with initial values.
///
/// Initial signal values.
public SignalContext(IDictionary signals)
{
_signals = new Dictionary(signals, StringComparer.Ordinal);
}
///
/// Gets whether a signal exists.
///
/// The signal name.
/// True if the signal exists.
public bool HasSignal(string name) => _signals.ContainsKey(name);
///
/// Gets a signal value.
///
/// The signal name.
/// The signal value, or null if not found.
public object? GetSignal(string name) => _signals.TryGetValue(name, out var value) ? value : null;
///
/// Gets a signal value as a specific type.
///
/// The expected type.
/// The signal name.
/// The signal value, or default if not found or wrong type.
public T? GetSignal(string name) => _signals.TryGetValue(name, out var value) && value is T t ? t : default;
///
/// Sets a signal value.
///
/// The signal name.
/// The signal value.
/// This context for chaining.
public SignalContext SetSignal(string name, object? value)
{
_signals[name] = value;
return this;
}
///
/// Removes a signal.
///
/// The signal name.
/// This context for chaining.
public SignalContext RemoveSignal(string name)
{
_signals.Remove(name);
return this;
}
///
/// Gets all signal names.
///
public IEnumerable SignalNames => _signals.Keys;
///
/// Gets all signals as a read-only dictionary.
///
public IReadOnlyDictionary Signals => _signals;
///
/// Creates a copy of this context.
///
/// A new context with the same signals.
public SignalContext Clone() => new(_signals);
///
/// Creates a signal context builder for fluent construction.
///
/// A new builder.
public static SignalContextBuilder Builder() => new();
}
///
/// Builder for creating signal contexts with fluent API.
///
public sealed class SignalContextBuilder
{
private readonly Dictionary _signals = new(StringComparer.Ordinal);
///
/// Adds a signal to the context.
///
/// The signal name.
/// The signal value.
/// This builder for chaining.
public SignalContextBuilder WithSignal(string name, object? value)
{
_signals[name] = value;
return this;
}
///
/// Adds a boolean signal to the context.
///
/// The signal name.
/// The boolean value.
/// This builder for chaining.
public SignalContextBuilder WithFlag(string name, bool value = true)
{
_signals[name] = value;
return this;
}
///
/// Adds a numeric signal to the context.
///
/// The signal name.
/// The numeric value.
/// This builder for chaining.
public SignalContextBuilder WithNumber(string name, decimal value)
{
_signals[name] = value;
return this;
}
///
/// Adds a string signal to the context.
///
/// The signal name.
/// The string value.
/// This builder for chaining.
public SignalContextBuilder WithString(string name, string value)
{
_signals[name] = value;
return this;
}
///
/// Adds a nested object signal to the context.
///
/// The signal name.
/// The nested properties.
/// This builder for chaining.
public SignalContextBuilder WithObject(string name, IDictionary properties)
{
_signals[name] = new Dictionary(properties, StringComparer.Ordinal);
return this;
}
///
/// Adds common finding signals.
///
/// The finding severity (e.g., "critical", "high", "medium", "low").
/// The confidence score (0.0 to 1.0).
/// Optional CVE identifier.
/// This builder for chaining.
public SignalContextBuilder WithFinding(string severity, decimal confidence, string? cveId = null)
{
_signals["finding"] = new Dictionary(StringComparer.Ordinal)
{
["severity"] = severity,
["confidence"] = confidence,
["cve_id"] = cveId,
};
return this;
}
///
/// Adds common reachability signals.
///
/// The reachability state (e.g., "reachable", "unreachable", "unknown").
/// The confidence score (0.0 to 1.0).
/// Whether there is runtime evidence.
/// This builder for chaining.
public SignalContextBuilder WithReachability(string state, decimal confidence, bool hasRuntimeEvidence = false)
{
_signals["reachability"] = new Dictionary(StringComparer.Ordinal)
{
["state"] = state,
["confidence"] = confidence,
["has_runtime_evidence"] = hasRuntimeEvidence,
};
return this;
}
///
/// Adds common trust score signals.
///
/// The trust score (0.0 to 1.0).
/// Whether the source is verified.
/// This builder for chaining.
public SignalContextBuilder WithTrustScore(decimal score, bool verified = false)
{
_signals["trust_score"] = score;
_signals["trust_verified"] = verified;
return this;
}
///
/// Builds the signal context.
///
/// A new signal context with the configured signals.
public SignalContext Build() => new(_signals);
}