moverox_codegen/
attributes.rs1use std::collections::HashSet;
2
3use move_syn::Attributes;
4use quote::quote;
5use unsynn::{CommaDelimitedVec, IParse as _, Ident, ToTokens as _, TokenStream};
6
7use crate::Result;
8
9#[expect(clippy::result_large_err, reason = "Error from the unsynn crate")]
10mod grammar {
11 use unsynn::*;
12
13 mod kw {
14 use unsynn::unsynn;
15
16 unsynn! {
17 pub(super) keyword Moverox = "moverox";
18 pub(super) keyword Otw = "OTW";
19 pub(super) keyword Type = "type_";
22 }
23 }
24
25 unsynn! {
26 pub(crate) struct Annotation {
35 kw: kw::Moverox,
36 contents: ParenthesisGroupContaining<CommaDelimitedVec<Setting>>
37 }
38
39 pub(super) enum Setting {
41 Type(Type)
43 }
44
45 pub(super) struct Type {
47 kw: kw::Type,
48 contents: ParenthesisGroupContaining<CommaDelimitedVec<TypeDefault>>,
49 }
50
51 struct TypeDefault {
53 ident: Ident,
55 assign: Assign,
56 default: kw::Otw,
58 }
59
60 pub(crate) enum ExtEntry {
66 Moverox(Annotation),
67 Other(OtherEntry),
68 }
69
70 pub(crate) struct OtherEntry {
74 tokens: Vec<Cons<Except<Comma>, TokenTree>>,
75 }
76 }
77
78 impl Annotation {
79 pub(super) fn settings(&self) -> impl Iterator<Item = &Setting> + '_ {
80 self.contents
81 .content
82 .iter()
83 .map(|delimited| &delimited.value)
84 }
85 }
86
87 impl Setting {
88 pub(super) fn otw_types(&self) -> impl Iterator<Item = &Ident> + '_ {
89 let Self::Type(ty) = self;
90 ty.contents
91 .content
92 .iter()
93 .map(|delimited| &delimited.value.ident)
94 }
95 }
96}
97
98pub(super) fn extract(attrs: &[Attributes]) -> Result<(TokenStream, HashSet<Ident>)> {
100 let (move_docs, other): (Vec<_>, Vec<_>) = attrs.iter().partition(|attr| attr.is_doc());
101
102 let rust_docs = move_docs.into_iter().map(process_doc).collect();
103
104 let custom: Vec<_> = other.into_iter().flat_map(as_moverox).collect();
105 let mut otw_types = HashSet::new();
106 for ident in custom
107 .iter()
108 .flat_map(|custom| custom.settings())
109 .flat_map(|setting| setting.otw_types())
110 {
111 if otw_types.contains(ident) {
112 return Err(format!("Type {ident} declared twice").into());
113 }
114 otw_types.insert(ident.to_owned());
115 }
116
117 Ok((rust_docs, otw_types))
118}
119
120pub(super) fn as_moverox(attr: &Attributes) -> impl Iterator<Item = self::grammar::Annotation> {
121 attr.external_attributes()
126 .filter_map(|ext| {
127 ext.to_token_iter()
128 .parse_all::<CommaDelimitedVec<self::grammar::ExtEntry>>()
129 .ok()
130 })
131 .flat_map(|entries| {
132 entries.into_iter().filter_map(|entry| match entry.value {
133 self::grammar::ExtEntry::Moverox(annotation) => Some(annotation),
134 self::grammar::ExtEntry::Other(_) => None,
135 })
136 })
137}
138
139fn process_doc(attr: &Attributes) -> TokenStream {
140 let inner = attr.contents().to_token_stream();
141 quote!(#[cfg_attr(not(doctest), #inner)])
144}