#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Site {
Module,
Struct,
Union,
Enum,
Bitset,
Bitmask,
Typedef,
Native,
Constant,
Exception,
Member,
UnionMember,
Enumerator,
BitmaskValue,
Interface,
Operation,
Parameter,
Attribute,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArgumentShape {
Marker,
Value,
OptionalValue,
String(&'static [&'static str]),
OptionalString(&'static [&'static str]),
Lifecycle,
Version,
ApiKey,
Http,
Named {
required: &'static [&'static str],
optional: &'static [&'static str],
},
Values,
Cors,
Deprecated,
Grammar,
}
#[derive(Debug, Clone, Copy)]
pub struct AnnotationDefinition {
pub name: &'static str,
pub kind: AnnotationKind,
pub sites: &'static [Site],
pub arguments: ArgumentShape,
}
impl AnnotationDefinition {
pub fn lookup(name: &str) -> Option<&'static Self> {
BUILTIN_ANNOTATIONS
.iter()
.find(|entry| entry.name.eq_ignore_ascii_case(name))
}
pub fn allows(&self, site: Site) -> bool {
self.sites.is_empty() || self.sites.contains(&site)
}
pub fn is_passthrough(name: &str) -> bool {
let name = name.to_ascii_lowercase();
["rust-", "rust_", "ts-", "ts_"]
.iter()
.any(|prefix| name.starts_with(prefix))
}
}
macro_rules! annotations {
($($kind:ident, $name:literal, $sites:expr, $args:expr;)+) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AnnotationKind {
$(#[doc = concat!("The `@", $name, "` annotation.")] $kind,)+
}
pub const BUILTIN_ANNOTATIONS: &[AnnotationDefinition] = &[
$(AnnotationDefinition { name: $name, kind: AnnotationKind::$kind,
sites: $sites, arguments: $args },)+
];
};
}
use ArgumentShape::*;
use Site::*;
const TYPES: &[Site] = &[
Struct, Union, Enum, Bitset, Bitmask, Typedef, Native, Exception,
];
const FIELDS: &[Site] = &[Member, UnionMember, Parameter];
const SECURITY: &[Site] = &[Interface, Operation, Attribute];
const LIFECYCLE: &[Site] = &[
Module,
Struct,
Union,
Enum,
Bitset,
Bitmask,
Typedef,
Native,
Exception,
Member,
UnionMember,
Enumerator,
BitmaskValue,
Interface,
Operation,
Parameter,
Attribute,
];
annotations! {
Id, "id", &[Member, UnionMember], Grammar;
AutoId, "autoid", &[Module, Struct, Union, Exception], Grammar;
Optional, "optional", FIELDS, OptionalValue;
Position, "position", &[BitmaskValue], Grammar;
Value, "value", &[Enumerator], Grammar;
Extensibility, "extensibility", TYPES, Grammar;
Final, "final", TYPES, Marker;
Appendable, "appendable", TYPES, Marker;
Mutable, "mutable", TYPES, Marker;
Key, "key", &[Member, UnionMember], OptionalValue;
MustUnderstand, "must_understand", &[Member, UnionMember], OptionalValue;
DefaultLiteral, "default_literal", &[Enumerator], Marker;
Default, "default", FIELDS, Value;
Range, "range", FIELDS, Named { required: &["min", "max"], optional: &[] };
Min, "min", FIELDS, Value;
Max, "max", FIELDS, Value;
Unit, "unit", FIELDS, Value;
BitBound, "bit_bound", &[Enum, Bitmask], Value;
External, "external", FIELDS, OptionalValue;
Nested, "nested", TYPES, OptionalValue;
Verbatim, "verbatim", &[], Grammar;
Service, "service", &[Interface], Grammar;
Oneway, "oneway", &[Operation], OptionalValue;
Ami, "ami", &[Interface, Operation], OptionalValue;
HashId, "hashid", &[Member, UnionMember], OptionalValue;
DefaultNested, "default_nested", &[Module], OptionalValue;
IgnoreLiteralNames, "ignore_literal_names", &[Enum], OptionalValue;
TryConstruct, "try_construct", FIELDS, Grammar;
NonSerialized, "non_serialized", FIELDS, OptionalValue;
DataRepresentation, "data_representation", TYPES, Grammar;
Topic, "topic", &[Struct, Union], Grammar;
Choice, "choice", &[Union], Marker;
Empty, "empty", &[Struct], Marker;
DdsService, "dds_service", &[Interface], Marker;
DdsRequestTopic, "dds_request_topic", &[Operation], Grammar;
DdsReplyTopic, "dds_reply_topic", &[Operation], Grammar;
Rename, "rename", &[], String(&["name"]);
Name, "name", &[], String(&["name"]);
RenameAll, "rename_all", &[Struct, Union, Enum, Bitmask, Exception, Interface], String(&["rule"]);
Skip, "skip", &[Member, UnionMember, Enumerator, BitmaskValue, Parameter], Marker;
OmitEmpty, "omitempty", &[Member], Marker;
Doc, "doc", &[], String(&["text", "doc"]);
Derive, "derive", TYPES, Values;
Rust, "rust", &[], Value;
Version, "version", &[Enum], Version;
Added, "added", LIFECYCLE, Lifecycle;
Removed, "removed", LIFECYCLE, Lifecycle;
Http, "http", &[Exception, Union], Http;
Get, "get", &[Operation], OptionalString(&["path"]);
Post, "post", &[Operation], OptionalString(&["path"]);
Put, "put", &[Operation], OptionalString(&["path"]);
Patch, "patch", &[Operation], OptionalString(&["path"]);
Delete, "delete", &[Operation], OptionalString(&["path"]);
Head, "head", &[Operation], OptionalString(&["path"]);
Options, "options", &[Operation], OptionalString(&["path"]);
Path, "path", &[Interface, Operation, Parameter], OptionalString(&["name", "path"]);
Query, "query", &[Parameter], OptionalString(&["name"]);
Header, "header", &[Parameter, Member], OptionalString(&["name"]);
Cookie, "cookie", &[Parameter, Member], OptionalString(&["name"]);
Body, "body", &[Parameter, Member], OptionalString(&[]);
Flatten, "flatten", &[Member, Parameter], Marker;
HttpBasic, "http_basic", SECURITY, OptionalString(&["realm"]);
HttpBearer, "http_bearer", SECURITY, Marker;
ApiKey, "api_key", SECURITY, ApiKey;
NoSecurity, "no_security", SECURITY, Marker;
Request, "request", &[Interface, Operation], String(&["format"]);
Response, "response", &[Interface, Operation], String(&["format"]);
ServerStream, "server_stream", &[Operation, Attribute], Marker;
ClientStream, "client_stream", &[Operation], Marker;
BidiStream, "bidi_stream", &[Operation], Marker;
StreamCodec, "stream_codec", &[Operation], OptionalString(&[]);
Upgrade, "upgrade", &[Operation], Named { required: &["protocol"], optional: &["codec", "subprotocol", "heartbeat", "max_message"] };
Deprecated, "deprecated", SECURITY, Deprecated;
Cors, "cors", &[Interface, Operation], Cors;
}
impl AnnotationKind {
pub fn from_name(name: &str) -> Option<Self> {
AnnotationDefinition::lookup(name).map(|entry| entry.kind)
}
pub fn is_lifecycle(self) -> bool {
matches!(self, Self::Version | Self::Added | Self::Removed)
}
}