1use heck::ToSnakeCase;
2use proc_macro::TokenStream;
3use proc_macro_error::abort;
4use quote::{format_ident, quote, ToTokens};
5use syn::{Block, FnArg, Ident, ImplItem, ItemImpl, ItemStruct, PatType, ReturnType, Type};
6
7#[proc_macro]
8pub fn top_route(_: TokenStream) -> TokenStream {
9 quote!(__srpc_inner_ctx).into()
10}
11
12#[proc_macro_attribute]
13#[proc_macro_error::proc_macro_error]
14pub fn rpc(attrs: TokenStream, item: TokenStream) -> TokenStream {
15 match syn::parse::<ItemStruct>(item.clone()) {
16 Ok(item) => struct_rpc(attrs, item),
17 Err(_) => {
18 let item = syn::parse::<ItemImpl>(item).unwrap();
19 match syn::parse::<Ident>(attrs.clone()) {
20 Ok(ident) => {
21 let rpc: Result<RpcProvider, String> =
22 Some(ident.to_string().as_str()).try_into();
23 match rpc {
24 Ok(rpc) => impl_rpc_provider(rpc, item),
25 Err(span) => {
26 return syn::Error::new(ident.span(), span)
27 .to_compile_error()
28 .into()
29 }
30 }
31 }
32 Err(_) => impl_rpc_provider(RpcProvider::RwLock, item),
33 }
34 }
35 }
36}
37
38fn struct_rpc(_: TokenStream, item: ItemStruct) -> TokenStream {
39 let ident = &item.ident;
40 let vis = &item.vis;
41 let peer_name = format_ident!("{}Peer", &ident);
42 let (implgen, tygen, whre) = item.generics.split_for_impl();
43
44 let ty_params = item.generics.type_params().map(|s| &s.ident);
45
46 quote!(
47 #item
48 #vis struct #peer_name #tygen #whre (pub ::srpc::__private::canary::Channel, ::core::marker::PhantomData<( #(#ty_params),* )>);
49 impl #implgen From<::srpc::__private::canary::Channel> for #peer_name #tygen #whre {
50 fn from(c: ::srpc::__private::canary::Channel) -> Self {
51 #peer_name(c, ::core::marker::PhantomData::default())
52 }
53 }
54 impl #implgen From<#peer_name #tygen> for ::srpc::__private::canary::Channel #whre {
55 fn from(c: #peer_name #tygen) -> Self {
56 c.0
57 }
58 }
59 impl #implgen ::srpc::Peer for #ident #tygen #whre {
60 type Struct = #peer_name #tygen;
61 }
62 )
63 .into()
64}
65
66#[derive(Clone)]
67struct Method<'a> {
68 ident: &'a Ident,
69 output: Option<&'a Box<Type>>,
70 inputs: Option<Vec<&'a PatType>>,
71 mutable: bool,
72 consume: MethodKind,
73 block: &'a Block,
74}
75
76#[derive(Clone)]
77enum MethodKind {
78 Normal, Consume, Manual, Server, Client, ServerManual, ClientManual, ServerConsume, ClientConsume, }
88
89enum RpcProvider {
90 RwLock,
91 Mutex,
92 Ref,
93}
94
95impl TryFrom<Option<&str>> for RpcProvider {
96 type Error = String;
97
98 fn try_from(value: Option<&str>) -> Result<Self, Self::Error> {
99 match value {
100 Some(s) => match s {
101 "rw" => Ok(RpcProvider::RwLock),
102 "rwlock" => Ok(RpcProvider::RwLock),
103 "mutex" => Ok(RpcProvider::Mutex),
104 "mtx" => Ok(RpcProvider::Mutex),
105 "none" => Ok(RpcProvider::Ref),
106 _ => Err(format!("{:?} not expected in this context, possible values are: `rw`, `rwlock`, `mutex`, `mtx` or `none`", s))
107 },
108 None => Ok(RpcProvider::RwLock)
109 }
110 }
111}
112
113impl RpcProvider {
114 fn get_meta(&self, mutable: bool, meta: &syn::Ident) -> quote::__private::TokenStream {
115 match self {
116 RpcProvider::RwLock => {
117 if mutable {
118 quote!(#meta.write().await)
119 } else {
120 quote!(#meta.read().await)
121 }
122 }
123 RpcProvider::Mutex => quote! {
124 #meta.lock().await
125 },
126 RpcProvider::Ref => quote! {
127 #meta
128 },
129 }
130 }
131 fn get_type<T: ToTokens>(&self, top: T) -> quote::__private::TokenStream {
132 match self {
133 RpcProvider::RwLock => quote! {
134 ::srpc::__private::RwLock<#top>
135 },
136 RpcProvider::Mutex => quote! {
137 ::srpc::__private::Mutex<#top>
138 },
139 RpcProvider::Ref => quote! {
140 #top
141 },
142 }
143 }
144}
145
146fn impl_rpc_provider(provider: RpcProvider, mut item: ItemImpl) -> TokenStream {
147 item.attrs.clear();
148 let methods = {
149 let mut items = item
150 .items
151 .clone()
152 .into_iter()
153 .filter(|s| {
154 if let ImplItem::Method(_) = s {
155 true
156 } else {
157 false
158 }
159 })
160 .map(|s| {
161 if let ImplItem::Method(s) = s {
162 s
163 } else {
164 unreachable!("please report this bug")
165 }
166 })
167 .collect::<Vec<_>>();
168 items.sort_by_cached_key(|elem| elem.attrs.len());
171 items
172 };
173
174 let top_type_name = {
175 if let Type::Path(s) = *item.self_ty.clone() {
176 s.path.segments.first().unwrap().clone().ident
177 } else {
178 panic!("unexpected type")
179 }
180 };
181 let top_type = &item.self_ty;
182
183 let top_type_meta = provider.get_type(&top_type);
184 let mut method_names = methods.iter().map(|s| &s.sig.ident).collect::<Vec<_>>();
185 method_names.sort();
186 method_names.dedup();
187 let repr = {
188 match method_names.len() {
189 0 => quote! { #[derive(::srpc::__private::Serialize, ::srpc::__private::Deserialize)] },
190 num if num < (u8::MAX as usize) => quote! {
191 #[derive(::srpc::__private::Serialize_repr, ::srpc::__private::Deserialize_repr)]
192 #[repr(u8)]
193 },
194 num if num < (u16::MAX as usize) => quote! {
195 #[derive(::srpc::__private::Serialize_repr, ::srpc::__private::Deserialize_repr)]
196 #[repr(u16)]
197 },
198 num if num < (u32::MAX as usize) => quote! {
199 #[derive(::srpc::__private::Serialize_repr, ::srpc::__private::Deserialize_repr)]
200 #[repr(u32)]
201 },
202 _ => quote! {
203 #[derive(::srpc::__private::Serialize_repr, ::srpc::__private::Deserialize_repr)]
204 #[repr(u64)]
205 },
206 }
207 };
208
209 let enum_repr = quote! (
210 #[allow(non_camel_case_types)]
211 #repr
212 enum __srpc_action {
213 #(#method_names),*
214 }
215 );
216
217 let methods = methods.iter().map(|s| {
218 let output = match &s.sig.output {
219 ReturnType::Default => None,
220 ReturnType::Type(_, ty) => Some(ty),
221 };
222 let block = &s.block;
223
224 let mut mutable = false;
225 let consume = s
226 .attrs
227 .iter()
228 .any(|attr| quote!(#[consume]).to_string() == quote!(#attr).to_string());
229 let manual = s
230 .attrs
231 .iter()
232 .any(|attr| quote!(#[manual]).to_string() == quote!(#attr).to_string());
233 let server = s
234 .attrs
235 .iter()
236 .any(|attr| quote!(#[server]).to_string() == quote!(#attr).to_string());
237 let server_consume = s
238 .attrs
239 .iter()
240 .any(|attr| quote!(#[server_consume]).to_string() == quote!(#attr).to_string());
241 let client = s
242 .attrs
243 .iter()
244 .any(|attr| quote!(#[client]).to_string() == quote!(#attr).to_string());
245 let client_consume = s
246 .attrs
247 .iter()
248 .any(|attr| quote!(#[client_consume]).to_string() == quote!(#attr).to_string());
249 let client_manual = s
250 .attrs
251 .iter()
252 .any(|attr| quote!(#[client_manual]).to_string() == quote!(#attr).to_string());
253 let server_manual = s
254 .attrs
255 .iter()
256 .any(|attr| quote!(#[server_manual]).to_string() == quote!(#attr).to_string());
257 let consume = match (
258 consume,
259 manual,
260 client,
261 server,
262 server_consume,
263 client_consume,
264 client_manual,
265 server_manual,
266 ) {
267 (true, false, false, false, false, false, false, false) => MethodKind::Consume,
268 (false, true, false, false, false, false, false, false) => MethodKind::Manual,
269 (false, false, true, false, false, false, false, false) => MethodKind::Client,
270 (false, false, false, true, false, false, false, false) => MethodKind::Server,
271 (false, false, false, false, true, false, false, false) => MethodKind::ServerConsume,
272 (false, false, false, false, false, true, false, false) => MethodKind::ClientConsume,
273 (false, false, false, false, false, false, false, false) => MethodKind::Normal,
274 (false, false, false, false, false, false, true, false) => MethodKind::ClientManual,
275 (false, false, false, false, false, false, false, true) => MethodKind::ServerManual,
276 (consume, manual, client, server, server_consume, client_consume, client_manual, server_manual)
277 if consume as u8
278 + manual as u8
279 + client as u8
280 + server as u8
281 + server_consume as u8
282 + client_consume as u8
283 + client_manual as u8
284 + server_manual as u8
285 > 1 =>
286 {
287 abort!(
288 &s.sig.ident.span(),
289 "cannot have a method with more than one modifier"
290 )
291 }
292 (_, _, _, _, _, _, _, _) => panic!("zasdfasdfasdf"),
293 };
294
295 let iter = s
296 .sig
297 .inputs
298 .iter()
299 .filter_map(|i| match i {
300 FnArg::Receiver(s) => {
301 mutable = s.mutability.is_some();
302 None
303 }
304 FnArg::Typed(ty) => Some(ty),
305 })
306 .collect::<Vec<_>>();
307 let inputs = if !iter.is_empty() { Some(iter) } else { None };
308 Method {
309 ident: &s.sig.ident,
310 output,
311 inputs,
312 mutable,
313 consume,
314 block,
315 }
316 });
317
318 let meta_ident = format_ident!("__srpc_inner_meta");
319 let channel_ident = format_ident!("__srpc_inner_channel");
320 let context_ident = format_ident!("__srpc_inner_ctx");
321
322 let matches = methods.clone().map(|method| {
323 let ident = method.ident;
324 let inputs = method.inputs;
325
326 let meta = provider.get_meta(method.mutable, &meta_ident);
327
328 match (inputs, method.output, method.consume) {
329 (None, None, MethodKind::Normal) => quote!(
330 __srpc_action::#ident => {
331 #meta.#ident(&#context_ident).await;
332 },
333 ),
334 (None, Some(_), MethodKind::Normal) => quote!(
335 __srpc_action::#ident => {
336 let res = #meta.#ident(&#context_ident).await;
337 #channel_ident.send(res).await?;
338 },
339 ),
340 (Some(inputs), None, MethodKind::Normal) => {
341 let mut args = vec![];
342 inputs.iter().map(|s| {
343 let s = &s.pat;
344 let arg = format_ident!("{}", quote!(#s).to_string());
345 args.push(arg);
346 }).for_each(drop);
347 let inputs = inputs.into_iter().map(|s| &s.ty);
348
349 let inputs = quote!( ( #(#args),* ): ( #(#inputs),* ) );
350 quote!(
351 __srpc_action::#ident => {
352 #[allow(unused_parens)]
353 let #inputs = #channel_ident.receive().await?;
354 #meta.#ident(#(#args),* , &#context_ident).await;
355 },
356 )
357 },
358 (Some(inputs), Some(_), MethodKind::Normal) => {
359 let mut args = vec![];
360 inputs.iter().map(|s| {
361 args.push(&s.pat);
362 }).for_each(drop);
363 let inputs = inputs.into_iter().map(|s| &s.ty);
364
365 let inputs = quote!( ( #(#args),* ): ( #(#inputs),* ) );
366 quote!(
367 __srpc_action::#ident => {
368 #[allow(unused_parens)]
369 let #inputs = #channel_ident.receive().await?;
370 let res = #meta.#ident(#(#args),* , &#context_ident).await;
371 #channel_ident.send(res).await?;
372 },
373 )
374 },
375 (Some(inputs), Some(_), MethodKind::Consume) => {
376 if inputs.len() != 1 {
377 abort!(method.ident.span(), "methods that consume can only have one argument with type Channel and return a canary::Result<()>")
378 }
379 quote! {
380 __srpc_action::#ident => {
381 return #meta.#ident(#channel_ident, &#context_ident).await;
382 },
383 }
384 }
385 (Some(inputs), Some(_), MethodKind::Manual) => {
386 if inputs.len() != 1 {
387 abort!(method.ident.span(), "manual methods can only have one argument with type Channel and return a canary::Result<Channel>")
388 }
389 quote! {
390 __srpc_action::#ident => {
391 match #meta.#ident(#channel_ident, &#context_ident).await {
392 Ok(chan) => #channel_ident = chan,
393 Err(e) => return Err(e),
394 }
395 },
396 }
397 },
398 (Some(inputs), Some(_), MethodKind::Server) => {
399 if inputs.len() != 1 {
400 abort!(method.ident.span(), "manual methods can only have one argument with type Channel and return a canary::Result<Channel>")
401 }
402 quote! {
403 __srpc_action::#ident => {
404 match #meta.#ident(&mut #channel_ident, &#context_ident).await {
405 Ok(_) => (),
406 Err(e) => return Err(e),
407 }
408 },
409 }
410 },
411 (Some(inputs), Some(_), MethodKind::ServerConsume) => {
412 if inputs.len() != 1 {
413 abort!(method.ident.span(), "methods that consume can only have one argument with type Channel and return a canary::Result<()>")
414 }
415 quote! {
416 __srpc_action::#ident => {
417 return #meta.#ident(#channel_ident, &#context_ident).await;
418 },
419 }
420 }
421
422 (Some(inputs), Some(_), MethodKind::ServerManual) => {
423 if inputs.len() != 1 {
424 abort!(method.ident.span(), "manual methods can only have one argument with type Channel and return a canary::Result<Channel>")
425 }
426 quote! {
427 __srpc_action::#ident => {
428 match #meta.#ident(#channel_ident, &#context_ident).await {
429 Ok(chan) => #channel_ident = chan,
430 Err(e) => return Err(e),
431 }
432 },
433 }
434 },
435
436 (_, _, MethodKind::Client) => quote!(),
437 (_, _, MethodKind::ClientConsume) => quote!(),
438 (_, _, MethodKind::ClientManual) => quote!(),
439 (_, _, MethodKind::Server) => abort!(method.ident.span(), "server methods need an argument with type Channel and return a canary::Result<Channel>"),
440 (_, _, MethodKind::ServerConsume) => abort!(method.ident.span(), "server methods need an argument with type Channel and a context, and return a canary::Result<()>"),
441 (_, _, MethodKind::ServerManual) => abort!(method.ident.span(), "server methods need an argument with type Channel and a context, and return a canary::Result<Channel>"),
442 (_, _, MethodKind::Consume) => abort!(method.ident.span(), "methods that consume need an argument with type Channel and return a canary::Result<()>"),
443 (None, None, MethodKind::Manual) => abort!(method.ident.span(), "manual methods need an argument with type Channel and return a canary::Result<Channel>"),
444 (_, _, MethodKind::Manual) => abort!(method.ident.span(), "manual methods can only return a canary::Result<Channel>"),
445 }
446 }).collect::<Vec<_>>();
447
448 let (_, ty_generics, whr) = item.generics.split_for_impl();
449 let impl_generics = {
450 let s = item.generics.type_params().map(|s| {
451 let mut s = s.clone();
452 s.bounds.push(syn::parse2(quote!(Send)).unwrap());
453 s.bounds.push(syn::parse2(quote!(Sync)).unwrap());
454 s.bounds.push(syn::parse2(quote!('static)).unwrap());
455 s.bounds
456 .push(syn::parse2(quote!(::srpc::__private::Serialize)).unwrap());
457 s.bounds
458 .push(syn::parse2(quote!(::srpc::__private::DeserializeOwned)).unwrap());
459 s
460 });
461 quote!(<#(#s),*>)
462 };
463 let endpoint = &top_type_name.to_string().to_snake_case();
464 let function = {
465 let impl_generics = impl_generics.clone();
466 quote! {
467 impl #impl_generics ::srpc::__private::canary::service::Service for #top_type #whr {
468 const ENDPOINT: &'static str = #endpoint;
469 type Pipeline = ();
470 type Meta = ::std::sync::Arc<#top_type_meta>;
471 fn service(
472 #meta_ident: ::std::sync::Arc<#top_type_meta>,
473 ) -> ::srpc::__private::canary::service::Svc {
474 ::srpc::__private::canary::service::run_metadata(
475 #meta_ident,
476 |#meta_ident: ::std::sync::Arc<#top_type_meta>, mut #channel_ident: ::srpc::__private::canary::Channel, #context_ident: ::srpc::__private::canary::Ctx| async move {
477 loop {
478 match #channel_ident.receive::<__srpc_action>().await? {
479 #(#matches)*
480 }
481 }
482 })
483 }
484 }
485 }
486 };
487
488 let peer_name = format_ident!("{}Peer", top_type_name);
489
490 let peer_methods = methods.map(|method| {
491 let has_output = method.output.is_some();
492 let has_input = method.inputs.is_some();
493
494 let res = method.output
495 .map(|s| *s.clone())
496 .unwrap_or(syn::parse2(quote!(())).unwrap());
497 let result = quote!( ::srpc::__private::canary::Result<#res> );
498
499 let inputs = method.inputs.unwrap_or_default();
500 let mut params = vec![];
501 let name = method.ident;
502
503 let args = inputs.clone().into_iter().map(|inp| {
504 let mut inp = inp.clone();
505 let ty = inp.ty;
506 let ty: Type = syn::parse2(quote!(impl std::borrow::Borrow<#ty>)).unwrap();
507 inp.ty = Box::new(ty);
508
509 let ret = quote!( #inp );
510
511 let arg = inp.pat;
512 params.push(arg);
513 ret
514 }).collect::<Vec<_>>();
515
516 match (has_output, has_input, &method.consume) {
517 (true, true, MethodKind::Normal) => quote! {
518 pub async fn #name(&mut self #(,#args)*) -> #result {
519 self.0.send(__srpc_action::#name).await?;
520 #[allow(unused_parens)]
521 self.0.send((#(#params.borrow()),*)).await?;
522 self.0.receive().await
523 }
524 },
525 (true, false, MethodKind::Normal) => quote! {
526 pub async fn #name(&mut self) -> #result {
527 self.0.send(__srpc_action::#name).await?;
528 self.0.receive().await
529 }
530 },
531 (false, true, MethodKind::Normal) => quote! {
532 pub async fn #name(&mut self #(,#args)*) -> #result {
533 self.0.send(__srpc_action::#name).await?;
534 #[allow(unused_parens)]
535 self.0.send((#(#params.borrow()),*)).await?;
536 Ok(())
537 }
538 },
539 (false, false, MethodKind::Normal) => quote! {
540 pub async fn #name(&mut self) -> #result {
541 self.0.send(__srpc_action::#name).await?;
542 Ok(())
543 }
544 },
545 (true, true, MethodKind::Consume) => quote! {
546 pub async fn #name(mut self) -> ::srpc::__private::canary::Result<::srpc::__private::canary::Channel> {
547 self.0.send(__srpc_action::#name).await?;
548 Ok(self.0)
549 }
550 },
551 (true, true, MethodKind::ClientConsume) => {
552 let block = method.block;
553 let mut inputs = inputs.clone();
554 let chan_ident = &inputs.first().take().unwrap().pat;
555 inputs.remove(0);
556 quote! { pub async fn #name(mut self #(,#inputs)*) -> #res {
558 self.0.send(__srpc_action::#name).await?;
559
560 let #chan_ident = self.0;
561
562 let __private_srpc_inner_result = {
563 #block
564 };
565 __private_srpc_inner_result
566 }
567 }
568 },
569 (true, true, MethodKind::ClientManual) => {
570 let block = method.block;
571 let mut inputs = inputs.clone();
572 let chan_ident = &inputs.first().take().unwrap().pat;
573 let chan = inputs.remove(0);
574 let chan_ty = &chan.ty;
575 quote! { pub async fn #name(&mut self #(,#inputs)*) -> #res {
577 self.0.send(__srpc_action::#name).await?;
578
579 let #chan_ident: #chan_ty = &mut self.0;
580
581 let __private_srpc_inner_result = {
582 #block
583 };
584 __private_srpc_inner_result
585 }
586 }
587 },
588
589 (true, true, MethodKind::Manual) => quote! {
590 pub async fn #name(mut self) -> ::srpc::__private::canary::Result<::srpc::__private::canary::Channel> {
591 self.0.send(__srpc_action::#name).await?;
592 Ok(self.0)
593 }
594 },
595 (_, true, MethodKind::Client) => {
596 let block = method.block;
597 let mut inputs = inputs.clone();
598 let chan_ident = &inputs.first().take().unwrap().pat;
599 inputs.remove(0);
600 quote! {
601 pub async fn #name(self #(,#inputs)*) -> #res {
602 self.0.send(__srpc_action::#name).await?;
603
604 let #chan_ident = self.0;
605
606 let __private_srpc_inner_result = {
607 #block
608 };
609 __private_srpc_inner_result
610 }
611 }
612 },
613 (_, _, MethodKind::Server) => quote!(),
614 (_, _, MethodKind::ServerConsume) => quote!(),
615 (_, _, MethodKind::ServerManual) => quote!(),
616 (_, _, MethodKind::Client) => abort!(method.ident.span(), "client methods can only have an argument with type &mut Channel"),
617 (_, _, MethodKind::ClientManual) => abort!(method.ident.span(), "manual methods need to have an argument with type &mut Channel and return a canary::Result<T>"),
618 (_, _, MethodKind::ClientConsume) => abort!(method.ident.span(), "methods that consume need to have an argument with type Channel and return a canary::Result<T>"),
619 (_, _, MethodKind::Consume) => abort!(method.ident.span(), "methods that consume can only have an argument with type Channel and return a canary::Result<()>"),
620 (_, _, MethodKind::Manual) => abort!(method.ident.span(), "manual methods can only have an argument with type Channel and return a canary::Result<Channel>"),
621 }
622
623 }).collect::<Vec<_>>();
624 let peer_impl = quote! {
625 impl #impl_generics #peer_name #ty_generics #whr {
626 #(#peer_methods)*
627 }
628 };
629
630 let items = item
631 .items
632 .into_iter()
633 .filter(|s| {
634 let mut stay = true;
635 if let ImplItem::Method(method) = &s {
636 let attrs = &method.attrs;
637 for attr in attrs {
638 if quote!(#[client]).to_string() == quote!(#attr).to_string()
639 || quote!(#[client_consume]).to_string() == quote!(#attr).to_string()
640 || quote!(#[client_manual]).to_string() == quote!(#attr).to_string()
641 {
642 stay = false;
643 }
644 }
645 }
646 stay
647 })
648 .map(|mut s| {
649 if let ImplItem::Method(method) = &mut s {
650 let attrs = method.attrs.clone();
651 let mut new_attrs = vec![];
652 for attr in attrs {
653 if quote!(#[consume]).to_string() != quote!(#attr).to_string()
654 && quote!(#[manual]).to_string() != quote!(#attr).to_string()
655 && quote!(#[server]).to_string() != quote!(#attr).to_string()
656 && quote!(#[server_consume]).to_string() != quote!(#attr).to_string()
657 && quote!(#[server_manual]).to_string() != quote!(#attr).to_string()
658 {
659 new_attrs.push(attr)
660 }
661 }
662 method.attrs = new_attrs;
663 }
664 s
665 })
666 .collect::<Vec<_>>();
667 item.items = items;
668 item.generics
669 .type_params_mut()
670 .map(|s| {
671 s.bounds
672 .push(syn::parse2(quote!(::srpc::__private::Serialize)).unwrap());
673 s.bounds
674 .push(syn::parse2(quote!(::srpc::__private::DeserializeOwned)).unwrap());
675 })
676 .for_each(drop);
677
678 item.items
679 .iter_mut()
680 .map(|f| {
681 if let ImplItem::Method(f) = f {
682 let typed =
683 syn::parse2(quote!(#context_ident: &::srpc::__private::canary::Ctx)).unwrap();
684 f.sig.inputs.push(typed);
685 }
686 })
687 .for_each(drop);
688
689 let ts = quote!(
690 const _: () = {
691 #[cfg(not(target_arch = "wasm32"))]
692 #item
693 #enum_repr
694 #[cfg(not(target_arch = "wasm32"))]
695 #function
696 #peer_impl
697 };
698 );
699 ts.into()
700}