diplomat_core/ast/
structs.rs

1use serde::Serialize;
2
3use super::docs::Docs;
4use super::{Attrs, Ident, LifetimeEnv, Method, Mutability, PathType, TypeName};
5
6/// A struct declaration in an FFI module that is not opaque.
7#[derive(Clone, PartialEq, Eq, Hash, Serialize, Debug)]
8#[non_exhaustive]
9pub struct Struct {
10    pub name: Ident,
11    pub docs: Docs,
12    pub lifetimes: LifetimeEnv,
13    pub fields: Vec<(Ident, TypeName, Docs)>,
14    pub methods: Vec<Method>,
15    pub output_only: bool,
16    pub attrs: Attrs,
17}
18
19impl Struct {
20    /// Extract a [`Struct`] metadata value from an AST node.
21    pub fn new(strct: &syn::ItemStruct, output_only: bool, parent_attrs: &Attrs) -> Self {
22        let self_path_type = PathType::extract_self_type(strct);
23        let fields: Vec<_> = strct
24            .fields
25            .iter()
26            .map(|field| {
27                // Non-opaque tuple structs will never be allowed
28                let name = field
29                    .ident
30                    .as_ref()
31                    .map(Into::into)
32                    .expect("non-opaque tuples structs are disallowed");
33                let type_name = TypeName::from_syn(&field.ty, Some(self_path_type.clone()));
34                let docs = Docs::from_attrs(&field.attrs);
35
36                (name, type_name, docs)
37            })
38            .collect();
39
40        let lifetimes = LifetimeEnv::from_struct_item(strct, &fields[..]);
41        let mut attrs = parent_attrs.clone();
42        attrs.add_attrs(&strct.attrs);
43        Struct {
44            name: (&strct.ident).into(),
45            docs: Docs::from_attrs(&strct.attrs),
46            lifetimes,
47            fields,
48            methods: vec![],
49            output_only,
50            attrs,
51        }
52    }
53}
54
55/// A struct annotated with [`diplomat::opaque`] whose fields are not visible.
56/// Opaque structs cannot be passed by-value across the FFI boundary, so they
57/// must be boxed or passed as references.
58#[derive(Clone, Serialize, Debug, Hash, PartialEq, Eq)]
59#[non_exhaustive]
60pub struct OpaqueStruct {
61    pub name: Ident,
62    pub docs: Docs,
63    pub lifetimes: LifetimeEnv,
64    pub methods: Vec<Method>,
65    pub mutability: Mutability,
66    pub attrs: Attrs,
67}
68
69impl OpaqueStruct {
70    /// Extract a [`OpaqueStruct`] metadata value from an AST node.
71    pub fn new(strct: &syn::ItemStruct, mutability: Mutability, parent_attrs: &Attrs) -> Self {
72        let mut attrs = parent_attrs.clone();
73        attrs.add_attrs(&strct.attrs);
74        OpaqueStruct {
75            name: Ident::from(&strct.ident),
76            docs: Docs::from_attrs(&strct.attrs),
77            lifetimes: LifetimeEnv::from_struct_item(strct, &[]),
78            methods: vec![],
79            mutability,
80            attrs,
81        }
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use insta::{self, Settings};
88
89    use syn;
90
91    use super::Struct;
92
93    #[test]
94    fn simple_struct() {
95        let mut settings = Settings::new();
96        settings.set_sort_maps(true);
97
98        settings.bind(|| {
99            insta::assert_yaml_snapshot!(Struct::new(
100                &syn::parse_quote! {
101                    /// Some docs.
102                    #[diplomat::rust_link(foo::Bar, Struct)]
103                    struct MyLocalStruct {
104                        a: i32,
105                        b: Box<MyLocalStruct>
106                    }
107                },
108                true,
109                &Default::default()
110            ));
111        });
112    }
113}