Skip to content

Commit 53c1cfd

Browse files
authored
Add RefMut (#3)
1 parent 7a4e6d9 commit 53c1cfd

File tree

2 files changed

+83
-2
lines changed

2 files changed

+83
-2
lines changed

src/lib.rs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ struct StructOpts {
2121
/// List of attributes to apply to the generated Ref type.
2222
#[darling(default)]
2323
ref_attributes: Option<NestedMetaList>,
24+
/// List of attributes to apply to the generated MutRef type.
25+
#[darling(default)]
26+
ref_mut_attributes: Option<NestedMetaList>,
2427
/// Error type and expression to use for casting methods.
2528
#[darling(default)]
2629
cast_error: CastErrOpts,
@@ -197,6 +200,37 @@ pub fn superstruct(args: TokenStream, input: TokenStream) -> TokenStream {
197200
};
198201
output_items.push(ref_ty.into());
199202

203+
// Construct a top-level mutable reference type.
204+
// TODO: check that variants aren't called `RefMut`
205+
let ref_mut_ty_name = format_ident!("{}RefMut", type_name);
206+
let ref_mut_ty_lifetime = Lifetime::new("'__superstruct", Span::call_site());
207+
// Muahaha, this is dank.
208+
// Inject the generated lifetime into the top-level type's generics.
209+
let mut ref_mut_ty_decl_generics = decl_generics.clone();
210+
ref_mut_ty_decl_generics.params.insert(
211+
0,
212+
GenericParam::Lifetime(LifetimeDef::new(ref_mut_ty_lifetime.clone())),
213+
);
214+
let (ref_mut_impl_generics, ref_mut_ty_generics, _) = &ref_mut_ty_decl_generics.split_for_impl();
215+
216+
// Prepare the attributes for the ref type.
217+
let ref_mut_attributes = opts
218+
.ref_mut_attributes
219+
.as_ref()
220+
.map_or(&[][..], |attrs| &attrs.metas);
221+
222+
let ref_mut_ty = quote! {
223+
#(
224+
#[#ref_mut_attributes]
225+
)*
226+
#visibility enum #ref_mut_ty_name #ref_mut_ty_decl_generics #where_clause {
227+
#(
228+
#variant_names(&#ref_mut_ty_lifetime mut #struct_names #ty_generics),
229+
)*
230+
}
231+
};
232+
output_items.push(ref_mut_ty.into());
233+
200234
// Construct the main impl block.
201235
let getters = common_fields.iter().map(|(field, getter_opts)| {
202236
let field_name = field.ident.as_ref().expect("named fields only");
@@ -220,6 +254,7 @@ pub fn superstruct(args: TokenStream, input: TokenStream) -> TokenStream {
220254
&variant_names,
221255
field_name,
222256
&field.ty,
257+
None,
223258
getter_opts,
224259
)
225260
});
@@ -255,6 +290,14 @@ pub fn superstruct(args: TokenStream, input: TokenStream) -> TokenStream {
255290
)*
256291
}
257292
}
293+
pub fn to_mut<#ref_mut_ty_lifetime>(&#ref_mut_ty_lifetime mut self) -> #ref_mut_ty_name #ref_mut_ty_generics {
294+
match self {
295+
#(
296+
#type_name::#variant_names(ref mut inner)
297+
=> #ref_mut_ty_name::#variant_names(inner),
298+
)*
299+
}
300+
}
258301
#(
259302
#cast_methods
260303
)*
@@ -296,6 +339,30 @@ pub fn superstruct(args: TokenStream, input: TokenStream) -> TokenStream {
296339
};
297340
output_items.push(ref_impl_block.into());
298341

342+
// Construct the impl block for the *MutRef type.
343+
let ref_mut_getters = common_fields.iter()
344+
.filter(|(_, getter_opts)| !getter_opts.no_mut)
345+
.map(|(field, getter_opts)| {
346+
let field_name = field.ident.as_ref().expect("named fields only");
347+
make_mut_field_getter(
348+
&ref_mut_ty_name,
349+
&variant_names,
350+
field_name,
351+
&field.ty,
352+
Some(&ref_mut_ty_lifetime),
353+
getter_opts,
354+
)
355+
});
356+
357+
let ref_mut_impl_block = quote! {
358+
impl #ref_mut_impl_generics #ref_mut_ty_name #ref_mut_ty_generics #where_clause {
359+
#(
360+
#ref_mut_getters
361+
)*
362+
}
363+
};
364+
output_items.push(ref_mut_impl_block.into());
365+
299366
TokenStream::from_iter(output_items)
300367
}
301368

@@ -342,12 +409,23 @@ fn make_mut_field_getter(
342409
variant_names: &[Ident],
343410
field_name: &Ident,
344411
field_type: &Type,
412+
lifetime: Option<&Lifetime>,
345413
getter_opts: &GetterOpts,
346414
) -> proc_macro2::TokenStream {
347415
let fn_name = format_ident!("{}_mut", getter_opts.rename.as_ref().unwrap_or(field_name));
416+
let return_type= if let Some(lifetime) = lifetime {
417+
quote! { &#lifetime mut #field_type}
418+
} else {
419+
quote! { &mut #field_type}
420+
};
421+
let param= if let Some(lifetime) = lifetime {
422+
quote! { &#lifetime mut self}
423+
} else {
424+
quote! { &mut self}
425+
};
348426
let return_expr = quote! { &mut inner.#field_name };
349427
quote! {
350-
pub fn #fn_name(&mut self) -> &mut #field_type {
428+
pub fn #fn_name(#param) -> #return_type {
351429
match self {
352430
#(
353431
#type_name::#variant_names(ref mut inner) => {

tests/basic.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ fn wow() {
3030
description: "oooeee look at this",
3131
};
3232

33-
let block1 = Block::Base(base);
33+
let mut block1 = Block::Base(base);
3434
let block2 = Block::Ext(lmao);
3535

3636
println!("{:?}", block1);
@@ -39,4 +39,7 @@ fn wow() {
3939

4040
let block_ref = block1.to_ref();
4141
println!("{:?}", block_ref.slot());
42+
43+
let mut block_mut_ref = block1.to_mut();
44+
println!("{:?}", block_mut_ref.slot_mut());
4245
}

0 commit comments

Comments
 (0)