ouroboros_macro/generate/
with_each.rs
1use crate::info_structures::{FieldType, Options, StructInfo};
2use proc_macro2::TokenStream;
3use proc_macro2_diagnostics::Diagnostic;
4use quote::{format_ident, quote};
5use syn::Error;
6
7pub enum ProcessingError {
8 Syntax(Error),
9 Covariance(Vec<Diagnostic>),
10}
11
12pub fn make_with_functions(info: &StructInfo, options: Options) -> (Vec<TokenStream>, Vec<Diagnostic>) {
13 let mut users = Vec::new();
14 let mut errors = Vec::new();
15 for field in &info.fields {
16 let visibility = &field.vis;
17 let field_name = &field.name;
18 let field_type = &field.typ;
19 if field.field_type == FieldType::Tail {
22 let user_name = format_ident!("with_{}", &field.name);
23 let documentation = format!(
24 concat!(
25 "Provides an immutable reference to `{0}`. This method was generated because ",
26 "`{0}` is a [tail field](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions)."
27 ),
28 field.name.to_string()
29 );
30 let documentation = if !options.do_no_doc {
31 quote! {
32 #[doc=#documentation]
33 }
34 } else {
35 quote! { #[doc(hidden)] }
36 };
37 users.push(quote! {
38 #documentation
39 #[inline(always)]
40 #visibility fn #user_name <'outer_borrow, ReturnType>(
41 &'outer_borrow self,
42 user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow #field_type) -> ReturnType,
43 ) -> ReturnType {
44 let field = &unsafe { self.actual_data.assume_init_ref() }.#field_name;
45 user(field)
46 }
47 });
48 if field.covariant == Some(true) {
49 let borrower_name = format_ident!("borrow_{}", &field.name);
50 users.push(quote! {
51 #documentation
52 #[inline(always)]
53 #visibility fn #borrower_name<'this>(
54 &'this self,
55 ) -> &'this #field_type {
56 &unsafe { self.actual_data.assume_init_ref() }.#field_name
57 }
58 });
59 } else if field.covariant.is_none() {
60 errors.push(field.covariance_error());
61 }
62 let user_name = format_ident!("with_{}_mut", &field.name);
64 let documentation = format!(
65 concat!(
66 "Provides a mutable reference to `{0}`. This method was generated because ",
67 "`{0}` is a [tail field](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions). ",
68 "No `borrow_{0}_mut` function was generated because Rust's borrow checker is ",
69 "currently unable to guarantee that such a method would be used safely."
70 ),
71 field.name.to_string()
72 );
73 let documentation = if !options.do_no_doc {
74 quote! {
75 #[doc=#documentation]
76 }
77 } else {
78 quote! { #[doc(hidden)] }
79 };
80 users.push(quote! {
81 #documentation
82 #[inline(always)]
83 #visibility fn #user_name <'outer_borrow, ReturnType>(
84 &'outer_borrow mut self,
85 user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow mut #field_type) -> ReturnType,
86 ) -> ReturnType {
87 let field = &mut unsafe { self.actual_data.assume_init_mut() }.#field_name;
88 user(field)
89 }
90 });
91 } else if field.field_type == FieldType::Borrowed {
92 let user_name = format_ident!("with_{}", &field.name);
93 let documentation = format!(
94 concat!(
95 "Provides limited immutable access to `{0}`. This method was generated ",
96 "because the contents of `{0}` are immutably borrowed by other fields."
97 ),
98 field.name.to_string()
99 );
100 let documentation = if !options.do_no_doc {
101 quote! {
102 #[doc=#documentation]
103 }
104 } else {
105 quote! { #[doc(hidden)] }
106 };
107 users.push(quote! {
108 #documentation
109 #[inline(always)]
110 #visibility fn #user_name <'outer_borrow, ReturnType>(
111 &'outer_borrow self,
112 user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow #field_type) -> ReturnType,
113 ) -> ReturnType {
114 let field = &unsafe { self.actual_data.assume_init_ref() }.#field_name;
115 user(field)
116 }
117 });
118 if field.self_referencing {
119 if field.covariant == Some(false) {
120 continue;
122 } else if field.covariant.is_none() {
123 errors.push(field.covariance_error());
124 }
125 }
126 let borrower_name = format_ident!("borrow_{}", &field.name);
127 users.push(quote! {
128 #documentation
129 #[inline(always)]
130 #visibility fn #borrower_name<'this>(
131 &'this self,
132 ) -> &'this #field_type {
133 &unsafe { self.actual_data.assume_init_ref() }.#field_name
134 }
135 });
136 } else if field.field_type == FieldType::BorrowedMut {
137 }
140 }
141 (users, errors)
142}