using System.Collections.Frozen; namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Management; /// /// Bidirectional name registry for management command records. The registry contains /// exactly the non-abstract *Command types declared in the /// ZB.MOM.WW.ScadaBridge.Commons.Messages.Management namespace; these are the commands that /// travel over the HTTP / ClusterClient management boundary. /// /// /// and are symmetric: /// Resolve(GetCommandName(t)) returns t for every type /// accepts. rejects any type /// the registry does not contain rather than computing an unresolvable name. /// public static class ManagementCommandRegistry { private static readonly FrozenDictionary Commands = BuildRegistry(); /// /// Names keyed by command type, for the reverse lookup. Keeps /// in lock-step with the forward registry. /// private static readonly FrozenDictionary NamesByType = Commands.ToFrozenDictionary(kv => kv.Value, kv => kv.Key); /// /// Resolves a management command wire name to its CLR type, or null if not registered. /// /// The wire name of the management command (without the "Command" suffix). public static Type? Resolve(string commandName) { return Commands.GetValueOrDefault(commandName); } /// /// Returns the registered wire name for a management command type. /// /// The CLR type of the management command to look up. /// /// Thrown when is not a registered management /// command — i.e. not a non-abstract *Command type in the /// ZB.MOM.WW.ScadaBridge.Commons.Messages.Management namespace. This keeps the method /// symmetric with : it never yields a name that /// cannot turn back into the same type. /// public static string GetCommandName(Type commandType) { ArgumentNullException.ThrowIfNull(commandType); if (NamesByType.TryGetValue(commandType, out var name)) return name; throw new ArgumentException( $"'{commandType.FullName}' is not a registered management command. " + $"Management commands must be non-abstract '*Command' records declared in " + $"the '{typeof(ManagementEnvelope).Namespace}' namespace.", nameof(commandType)); } private static FrozenDictionary BuildRegistry() { var ns = typeof(ManagementEnvelope).Namespace!; var assembly = typeof(ManagementEnvelope).Assembly; return assembly.GetTypes() .Where(t => t.Namespace == ns && t.Name.EndsWith("Command", StringComparison.Ordinal) && !t.IsAbstract) .ToFrozenDictionary( t => t.Name[..^"Command".Length], t => t, StringComparer.OrdinalIgnoreCase); } }