Skip to main content

aranya_capi_codegen/syntax/
opaque.rs

1use proc_macro2::TokenStream;
2use quote::{ToTokens, quote};
3use syn::{
4    Error, Ident, LitInt,
5    parse::{Parse, ParseStream, Result},
6};
7
8use crate::{
9    attr::{Attr, Symbol},
10    ctx::Ctx,
11    util::KeyValPair,
12};
13
14/// The `#[capi::opaque(size = 42, align = 12)]` attribute.
15///
16/// It can only be applied to aliases.
17#[derive(Clone, Debug, Eq, PartialEq)]
18pub struct Opaque {
19    /// The size in bytes of the type.
20    pub size: LitInt,
21    /// The alignment in bytes of the type.
22    pub align: LitInt,
23    /// Path to the `capi` crate.
24    pub capi: Option<Ident>,
25}
26
27impl Opaque {
28    pub(super) fn parse(ctx: Option<&Ctx>, input: ParseStream<'_>) -> Result<Self> {
29        mod kw {
30            syn::custom_keyword!(size);
31            syn::custom_keyword!(align);
32            syn::custom_keyword!(capi);
33        }
34        const SIZE: Symbol = Symbol("size");
35        const ALIGN: Symbol = Symbol("align");
36        const CAPI: Symbol = Symbol("capi");
37
38        let mut size = Attr::none(ALIGN);
39        let mut align = Attr::none(SIZE);
40        let mut capi = Attr::none(CAPI);
41
42        while !input.is_empty() {
43            let lookahead = input.lookahead1();
44            if lookahead.peek(kw::size) {
45                let KeyValPair { key, val } = input.parse::<KeyValPair<kw::size, LitInt>>()?;
46                size.set(key, val)?;
47            } else if lookahead.peek(kw::align) {
48                let KeyValPair { key, val } = input.parse::<KeyValPair<kw::align, LitInt>>()?;
49                align.set(key, val)?;
50            } else if lookahead.peek(kw::capi) {
51                let KeyValPair { key, val } = input.parse::<KeyValPair<kw::capi, Ident>>()?;
52                capi.set(key, val)?;
53            } else {
54                return Err(lookahead.error());
55            }
56        }
57
58        let size = size.get().ok_or(Error::new(
59            input.span(),
60            format!("missing `{SIZE}` argument"),
61        ))?;
62        let align = align.get().ok_or(Error::new(
63            input.span(),
64            format!("missing `{ALIGN}` argument"),
65        ))?;
66        let capi = capi.get().or_else(|| ctx.map(|ctx| ctx.capi.clone()));
67        Ok(Self { size, align, capi })
68    }
69}
70
71impl Parse for Opaque {
72    fn parse(input: ParseStream<'_>) -> Result<Self> {
73        Self::parse(None, input)
74    }
75}
76
77impl ToTokens for Opaque {
78    fn to_tokens(&self, tokens: &mut TokenStream) {
79        let size = &self.size;
80        let align = &self.align;
81        let capi = &self.capi;
82        // TODO(eric): `capi`?
83        tokens.extend(quote! {
84            #[#capi::opaque(size = #size, align = #align)]
85        });
86    }
87}