ouroboros_macro/
lib.rs
1extern crate proc_macro;
2
3mod covariance_detection;
4mod generate;
5mod info_structures;
6mod parse;
7mod utils;
8
9use crate::{
10 generate::{
11 constructor::create_builder_and_constructor, derives::create_derives,
12 into_heads::make_into_heads, struc::create_internal_struct_def,
13 summon_checker::generate_checker_summoner,
14 try_constructor::create_try_builder_and_constructor, type_asserts::make_type_asserts,
15 with::make_with_all_function, with_each::make_with_functions,
16 },
17 info_structures::Options,
18 parse::parse_struct,
19};
20use generate::{
21 drop::create_drop_impl, struc::create_actual_struct_def, with_mut::make_with_all_mut_function,
22};
23use heck::ToSnakeCase;
24use info_structures::BuilderType;
25use proc_macro::TokenStream;
26use proc_macro2::TokenStream as TokenStream2;
27use proc_macro2::TokenTree;
28use quote::{format_ident, quote};
29use syn::{Error, ItemStruct};
30
31fn self_referencing_impl(
32 original_struct_def: &ItemStruct,
33 options: Options,
34) -> Result<TokenStream, Error> {
35 let struct_name = &original_struct_def.ident;
36 let mod_name = format_ident!("ouroboros_impl_{}", struct_name.to_string().to_snake_case());
37 let visibility = &original_struct_def.vis;
38
39 let info = parse_struct(original_struct_def)?;
40
41 let actual_struct_def = create_actual_struct_def(&info)?;
42 let internal_struct_def = create_internal_struct_def(&info)?;
43 let drop_impl = create_drop_impl(&info)?;
44
45 let borrowchk_summoner = generate_checker_summoner(&info)?;
46
47 let (builder_struct_name, builder_def, constructor_def) =
48 create_builder_and_constructor(&info, options, BuilderType::Sync)?;
49 let (async_builder_struct_name, async_builder_def, async_constructor_def) =
50 create_builder_and_constructor(&info, options, BuilderType::Async)?;
51 let (async_send_builder_struct_name, async_send_builder_def, async_send_constructor_def) =
52 create_builder_and_constructor(&info, options, BuilderType::AsyncSend)?;
53 let (try_builder_struct_name, try_builder_def, try_constructor_def) =
54 create_try_builder_and_constructor(&info, options, BuilderType::Sync)?;
55 let (async_try_builder_struct_name, async_try_builder_def, async_try_constructor_def) =
56 create_try_builder_and_constructor(&info, options, BuilderType::Async)?;
57 let (
58 async_send_try_builder_struct_name,
59 async_send_try_builder_def,
60 async_send_try_constructor_def,
61 ) = create_try_builder_and_constructor(&info, options, BuilderType::AsyncSend)?;
62
63 let (with_defs, with_errors) = make_with_functions(&info, options);
64 let with_errors = with_errors
65 .into_iter()
66 .map(|err| err.emit_as_item_tokens())
67 .collect::<Vec<_>>();
68 let (with_all_struct_def, with_all_fn_def) = make_with_all_function(&info, options)?;
69 let (with_all_mut_struct_def, with_all_mut_fn_def) =
70 make_with_all_mut_function(&info, options)?;
71 let (heads_struct_def, into_heads_fn) = make_into_heads(&info, options);
72
73 let impls = create_derives(&info)?;
74
75 let type_asserts_def = make_type_asserts(&info);
78
79 let extra_visibility = if options.do_pub_extras {
80 visibility.clone()
81 } else {
82 syn::Visibility::Inherited
83 };
84
85 let generic_params = info.generic_params();
86 let generic_args = info.generic_arguments();
87 let generic_where = &info.generics.where_clause;
88 Ok(TokenStream::from(quote! {
89 #[doc="Encapsulates implementation details for a self-referencing struct. This module is only visible when using --document-private-items."]
90 mod #mod_name {
91 use super::*;
92 #[doc="The self-referencing struct."]
93 #actual_struct_def
94 #internal_struct_def
95 #drop_impl
96 #borrowchk_summoner
97 #builder_def
98 #async_builder_def
99 #async_send_builder_def
100 #try_builder_def
101 #async_try_builder_def
102 #async_send_try_builder_def
103 #with_all_struct_def
104 #with_all_mut_struct_def
105 #(#with_errors)*
106 #heads_struct_def
107 #impls
108 impl <#generic_params> #struct_name <#(#generic_args),*> #generic_where {
109 #constructor_def
110 #async_constructor_def
111 #async_send_constructor_def
112 #try_constructor_def
113 #async_try_constructor_def
114 #async_send_try_constructor_def
115 #(#with_defs)*
116 #with_all_fn_def
117 #with_all_mut_fn_def
118 #into_heads_fn
119 }
120 #type_asserts_def
121 }
122 #visibility use #mod_name :: #struct_name;
123 #extra_visibility use #mod_name :: #builder_struct_name;
124 #extra_visibility use #mod_name :: #async_builder_struct_name;
125 #extra_visibility use #mod_name :: #async_send_builder_struct_name;
126 #extra_visibility use #mod_name :: #try_builder_struct_name;
127 #extra_visibility use #mod_name :: #async_try_builder_struct_name;
128 #extra_visibility use #mod_name :: #async_send_try_builder_struct_name;
129 }))
130}
131
132#[proc_macro_attribute]
133pub fn self_referencing(attr: TokenStream, item: TokenStream) -> TokenStream {
134 let mut options = Options {
135 do_no_doc: false,
136 do_pub_extras: false,
137 };
138 let mut expecting_comma = false;
139 for token in <TokenStream as std::convert::Into<TokenStream2>>::into(attr).into_iter() {
140 if let TokenTree::Ident(ident) = &token {
141 if expecting_comma {
142 return Error::new(token.span(), "Unexpected identifier, expected comma.")
143 .to_compile_error()
144 .into();
145 }
146 match &ident.to_string()[..] {
147 "no_doc" => options.do_no_doc = true,
148 "pub_extras" => options.do_pub_extras = true,
149 _ => {
150 return Error::new_spanned(
151 ident,
152 "Unknown identifier, expected 'no_doc' or 'pub_extras'.",
153 )
154 .to_compile_error()
155 .into()
156 }
157 }
158 expecting_comma = true;
159 } else if let TokenTree::Punct(punct) = &token {
160 if !expecting_comma {
161 return Error::new(token.span(), "Unexpected punctuation, expected identifier.")
162 .to_compile_error()
163 .into();
164 }
165 if punct.as_char() != ',' {
166 return Error::new(token.span(), "Unknown punctuation, expected comma.")
167 .to_compile_error()
168 .into();
169 }
170 expecting_comma = false;
171 } else {
172 return Error::new(token.span(), "Unknown syntax, expected identifier.")
173 .to_compile_error()
174 .into();
175 }
176 }
177 let original_struct_def: ItemStruct = syn::parse_macro_input!(item);
178 match self_referencing_impl(&original_struct_def, options) {
179 Ok(content) => content,
180 Err(err) => err.to_compile_error().into(),
181 }
182}