1use convert_case::{Case, Casing};
2use proc_macro::TokenStream;
3use quote::{ToTokens, format_ident, quote};
4use syn::{
5 AngleBracketedGenericArguments, FnArg, GenericParam, Generics, Ident, ImplGenerics, ImplItem,
6 ImplItemFn, ItemImpl, LitStr, Pat, PatType, PathArguments, Token, Type, TypeGenerics,
7 Visibility, WhereClause, WherePredicate,
8 parse::{Parse, ParseStream},
9 parse_macro_input, parse_quote,
10 punctuated::Punctuated,
11 token::Comma,
12};
13
14struct UniPipeArgs {
15 name: Option<String>,
16 visibility: Option<Visibility>,
17 extensions: Vec<Ident>,
18}
19
20impl Parse for UniPipeArgs {
21 fn parse(input: ParseStream) -> syn::Result<Self> {
22 let name = if input.peek(LitStr) {
23 let string_literal: LitStr = input.parse()?;
24
25 if input.peek(Token![,]) {
26 input.parse::<Token![,]>()?;
27 }
28
29 Some(string_literal.value())
30 } else {
31 None
32 };
33
34 let visibility = if input.peek(Token![pub]) || input.peek(Token![crate]) {
35 Some(input.parse()?)
36 } else {
37 None
38 };
39
40 let extensions = Punctuated::<Ident, Token![,]>::parse_terminated(input)?
41 .into_iter()
42 .collect();
43
44 Ok(UniPipeArgs {
45 name,
46 visibility,
47 extensions,
48 })
49 }
50}
51
52#[proc_macro_attribute]
53pub fn unipipe(attr: TokenStream, item: TokenStream) -> TokenStream {
54 let args = parse_macro_input!(attr as UniPipeArgs);
55 let input = parse_macro_input!(item as ItemImpl);
56
57 let (struct_name, impl_struct_generics, impl_struct_impl_generics) =
58 match &input.self_ty.as_ref() {
59 Type::Path(type_path) => {
60 let segment = type_path
61 .path
62 .segments
63 .last()
64 .expect("Expected struct name");
65
66 let struct_generics =
67 if let PathArguments::AngleBracketed(generics) = &segment.arguments {
68 Some(generics)
69 } else {
70 None
71 };
72
73 (&segment.ident, struct_generics, &input.generics)
74 }
75 _ => panic!("Expected a simple struct name in impl"),
76 };
77
78 let mut constructor_methods = Vec::new();
79
80 for item in &input.items {
81 if let ImplItem::Fn(method) = item
82 && (method.vis == Visibility::Inherited || matches!(method.vis, Visibility::Public(_)))
83 {
84 constructor_methods.push(method);
85 }
86 }
87
88 if constructor_methods.is_empty() {
89 panic!("No public constructor methods found in impl block");
90 }
91
92 let visibility = args.visibility.unwrap_or_else(|| parse_quote!(pub));
93
94 let mut output = quote! {
95 #[allow(clippy::new_without_default)]
96 #input
97 };
98
99 for extension_type in args.extensions {
100 let extension = match extension_type.to_string().as_str() {
101 "iterator" => IteratorExtension::generate(
102 args.name.as_deref(),
103 &visibility,
104 struct_name,
105 impl_struct_generics,
106 impl_struct_impl_generics,
107 &constructor_methods,
108 ),
109 "try_iterator" => TryIteratorExtension::generate(
110 args.name.as_deref(),
111 &visibility,
112 struct_name,
113 impl_struct_generics,
114 impl_struct_impl_generics,
115 &constructor_methods,
116 ),
117 "stream" => StreamExtension::generate(
118 args.name.as_deref(),
119 &visibility,
120 struct_name,
121 impl_struct_generics,
122 impl_struct_impl_generics,
123 &constructor_methods,
124 ),
125 "try_stream" => TryStreamExtension::generate(
126 args.name.as_deref(),
127 &visibility,
128 struct_name,
129 impl_struct_generics,
130 impl_struct_impl_generics,
131 &constructor_methods,
132 ),
133 _ => panic!("Unknown extension type: {extension_type}"),
134 };
135
136 output.extend(extension);
137 }
138
139 output.into()
140}
141
142trait Extension {
143 fn get_name() -> &'static str;
144
145 fn get_pipe_method_name_prefix() -> &'static str;
146
147 fn get_extra_trait_params() -> Vec<proc_macro2::TokenStream>;
148
149 fn get_extra_impl_for_params() -> Vec<proc_macro2::TokenStream>;
150
151 fn generate_trait(
152 visibility: &Visibility,
153 trait_name: &Ident,
154 trait_generics: proc_macro2::TokenStream,
155 trait_ty_generics: &TypeGenerics,
156 struct_path_with_generics: &proc_macro2::TokenStream,
157 where_clause: Option<&WhereClause>,
158 where_clause_predicates: Option<&Punctuated<WherePredicate, Comma>>,
159 impl_generics: &ImplGenerics,
160 method_signatures: Vec<proc_macro2::TokenStream>,
161 methods: Vec<proc_macro2::TokenStream>,
162 ) -> proc_macro2::TokenStream;
163
164 fn generate_trait_method_signature(
165 pipe_method_name: &Ident,
166 method_impl_generics: &ImplGenerics,
167 where_clause: Option<&WhereClause>,
168 args: &[PatType],
169 ) -> proc_macro2::TokenStream;
170
171 fn generate_trait_method(
172 pipe_method_name: &Ident,
173 method_name: &Ident,
174 method_impl_generics: &ImplGenerics,
175 where_clause: Option<&WhereClause>,
176 args: &[PatType],
177 arg_names: &[&Ident],
178 struct_with_generics: &proc_macro2::TokenStream,
179 struct_path_with_generics: &proc_macro2::TokenStream,
180 ) -> proc_macro2::TokenStream;
181
182 fn generate(
183 specified_name: Option<&str>,
184 visibility: &Visibility,
185 struct_name: &Ident,
186 impl_struct_generics: Option<&AngleBracketedGenericArguments>,
187 impl_struct_impl_generics: &Generics,
188 constructor_methods: &[&ImplItemFn],
189 ) -> proc_macro2::TokenStream {
190 let trait_name = format_ident!(
191 "{}UniPipe{}Ext",
192 specified_name.unwrap_or(struct_name.to_string().as_str()),
193 Self::get_name()
194 );
195
196 let where_clause = impl_struct_impl_generics.where_clause.as_ref();
197
198 let (struct_with_generics, struct_path_with_generics) = if impl_struct_generics.is_none() {
199 (quote! { #struct_name }, quote! { #struct_name })
200 } else {
201 (
202 quote! { #struct_name #impl_struct_generics },
203 quote! { #struct_name::#impl_struct_generics },
204 )
205 };
206
207 let trait_generics = {
208 let mut generics = impl_struct_impl_generics.clone();
209
210 let extra_params = Self::get_extra_trait_params();
211
212 generics
213 .params
214 .extend::<Punctuated<GenericParam, Comma>>(parse_quote! {
215 #(#extra_params),*
216 });
217
218 generics
219 };
220
221 let impl_for_generics = {
222 let mut generics = trait_generics.clone();
223
224 let extra_params = Self::get_extra_impl_for_params();
225
226 generics
227 .params
228 .extend::<Punctuated<GenericParam, Comma>>(parse_quote! {
229 #(#extra_params),*
230 });
231
232 generics
233 };
234
235 let (trait_generics, trait_ty_generics, _) = trait_generics.split_for_impl();
236
237 let mut trait_generics: Generics = parse_quote! { #trait_generics };
245 let self_ty_param: GenericParam = parse_quote! {
246 _SELF: ::unipipe::UniPipe = #struct_with_generics
247 };
248 trait_generics.params.push(self_ty_param);
249
250 let (impl_generics, _, _) = impl_for_generics.split_for_impl();
251
252 let where_clause_predicates = where_clause.map(|clause| &clause.predicates);
253
254 let mut trait_method_signatures = Vec::new();
255 let mut trait_method_bodies = Vec::new();
256
257 for method in constructor_methods {
258 let args: Vec<_> = method
259 .sig
260 .inputs
261 .iter()
262 .enumerate()
263 .filter_map(|(index, arg)| match arg {
264 FnArg::Typed(pat_type) => Some(simplify_arg_pat_type(pat_type, index)),
265 FnArg::Receiver(_) => None,
266 })
267 .collect();
268
269 if args.len() != method.sig.inputs.len() {
270 continue;
271 }
272
273 let arg_names: Vec<_> = args
274 .iter()
275 .map(|arg| match &*arg.pat {
276 Pat::Ident(pat_ident) => &pat_ident.ident,
277 _ => panic!("Expected PatIdent in simplified args"),
278 })
279 .collect();
280
281 let pipe_method_name = format_ident!(
282 "{}{}",
283 Self::get_pipe_method_name_prefix(),
284 method_name_to_pipe_method(&method.sig.ident, struct_name)
285 );
286
287 let (method_impl_generics, _, where_clause) = method.sig.generics.split_for_impl();
288
289 trait_method_signatures.push(Self::generate_trait_method_signature(
290 &pipe_method_name,
291 &method_impl_generics,
292 where_clause,
293 &args,
294 ));
295 trait_method_bodies.push(Self::generate_trait_method(
296 &pipe_method_name,
297 &method.sig.ident,
298 &method_impl_generics,
299 where_clause,
300 &args,
301 &arg_names,
302 &struct_with_generics,
303 &struct_path_with_generics,
304 ));
305 }
306
307 Self::generate_trait(
308 visibility,
309 &trait_name,
310 trait_generics.to_token_stream(),
311 &trait_ty_generics,
312 &struct_path_with_generics,
313 where_clause,
314 where_clause_predicates,
315 &impl_generics,
316 trait_method_signatures,
317 trait_method_bodies,
318 )
319 }
320}
321
322struct IteratorExtension;
323
324impl Extension for IteratorExtension {
325 fn get_name() -> &'static str {
326 "Iterator"
327 }
328
329 fn get_pipe_method_name_prefix() -> &'static str {
330 ""
331 }
332
333 fn get_extra_trait_params() -> Vec<proc_macro2::TokenStream> {
334 vec![]
335 }
336
337 fn get_extra_impl_for_params() -> Vec<proc_macro2::TokenStream> {
338 vec![quote! { TIterator }]
339 }
340
341 fn generate_trait(
342 visibility: &Visibility,
343 trait_name: &Ident,
344 trait_generics: proc_macro2::TokenStream,
345 trait_ty_generics: &TypeGenerics,
346 struct_path_with_generics: &proc_macro2::TokenStream,
347 where_clause: Option<&WhereClause>,
348 where_clause_predicates: Option<&Punctuated<WherePredicate, Comma>>,
349 impl_generics: &ImplGenerics,
350 method_signatures: Vec<proc_macro2::TokenStream>,
351 method_bodies: Vec<proc_macro2::TokenStream>,
352 ) -> proc_macro2::TokenStream {
353 quote! {
354 #visibility trait #trait_name #trait_generics:
355 Iterator<Item = <_SELF as ::unipipe::UniPipe>::Input> + Sized
356 #where_clause
357 {
358 #(#method_signatures)*
359 }
360
361 impl #impl_generics #trait_name #trait_ty_generics for TIterator
362 where
363 TIterator: Iterator<Item = <#struct_path_with_generics as ::unipipe::UniPipe>::Input>,
364 #where_clause_predicates
365 {
366 #(#method_bodies)*
367 }
368 }
369 }
370
371 fn generate_trait_method_signature(
372 pipe_method_name: &Ident,
373 method_impl_generics: &ImplGenerics,
374 where_clause: Option<&WhereClause>,
375 args: &[PatType],
376 ) -> proc_macro2::TokenStream {
377 quote! {
378 fn #pipe_method_name #method_impl_generics(
379 self,
380 #(#args),*
381 ) -> impl Iterator<Item = <_SELF as ::unipipe::UniPipe>::Output>
382 #where_clause;
383 }
384 }
385
386 fn generate_trait_method(
387 pipe_method_name: &Ident,
388 method_name: &Ident,
389 method_impl_generics: &ImplGenerics,
390 where_clause: Option<&WhereClause>,
391 args: &[PatType],
392 arg_names: &[&Ident],
393 struct_with_generics: &proc_macro2::TokenStream,
394 struct_path_with_generics: &proc_macro2::TokenStream,
395 ) -> proc_macro2::TokenStream {
396 quote! {
397 fn #pipe_method_name #method_impl_generics(
398 mut self,
399 #(#args),*
400 ) -> impl Iterator<Item = <#struct_with_generics as ::unipipe::UniPipe>::Output>
401 #where_clause {
402 use ::unipipe::*;
403
404 let mut pipe = #struct_path_with_generics::#method_name(#(#arg_names),*);
405
406 let mut pending = std::collections::VecDeque::new();
407 let mut done = false;
408
409 std::iter::from_fn(move || {
410 if let Some(output) = pending.pop_front() {
411 return Some(output);
412 }
413
414 loop {
415 if done {
416 return None;
417 }
418
419 let input = self.next();
420
421 if input.is_none() {
422 done = true;
423 }
424
425 let next_output: Output<_> = pipe.next(input).into();
426
427 if next_output.is_done() {
428 done = true;
429 }
430
431 match next_output {
432 Output::One(output) | Output::DoneWithOne(output) => return Some(output),
433 Output::Many(outputs) | Output::DoneWithMany(outputs) => {
434 let mut outputs = outputs.into_iter();
435
436 if let Some(output) = outputs.next() {
437 pending.extend(outputs);
438 return Some(output);
439 }
440 }
441 Output::Next | Output::Done => {}
442 }
443 }
444 })
445 }
446 }
447 }
448}
449
450struct TryIteratorExtension;
451
452impl Extension for TryIteratorExtension {
453 fn get_name() -> &'static str {
454 "TryIterator"
455 }
456
457 fn get_pipe_method_name_prefix() -> &'static str {
458 "try_"
459 }
460
461 fn get_extra_trait_params() -> Vec<proc_macro2::TokenStream> {
462 vec![quote! { TError }]
463 }
464
465 fn get_extra_impl_for_params() -> Vec<proc_macro2::TokenStream> {
466 vec![quote! { TIterator }]
467 }
468
469 fn generate_trait(
470 visibility: &Visibility,
471 trait_name: &Ident,
472 trait_generics: proc_macro2::TokenStream,
473 trait_ty_generics: &TypeGenerics,
474 struct_path_with_generics: &proc_macro2::TokenStream,
475 where_clause: Option<&WhereClause>,
476 where_clause_predicates: Option<&Punctuated<WherePredicate, Comma>>,
477 impl_generics: &ImplGenerics,
478 method_signatures: Vec<proc_macro2::TokenStream>,
479 method_bodies: Vec<proc_macro2::TokenStream>,
480 ) -> proc_macro2::TokenStream {
481 quote! {
482 #visibility trait #trait_name #trait_generics:
483 Iterator<Item = Result<<_SELF as ::unipipe::UniPipe>::Input, TError>> + Sized
484 #where_clause
485 {
486 #(#method_signatures)*
487 }
488
489 impl #impl_generics #trait_name #trait_ty_generics for TIterator
490 where
491 TIterator: Iterator<Item = Result<<#struct_path_with_generics as ::unipipe::UniPipe>::Input, TError>>,
492 #where_clause_predicates
493 {
494 #(#method_bodies)*
495 }
496 }
497 }
498
499 fn generate_trait_method_signature(
500 pipe_method_name: &Ident,
501 method_impl_generics: &ImplGenerics,
502 where_clause: Option<&WhereClause>,
503 args: &[PatType],
504 ) -> proc_macro2::TokenStream {
505 quote! {
506 fn #pipe_method_name #method_impl_generics(
507 self,
508 #(#args),*
509 ) -> impl Iterator<Item = Result<<_SELF as ::unipipe::UniPipe>::Output, TError>>
510 #where_clause;
511 }
512 }
513
514 fn generate_trait_method(
515 pipe_method_name: &Ident,
516 method_name: &Ident,
517 method_impl_generics: &ImplGenerics,
518 where_clause: Option<&WhereClause>,
519 args: &[PatType],
520 arg_names: &[&Ident],
521 struct_with_generics: &proc_macro2::TokenStream,
522 struct_path_with_generics: &proc_macro2::TokenStream,
523 ) -> proc_macro2::TokenStream {
524 quote! {
525 fn #pipe_method_name #method_impl_generics(
526 mut self,
527 #(#args),*
528 ) -> impl Iterator<Item = Result<<#struct_with_generics as ::unipipe::UniPipe>::Output, TError>>
529 #where_clause {
530 use ::unipipe::*;
531
532 let mut pipe = #struct_path_with_generics::#method_name(#(#arg_names),*);
533
534 let mut pending = std::collections::VecDeque::new();
535 let mut done = false;
536
537 std::iter::from_fn(move || {
538 if let Some(output) = pending.pop_front() {
539 return Some(Ok(output));
540 }
541
542 loop {
543 if done {
544 return None;
545 }
546
547 let input = self.next();
548
549 if input.is_none() {
550 done = true;
551 }
552
553 let input = match input {
554 Some(Err(error)) => return Some(Err(error)),
555 Some(Ok(input)) => Some(input),
556 None => None,
557 };
558
559 let next_output: Output<_> = pipe.next(input).into();
560
561 if next_output.is_done() {
562 done = true;
563 }
564
565 match next_output {
566 Output::One(output) | Output::DoneWithOne(output) => return Some(Ok(output)),
567 Output::Many(outputs) | Output::DoneWithMany(outputs) => {
568 let mut outputs = outputs.into_iter();
569
570 if let Some(output) = outputs.next() {
571 pending.extend(outputs);
572 return Some(Ok(output));
573 }
574 }
575 Output::Next | Output::Done => {}
576 }
577 }
578 })
579 }
580 }
581 }
582}
583
584struct StreamExtension;
585
586impl Extension for StreamExtension {
587 fn get_name() -> &'static str {
588 "Stream"
589 }
590
591 fn get_pipe_method_name_prefix() -> &'static str {
592 ""
593 }
594
595 fn get_extra_trait_params() -> Vec<proc_macro2::TokenStream> {
596 vec![]
597 }
598
599 fn get_extra_impl_for_params() -> Vec<proc_macro2::TokenStream> {
600 vec![quote! { TStream }]
601 }
602
603 fn generate_trait(
604 visibility: &Visibility,
605 trait_name: &Ident,
606 trait_generics: proc_macro2::TokenStream,
607 trait_ty_generics: &TypeGenerics,
608 struct_path_with_generics: &proc_macro2::TokenStream,
609 where_clause: Option<&WhereClause>,
610 where_clause_predicates: Option<&Punctuated<WherePredicate, Comma>>,
611 impl_generics: &ImplGenerics,
612 method_signatures: Vec<proc_macro2::TokenStream>,
613 method_bodies: Vec<proc_macro2::TokenStream>,
614 ) -> proc_macro2::TokenStream {
615 quote! {
616 #visibility trait #trait_name #trait_generics:
617 ::unipipe::Stream<Item = <_SELF as ::unipipe::UniPipe>::Input> + Sized
618 #where_clause
619 {
620 #(#method_signatures)*
621 }
622
623 impl #impl_generics #trait_name #trait_ty_generics for TStream
624 where
625 TStream: ::unipipe::Stream<Item = <#struct_path_with_generics as ::unipipe::UniPipe>::Input>,
626 #where_clause_predicates
627 {
628 #(#method_bodies)*
629 }
630 }
631 }
632
633 fn generate_trait_method_signature(
634 pipe_method_name: &Ident,
635 method_impl_generics: &ImplGenerics,
636 where_clause: Option<&WhereClause>,
637 args: &[PatType],
638 ) -> proc_macro2::TokenStream {
639 quote! {
640 fn #pipe_method_name #method_impl_generics(
641 self,
642 #(#args),*
643 ) -> impl ::unipipe::Stream<Item = <_SELF as ::unipipe::UniPipe>::Output> + Unpin
644 #where_clause;
645 }
646 }
647
648 fn generate_trait_method(
649 pipe_method_name: &Ident,
650 method_name: &Ident,
651 method_impl_generics: &ImplGenerics,
652 where_clause: Option<&WhereClause>,
653 args: &[PatType],
654 arg_names: &[&Ident],
655 struct_with_generics: &proc_macro2::TokenStream,
656 struct_path_with_generics: &proc_macro2::TokenStream,
657 ) -> proc_macro2::TokenStream {
658 quote! {
659 fn #pipe_method_name #method_impl_generics(
660 mut self,
661 #(#args),*
662 ) -> impl ::unipipe::Stream<Item = <#struct_with_generics as ::unipipe::UniPipe>::Output> + Unpin
663 #where_clause {
664 use ::unipipe::*;
665
666 Box::pin(::unipipe::stream!({
667 let mut pipe = #struct_path_with_generics::#method_name(#(#arg_names),*);
668
669 let mut source = Box::pin(self);
670 let mut done = false;
671
672 loop {
673 if done {
674 break;
675 }
676
677 let input = source.next().await;
678
679 if input.is_none() {
680 done = true;
681 }
682
683 let next_output: Output<_> = pipe.next(input).into();
684
685 if next_output.is_done() {
686 done = true;
687 }
688
689 match next_output {
690 Output::One(output) | Output::DoneWithOne(output) => yield output,
691 Output::Many(outputs) | Output::DoneWithMany(outputs) => {
692 for output in outputs {
693 yield output;
694 }
695 }
696 Output::Next | Output::Done => {}
697 }
698 }
699 }))
700 }
701 }
702 }
703}
704
705struct TryStreamExtension;
706
707impl Extension for TryStreamExtension {
708 fn get_name() -> &'static str {
709 "TryStream"
710 }
711
712 fn get_pipe_method_name_prefix() -> &'static str {
713 "try_"
714 }
715
716 fn get_extra_trait_params() -> Vec<proc_macro2::TokenStream> {
717 vec![quote! { TError }]
718 }
719
720 fn get_extra_impl_for_params() -> Vec<proc_macro2::TokenStream> {
721 vec![quote! { TStream }]
722 }
723
724 fn generate_trait(
725 visibility: &Visibility,
726 trait_name: &Ident,
727 trait_generics: proc_macro2::TokenStream,
728 trait_ty_generics: &TypeGenerics,
729 struct_path_with_generics: &proc_macro2::TokenStream,
730 where_clause: Option<&WhereClause>,
731 where_clause_predicates: Option<&Punctuated<WherePredicate, Comma>>,
732 impl_generics: &ImplGenerics,
733 method_signatures: Vec<proc_macro2::TokenStream>,
734 method_bodies: Vec<proc_macro2::TokenStream>,
735 ) -> proc_macro2::TokenStream {
736 quote! {
737 #visibility trait #trait_name #trait_generics:
738 ::unipipe::Stream<Item = Result<<_SELF as ::unipipe::UniPipe>::Input, TError>> + Sized
739 #where_clause
740 {
741 #(#method_signatures)*
742 }
743
744 impl #impl_generics #trait_name #trait_ty_generics for TStream
745 where
746 TStream: ::unipipe::Stream<Item = Result<<#struct_path_with_generics as ::unipipe::UniPipe>::Input, TError>>,
747 #where_clause_predicates
748 {
749 #(#method_bodies)*
750 }
751 }
752 }
753
754 fn generate_trait_method_signature(
755 pipe_method_name: &Ident,
756 method_impl_generics: &ImplGenerics,
757 where_clause: Option<&WhereClause>,
758 args: &[PatType],
759 ) -> proc_macro2::TokenStream {
760 quote! {
761 fn #pipe_method_name #method_impl_generics(
762 self,
763 #(#args),*
764 ) -> impl ::unipipe::Stream<Item = Result<<_SELF as ::unipipe::UniPipe>::Output, TError>> + Unpin
765 #where_clause;
766 }
767 }
768
769 fn generate_trait_method(
770 pipe_method_name: &Ident,
771 method_name: &Ident,
772 method_impl_generics: &ImplGenerics,
773 where_clause: Option<&WhereClause>,
774 args: &[PatType],
775 arg_names: &[&Ident],
776 struct_with_generics: &proc_macro2::TokenStream,
777 struct_path_with_generics: &proc_macro2::TokenStream,
778 ) -> proc_macro2::TokenStream {
779 quote! {
780 fn #pipe_method_name #method_impl_generics(
781 mut self,
782 #(#args),*
783 ) -> impl ::unipipe::Stream<Item = Result<<#struct_with_generics as ::unipipe::UniPipe>::Output, TError>> + Unpin
784 #where_clause {
785 use ::unipipe::*;
786
787 Box::pin(::unipipe::stream!({
788 let mut pipe = #struct_path_with_generics::#method_name(#(#arg_names),*);
789
790 let mut source = Box::pin(self);
791 let mut done = false;
792
793 loop {
794 if done {
795 break;
796 }
797
798 let input = source.next().await;
799
800 if input.is_none() {
801 done = true;
802 }
803
804 let input = match input {
805 Some(Err(error)) => {
806 yield Err(error);
807 continue;
808 }
809 Some(Ok(input)) => Some(input),
810 None => None,
811 };
812
813 let next_output: Output<_> = pipe.next(input).into();
814
815 if next_output.is_done() {
816 done = true;
817 }
818
819 match next_output {
820 Output::One(output) | Output::DoneWithOne(output) => yield Ok(output),
821 Output::Many(outputs) | Output::DoneWithMany(outputs) => {
822 for output in outputs {
823 yield Ok(output);
824 }
825 }
826 Output::Next | Output::Done => {}
827 }
828 }
829 }))
830 }
831 }
832 }
833}
834
835fn method_name_to_pipe_method(method_name: &Ident, struct_name: &Ident) -> Ident {
836 let method_str = method_name.to_string();
837 let struct_snake = struct_name.to_string().to_case(Case::Snake);
838
839 if method_str == "new" {
840 return format_ident!("{}", struct_snake);
841 }
842
843 if let Some(suffix) = method_str.strip_prefix("new_") {
844 return format_ident!("{}_{}", struct_snake, suffix);
845 }
846
847 format_ident!("{}", method_str.to_case(Case::Snake))
848}
849
850fn simplify_arg_pat_type(pat_type: &PatType, index: usize) -> PatType {
851 let mut simplified = pat_type.clone();
852
853 let ident = match &*pat_type.pat {
854 Pat::Ident(pat_ident) => pat_ident.ident.clone(),
855 _ => format_ident!("arg_{}", index),
856 };
857
858 simplified.pat = Box::new(Pat::Ident(syn::PatIdent {
859 attrs: vec![],
860 by_ref: None,
861 mutability: None,
862 ident,
863 subpat: None,
864 }));
865
866 simplified
867}