miniextendr_macros/
newtype_derive.rs1use proc_macro2::TokenStream;
34use quote::quote;
35use syn::{Data, DeriveInput, Fields, Type};
36
37struct Newtype {
39 inner: Type,
40 named_field: Option<syn::Ident>,
42}
43
44impl Newtype {
45 fn wrap(&self, val: &TokenStream) -> TokenStream {
47 match &self.named_field {
48 Some(field) => quote! { Self { #field: #val } },
49 None => quote! { Self(#val) },
50 }
51 }
52
53 fn unwrap(&self, binding: &TokenStream) -> TokenStream {
55 match &self.named_field {
56 Some(field) => quote! { #binding.#field },
57 None => quote! { #binding.0 },
58 }
59 }
60}
61
62fn parse_newtype(input: &DeriveInput) -> syn::Result<Newtype> {
63 let data = match &input.data {
64 Data::Struct(data) => data,
65 _ => {
66 return Err(syn::Error::new_spanned(
67 &input.ident,
68 "this derive only works on single-field newtype structs",
69 ));
70 }
71 };
72 match &data.fields {
73 Fields::Unnamed(fields) if fields.unnamed.len() == 1 => Ok(Newtype {
74 inner: fields.unnamed.first().unwrap().ty.clone(),
75 named_field: None,
76 }),
77 Fields::Named(fields) if fields.named.len() == 1 => {
78 let field = fields.named.first().unwrap();
79 Ok(Newtype {
80 inner: field.ty.clone(),
81 named_field: Some(field.ident.clone().unwrap()),
82 })
83 }
84 _ => Err(syn::Error::new_spanned(
85 &input.ident,
86 "this derive requires a struct with exactly one field",
87 )),
88 }
89}
90
91fn where_with(base: &Option<syn::WhereClause>, extra: TokenStream) -> TokenStream {
94 let mut preds: Vec<TokenStream> = base
95 .iter()
96 .flat_map(|w| w.predicates.iter().map(|p| quote! { #p }))
97 .collect();
98 preds.push(extra);
99 quote! { where #(#preds),* }
100}
101
102pub fn derive_try_from_sexp(input: DeriveInput) -> syn::Result<TokenStream> {
104 let nt = parse_newtype(&input)?;
105 let inner = &nt.inner;
106 let name = &input.ident;
107 let (impl_generics, ty_generics, _) = input.generics.split_for_impl();
108 let base_where = &input.generics.where_clause;
109
110 let wrap_val = nt.wrap("e! { val });
111 let wrap_inner = nt.wrap("e! { inner });
112 let from_where = where_with(
113 base_where,
114 quote! { #inner: ::miniextendr_api::TryFromSexp },
115 );
116
117 Ok(quote! {
118 #[automatically_derived]
119 impl #impl_generics ::miniextendr_api::TryFromSexp for #name #ty_generics #from_where {
120 type Error = <#inner as ::miniextendr_api::TryFromSexp>::Error;
121 #[inline]
122 fn try_from_sexp(sexp: ::miniextendr_api::SEXP) -> ::core::result::Result<Self, Self::Error> {
123 <#inner as ::miniextendr_api::TryFromSexp>::try_from_sexp(sexp).map(|val| #wrap_val)
124 }
125 #[inline]
126 unsafe fn try_from_sexp_unchecked(sexp: ::miniextendr_api::SEXP) -> ::core::result::Result<Self, Self::Error> {
127 unsafe { <#inner as ::miniextendr_api::TryFromSexp>::try_from_sexp_unchecked(sexp) }.map(|val| #wrap_val)
128 }
129 }
130
131 #[automatically_derived]
132 impl #impl_generics ::miniextendr_api::FromRNewtype for #name #ty_generics #base_where {
133 type Inner = #inner;
134 #[inline]
135 fn from_inner(inner: #inner) -> Self {
136 #wrap_inner
137 }
138 }
139 })
140}
141
142pub fn derive_into_r(input: DeriveInput) -> syn::Result<TokenStream> {
145 let nt = parse_newtype(&input)?;
146 let inner = &nt.inner;
147 let name = &input.ident;
148 let (impl_generics, ty_generics, _) = input.generics.split_for_impl();
149 let base_where = &input.generics.where_clause;
150
151 let unwrap_self = nt.unwrap("e! { self });
152 let unwrap_v = nt.unwrap("e! { v });
153 let into_where = where_with(base_where, quote! { #inner: ::miniextendr_api::IntoR });
154 let vec_elem_where = where_with(
157 base_where,
158 quote! { ::std::vec::Vec<#inner>: ::miniextendr_api::IntoR },
159 );
160
161 Ok(quote! {
162 #[automatically_derived]
163 impl #impl_generics ::miniextendr_api::IntoR for #name #ty_generics #into_where {
164 type Error = <#inner as ::miniextendr_api::IntoR>::Error;
165 #[inline]
166 fn try_into_sexp(self) -> ::core::result::Result<::miniextendr_api::SEXP, Self::Error> {
167 <#inner as ::miniextendr_api::IntoR>::try_into_sexp(#unwrap_self)
168 }
169 #[inline]
170 unsafe fn try_into_sexp_unchecked(self) -> ::core::result::Result<::miniextendr_api::SEXP, Self::Error> {
171 unsafe { <#inner as ::miniextendr_api::IntoR>::try_into_sexp_unchecked(#unwrap_self) }
172 }
173 #[inline]
174 fn into_sexp(self) -> ::miniextendr_api::SEXP {
175 <#inner as ::miniextendr_api::IntoR>::into_sexp(#unwrap_self)
176 }
177 #[inline]
178 unsafe fn into_sexp_unchecked(self) -> ::miniextendr_api::SEXP {
179 unsafe { <#inner as ::miniextendr_api::IntoR>::into_sexp_unchecked(#unwrap_self) }
180 }
181 }
182
183 #[automatically_derived]
184 impl #impl_generics ::miniextendr_api::IntoRNewtype for #name #ty_generics #base_where {
185 type Inner = #inner;
186 #[inline]
187 fn into_inner(self) -> #inner {
188 #unwrap_self
189 }
190 }
191
192 #[automatically_derived]
193 impl #impl_generics ::miniextendr_api::IntoRVecElement for #name #ty_generics #vec_elem_where {
194 #[inline]
195 fn elements_into_sexp(values: ::std::vec::Vec<Self>) -> ::miniextendr_api::SEXP {
196 <::std::vec::Vec<#inner> as ::miniextendr_api::IntoR>::into_sexp(
197 values.into_iter().map(|v| #unwrap_v).collect::<::std::vec::Vec<#inner>>()
198 )
199 }
200 }
201 })
202}