1use serde::ser::{SerializeStruct, Serializer};
4use serde::Serialize;
5use std::borrow::Cow;
6use std::convert::Infallible;
7use std::str::FromStr;
8use syn::parse::{Error as ParseError, Parse, ParseStream};
9use syn::{Attribute, Expr, Ident, Lit, LitStr, Meta, MetaList, Token};
10
11#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
28#[non_exhaustive]
29pub struct Attrs {
30 pub cfg: Vec<Attribute>,
33 pub attrs: Vec<DiplomatBackendAttr>,
40 pub skip_if_ast: bool,
47
48 pub abi_rename: RenameAttr,
56}
57
58impl Attrs {
59 fn add_attr(&mut self, attr: Attr) {
60 match attr {
61 Attr::Cfg(attr) => self.cfg.push(attr),
62 Attr::DiplomatBackend(attr) => self.attrs.push(attr),
63 Attr::SkipIfAst => self.skip_if_ast = true,
64 Attr::CRename(rename) => self.abi_rename.extend(&rename),
65 }
66 }
67
68 pub(crate) fn attrs_for_inheritance(&self, context: AttrInheritContext) -> Self {
71 let attrs = if context == AttrInheritContext::MethodFromImpl {
77 self.attrs.clone()
78 } else {
79 Vec::new()
80 };
81
82 let abi_rename = self.abi_rename.attrs_for_inheritance(context, true);
83 Self {
84 cfg: self.cfg.clone(),
85
86 attrs,
87 skip_if_ast: false,
89 abi_rename,
90 }
91 }
92
93 pub(crate) fn add_attrs(&mut self, attrs: &[Attribute]) {
94 for attr in syn_attr_to_ast_attr(attrs) {
95 self.add_attr(attr)
96 }
97 }
98 pub(crate) fn from_attrs(attrs: &[Attribute]) -> Self {
99 let mut this = Self::default();
100 this.add_attrs(attrs);
101 this
102 }
103}
104
105impl From<&[Attribute]> for Attrs {
106 fn from(other: &[Attribute]) -> Self {
107 Self::from_attrs(other)
108 }
109}
110
111enum Attr {
112 Cfg(Attribute),
113 DiplomatBackend(DiplomatBackendAttr),
114 SkipIfAst,
115 CRename(RenameAttr),
116 }
118
119fn syn_attr_to_ast_attr(attrs: &[Attribute]) -> impl Iterator<Item = Attr> + '_ {
120 let cfg_path: syn::Path = syn::parse_str("cfg").unwrap();
121 let dattr_path: syn::Path = syn::parse_str("diplomat::attr").unwrap();
122 let crename_attr: syn::Path = syn::parse_str("diplomat::abi_rename").unwrap();
123 let skipast: syn::Path = syn::parse_str("diplomat::skip_if_ast").unwrap();
124 attrs.iter().filter_map(move |a| {
125 if a.path() == &cfg_path {
126 Some(Attr::Cfg(a.clone()))
127 } else if a.path() == &dattr_path {
128 Some(Attr::DiplomatBackend(
129 a.parse_args()
130 .expect("Failed to parse malformed diplomat::attr"),
131 ))
132 } else if a.path() == &crename_attr {
133 Some(Attr::CRename(RenameAttr::from_meta(&a.meta).unwrap()))
134 } else if a.path() == &skipast {
135 Some(Attr::SkipIfAst)
136 } else {
137 None
138 }
139 })
140}
141
142impl Serialize for Attrs {
143 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
144 where
145 S: Serializer,
146 {
147 let mut state = serializer.serialize_struct("Attrs", 1)?;
149 if !self.cfg.is_empty() {
150 let cfg: Vec<_> = self
151 .cfg
152 .iter()
153 .map(|a| quote::quote!(#a).to_string())
154 .collect();
155 state.serialize_field("cfg", &cfg)?;
156 }
157 if !self.attrs.is_empty() {
158 state.serialize_field("attrs", &self.attrs)?;
159 }
160 if self.skip_if_ast {
161 state.serialize_field("skip_if_ast", &self.skip_if_ast)?;
162 }
163 if !self.abi_rename.is_empty() {
164 state.serialize_field("abi_rename", &self.abi_rename)?;
165 }
166 state.end()
167 }
168}
169
170#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize)]
177#[non_exhaustive]
178pub struct DiplomatBackendAttr {
179 pub cfg: DiplomatBackendAttrCfg,
180 #[serde(serialize_with = "serialize_meta")]
181 pub meta: Meta,
182}
183
184fn serialize_meta<S>(m: &Meta, s: S) -> Result<S::Ok, S::Error>
185where
186 S: Serializer,
187{
188 quote::quote!(#m).to_string().serialize(s)
189}
190
191#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize)]
192#[non_exhaustive]
193pub enum DiplomatBackendAttrCfg {
194 Not(Box<DiplomatBackendAttrCfg>),
195 Any(Vec<DiplomatBackendAttrCfg>),
196 All(Vec<DiplomatBackendAttrCfg>),
197 Star,
198 BackendName(String),
199 NameValue(String, String),
200}
201
202impl Parse for DiplomatBackendAttrCfg {
203 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
204 let lookahead = input.lookahead1();
205 if lookahead.peek(Ident) {
206 let name: Ident = input.parse()?;
207 if name == "not" {
208 let content;
209 let _paren = syn::parenthesized!(content in input);
210 Ok(DiplomatBackendAttrCfg::Not(Box::new(content.parse()?)))
211 } else if name == "any" || name == "all" {
212 let content;
213 let _paren = syn::parenthesized!(content in input);
214 let list = content.parse_terminated(Self::parse, Token![,])?;
215 let vec = list.into_iter().collect();
216 if name == "any" {
217 Ok(DiplomatBackendAttrCfg::Any(vec))
218 } else {
219 Ok(DiplomatBackendAttrCfg::All(vec))
220 }
221 } else if input.peek(Token![=]) {
222 let _t: Token![=] = input.parse()?;
223 if input.peek(Ident) {
224 let value: Ident = input.parse()?;
225 Ok(DiplomatBackendAttrCfg::NameValue(
226 name.to_string(),
227 value.to_string(),
228 ))
229 } else {
230 let value: LitStr = input.parse()?;
231 Ok(DiplomatBackendAttrCfg::NameValue(
232 name.to_string(),
233 value.value(),
234 ))
235 }
236 } else {
237 Ok(DiplomatBackendAttrCfg::BackendName(name.to_string()))
238 }
239 } else if lookahead.peek(Token![*]) {
240 let _t: Token![*] = input.parse()?;
241 Ok(DiplomatBackendAttrCfg::Star)
242 } else {
243 Err(ParseError::new(
244 input.span(),
245 "CFG portion of #[diplomat::attr] fails to parse",
246 ))
247 }
248 }
249}
250
251impl Parse for DiplomatBackendAttr {
253 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
254 let cfg = input.parse()?;
255 let _comma: Token![,] = input.parse()?;
256 let meta = input.parse()?;
257 Ok(Self { cfg, meta })
258 }
259}
260
261#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
262pub(crate) enum AttrInheritContext {
263 Variant,
264 Type,
265 MethodOrImplFromModule,
267 MethodFromImpl,
272 #[allow(unused)]
275 Module,
276}
277
278#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize)]
286pub struct RenameAttr {
287 pattern: Option<RenamePattern>,
288}
289
290impl RenameAttr {
291 pub fn apply<'a>(&'a self, name: Cow<'a, str>) -> Cow<'a, str> {
293 if let Some(ref pattern) = self.pattern {
294 let replacement = &pattern.replacement;
295 if let Some(index) = pattern.insertion_index {
296 format!("{}{name}{}", &replacement[..index], &replacement[index..]).into()
297 } else {
298 replacement.into()
299 }
300 } else {
301 name
302 }
303 }
304
305 fn is_empty(&self) -> bool {
307 self.pattern.is_none()
308 }
309
310 pub(crate) fn extend(&mut self, other: &Self) {
311 if other.pattern.is_some() {
312 self.pattern.clone_from(&other.pattern);
313 }
314
315 }
318
319 pub(crate) fn attrs_for_inheritance(
322 &self,
323 context: AttrInheritContext,
324 is_abi_rename: bool,
325 ) -> Self {
326 let pattern = match context {
327 AttrInheritContext::MethodOrImplFromModule if !is_abi_rename => Default::default(),
329 AttrInheritContext::Variant => Default::default(),
331 _ => self.pattern.clone(),
332 };
333 Self { pattern }
336 }
337
338 fn from_pattern(s: &str) -> Self {
340 Self {
341 pattern: Some(s.parse().unwrap()),
342 }
343 }
344
345 pub(crate) fn from_meta(meta: &Meta) -> Result<Self, &'static str> {
346 let attr = StandardAttribute::from_meta(meta)
347 .map_err(|_| "#[diplomat::abi_rename] must be given a string value")?;
348
349 match attr {
350 StandardAttribute::String(s) => Ok(RenameAttr::from_pattern(&s)),
351 StandardAttribute::List(_) => {
352 Err("Failed to parse malformed #[diplomat::abi_rename(...)]: found list")
353 }
354 StandardAttribute::Empty => {
355 Err("Failed to parse malformed #[diplomat::abi_rename(...)]: found no parameters")
356 }
357 }
358 }
359}
360
361impl FromStr for RenamePattern {
362 type Err = Infallible;
363 fn from_str(s: &str) -> Result<Self, Infallible> {
364 if let Some(index) = s.find("{0}") {
365 let replacement = format!("{}{}", &s[..index], &s[index + 3..]);
366 Ok(Self {
367 replacement,
368 insertion_index: Some(index),
369 })
370 } else {
371 Ok(Self {
372 replacement: s.into(),
373 insertion_index: None,
374 })
375 }
376 }
377}
378
379#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize)]
380struct RenamePattern {
381 replacement: String,
383 insertion_index: Option<usize>,
386}
387
388pub(crate) enum StandardAttribute<'a> {
397 String(String),
398 List(#[allow(dead_code)] &'a MetaList),
399 Empty,
400}
401
402impl<'a> StandardAttribute<'a> {
403 pub(crate) fn from_meta(meta: &'a Meta) -> Result<Self, ()> {
405 match meta {
406 Meta::Path(..) => Ok(Self::Empty),
407 Meta::NameValue(ref nv) => {
408 let Expr::Lit(ref lit) = nv.value else {
410 return Err(());
411 };
412 let Lit::Str(ref lit) = lit.lit else {
413 return Err(());
414 };
415 Ok(Self::String(lit.value()))
416 }
417 Meta::List(list) => {
419 if let Ok(lit) = list.parse_args::<LitStr>() {
420 Ok(Self::String(lit.value()))
421 } else {
422 Ok(Self::List(list))
423 }
424 }
425 }
426 }
427}
428
429#[cfg(test)]
430mod tests {
431 use insta;
432
433 use syn;
434
435 use super::{DiplomatBackendAttr, DiplomatBackendAttrCfg, RenameAttr};
436
437 #[test]
438 fn test_cfgs() {
439 let attr_cfg: DiplomatBackendAttrCfg = syn::parse_quote!(*);
440 insta::assert_yaml_snapshot!(attr_cfg);
441 let attr_cfg: DiplomatBackendAttrCfg = syn::parse_quote!(cpp);
442 insta::assert_yaml_snapshot!(attr_cfg);
443 let attr_cfg: DiplomatBackendAttrCfg = syn::parse_quote!(has = overloading);
444 insta::assert_yaml_snapshot!(attr_cfg);
445 let attr_cfg: DiplomatBackendAttrCfg = syn::parse_quote!(has = "overloading");
446 insta::assert_yaml_snapshot!(attr_cfg);
447 let attr_cfg: DiplomatBackendAttrCfg =
448 syn::parse_quote!(any(all(*, cpp, has="overloading"), not(js)));
449 insta::assert_yaml_snapshot!(attr_cfg);
450 }
451
452 #[test]
453 fn test_attr() {
454 let attr: syn::Attribute =
455 syn::parse_quote!(#[diplomat::attr(any(cpp, has = "overloading"), namespacing)]);
456 let attr: DiplomatBackendAttr = attr.parse_args().unwrap();
457 insta::assert_yaml_snapshot!(attr);
458 }
459
460 #[test]
461 fn test_rename() {
462 let attr: syn::Attribute = syn::parse_quote!(#[diplomat::abi_rename = "foobar_{0}"]);
463 let attr = RenameAttr::from_meta(&attr.meta).unwrap();
464 insta::assert_yaml_snapshot!(attr);
465 let attr: syn::Attribute = syn::parse_quote!(#[diplomat::abi_rename("foobar_{0}")]);
466 let attr = RenameAttr::from_meta(&attr.meta).unwrap();
467 insta::assert_yaml_snapshot!(attr);
468 }
469}