namespace Fleet.Rpc; /// 连接的哪一侧。节点面:服务端和节点;对等面:拨号方和监听方。 public enum RpcSide { Server, Node, Dialer, Listener, } public enum RpcPlane { Node, Peer, } public enum MethodKind { Request, Notification, } public static class RpcSides { public static RpcPlane PlaneOf(RpcSide side) => side is RpcSide.Server or RpcSide.Node ? RpcPlane.Node : RpcPlane.Peer; public static RpcSide Opposite(RpcSide side) => side switch { RpcSide.Server => RpcSide.Node, RpcSide.Node => RpcSide.Server, RpcSide.Dialer => RpcSide.Listener, _ => RpcSide.Dialer, }; } /// /// 一个方法的声明:只有 能发起它(不存在双向方法),类型是请求还是通知,哪些节点角色可以参与。 /// 表示它在认证或握手阶段由连接代码直接处理,不经过路由器。 /// public sealed record MethodSpec(string Name, RpcSide From, MethodKind Kind, IReadOnlyList? Roles = null, bool Inline = false) { public RpcPlane Plane => RpcSides.PlaneOf(From); /// 接收方。 public RpcSide To => RpcSides.Opposite(From); public bool AllowsRole(string? role) => Roles is null || (role is not null && Roles.Contains(role)); public static MethodSpec Request(string name, RpcSide from, params string[] roles) => new(name, from, MethodKind.Request, roles.Length == 0 ? null : roles); public static MethodSpec Notification(string name, RpcSide from, params string[] roles) => new(name, from, MethodKind.Notification, roles.Length == 0 ? null : roles); public MethodSpec AsInline() => this with { Inline = true }; } /// 二进制通道的声明:谁能发、哪些角色可以发。通道 0–15 保留给框架,产品从 16 开始。 public sealed record ChannelSpec(byte Channel, RpcSide From, IReadOnlyList? Roles = null) { public RpcPlane Plane => RpcSides.PlaneOf(From); public bool AllowsRole(string? role) => Roles is null || (role is not null && Roles.Contains(role)); } /// /// 协议方法和二进制通道的声明表,单一真源。路由器用它在注册时拒绝方向、类型、角色不符的处理函数, /// 在收发时拒绝没有声明或不该由对方发起的方法。同一个方法名在不同平面里可以有不同的发起方,所以按(平面,名字)查找。 /// public sealed class MethodTable { private readonly Dictionary<(RpcPlane, string), MethodSpec> _methods = []; private readonly Dictionary<(RpcPlane, byte), ChannelSpec> _channels = []; public MethodTable(IEnumerable methods, IEnumerable? channels = null) { foreach (MethodSpec spec in methods) { if (!_methods.TryAdd((spec.Plane, spec.Name), spec)) { throw new ArgumentException($"method {spec.Name} is declared twice in the {spec.Plane} plane"); } } foreach (ChannelSpec spec in channels ?? []) { if (!_channels.TryAdd((spec.Plane, spec.Channel), spec)) { throw new ArgumentException($"channel {spec.Channel} is declared twice in the {spec.Plane} plane"); } } } public IEnumerable Methods => _methods.Values; public IEnumerable Channels => _channels.Values; public MethodSpec? Find(RpcPlane plane, string method) => _methods.GetValueOrDefault((plane, method)); public ChannelSpec? FindChannel(RpcPlane plane, byte channel) => _channels.GetValueOrDefault((plane, channel)); /// 合并多张表(框架的加产品的)。 public static MethodTable Combine(params MethodTable[] tables) => new(tables.SelectMany(t => t.Methods), tables.SelectMany(t => t.Channels)); /// /// 对账: 这一侧、 这个角色应该处理、但没有注册处理函数的方法 /// (认证阶段直接处理的 方法除外)。 为空表示不限角色(服务端要处理所有角色发来的)。 /// public IReadOnlyList MissingHandlers(RpcSide receiver, string? role, IEnumerable registered) { HashSet have = [.. registered]; return [ .. Methods .Where(m => m.To == receiver && !m.Inline && (role is null || m.AllowsRole(role)) && !have.Contains(m.Name)) .Select(m => m.Name) .Order(StringComparer.Ordinal), ]; } }