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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
use proc_macro2::TokenStream;
use quote::quote;
use crate::{input::Input, names};
pub fn derive(input: &Input) -> TokenStream {
let name = &input.name;
let visibility = &input.visibility;
let attrs = &input.attrs.ref_;
let mut_attrs = &input.attrs.ref_mut;
let vec_name = names::vec_name(&input.name);
let ref_name = names::ref_name(&input.name);
let ref_mut_name = names::ref_mut_name(&input.name);
let fields_types = &input
.fields
.iter()
.map(|field| &field.ty)
.collect::<Vec<_>>();
let doc_url = format!("[`{0}`](struct.{0}.html)", name);
let vec_doc_url = format!("[`{0}`](struct.{0}.html)", vec_name);
let ref_doc_url = format!("[`{0}`](struct.{0}.html)", ref_name);
let ref_mut_doc_url = format!("[`{0}`](struct.{0}.html)", ref_mut_name);
let fields_names = &input.field_idents();
let fields_names_hygienic = input.hygienic_idents("");
let ref_fields_types = input
.map_fields_nested_or(
|_, field_type, compact| {
// Immutable access to a compact field yields the owning
// `Compact<T>` value (Copy snapshot).
if compact.is_some() {
quote! { #field_type }
} else {
let field_ptr_type = names::ref_name(field_type);
quote! { #field_ptr_type<'a> }
}
},
|_, field_type| quote! { &'a #field_type },
)
.collect::<Vec<_>>();
let ref_mut_fields_types = input
.map_fields_nested_or(
|_, field_type, compact| {
names::nested_ref_mut_ty(field_type, compact)
},
|_, field_type| quote! { &'a mut #field_type },
)
.collect::<Vec<_>>();
let as_ref = input
.map_fields_nested_or(
|ident, _, compact| {
// Compact<T> is Copy: snapshot the value directly.
if compact.is_some() {
quote! { self.#ident }
} else {
quote! { self.#ident.as_ref() }
}
},
|ident, _| quote! { &self.#ident },
)
.collect::<Vec<_>>();
let as_mut = input
.map_fields_nested_or(
|ident, _, _| quote! { self.#ident.as_mut() },
|ident, _| quote! { &mut self.#ident },
)
.collect::<Vec<_>>();
let to_owned = input
.map_fields_nested_or(
|ident, _, compact| {
// Works for both Ref (field: Compact<T>) and RefMut
// (field: CompactRefMut<T>): read via `.get()`.
if compact.is_some() {
quote! { ::layout::Compact::new(self.#ident.get()) }
} else {
quote! { self.#ident.to_owned() }
}
},
|ident, _| quote! { self.#ident.clone() },
)
.collect::<Vec<_>>();
let ref_replace = input
.map_fields_nested_or(
|ident, _, _| quote! { self.#ident.replace(field) },
|ident, _| quote! { ::core::mem::replace(&mut *self.#ident, field) },
)
.collect::<Vec<_>>();
// When every field is compact, the immutable `Ref<'a>` would otherwise have
// an unused lifetime; add a hidden PhantomData marker to keep it
// structural.
let ref_marker_field: TokenStream = if input.ref_needs_lifetime_marker() {
quote! {
#[doc(hidden)]
__layout_ref_marker: ::core::marker::PhantomData<&'a ()>,
}
} else {
quote! {}
};
// `as_ref` builds the ref with a trailing-comma field list, so the init
// carries no leading comma.
let ref_marker_init = input.ref_marker_init(false);
quote! {
/// A reference to a
#[doc = #doc_url]
/// with struct of array layout.
#(#[#attrs])*
#[derive(Copy, Clone)]
#visibility struct #ref_name<'a> {
#(
/// reference to the `
#[doc = stringify!(#fields_names)]
///` field of a single
#[doc = #doc_url]
/// inside a
#[doc = #vec_doc_url]
pub #fields_names: #ref_fields_types,
)*
#ref_marker_field
}
/// A mutable reference to a
#[doc = #doc_url]
/// with struct of array layout.
#(#[#mut_attrs])*
#visibility struct #ref_mut_name<'a> {
#(
/// reference to the `
#[doc = stringify!(#fields_names)]
///` field of a single
#[doc = #doc_url]
/// inside a
#[doc = #vec_doc_url]
pub #fields_names: #ref_mut_fields_types,
)*
}
#[allow(dead_code)]
impl #name {
/// Create a
#[doc = #ref_doc_url]
/// from a borrowed
#[doc = #doc_url]
/// .
#visibility fn as_ref(&self) -> #ref_name {
#ref_name {
#( #fields_names: #as_ref, )*
#ref_marker_init
}
}
/// Create a
#[doc = #ref_mut_doc_url]
/// from a mutably borrowed
#[doc = #doc_url]
/// .
#visibility fn as_mut(&mut self) -> #ref_mut_name {
#ref_mut_name {
#( #fields_names: #as_mut, )*
}
}
}
impl<'a> #ref_name<'a> {
/// Convert a reference to
#[doc = #doc_url]
/// into an owned value. This is only available if all fields
/// implement `Clone`.
pub fn to_owned(&self) -> #name
// only expose to_owned if all fields are Clone
// https://github.com/rust-lang/rust/issues/48214#issuecomment-1150463333
where #( for<'b> #fields_types: Clone, )*
{
#name {
#( #fields_names: #to_owned, )*
}
}
}
impl<'a> From<#ref_name<'a>> for #name where #( for<'b> #fields_types: Clone, )* {
fn from(value: #ref_name<'a>) -> #name {
value.to_owned()
}
}
impl<'a> From<&'a #ref_name<'a>> for #name where #( for<'b> #fields_types: Clone, )* {
fn from(value: &'a #ref_name<'a>) -> #name {
value.to_owned()
}
}
impl<'a> #ref_mut_name<'a> {
/// Convert a mutable reference to
#[doc = #doc_url]
/// into an owned value. This is only available if all fields
/// implement `Clone`.
pub fn to_owned(&self) -> #name
// only expose to_owned if all fields are Clone
// https://github.com/rust-lang/rust/issues/48214#issuecomment-1150463333
where #( for<'b> #fields_types: Clone, )*
{
#name {
#( #fields_names: #to_owned, )*
}
}
/// Similar to [`core::mem::replace()`](https://doc.rust-lang.org/std/mem/fn.replace.html).
pub fn replace(&mut self, val: #name) -> #name {
// ManuallyDrop: fields are read out via ptr::read, so a mid-replace unwind can't double-free them.
let mut val = ::core::mem::ManuallyDrop::new(val);
#(
let field = unsafe { ::core::ptr::read(&val.#fields_names) };
let #fields_names_hygienic = #ref_replace;
)*
#name{#(#fields_names: #fields_names_hygienic),*}
}
}
impl<'a> From<#ref_mut_name<'a>> for #name where #( for<'b> #fields_types: Clone, )* {
fn from(value: #ref_mut_name<'a>) -> #name {
value.to_owned()
}
}
impl<'a> From<&'a #ref_mut_name<'a>> for #name where #( for<'b> #fields_types: Clone, )* {
fn from(value: &'a #ref_mut_name<'a>) -> #name {
value.to_owned()
}
}
}
}