1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use syn::{parse_quote, spanned::Spanned};

#[expect(clippy::module_name_repetitions)]
pub enum CudaReprFieldTy {
    SafeDeviceCopy,
    RustToCuda {
        field_ty: Box<syn::Type>,
    },
    RustToCudaProxy {
        proxy_ty: Box<syn::Type>,
        field_ty: Box<syn::Type>,
    },
}

pub fn swap_field_type_and_filter_attrs(
    crate_path: &syn::Path,
    field: &mut syn::Field,
) -> CudaReprFieldTy {
    let mut cuda_repr_field_ty: Option<CudaReprFieldTy> = None;
    let mut field_ty = field.ty.clone();

    let mut r2c_ignore = false;

    // Remove all attributes from the fields in the Cuda representation
    field.attrs.retain(|attr| {
        if attr.path().is_ident("cuda") {
            if let Err(err) = attr.parse_nested_meta(|meta| {
                if meta.path.is_ident("ignore") {
                    r2c_ignore = true;
                    return Ok(());
                }

                if meta.path.is_ident("embed") {
                    if cuda_repr_field_ty.is_some() {
                        emit_error!(
                            attr.span(),
                            "[rust-cuda]: Duplicate #[cuda(embed)] field attribute."
                        );
                        return Ok(());
                    }

                    if let Ok(meta) = meta.value() {
                        match meta.parse::<syn::LitStr>().and_then(|s| syn::parse_str(&s.value())) {
                            Ok(proxy_ty) => {
                                let old_field_ty = Box::new(field_ty.clone());
                                field_ty = parse_quote! {
                                    #crate_path::utils::ffi::DeviceAccessible<
                                        <#proxy_ty as #crate_path::lend::RustToCuda>::CudaRepresentation
                                    >
                                };
                                cuda_repr_field_ty = Some(CudaReprFieldTy::RustToCudaProxy {
                                    proxy_ty: Box::new(proxy_ty),
                                    field_ty: old_field_ty,
                                });
                            },
                            Err(err) => emit_error!(
                                meta.span(),
                                "[rust-cuda]: Invalid #[cuda(embed = \
                                \"<proxy-type>\")] field attribute: {}.",
                                err
                            ),
                        }
                    } else {
                        cuda_repr_field_ty = Some(CudaReprFieldTy::RustToCuda {
                            field_ty: Box::new(field_ty.clone()),
                        });
                        field_ty = parse_quote! {
                            #crate_path::utils::ffi::DeviceAccessible<
                                <#field_ty as #crate_path::lend::RustToCuda>::CudaRepresentation
                            >
                        };
                    }

                    return Ok(());
                }

                emit_error!(
                    meta.path.span(),
                    "[rust-cuda]: Expected #[cuda(ignore)] / #[cuda(embed)] / \
                    #[cuda(embed = \"<proxy-type>\")] field attribute"
                );

                Ok(())
            }) {
                emit_error!(
                    attr.span(),
                    "[rust-cuda]: Expected #[cuda(ignore)] / #[cuda(embed)] / \
                    #[cuda(embed = \"<proxy-type>\")] field attribute: {}",
                    err
                );
            }

            false
        } else {
            !r2c_ignore
        }
    });

    #[expect(clippy::option_if_let_else)]
    let cuda_repr_field_ty = if let Some(cuda_repr_field_ty) = cuda_repr_field_ty {
        cuda_repr_field_ty
    } else {
        field_ty = parse_quote! {
            #crate_path::utils::ffi::DeviceAccessible<
                #crate_path::utils::adapter::RustToCudaWithPortableBitCopySemantics<#field_ty>
            >
        };

        CudaReprFieldTy::SafeDeviceCopy
    };

    field.ty = field_ty;

    cuda_repr_field_ty
}