Files
dnsmasq-webui/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/Validation/IpsetSemanticHandler.cs
T
alex 36a160dfc7 Refactor: option semantics to metadata + handlers, shared syntax, registry/save single path
- OptionSemantics: metadata only (no validator delegates); OptionValidationKind + PathPolicy
- IOptionSemanticHandler: handler-first validation; handlers registered via IApplicationMultiSingleton
- OptionSemanticValidator: single engine, handler dispatch then generic kind-based validation
- Shared syntax: DnsmasqScopedDomainSyntax, DnsmasqDhcpRangeValueParser, DnsmasqDhcpOptionSyntax,
  DnsmasqRelaySyntax, DnsmasqDhcpTagSyntax, DnsmasqIpPrefixSyntax for reuse across handlers
- New handlers: Server, Local, RevServer, Address, Alias, Ipset, Nftset, DhcpRange, DhcpHost,
  DhcpOption, DhcpRelay, DhcpProxy, IgnoreAddress, ConnmarkAllowlist, DhcpMatch, DhcpMac,
  PxeService, TrustAnchor, RebindDomainOk, BogusNxdomain, DhcpIgnoreNames, DhcpBoot, Slaac,
  BridgeInterface, SharedNetwork, DhcpOptionPxe, DhcpNameMatch, DhcpIgnore, DhcpVendorclass, DhcpUserclass, TagIf
- Registry and pre-save validation use engine only; placeholders via DnsmasqOptionPlaceholders
- DnsmasqConfigSetService uses DnsmasqDhcpRangeValueParser; DhcpRangeValueParserTests added
2026-03-10 23:44:51 +10:00

38 lines
1.4 KiB
C#

using DnsmasqWebUI.Infrastructure.Helpers.Config;
namespace DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.Validation;
/// <summary>
/// Specialized semantic behavior for <c>ipset</c> values.
/// Validates scoped domain syntax followed by one or more ipset names.
/// </summary>
public sealed class IpsetSemanticHandler : IOptionSemanticHandler
{
public bool CanHandle(string optionName) =>
optionName == DnsmasqConfKeys.Ipset;
public string? ValidateSingle(object? value) => null;
public string? ValidateMultiItem(string? value)
{
var s = value?.Trim() ?? "";
if (s.Length == 0)
return "Value cannot be empty.";
if (!DnsmasqScopedDomainSyntax.TrySplitScopedValue(s, out var domains, out var setList, out var error))
return error == "Value must start with '/'."
? "ipset must start with '/'."
: "ipset must use /domain[/domain...]/set[,set...] syntax.";
error = DnsmasqScopedDomainSyntax.ValidateDomainPatterns(domains);
if (error is not null)
return error.Replace("Value", "ipset", StringComparison.Ordinal);
var sets = setList.Split(',').Select(t => t.Trim()).ToArray();
if (sets.Length == 0 || sets.Any(string.IsNullOrWhiteSpace))
return "ipset must include at least one non-empty set name.";
return null;
}
}