#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Role {
BusRoot,
Transport,
Host,
ReceiveEndpoint,
EndpointConfiguration,
Registration,
NameFormatter,
SendAddress,
RequestClient,
EndpointConvention,
EntityName,
OptionsBinding,
Outbox,
TestTransport,
}
pub(crate) const ROLE_WORDS: [&str; 14] = [
"bus-root",
"transport",
"host",
"receive-endpoint",
"endpoint-configuration",
"registration",
"name-formatter",
"send-address",
"request-client",
"endpoint-convention",
"entity-name",
"options-binding",
"outbox",
"test-transport",
];
impl Role {
pub(crate) fn word(self) -> &'static str {
ROLE_WORDS[self as usize]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Act {
Bus,
Transport,
Host,
Receive,
Configured,
Registration,
Formatter,
ConfigureEndpoints,
Send,
Request,
Convention,
Entity,
Topology,
Options,
OptionsKey,
Outbox,
EndpointName,
Pass,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Val {
Name,
EntityName,
Address,
HostName,
VirtualHost,
Region,
HostUri,
Key,
FormatterArg,
Ignore,
}
impl Val {
pub(crate) fn word(self) -> &'static str {
match self {
Val::Name => "name",
Val::EntityName => "entity-name",
Val::Address => "address",
Val::HostName => "host",
Val::VirtualHost => "virtual-host",
Val::Region => "region",
Val::HostUri => "host-uri",
Val::Key => "key",
Val::FormatterArg => "formatter-argument",
Val::Ignore => "ignore",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Ty {
Message,
Consumer,
Saga,
StateMachine,
State,
Handler,
Definition,
Anchor,
Options,
DbContext,
Bus,
Unused,
}
pub(crate) const TYPE_ROLE_WORDS: [&str; 11] = [
"message",
"consumer",
"saga",
"state-machine",
"state",
"handler",
"definition",
"anchor",
"options",
"db-context",
"bus",
];
impl Ty {
pub(crate) fn word(self) -> &'static str {
match self {
Ty::Message => "message",
Ty::Consumer => "consumer",
Ty::Saga => "saga",
Ty::StateMachine => "state-machine",
Ty::State => "state",
Ty::Handler => "handler",
Ty::Definition => "definition",
Ty::Anchor => "anchor",
Ty::Options => "options",
Ty::DbContext => "db-context",
Ty::Bus => "bus",
Ty::Unused => "",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum P {
Val(Val),
Uri(Val),
Lambda(&'static [&'static str]),
Filter,
Formatter,
Opaque,
Text,
TypeOf(Ty),
Section,
Rest,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Param {
pub(crate) kind: P,
pub(crate) optional: bool,
}
const fn r(kind: P) -> Param {
Param {
kind,
optional: false,
}
}
const fn o(kind: P) -> Param {
Param {
kind,
optional: true,
}
}
#[derive(Debug)]
pub(crate) struct Overload {
pub(crate) decl: &'static str,
pub(crate) on: &'static str,
pub(crate) extension: bool,
pub(crate) types: &'static [Ty],
pub(crate) params: &'static [Param],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Form {
Call,
Assign,
Property,
}
#[derive(Debug)]
pub(crate) struct Member {
pub(crate) name: &'static str,
pub(crate) form: Form,
pub(crate) role: Role,
pub(crate) act: Act,
pub(crate) start: bool,
pub(crate) word: Option<&'static str>,
pub(crate) returns: Option<&'static str>,
pub(crate) overloads: &'static [Overload],
}
#[derive(Debug)]
pub(crate) struct TypeEntry {
pub(crate) name: &'static str,
pub(crate) role: Role,
pub(crate) bases: &'static [&'static str],
pub(crate) endpoint: Option<&'static str>,
}
const fn t(name: &'static str, role: Role, bases: &'static [&'static str]) -> TypeEntry {
TypeEntry {
name,
role,
bases,
endpoint: None,
}
}
const fn bus_cfg(name: &'static str, endpoint: &'static str) -> TypeEntry {
TypeEntry {
name,
role: Role::Transport,
bases: &["MassTransit.IBusFactoryConfigurator`1"],
endpoint: Some(endpoint),
}
}
pub(crate) const URI_TYPE: &str = "System.Uri";
pub(crate) const DEFAULT_ENDPOINT: &str = "MassTransit.IReceiveEndpointConfigurator";
pub(crate) static TYPES: &[TypeEntry] = &[
t(
"Microsoft.Extensions.DependencyInjection.IServiceCollection",
Role::BusRoot,
&[],
),
t(
"MassTransit.IRegistrationConfigurator",
Role::Registration,
&["Microsoft.Extensions.DependencyInjection.IServiceCollection"],
),
t(
"MassTransit.IBusRegistrationConfigurator",
Role::Registration,
&["MassTransit.IRegistrationConfigurator"],
),
t(
"MassTransit.IBusRegistrationConfigurator`1",
Role::Registration,
&["MassTransit.IBusRegistrationConfigurator"],
),
t("MassTransit.IRegistrationContext", Role::Registration, &[]),
t(
"MassTransit.IBusRegistrationContext",
Role::Registration,
&["MassTransit.IRegistrationContext"],
),
t("MassTransit.Bus", Role::BusRoot, &[]),
t("MassTransit.IBusFactorySelector", Role::BusRoot, &[]),
t(
"MassTransit.IReceiveConfigurator",
Role::ReceiveEndpoint,
&[],
),
t(
"MassTransit.IReceiveConfigurator`1",
Role::ReceiveEndpoint,
&["MassTransit.IReceiveConfigurator"],
),
t(
"MassTransit.IBusFactoryConfigurator",
Role::Transport,
&["MassTransit.IReceiveConfigurator"],
),
t(
"MassTransit.IBusFactoryConfigurator`1",
Role::Transport,
&[
"MassTransit.IBusFactoryConfigurator",
"MassTransit.IReceiveConfigurator`1",
],
),
bus_cfg(
"MassTransit.IRabbitMqBusFactoryConfigurator",
"MassTransit.IRabbitMqReceiveEndpointConfigurator",
),
bus_cfg(
"MassTransit.IAmazonSqsBusFactoryConfigurator",
"MassTransit.IAmazonSqsReceiveEndpointConfigurator",
),
bus_cfg(
"MassTransit.IInMemoryBusFactoryConfigurator",
"MassTransit.IInMemoryReceiveEndpointConfigurator",
),
t(
"MassTransit.IReceiveEndpointConfigurator",
Role::EndpointConfiguration,
&[],
),
t(
"MassTransit.IRabbitMqReceiveEndpointConfigurator",
Role::EndpointConfiguration,
&["MassTransit.IReceiveEndpointConfigurator"],
),
t(
"MassTransit.IAmazonSqsReceiveEndpointConfigurator",
Role::EndpointConfiguration,
&["MassTransit.IReceiveEndpointConfigurator"],
),
t(
"MassTransit.IInMemoryReceiveEndpointConfigurator",
Role::EndpointConfiguration,
&["MassTransit.IReceiveEndpointConfigurator"],
),
t("MassTransit.ISendEndpointProvider", Role::SendAddress, &[]),
t(
"MassTransit.IBus",
Role::SendAddress,
&["MassTransit.ISendEndpointProvider"],
),
t(
"MassTransit.IBusControl",
Role::SendAddress,
&["MassTransit.IBus"],
),
t(
"MassTransit.ConsumeContext",
Role::SendAddress,
&["MassTransit.ISendEndpointProvider"],
),
t(
"MassTransit.ConsumeContext`1",
Role::SendAddress,
&["MassTransit.ConsumeContext"],
),
t(
"MassTransit.EndpointConvention",
Role::EndpointConvention,
&[],
),
t(
"MassTransit.Configuration.IMessageTopologyConfigurator`1",
Role::EntityName,
&[],
),
t("MassTransit.EntityNameAttribute", Role::EntityName, &[]),
t(
"MassTransit.AmazonSqsTransportOptions",
Role::OptionsBinding,
&[],
),
t(
"MassTransit.RabbitMqTransportOptions",
Role::OptionsBinding,
&[],
),
t(
"Microsoft.Extensions.Options.OptionsBuilder`1",
Role::OptionsBinding,
&[],
),
t(
"MassTransit.IEntityFrameworkOutboxConfigurator",
Role::Outbox,
&[],
),
t(
"MassTransit.ConsumerDefinition`1",
Role::EndpointConfiguration,
&[],
),
t(
"MassTransit.SagaDefinition`1",
Role::EndpointConfiguration,
&[],
),
t(
"MassTransit.IEndpointRegistrationConfigurator",
Role::EndpointConfiguration,
&[],
),
t(
"MassTransit.DefaultEndpointNameFormatter",
Role::NameFormatter,
&[],
),
t(
"MassTransit.SnakeCaseEndpointNameFormatter",
Role::NameFormatter,
&["MassTransit.DefaultEndpointNameFormatter"],
),
t(
"MassTransit.KebabCaseEndpointNameFormatter",
Role::NameFormatter,
&["MassTransit.SnakeCaseEndpointNameFormatter"],
),
t("System.Uri", Role::SendAddress, &[]),
];
pub(crate) static FORMATTERS: &[(&str, &str)] = &[
("MassTransit.KebabCaseEndpointNameFormatter", "kebab-case"),
("MassTransit.SnakeCaseEndpointNameFormatter", "snake-case"),
("MassTransit.DefaultEndpointNameFormatter", "default"),
];
pub(crate) static TRANSPORTS: &[(&str, &str)] = &[
("MassTransit.IRabbitMqBusFactoryConfigurator", "rabbitmq"),
("MassTransit.IAmazonSqsBusFactoryConfigurator", "amazon-sqs"),
("MassTransit.IInMemoryBusFactoryConfigurator", "in-memory"),
];
pub(crate) static DEFINITIONS: &[(&str, Ty)] = &[
("MassTransit.ConsumerDefinition`1", Ty::Consumer),
("MassTransit.SagaDefinition`1", Ty::Saga),
];
pub(crate) static ATTRIBUTES: &[(&str, Val, Ty)] = &[(
"MassTransit.EntityNameAttribute",
Val::EntityName,
Ty::Message,
)];
pub(crate) static OPTIONS: &[(&str, &[(&str, Val)])] = &[
(
"MassTransit.AmazonSqsTransportOptions",
&[("Region", Val::Region)],
),
(
"MassTransit.RabbitMqTransportOptions",
&[("Host", Val::HostName), ("VHost", Val::VirtualHost)],
),
];
mod members;
mod registrations;
pub(crate) fn members() -> impl Iterator<Item = &'static Member> {
members::BUS_MEMBERS
.iter()
.chain(registrations::CONFIG_MEMBERS.iter())
}
pub(crate) static IMPLICIT_USINGS: &[(&str, &[&str])] = &[
("Microsoft.NET.Sdk", BASE_USINGS),
("Microsoft.NET.Sdk.Web", WEB_USINGS),
("Microsoft.NET.Sdk.Worker", WORKER_USINGS),
];
const BASE_USINGS: &[&str] = &[
"System",
"System.Collections.Generic",
"System.IO",
"System.Linq",
"System.Net.Http",
"System.Threading",
"System.Threading.Tasks",
];
const WEB_USINGS: &[&str] = &[
"System",
"System.Collections.Generic",
"System.IO",
"System.Linq",
"System.Net.Http",
"System.Net.Http.Json",
"System.Threading",
"System.Threading.Tasks",
"Microsoft.AspNetCore.Builder",
"Microsoft.AspNetCore.Hosting",
"Microsoft.AspNetCore.Http",
"Microsoft.AspNetCore.Routing",
"Microsoft.Extensions.Configuration",
"Microsoft.Extensions.DependencyInjection",
"Microsoft.Extensions.Hosting",
"Microsoft.Extensions.Logging",
];
const WORKER_USINGS: &[&str] = &[
"System",
"System.Collections.Generic",
"System.IO",
"System.Linq",
"System.Net.Http",
"System.Threading",
"System.Threading.Tasks",
"Microsoft.Extensions.Configuration",
"Microsoft.Extensions.DependencyInjection",
"Microsoft.Extensions.Hosting",
"Microsoft.Extensions.Logging",
];
pub(crate) fn simple(name: &str) -> &str {
let last = name.rsplit('.').next().unwrap_or(name);
last.split('`').next().unwrap_or(last)
}
pub(crate) fn arity(name: &str) -> usize {
name.rsplit_once('`')
.and_then(|(_, n)| n.parse().ok())
.unwrap_or(0)
}
pub(crate) fn namespace_of(name: &str) -> &str {
name.rsplit_once('.').map_or("", |(ns, _)| ns)
}
pub(crate) fn type_entry(name: &str) -> Option<&'static TypeEntry> {
TYPES.iter().find(|t| t.name == name)
}
pub(crate) fn types_named(simple_name: &str, type_arity: usize) -> Vec<&'static TypeEntry> {
TYPES
.iter()
.filter(|t| simple(t.name) == simple_name && arity(t.name) == type_arity)
.collect()
}
pub(crate) fn derives(ty: &str, target: &str) -> bool {
if ty == target {
return true;
}
type_entry(ty).is_some_and(|t| t.bases.iter().any(|b| derives(b, target)))
}
pub(crate) fn endpoint_of(ty: &str) -> &'static str {
type_entry(ty)
.and_then(|t| t.endpoint)
.unwrap_or(DEFAULT_ENDPOINT)
}
pub(crate) fn members_named(name: &str, form: Form) -> impl Iterator<Item = &'static Member> + '_ {
members().filter(move |m| m.name == name && m.form == form)
}
pub(crate) fn member_by_key(key: &str) -> Option<(&'static Member, &'static Overload)> {
let (decl, name) = key.rsplit_once('.')?;
members()
.filter(|m| m.name == name)
.find_map(|m| m.overloads.iter().find(|o| o.decl == decl).map(|o| (m, o)))
}
pub(crate) fn is_member_name(name: &str) -> bool {
members().any(|m| m.name == name)
}
pub(crate) fn prefilter_words() -> Vec<&'static str> {
let mut words: Vec<&'static str> = TYPES
.iter()
.filter(|t| !UNFILTERED.contains(&t.name))
.map(|t| simple(t.name))
.collect();
words.extend(
members()
.filter(|m| m.start && m.act == Act::Bus)
.map(|m| m.name),
);
words.sort_unstable();
words.dedup();
words
}
const UNFILTERED: [&str; 3] = [
members::ISC,
"Microsoft.Extensions.Options.OptionsBuilder`1",
URI_TYPE,
];
#[cfg(test)]
#[path = "catalogue_tests.rs"]
mod tests;