rs_jsonrpc_macros/
auto_args.rs

1// because we reuse the type names as idents in the macros as a dirty hack to
2// work around `concat_idents!` being unstable.
3#![allow(non_snake_case)]
4
5///! Automatically serialize and deserialize parameters around a strongly-typed function.
6
7use rs_jsonrpc_core::{Error, Params, Value, Metadata, Result};
8use rs_jsonrpc_core::futures::{self, Future, IntoFuture};
9use rs_jsonrpc_core::futures::future::{self, Either};
10use rs_jsonrpc_pubsub::{PubSubMetadata, Subscriber};
11use pubsub;
12use serde::Serialize;
13use serde::de::DeserializeOwned;
14use util::{invalid_params, expect_no_params, to_value};
15
16/// Auto-generates an RPC trait from trait definition.
17///
18/// This just copies out all the methods, docs, and adds another
19/// function `to_delegate` which will automatically wrap each strongly-typed
20/// function in a wrapper which handles parameter and output type serialization.
21///
22/// RPC functions may come in a couple forms: synchronous, async and async with metadata.
23/// These are parsed with the custom `#[rpc]` attribute, which must follow
24/// documentation.
25///
26/// ## The #[rpc] attribute
27///
28/// Valid forms:
29///  - `#[rpc(name = "name_here")]` (an async rpc function which should be bound to the given name)
30///  - `#[rpc(meta, name = "name_here")]` (an async rpc function with metadata which should be bound to the given name)
31///
32/// Synchronous function format:
33/// `fn foo(&self, Param1, Param2, Param3) -> Result<Out>`.
34///
35/// Asynchronous RPC functions must come in this form:
36/// `fn foo(&self, Param1, Param2, Param3) -> BoxFuture<Out>;
37///
38/// Asynchronous RPC functions with metadata must come in this form:
39/// `fn foo(&self, Self::Metadata, Param1, Param2, Param3) -> BoxFuture<Out>;
40///
41/// Anything else will be rejected by the code generator.
42///
43/// ## The #[pubsub] attribute
44///
45/// Valid form:
46/// ```rust,ignore
47///	#[pubsub(name = "hello")] {
48///	  #[rpc(name = "hello_subscribe")]
49///	  fn subscribe(&self, Self::Metadata, pubsub::Subscriber<String>, u64);
50///	  #[rpc(name = "hello_unsubscribe")]
51///	  fn unsubscribe(&self, SubscriptionId) -> Result<bool>;
52///	}
53///	```
54///
55/// The attribute is used to create a new pair of subscription methods
56/// (if underlying transport supports that.)
57
58
59#[macro_export]
60macro_rules! metadata {
61	() => {
62		/// Requests metadata
63		type Metadata: $crate::rs_jsonrpc_core::Metadata;
64	};
65	(
66		$( $sub_name: ident )+
67	) => {
68		/// Requests metadata
69		type Metadata: $crate::rs_jsonrpc_pubsub::PubSubMetadata;
70	};
71}
72
73#[macro_export]
74macro_rules! build_rpc_trait {
75	// entry-point. todo: make another for traits w/ bounds.
76	(
77		$(#[$t_attr: meta])*
78		pub trait $name: ident {
79			$(
80				$( #[doc=$m_doc:expr] )*
81				#[ rpc( $($t:tt)* ) ]
82				fn $m_name: ident ( $($p: tt)* ) -> $result: tt <$out: ty $(, $error: ty)* >;
83			)*
84		}
85	) => {
86		$(#[$t_attr])*
87		pub trait $name: Sized + Send + Sync + 'static {
88			$(
89				$(#[doc=$m_doc])*
90				fn $m_name ( $($p)* ) -> $result<$out $(, $error)* > ;
91			)*
92
93			/// Transform this into an `IoDelegate`, automatically wrapping
94			/// the parameters.
95			fn to_delegate<M: $crate::rs_jsonrpc_core::Metadata>(self) -> $crate::IoDelegate<Self, M> {
96				let mut del = $crate::IoDelegate::new(self.into());
97				$(
98					build_rpc_trait!(WRAP del =>
99						( $($t)* )
100						fn $m_name ( $($p)* ) -> $result <$out $(, $error)* >
101					);
102				)*
103				del
104			}
105		}
106	};
107
108	// entry-point for trait with metadata methods
109	(
110		$(#[$t_attr: meta])*
111		pub trait $name: ident {
112			type Metadata;
113
114			$(
115				$( #[ doc=$m_doc:expr ] )*
116				#[ rpc( $($t:tt)* ) ]
117				fn $m_name: ident ( $($p: tt)* ) -> $result: tt <$out: ty $(, $error_std: ty) *>;
118			)*
119
120			$(
121				#[ pubsub( $($pubsub_t:tt)+ ) ] {
122					$( #[ doc= $sub_doc:expr ] )*
123					#[ rpc( $($sub_t:tt)* ) ]
124					fn $sub_name: ident ( $($sub_p: tt)* );
125					$( #[ doc= $unsub_doc:expr ] )*
126					#[ rpc( $($unsub_t:tt)* ) ]
127					fn $unsub_name: ident ( $($unsub_p: tt)* ) -> $sub_result: tt <$sub_out: ty $(, $error_unsub: ty)* >;
128				}
129			)*
130
131		}
132	) => {
133		$(#[$t_attr])*
134		pub trait $name: Sized + Send + Sync + 'static {
135			// Metadata bound differs for traits with subscription methods.
136			metadata! (
137				$( $sub_name )*
138			);
139
140			$(
141				$(#[doc=$m_doc])*
142				fn $m_name ( $($p)* ) -> $result <$out $(, $error_std) *>;
143			)*
144
145			$(
146				$(#[doc=$sub_doc])*
147				fn $sub_name ( $($sub_p)* );
148				$(#[doc=$unsub_doc])*
149				fn $unsub_name ( $($unsub_p)* ) -> $sub_result <$sub_out $(, $error_unsub)* >;
150			)*
151
152			/// Transform this into an `IoDelegate`, automatically wrapping
153			/// the parameters.
154			fn to_delegate(self) -> $crate::IoDelegate<Self, Self::Metadata> {
155				let mut del = $crate::IoDelegate::new(self.into());
156				$(
157					build_rpc_trait!(WRAP del =>
158						( $($t)* )
159						fn $m_name ( $($p)* ) -> $result <$out $(, $error_std)* >
160					);
161				)*
162				$(
163					build_rpc_trait!(WRAP del =>
164						pubsub: ( $($pubsub_t)* )
165						subscribe: ( $($sub_t)* )
166						fn $sub_name ( $($sub_p)* );
167						unsubscribe: ( $($unsub_t)* )
168						fn $unsub_name ( $($unsub_p)* ) -> $sub_result <$sub_out $(, $error_unsub)* >;
169					);
170				)*
171				del
172			}
173		}
174	};
175
176	( WRAP $del: expr =>
177		(name = $name: expr $(, alias = [ $( $alias: expr, )+ ])*)
178		fn $method: ident (&self $(, $param: ty)*) -> $result: tt <$out: ty $(, $error: ty)* >
179	) => {
180		$del.add_method($name, move |base, params| {
181			$crate::WrapAsync::wrap_rpc(&(Self::$method as fn(&_ $(, $param)*) -> $result <$out $(, $error)*>), base, params)
182		});
183		$(
184			$(
185				$del.add_alias($alias, $name);
186			)+
187		)*
188	};
189
190	( WRAP $del: expr =>
191		(meta, name = $name: expr $(, alias = [ $( $alias: expr, )+ ])*)
192		fn $method: ident (&self, Self::Metadata $(, $param: ty)*) -> $result: tt <$out: ty $(, $error: ty)* >
193	) => {
194		$del.add_method_with_meta($name, move |base, params, meta| {
195			$crate::WrapMeta::wrap_rpc(&(Self::$method as fn(&_, Self::Metadata $(, $param)*) -> $result <$out $(, $error)* >), base, params, meta)
196		});
197		$(
198			$(
199				$del.add_alias($alias, $name);
200			)+
201		)*
202	};
203
204	( WRAP $del: expr =>
205		pubsub: (name = $name: expr)
206		subscribe: (name = $subscribe: expr $(, alias = [ $( $sub_alias: expr, )+ ])*)
207		fn $sub_method: ident (&self, Self::Metadata $(, $sub_p: ty)+);
208		unsubscribe: (name = $unsubscribe: expr $(, alias = [ $( $unsub_alias: expr, )+ ])*)
209		fn $unsub_method: ident (&self $(, $unsub_p: ty)+) -> $result: tt <$out: ty $(, $error_unsub: ty)* >;
210	) => {
211		$del.add_subscription(
212			$name,
213			($subscribe, move |base, params, meta, subscriber| {
214				$crate::WrapSubscribe::wrap_rpc(
215					&(Self::$sub_method as fn(&_, Self::Metadata $(, $sub_p)*)),
216					base,
217					params,
218					meta,
219					subscriber,
220				)
221			}),
222			($unsubscribe, move |base, id| {
223				use $crate::rs_jsonrpc_core::futures::{IntoFuture, Future};
224				Self::$unsub_method(base, id).into_future().map($crate::to_value)
225			}),
226		);
227
228		$(
229			$(
230				$del.add_alias($sub_alias, $subscribe);
231			)*
232		)*
233		$(
234			$(
235				$del.add_alias($unsub_alias, $unsubscribe);
236			)*
237		)*
238	};
239}
240
241/// A wrapper type without an implementation of `Deserialize`
242/// which allows a special implementation of `Wrap` for functions
243/// that take a trailing default parameter.
244pub struct Trailing<T>(Option<T>);
245
246impl<T> Into<Option<T>> for Trailing<T> {
247	fn into(self) -> Option<T> {
248		self.0
249	}
250}
251
252impl<T: DeserializeOwned> Trailing<T> {
253	/// Returns a underlying value if present or provided value.
254	pub fn unwrap_or(self, other: T) -> T {
255		self.0.unwrap_or(other)
256	}
257
258	/// Returns an underlying value or computes it if not present.
259	pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
260		self.0.unwrap_or_else(f)
261	}
262}
263
264impl<T: Default + DeserializeOwned> Trailing<T> {
265	/// Returns an underlying value or the default value.
266	pub fn unwrap_or_default(self) -> T {
267		self.0.unwrap_or_default()
268	}
269}
270
271type WrappedFuture<F, OUT, E> = future::MapErr<
272	future::Map<F, fn(OUT) -> Value>,
273	fn(E) -> Error
274>;
275type WrapResult<F, OUT, E> = Either<
276	WrappedFuture<F, OUT, E>,
277	future::FutureResult<Value, Error>,
278>;
279
280fn as_future<F, OUT, E, I>(el: I) -> WrappedFuture<F, OUT, E> where
281	OUT: Serialize,
282	E: Into<Error>,
283	F: Future<Item = OUT, Error = E>,
284	I: IntoFuture<Item = OUT, Error = E, Future = F>
285{
286	el.into_future()
287		.map(to_value as fn(OUT) -> Value)
288		.map_err(Into::into as fn(E) -> Error)
289}
290
291/// Wrapper trait for asynchronous RPC functions.
292pub trait WrapAsync<B> {
293	/// Output type.
294	type Out: IntoFuture<Item = Value, Error = Error>;
295
296	/// Invokes asynchronous RPC method.
297	fn wrap_rpc(&self, base: &B, params: Params) -> Self::Out;
298}
299
300/// Wrapper trait for meta RPC functions.
301pub trait WrapMeta<B, M> {
302	/// Output type.
303	type Out: IntoFuture<Item = Value, Error = Error>;
304	/// Invokes asynchronous RPC method with Metadata.
305	fn wrap_rpc(&self, base: &B, params: Params, meta: M) -> Self::Out;
306}
307
308/// Wrapper trait for subscribe RPC functions.
309pub trait WrapSubscribe<B, M> {
310	/// Invokes subscription.
311	fn wrap_rpc(&self, base: &B, params: Params, meta: M, subscriber: Subscriber);
312}
313
314// special impl for no parameters.
315impl<B, OUT, E, F, I> WrapAsync<B> for fn(&B) -> I where
316	B: Send + Sync + 'static,
317	OUT: Serialize + 'static,
318	E: Into<Error> + 'static,
319	F: Future<Item = OUT, Error = E> + Send + 'static,
320	I: IntoFuture<Item = OUT, Error = E, Future = F>,
321{
322	type Out = WrapResult<F, OUT, E>;
323
324	fn wrap_rpc(&self, base: &B, params: Params) -> Self::Out {
325		match expect_no_params(params) {
326			Ok(()) => Either::A(as_future((self)(base))),
327			Err(e) => Either::B(futures::failed(e)),
328		}
329	}
330}
331
332impl<M, B, OUT, E, F, I> WrapMeta<B, M> for fn(&B, M) -> I where
333	M: Metadata,
334	B: Send + Sync + 'static,
335	OUT: Serialize + 'static,
336	E: Into<Error> + 'static,
337	F: Future<Item = OUT, Error = E> + Send + 'static,
338	I: IntoFuture<Item = OUT, Error = E, Future = F>,
339{
340	type Out = WrapResult<F, OUT, E>;
341
342	fn wrap_rpc(&self, base: &B, params: Params, meta: M) -> Self::Out {
343		match expect_no_params(params) {
344			Ok(()) => Either::A(as_future((self)(base, meta))),
345			Err(e) => Either::B(futures::failed(e)),
346		}
347	}
348}
349
350impl<M, B, OUT> WrapSubscribe<B, M> for fn(&B, M, pubsub::Subscriber<OUT>) where
351	M: PubSubMetadata,
352	B: Send + Sync + 'static,
353	OUT: Serialize,
354{
355	fn wrap_rpc(&self, base: &B, params: Params, meta: M, subscriber: Subscriber) {
356		match expect_no_params(params) {
357			Ok(()) => (self)(base, meta, pubsub::Subscriber::new(subscriber)),
358			Err(e) => {
359				let _ = subscriber.reject(e);
360			},
361		}
362	}
363}
364
365// creates a wrapper implementation which deserializes the parameters,
366// calls the function with concrete type, and serializes the output.
367macro_rules! wrap {
368	($($x: ident),+) => {
369
370		// asynchronous implementation
371		impl <
372			BASE: Send + Sync + 'static,
373			OUT: Serialize + 'static,
374			$($x: DeserializeOwned,)+
375			ERR: Into<Error> + 'static,
376			X: Future<Item = OUT, Error = ERR> + Send + 'static,
377			Z: IntoFuture<Item = OUT, Error = ERR, Future = X>,
378		> WrapAsync<BASE> for fn(&BASE, $($x,)+ ) -> Z {
379			type Out = WrapResult<X, OUT, ERR>;
380			fn wrap_rpc(&self, base: &BASE, params: Params) -> Self::Out {
381				match params.parse::<($($x,)+)>() {
382					Ok(($($x,)+)) => Either::A(as_future((self)(base, $($x,)+))),
383					Err(e) => Either::B(futures::failed(e)),
384				}
385			}
386		}
387
388		// asynchronous implementation with meta
389		impl <
390			BASE: Send + Sync + 'static,
391			META: Metadata,
392			OUT: Serialize + 'static,
393			$($x: DeserializeOwned,)+
394			ERR: Into<Error> + 'static,
395			X: Future<Item = OUT, Error = ERR> + Send + 'static,
396			Z: IntoFuture<Item = OUT, Error = ERR, Future = X>,
397		> WrapMeta<BASE, META> for fn(&BASE, META, $($x,)+) -> Z {
398			type Out = WrapResult<X, OUT, ERR>;
399			fn wrap_rpc(&self, base: &BASE, params: Params, meta: META) -> Self::Out {
400				match params.parse::<($($x,)+)>() {
401					Ok(($($x,)+)) => Either::A(as_future((self)(base, meta, $($x,)+))),
402					Err(e) => Either::B(futures::failed(e)),
403				}
404			}
405		}
406
407		// subscribe implementation
408		impl <
409			BASE: Send + Sync + 'static,
410			META: PubSubMetadata,
411			OUT: Serialize,
412			$($x: DeserializeOwned,)+
413		> WrapSubscribe<BASE, META> for fn(&BASE, META, pubsub::Subscriber<OUT>, $($x,)+) {
414			fn wrap_rpc(&self, base: &BASE, params: Params, meta: META, subscriber: Subscriber) {
415				match params.parse::<($($x,)+)>() {
416					Ok(($($x,)+)) => (self)(base, meta, pubsub::Subscriber::new(subscriber), $($x,)+),
417					Err(e) => {
418						let _ = subscriber.reject(e);
419					},
420				}
421			}
422		}
423	}
424}
425
426fn params_len(params: &Params) -> Result<usize> {
427	match *params {
428		Params::Array(ref v) => Ok(v.len()),
429		Params::None => Ok(0),
430		_ => Err(invalid_params("`params` should be an array", "")),
431	}
432}
433
434fn require_len(params: &Params, required: usize) -> Result<usize> {
435	let len = params_len(params)?;
436	if len < required {
437		return Err(invalid_params(&format!("`params` should have at least {} argument(s)", required), ""));
438	}
439	Ok(len)
440}
441
442fn parse_trailing_param<T: DeserializeOwned>(params: Params) -> Result<(Option<T>, )> {
443	let len = try!(params_len(&params));
444	let id = match len {
445		0 => Ok((None,)),
446		1 => params.parse::<(T,)>().map(|(x, )| (Some(x), )),
447		_ => Err(invalid_params("Expecting only one optional parameter.", "")),
448	};
449
450	id
451}
452
453// special impl for no parameters other than block parameter.
454impl<B, OUT, T, E, F, I> WrapAsync<B> for fn(&B, Trailing<T>) -> I where
455	B: Send + Sync + 'static,
456	OUT: Serialize + 'static,
457	T: DeserializeOwned,
458	E: Into<Error> + 'static,
459	F: Future<Item = OUT, Error = E> + Send + 'static,
460	I: IntoFuture<Item = OUT, Error = E, Future = F>,
461{
462	type Out = WrapResult<F, OUT, E>;
463	fn wrap_rpc(&self, base: &B, params: Params) -> Self::Out {
464		let id = parse_trailing_param(params);
465
466		match id {
467			Ok((id,)) => Either::A(as_future((self)(base, Trailing(id)))),
468			Err(e) => Either::B(futures::failed(e)),
469		}
470	}
471}
472
473impl<M, B, OUT, T, E, F, I> WrapMeta<B, M> for fn(&B, M, Trailing<T>) -> I where
474	M: Metadata,
475	B: Send + Sync + 'static,
476	OUT: Serialize + 'static,
477	T: DeserializeOwned,
478	E: Into<Error> + 'static,
479	F: Future<Item = OUT, Error = E> + Send + 'static,
480	I: IntoFuture<Item = OUT, Error = E, Future = F>,
481{
482	type Out = WrapResult<F, OUT, E>;
483	fn wrap_rpc(&self, base: &B, params: Params, meta: M) -> Self::Out {
484		let id = parse_trailing_param(params);
485
486		match id {
487			Ok((id,)) => Either::A(as_future((self)(base, meta, Trailing(id)))),
488			Err(e) => Either::B(futures::failed(e)),
489		}
490	}
491}
492
493impl<M, B, OUT, T> WrapSubscribe<B, M> for fn(&B, M, pubsub::Subscriber<OUT>, Trailing<T>) where
494	M: PubSubMetadata,
495	B: Send + Sync + 'static,
496	OUT: Serialize,
497	T: DeserializeOwned,
498{
499	fn wrap_rpc(&self, base: &B, params: Params, meta: M, subscriber: Subscriber) {
500		let id = parse_trailing_param(params);
501
502		match id {
503			Ok((id,)) => (self)(base, meta, pubsub::Subscriber::new(subscriber), Trailing(id)),
504			Err(e) => {
505				let _ = subscriber.reject(e);
506			},
507		}
508	}
509}
510
511// similar to `wrap!`, but handles a single default trailing parameter
512// accepts an additional argument indicating the number of non-trailing parameters.
513macro_rules! wrap_with_trailing {
514	($num: expr, $($x: ident),+) => {
515		// asynchronous implementation
516		impl <
517			BASE: Send + Sync + 'static,
518			OUT: Serialize + 'static,
519			$($x: DeserializeOwned,)+
520			TRAILING: DeserializeOwned,
521			ERR: Into<Error> + 'static,
522			X: Future<Item = OUT, Error = ERR> + Send + 'static,
523			Z: IntoFuture<Item = OUT, Error = ERR, Future = X>,
524		> WrapAsync<BASE> for fn(&BASE, $($x,)+ Trailing<TRAILING>) -> Z {
525			type Out = WrapResult<X, OUT, ERR>;
526			fn wrap_rpc(&self, base: &BASE, params: Params) -> Self::Out {
527				let len = match require_len(&params, $num) {
528					Ok(len) => len,
529					Err(e) => return Either::B(futures::failed(e)),
530				};
531
532				let params = match len - $num {
533					0 => params.parse::<($($x,)+)>()
534						.map(|($($x,)+)| ($($x,)+ None)).map_err(Into::into),
535					1 => params.parse::<($($x,)+ TRAILING)>()
536						.map(|($($x,)+ id)| ($($x,)+ Some(id))).map_err(Into::into),
537					_ => Err(invalid_params(&format!("Expected {} or {} parameters.", $num, $num + 1), format!("Got: {}", len))),
538				};
539
540				match params {
541					Ok(($($x,)+ id)) => Either::A(as_future((self)(base, $($x,)+ Trailing(id)))),
542					Err(e) => Either::B(futures::failed(e)),
543				}
544			}
545		}
546
547		// asynchronous implementation with meta
548		impl <
549			BASE: Send + Sync + 'static,
550			META: Metadata,
551			OUT: Serialize + 'static,
552			$($x: DeserializeOwned,)+
553			TRAILING: DeserializeOwned,
554			ERR: Into<Error> + 'static,
555			X: Future<Item = OUT, Error = ERR> + Send + 'static,
556			Z: IntoFuture<Item = OUT, Error = ERR, Future = X>,
557		> WrapMeta<BASE, META> for fn(&BASE, META, $($x,)+ Trailing<TRAILING>) -> Z {
558			type Out = WrapResult<X, OUT, ERR>;
559			fn wrap_rpc(&self, base: &BASE, params: Params, meta: META) -> Self::Out {
560				let len = match require_len(&params, $num) {
561					Ok(len) => len,
562					Err(e) => return Either::B(futures::failed(e)),
563				};
564
565				let params = match len - $num {
566					0 => params.parse::<($($x,)+)>()
567						.map(|($($x,)+)| ($($x,)+ None)).map_err(Into::into),
568					1 => params.parse::<($($x,)+ TRAILING)>()
569						.map(|($($x,)+ id)| ($($x,)+ Some(id))).map_err(Into::into),
570					_ => Err(invalid_params(&format!("Expected {} or {} parameters.", $num, $num + 1), format!("Got: {}", len))),
571				};
572
573				match params {
574					Ok(($($x,)+ id)) => Either::A(as_future((self)(base, meta, $($x,)+ Trailing(id)))),
575					Err(e) => Either::B(futures::failed(e)),
576				}
577			}
578		}
579
580		// subscribe implementation
581		impl <
582			BASE: Send + Sync + 'static,
583			META: PubSubMetadata,
584			OUT: Serialize,
585			$($x: DeserializeOwned,)+
586			TRAILING: DeserializeOwned,
587		> WrapSubscribe<BASE, META> for fn(&BASE, META, pubsub::Subscriber<OUT>, $($x,)+ Trailing<TRAILING>) {
588			fn wrap_rpc(&self, base: &BASE, params: Params, meta: META, subscriber: Subscriber) {
589				let len = match require_len(&params, $num) {
590					Ok(len) => len,
591					Err(e) => {
592						let _ = subscriber.reject(e);
593						return;
594					},
595				};
596
597				let params = match len - $num {
598					0 => params.parse::<($($x,)+)>()
599						.map(|($($x,)+)| ($($x,)+ None)),
600					1 => params.parse::<($($x,)+ TRAILING)>()
601						.map(|($($x,)+ id)| ($($x,)+ Some(id))),
602					_ => {
603						let _ = subscriber.reject(invalid_params(&format!("Expected {} or {} parameters.", $num, $num + 1), format!("Got: {}", len)));
604						return;
605					},
606				};
607
608				match params {
609					Ok(($($x,)+ id)) => (self)(base, meta, pubsub::Subscriber::new(subscriber), $($x,)+ Trailing(id)),
610					Err(e) => {
611						let _ = subscriber.reject(e);
612						return;
613					},
614				}
615			}
616		}
617	}
618}
619
620wrap!(A, B, C, D, E, F);
621wrap!(A, B, C, D, E);
622wrap!(A, B, C, D);
623wrap!(A, B, C);
624wrap!(A, B);
625wrap!(A);
626
627wrap_with_trailing!(6, A, B, C, D, E, F);
628wrap_with_trailing!(5, A, B, C, D, E);
629wrap_with_trailing!(4, A, B, C, D);
630wrap_with_trailing!(3, A, B, C);
631wrap_with_trailing!(2, A, B);
632wrap_with_trailing!(1, A);
633