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),
];
}
}