1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{
4 FnArg, GenericArgument, ImplItem, ImplItemFn, Item, ItemFn, ItemImpl, ItemStruct, LitStr, Pat,
5 PathArguments, ReturnType, Signature, Type, parse_macro_input,
6};
7
8#[derive(Default, Clone)]
9struct ProviderOptions {
10 name: Option<String>,
11 primary: bool,
12 scope: Option<String>,
13 eager: bool,
14 profile: Option<String>,
15 condition_key: Option<String>,
16 condition_value: Option<String>,
17 post_construct: Option<String>,
18 pre_destroy: Option<String>,
19}
20
21#[proc_macro_attribute]
27pub fn singleton(attribute: TokenStream, item: TokenStream) -> TokenStream {
28 let options = match parse_provider_options(attribute) {
29 Ok(options) => options,
30 Err(error) => return error.to_compile_error().into(),
31 };
32 let item = parse_macro_input!(item as Item);
33
34 let expanded = match item {
35 Item::Fn(function) => expand_function(function, &options),
36 Item::Impl(item_impl) => expand_impl(item_impl, &options),
37 other => Err(syn::Error::new_spanned(
38 other,
39 "#[singleton] can only be used on a function or an inherent impl block",
40 )),
41 };
42
43 match expanded {
44 Ok(tokens) => tokens.into(),
45 Err(error) => error.to_compile_error().into(),
46 }
47}
48
49fn parse_provider_options(attribute: TokenStream) -> syn::Result<ProviderOptions> {
50 parse_provider_options2(attribute.into())
51}
52
53fn parse_provider_options2(attribute: proc_macro2::TokenStream) -> syn::Result<ProviderOptions> {
54 use syn::parse::Parser;
55 let mut options = ProviderOptions::default();
56 let parser = syn::meta::parser(|meta| {
57 if meta.path.is_ident("primary") {
58 options.primary = true;
59 return Ok(());
60 }
61 if meta.path.is_ident("eager") {
62 options.eager = true;
63 return Ok(());
64 }
65 let value = meta.value()?.parse::<LitStr>()?.value();
66 if meta.path.is_ident("name") {
67 options.name = Some(value);
68 } else if meta.path.is_ident("scope") {
69 options.scope = Some(value);
70 } else if meta.path.is_ident("profile") {
71 options.profile = Some(value);
72 } else if meta.path.is_ident("condition") {
73 let (key, expected) = value
74 .split_once('=')
75 .map_or((value.as_str(), None), |(k, v)| (k, Some(v)));
76 options.condition_key = Some(key.to_owned());
77 options.condition_value = expected.map(str::to_owned);
78 } else if meta.path.is_ident("post_construct") {
79 options.post_construct = Some(value);
80 } else if meta.path.is_ident("pre_destroy") {
81 options.pre_destroy = Some(value);
82 } else {
83 return Err(meta.error("unknown provider option"));
84 }
85 Ok(())
86 });
87 parser.parse2(attribute)?;
88 if let Some(scope) = &options.scope {
89 if !matches!(scope.as_str(), "singleton" | "prototype" | "request") {
90 return Err(syn::Error::new(
91 proc_macro2::Span::call_site(),
92 "scope must be singleton, prototype, or request",
93 ));
94 }
95 }
96 Ok(options)
97}
98
99#[proc_macro_attribute]
100pub fn component(attribute: TokenStream, item: TokenStream) -> TokenStream {
101 singleton(attribute, item)
102}
103
104#[proc_macro_attribute]
105pub fn service(attribute: TokenStream, item: TokenStream) -> TokenStream {
106 singleton(attribute, item)
107}
108
109#[proc_macro_attribute]
110pub fn repository(attribute: TokenStream, item: TokenStream) -> TokenStream {
111 singleton(attribute, item)
112}
113
114#[proc_macro_attribute]
116pub fn qualifier(_attribute: TokenStream, item: TokenStream) -> TokenStream {
117 item
118}
119
120#[proc_macro_attribute]
122pub fn application(_attribute: TokenStream, item: TokenStream) -> TokenStream {
123 let mut function = parse_macro_input!(item as ItemFn);
124 if function.sig.asyncness.is_none() {
125 return syn::Error::new_spanned(&function.sig, "#[application] requires async fn")
126 .to_compile_error()
127 .into();
128 }
129 let body = function.block;
130 function
131 .attrs
132 .push(syn::parse_quote!(#[::auto_di::__private::tokio::main]));
133 function.block = Box::new(syn::parse_quote!({
134 let __container = ::auto_di::global_container()?;
135 __container.initialize_eager().await?;
136 let __result = (async move #body).await;
137 __container.shutdown().await?;
138 __result
139 }));
140 quote!(#function).into()
141}
142
143#[proc_macro_attribute]
145pub fn configuration_properties(attribute: TokenStream, item: TokenStream) -> TokenStream {
146 let prefix = parse_macro_input!(attribute as LitStr).value();
147 let structure = parse_macro_input!(item as ItemStruct);
148 match expand_configuration_properties(prefix, structure) {
149 Ok(tokens) => tokens.into(),
150 Err(error) => error.to_compile_error().into(),
151 }
152}
153
154fn expand_configuration_properties(
155 prefix: String,
156 structure: ItemStruct,
157) -> syn::Result<proc_macro2::TokenStream> {
158 let ident = &structure.ident;
159 let fields = match &structure.fields {
160 syn::Fields::Named(fields) => &fields.named,
161 _ => {
162 return Err(syn::Error::new_spanned(
163 &structure,
164 "configuration properties require named fields",
165 ));
166 }
167 };
168 let bindings = fields.iter().map(|field| {
169 let name = field.ident.as_ref().expect("named field");
170 let ty = &field.ty;
171 let key = format!("{}_{}", prefix, name).replace(['.', '-'], "_").to_uppercase();
172 quote! {
173 #name: ::std::env::var(#key)
174 .map_err(|error| ::auto_di::DiError::Configuration { key: #key.into(), message: error.to_string() })?
175 .parse::<#ty>()
176 .map_err(|_| ::auto_di::DiError::Configuration { key: #key.into(), message: "value could not be parsed".into() })?
177 }
178 });
179 let provider_name = format_ident!("configuration_properties_{ident}");
180 let invocation = quote! { <#ident as ::auto_di::ConfigurationProperties>::from_environment()? };
181 let registration = registration(
182 &provider_name,
183 &syn::parse_quote!(#ident),
184 &[],
185 &[],
186 &[],
187 invocation,
188 );
189 Ok(quote! {
190 #structure
191 impl ::auto_di::ConfigurationProperties for #ident {
192 fn from_environment() -> ::std::result::Result<Self, ::auto_di::DiError> {
193 Ok(Self { #(#bindings),* })
194 }
195 }
196 #registration
197 })
198}
199
200#[proc_macro_attribute]
203pub fn configuration(_attribute: TokenStream, item: TokenStream) -> TokenStream {
204 let item_impl = parse_macro_input!(item as ItemImpl);
205 match expand_configuration(item_impl) {
206 Ok(tokens) => tokens.into(),
207 Err(error) => error.to_compile_error().into(),
208 }
209}
210
211#[proc_macro_attribute]
213pub fn bean(_attribute: TokenStream, item: TokenStream) -> TokenStream {
214 item
215}
216
217fn expand_function(
218 mut function: ItemFn,
219 options: &ProviderOptions,
220) -> syn::Result<proc_macro2::TokenStream> {
221 let function_name = function.sig.ident.clone();
222 let output_type = match &function.sig.output {
223 ReturnType::Type(_, ty) => ty.as_ref().clone(),
224 ReturnType::Default => {
225 return Err(syn::Error::new_spanned(
226 &function.sig,
227 "a #[singleton] provider must return a value",
228 ));
229 }
230 };
231 let (argument_names, dependency_types, qualifiers) = dependencies(&mut function.sig)?;
232 let invocation = if function.sig.asyncness.is_some() {
233 quote! { #function_name(#(#argument_names),*).await }
234 } else {
235 quote! { #function_name(#(#argument_names),*) }
236 };
237
238 let registration = registration_with_options(
239 &function_name,
240 &output_type,
241 &argument_names,
242 &dependency_types,
243 &qualifiers,
244 invocation,
245 options,
246 );
247
248 Ok(quote! {
249 #function
250 #registration
251 })
252}
253
254fn expand_impl(
255 mut item_impl: ItemImpl,
256 options: &ProviderOptions,
257) -> syn::Result<proc_macro2::TokenStream> {
258 if item_impl.trait_.is_some() {
259 return Err(syn::Error::new_spanned(
260 &item_impl,
261 "#[singleton] requires an inherent impl, not a trait impl",
262 ));
263 }
264 if !item_impl.generics.params.is_empty() {
265 return Err(syn::Error::new_spanned(
266 &item_impl.generics,
267 "generic singleton impl blocks are not supported yet",
268 ));
269 }
270
271 let constructor_index = item_impl
272 .items
273 .iter()
274 .position(|item| match item {
275 ImplItem::Fn(function) => function.sig.ident == "new",
276 _ => false,
277 })
278 .ok_or_else(|| {
279 syn::Error::new_spanned(
280 &item_impl.self_ty,
281 "a #[singleton] impl must contain a new(...) -> Self constructor",
282 )
283 })?;
284 let output_type = item_impl.self_ty.as_ref().clone();
285 let constructor = match &mut item_impl.items[constructor_index] {
286 ImplItem::Fn(function) => function,
287 _ => unreachable!(),
288 };
289 validate_impl_constructor(constructor)?;
290
291 let (argument_names, dependency_types, qualifiers) = dependencies(&mut constructor.sig)?;
292 let type_ident = singleton_type_ident(&output_type)?;
293 let constructor_name = constructor.sig.ident.clone();
294 let invocation = if constructor.sig.asyncness.is_some() {
295 quote! { <#output_type>::#constructor_name(#(#argument_names),*).await }
296 } else {
297 quote! { <#output_type>::#constructor_name(#(#argument_names),*) }
298 };
299 let registration = registration_with_options(
300 type_ident,
301 &output_type,
302 &argument_names,
303 &dependency_types,
304 &qualifiers,
305 invocation,
306 options,
307 );
308
309 Ok(quote! {
310 #item_impl
311 #registration
312 })
313}
314
315fn expand_configuration(mut item_impl: ItemImpl) -> syn::Result<proc_macro2::TokenStream> {
316 if item_impl.trait_.is_some() || !item_impl.generics.params.is_empty() {
317 return Err(syn::Error::new_spanned(
318 &item_impl,
319 "#[configuration] requires a non-generic inherent impl",
320 ));
321 }
322
323 let configuration_type = item_impl.self_ty.as_ref().clone();
324 let configuration_ident = singleton_type_ident(&configuration_type)?.clone();
325 let configuration_registration = registration(
326 &format_ident!("configuration_{configuration_ident}"),
327 &configuration_type,
328 &[],
329 &[],
330 &[],
331 quote! { <#configuration_type as ::std::default::Default>::default() },
332 );
333 let mut registrations = Vec::new();
334
335 for item in &mut item_impl.items {
336 let ImplItem::Fn(method) = item else { continue };
337 let Some(bean_attribute) = method
338 .attrs
339 .iter()
340 .find(|attr| attr.path().is_ident("bean"))
341 else {
342 continue;
343 };
344 let bean_options = match &bean_attribute.meta {
345 syn::Meta::Path(_) => ProviderOptions::default(),
346 syn::Meta::List(list) => parse_provider_options2(list.tokens.clone())?,
347 syn::Meta::NameValue(_) => {
348 return Err(syn::Error::new_spanned(bean_attribute, "use #[bean(...)]"));
349 }
350 };
351 method.attrs.retain(|attr| !attr.path().is_ident("bean"));
352
353 let output_type = match &method.sig.output {
354 ReturnType::Type(_, ty) => ty.as_ref().clone(),
355 ReturnType::Default => {
356 return Err(syn::Error::new_spanned(
357 &method.sig,
358 "a #[bean] method must return a value",
359 ));
360 }
361 };
362 let (has_receiver, argument_names, dependency_types, qualifiers) =
363 bean_dependencies(&mut method.sig)?;
364 let method_name = method.sig.ident.clone();
365 let registration_name = format_ident!("bean_{configuration_ident}_{method_name}");
366 let invocation = if has_receiver {
367 quote! { configuration.#method_name(#(#argument_names),*) }
368 } else {
369 quote! { <#configuration_type>::#method_name(#(#argument_names),*) }
370 };
371 let invocation = if method.sig.asyncness.is_some() {
372 quote! { #invocation.await }
373 } else {
374 invocation
375 };
376 let prelude = has_receiver.then(|| {
377 quote! {
378 let configuration: ::std::sync::Arc<#configuration_type> =
379 container.resolve_dependency::<#configuration_type>(&context).await?;
380 }
381 });
382
383 registrations.push(registration_with_prelude(
384 ®istration_name,
385 &output_type,
386 &argument_names,
387 &dependency_types,
388 &qualifiers,
389 prelude.unwrap_or_default(),
390 invocation,
391 &bean_options,
392 ));
393 }
394
395 if registrations.is_empty() {
396 return Err(syn::Error::new_spanned(
397 &item_impl,
398 "#[configuration] must contain at least one #[bean] method",
399 ));
400 }
401
402 Ok(quote! {
403 #item_impl
404 #configuration_registration
405 #(#registrations)*
406 })
407}
408
409fn validate_impl_constructor(constructor: &ImplItemFn) -> syn::Result<()> {
410 if constructor.sig.receiver().is_some() {
411 return Err(syn::Error::new_spanned(
412 &constructor.sig,
413 "the singleton new constructor must be an associated function",
414 ));
415 }
416 match &constructor.sig.output {
417 ReturnType::Type(_, ty) if matches!(ty.as_ref(), Type::Path(path) if path.path.is_ident("Self")) => {
418 Ok(())
419 }
420 _ => Err(syn::Error::new_spanned(
421 &constructor.sig.output,
422 "the singleton new constructor must return Self",
423 )),
424 }
425}
426
427fn dependencies(
428 signature: &mut Signature,
429) -> syn::Result<(Vec<syn::Ident>, Vec<Type>, Vec<Option<String>>)> {
430 let mut argument_names = Vec::new();
431 let mut dependency_types = Vec::new();
432 let mut qualifiers = Vec::new();
433
434 for input in &mut signature.inputs {
435 let FnArg::Typed(argument) = input else {
436 return Err(syn::Error::new_spanned(
437 input,
438 "singleton constructors cannot have a self receiver",
439 ));
440 };
441 let Pat::Ident(pattern) = argument.pat.as_ref() else {
442 return Err(syn::Error::new_spanned(
443 &argument.pat,
444 "use a simple parameter name for an injected dependency",
445 ));
446 };
447 argument_names.push(pattern.ident.clone());
448 validate_dependency_type(argument.ty.as_ref())?;
449 dependency_types.push(argument.ty.as_ref().clone());
450 qualifiers.push(take_qualifier(&mut argument.attrs)?);
451 }
452 Ok((argument_names, dependency_types, qualifiers))
453}
454
455fn bean_dependencies(
456 signature: &mut Signature,
457) -> syn::Result<(bool, Vec<syn::Ident>, Vec<Type>, Vec<Option<String>>)> {
458 let mut has_receiver = false;
459 let mut names = Vec::new();
460 let mut types = Vec::new();
461 let mut qualifiers = Vec::new();
462 for input in &mut signature.inputs {
463 match input {
464 FnArg::Receiver(receiver) => {
465 if has_receiver || receiver.reference.is_none() || receiver.mutability.is_some() {
466 return Err(syn::Error::new_spanned(
467 receiver,
468 "a #[bean] method only supports an immutable &self receiver",
469 ));
470 }
471 has_receiver = true;
472 }
473 FnArg::Typed(argument) => {
474 let Pat::Ident(pattern) = argument.pat.as_ref() else {
475 return Err(syn::Error::new_spanned(
476 &argument.pat,
477 "use a simple parameter name for an injected dependency",
478 ));
479 };
480 names.push(pattern.ident.clone());
481 validate_dependency_type(argument.ty.as_ref())?;
482 types.push(argument.ty.as_ref().clone());
483 qualifiers.push(take_qualifier(&mut argument.attrs)?);
484 }
485 }
486 }
487 Ok((has_receiver, names, types, qualifiers))
488}
489
490fn take_qualifier(attributes: &mut Vec<syn::Attribute>) -> syn::Result<Option<String>> {
491 let mut value = None;
492 for attribute in attributes
493 .iter()
494 .filter(|attr| attr.path().is_ident("qualifier"))
495 {
496 if value.is_some() {
497 return Err(syn::Error::new_spanned(
498 attribute,
499 "only one qualifier is allowed",
500 ));
501 }
502 value = Some(attribute.parse_args::<LitStr>()?.value());
503 }
504 attributes.retain(|attr| !attr.path().is_ident("qualifier"));
505 Ok(value)
506}
507
508fn registration(
509 name: &syn::Ident,
510 output_type: &Type,
511 argument_names: &[syn::Ident],
512 dependency_types: &[Type],
513 qualifiers: &[Option<String>],
514 invocation: proc_macro2::TokenStream,
515) -> proc_macro2::TokenStream {
516 registration_with_options(
517 name,
518 output_type,
519 argument_names,
520 dependency_types,
521 qualifiers,
522 invocation,
523 &ProviderOptions::default(),
524 )
525}
526
527fn registration_with_options(
528 name: &syn::Ident,
529 output_type: &Type,
530 argument_names: &[syn::Ident],
531 dependency_types: &[Type],
532 qualifiers: &[Option<String>],
533 invocation: proc_macro2::TokenStream,
534 options: &ProviderOptions,
535) -> proc_macro2::TokenStream {
536 registration_with_prelude(
537 name,
538 output_type,
539 argument_names,
540 dependency_types,
541 qualifiers,
542 quote! {},
543 invocation,
544 options,
545 )
546}
547
548fn registration_with_prelude(
549 name: &syn::Ident,
550 output_type: &Type,
551 argument_names: &[syn::Ident],
552 dependency_types: &[Type],
553 qualifiers: &[Option<String>],
554 prelude: proc_macro2::TokenStream,
555 invocation: proc_macro2::TokenStream,
556 options: &ProviderOptions,
557) -> proc_macro2::TokenStream {
558 let factory_name = format_ident!("__di_factory_{name}");
559 let type_id_name = format_ident!("__di_type_id_{name}");
560 let type_name_name = format_ident!("__di_type_name_{name}");
561 let destroy_name = format_ident!("__di_destroy_{name}");
562 let option_tokens = |value: Option<&str>| match value {
563 Some(value) => quote!(Some(#value)),
564 None => quote!(None),
565 };
566 let bean_name = option_tokens(options.name.as_deref());
567 let primary = options.primary;
568 let eager = options.eager;
569 let profile = option_tokens(options.profile.as_deref());
570 let condition_key = option_tokens(options.condition_key.as_deref());
571 let condition_value = option_tokens(options.condition_value.as_deref());
572 let scope = match options.scope.as_deref().unwrap_or("singleton") {
573 "prototype" => quote!(::auto_di::Scope::Prototype),
574 "request" => quote!(::auto_di::Scope::Request),
575 _ => quote!(::auto_di::Scope::Singleton),
576 };
577 let post_construct = options.post_construct.as_ref().map(|method| {
578 let method = format_ident!("{method}");
579 quote! { value.#method().await; }
580 });
581 let (destroy_function, destroy_value) = if let Some(method) = &options.pre_destroy {
582 let method = format_ident!("{method}");
583 (
584 quote! {
585 #[doc(hidden)]
586 fn #destroy_name(value: ::auto_di::DynArc) -> ::auto_di::BoxFuture<'static, ::std::result::Result<(), ::auto_di::DiError>> {
587 ::std::boxed::Box::pin(async move {
588 let value = value.downcast::<#output_type>()
589 .map_err(|_| ::auto_di::DiError::TypeMismatch(::std::any::type_name::<#output_type>()))?;
590 value.#method().await;
591 Ok(())
592 })
593 }
594 },
595 quote!(Some(#destroy_name as _)),
596 )
597 } else {
598 (quote! {}, quote!(None))
599 };
600 let resolve_dependencies = argument_names
601 .iter()
602 .zip(dependency_types.iter())
603 .zip(qualifiers.iter())
604 .map(|((name, ty), qualifier)| dependency_resolution(name, ty, qualifier.as_deref()));
605
606 quote! {
607 #[doc(hidden)]
608 fn #type_id_name() -> ::std::any::TypeId {
609 ::std::any::TypeId::of::<#output_type>()
610 }
611
612 #[doc(hidden)]
613 fn #type_name_name() -> &'static str {
614 ::std::any::type_name::<#output_type>()
615 }
616
617 #[doc(hidden)]
618 fn #factory_name<'a>(
619 container: &'a ::auto_di::Container,
620 context: ::auto_di::ResolutionContext,
621 ) -> ::auto_di::BoxFuture<'a, ::std::result::Result<::auto_di::DynArc, ::auto_di::DiError>> {
622 ::std::boxed::Box::pin(async move {
623 #prelude
624 #(#resolve_dependencies)*
625 let value: #output_type = #invocation;
626 #post_construct
627 Ok(::std::sync::Arc::new(value) as ::auto_di::DynArc)
628 })
629 }
630
631 #destroy_function
632
633 ::auto_di::__private::inventory::submit! {
634 ::auto_di::ProviderDescriptor::configured(
635 #type_id_name,
636 #type_name_name,
637 #factory_name,
638 #bean_name,
639 #primary,
640 #scope,
641 #eager,
642 #profile,
643 #condition_key,
644 #condition_value,
645 #destroy_value,
646 )
647 }
648 }
649}
650
651fn singleton_type_ident(ty: &Type) -> syn::Result<&syn::Ident> {
652 let Type::Path(path) = ty else {
653 return Err(syn::Error::new_spanned(
654 ty,
655 "singleton impl type must be a named type",
656 ));
657 };
658 path.path
659 .segments
660 .last()
661 .map(|segment| &segment.ident)
662 .ok_or_else(|| syn::Error::new_spanned(ty, "singleton impl type must be a named type"))
663}
664
665fn validate_dependency_type(ty: &Type) -> syn::Result<()> {
666 if generic_inner(ty, "Arc").is_some()
667 || generic_inner(ty, "Provider").is_some()
668 || generic_inner(ty, "Lazy").is_some()
669 {
670 return Ok(());
671 }
672 if let Some(inner) = generic_inner(ty, "Option").or_else(|| generic_inner(ty, "Vec")) {
673 if generic_inner(inner, "Arc").is_some() {
674 return Ok(());
675 }
676 }
677 Err(syn::Error::new_spanned(
678 ty,
679 "dependency must be Arc<T>, Option<Arc<T>>, Vec<Arc<T>>, Provider<T>, or Lazy<T>",
680 ))
681}
682
683fn generic_inner<'a>(ty: &'a Type, expected: &str) -> Option<&'a Type> {
684 let Type::Path(path) = ty else { return None };
685 let segment = path.path.segments.last()?;
686 if segment.ident != expected {
687 return None;
688 }
689 let PathArguments::AngleBracketed(arguments) = &segment.arguments else {
690 return None;
691 };
692 match arguments.args.first() {
693 Some(GenericArgument::Type(inner)) if arguments.args.len() == 1 => Some(inner),
694 _ => None,
695 }
696}
697
698fn dependency_resolution(
699 name: &syn::Ident,
700 ty: &Type,
701 qualifier: Option<&str>,
702) -> proc_macro2::TokenStream {
703 if let Some(inner) = generic_inner(ty, "Arc") {
704 if matches!(inner, Type::TraitObject(_)) {
705 if let Some(qualifier) = qualifier {
706 return quote! {
707 let #name: #ty = (*container.resolve_named_dependency::<#ty>(#qualifier, &context).await?).clone();
708 };
709 }
710 return quote! {
711 let #name: #ty = (*container.resolve_dependency::<#ty>(&context).await?).clone();
712 };
713 }
714 if let Some(qualifier) = qualifier {
715 return quote! {
716 let #name: #ty = container.resolve_named_dependency::<#inner>(#qualifier, &context).await?;
717 };
718 }
719 return quote! {
720 let #name: #ty = container.resolve_dependency::<#inner>(&context).await?;
721 };
722 }
723 if let Some(wrapped) = generic_inner(ty, "Option") {
724 let inner = generic_inner(wrapped, "Arc").expect("validated Option<Arc<T>>");
725 return quote! {
726 let #name: #ty = container.resolve_optional_dependency::<#inner>(&context).await?;
727 };
728 }
729 if let Some(wrapped) = generic_inner(ty, "Vec") {
730 let inner = generic_inner(wrapped, "Arc").expect("validated Vec<Arc<T>>");
731 return quote! {
732 let #name: #ty = container.resolve_all_dependency::<#inner>(&context).await?;
733 };
734 }
735 quote! { let #name: #ty = ::std::default::Default::default(); }
736}