1#[macro_use]
33extern crate syn;
34#[macro_use]
35extern crate quote;
36extern crate proc_macro;
37extern crate proc_macro2;
38
39use proc_macro::TokenStream;
42use syn::{FnArg, Ident, ItemTrait, ReturnType, Signature, TraitItem, TraitItemMethod};
43
44#[cfg(feature = "invoke")]
71#[proc_macro_attribute]
72pub fn invoke_interface(_attr: TokenStream, input: TokenStream) -> TokenStream {
73 let ItemTrait {
75 attrs,
76 vis,
77 unsafety,
78 auto_token,
79 trait_token,
80 ident,
81 generics,
82 colon_token,
83 supertraits,
84 items,
86 ..
87 } = parse_macro_input!(input as ItemTrait);
88
89 let methods: Vec<TraitItemMethod> = items
90 .iter()
91 .map(|i| {
92 if let TraitItem::Method(m) = i {
93 Some(m)
94 } else {
95 None
96 }
97 })
98 .filter(|i| i.is_some())
99 .map(|x| x.unwrap().clone())
100 .collect();
101 let idents_collected: Vec<_> = methods
102 .iter()
103 .map(|x| {
104 let TraitItemMethod {
105 attrs,
106 sig,
107 default,
108 semi_token,
109 } = x;
110 let Signature {
111 constness,
112 asyncness,
113 unsafety,
114 abi,
115 fn_token,
116 ident,
117 generics,
118 inputs,
120 variadic,
121 output,
122 ..
123 } = sig;
124
125 let output_type = match output.clone() {
126 ReturnType::Default => quote! {()},
127 ReturnType::Type(_, t) => quote! {#t},
128 };
129 let ident_camel = Ident::new(&snake_to_camel(&ident.to_string()), ident.span());
130 let args: Vec<_> = inputs
131 .iter()
132 .map(|i| {
133 if let FnArg::Typed(pat) = i {
134 Some(pat)
135 } else {
136 None
137 }
138 })
139 .filter(|i| i.is_some())
140 .map(|x| {
141 let x = &x.unwrap().pat;
142 quote! {#x,}
143 })
144 .collect();
145 let input_receiver: Vec<_> = inputs
146 .iter()
147 .map(|i| {
148 if let FnArg::Receiver(receiver) = i {
149 Some(receiver)
150 } else {
151 None
152 }
153 })
154 .filter(|i| i.is_some())
155 .map(|x| {
156 let x = x.unwrap();
157 quote! {#x,}
158 })
159 .collect();
160 let inputs: Vec<_> = inputs
161 .iter()
162 .map(|i| {
163 if let FnArg::Typed(pat) = i {
164 Some(pat)
165 } else {
166 None
167 }
168 })
169 .filter(|i| i.is_some())
170 .map(|x| {
171 let x = x.unwrap();
172 quote! {#x,}
173 })
174 .collect();
175 let method = quote! {
176 #(#attrs)*
177 #constness #asyncness #unsafety #abi #fn_token #ident #generics (
178 #(#input_receiver)* ctx: Option<servant::Context>,
179 #(#inputs)* #variadic
180 ) #output
181 #default #semi_token
182 };
183 (ident, ident_camel, args, inputs, method, output_type)
184 })
185 .collect();
186 let ident_vec: Vec<_> = idents_collected.iter().map(|i| i.0.clone()).collect();
187 let idents_camel_vec: Vec<_> = idents_collected.iter().map(|i| i.1.clone()).collect();
188 let args_vec: Vec<_> = idents_collected.iter().map(|i| i.2.clone()).collect();
189 let inputs_vec: Vec<_> = idents_collected.iter().map(|i| i.3.clone()).collect();
190 let methods_vec: Vec<_> = idents_collected.iter().map(|i| i.4.clone()).collect();
191 let output_vec: Vec<_> = idents_collected.iter().map(|i| i.5.clone()).collect();
192
193 let request_ident = Ident::new(&format!("{}Request", ident), ident.span());
194 let request_ident_vect: Vec<_> = idents_collected
195 .iter()
196 .map(|_| request_ident.clone())
197 .collect();
198 let servant_ident = Ident::new(&format!("{}Servant", ident), ident.span());
199 let proxy_ident = Ident::new(&format!("{}Proxy", ident), ident.span());
200
201 let output1 = if cfg!(any(feature = "adapter", feature = "terminal")) { quote! {
202 #[derive(serde::Serialize, serde::Deserialize)]
203 enum #request_ident {
204 #(#idents_camel_vec { #(#inputs_vec)* },)*
205 }}
206 } else {
207 proc_macro2::TokenStream::new()
208 };
209 let output2 = if cfg!(feature = "adapter") { quote! {
210 #( #attrs )*
211 #vis #unsafety #auto_token #trait_token #ident #generics #colon_token #supertraits {
212 #(#methods_vec)*
213 }
214 pub struct #servant_ident<S> {
215 name: String,
216 entity: S,
217 }
218 impl<S> #servant_ident<S> {
219 pub fn new(name: &str, entity: S) -> Self {
220 Self { name: name.to_string(), entity }
221 }
222 }
223 impl<S> servant::Servant for #servant_ident<S>
224 where
225 S: #ident + 'static,
226 {
227 fn name(&self) -> &str {
228 &self.name
229 }
230 fn category(&self) -> &'static str {
231 stringify!(#ident)
232 }
233 fn serve(&mut self, ctx: Option<servant::Context>, req: Vec<u8>) -> Vec<u8> {
234 let req: #request_ident = bincode::deserialize(&req).unwrap();
235 let reps = match req {
236 #(
237 #request_ident_vect::#idents_camel_vec{ #(#args_vec)* } =>
238 bincode::serialize(&self.entity.#ident_vec(ctx, #(#args_vec)*)),
239 )*
240 }
241 .unwrap();
242 reps
243 }
244 }}
245 } else {
246 proc_macro2::TokenStream::new()
247 };
248
249 let output3 = if cfg!(feature = "terminal") { quote!{
250 #[derive(Clone)]
251 pub struct #proxy_ident(servant::Context, servant::Oid, servant::Terminal);
252
253 impl #proxy_ident {
254 pub fn new(ctx: servant::Context, name: &str, t: &servant::Terminal) -> Self {
255 let oid = servant::Oid::new(name, stringify!(#ident));
256 Self(ctx, oid, t.clone())
257 }
258
259 #(
260 pub async fn #ident_vec(
261 &mut self,
262 #(#inputs_vec)*
263 ) -> servant::ServantResult<#output_vec> {
264 let request = #request_ident_vect::#idents_camel_vec { #(#args_vec)* };
265 let response = self
266 .2
267 .invoke(Some(self.0.clone()), Some(self.1.clone()), bincode::serialize(&request).unwrap())
268 .await;
269 response.map(|x| bincode::deserialize(&x).unwrap())
270 }
271 )*
272 }}
273 } else {
274 proc_macro2::TokenStream::new()
275 };
276
277 let output = quote! {
278 #output1
279 #output2
280 #output3
281 };
282 output.into()
283}
284
285#[cfg(feature = "report")]
312#[proc_macro_attribute]
313pub fn report_interface(_attr: TokenStream, input: TokenStream) -> TokenStream {
314 let ItemTrait {
315 attrs,
316 vis,
317 unsafety,
318 auto_token,
319 trait_token,
320 ident,
321 generics,
322 colon_token,
323 supertraits,
324 items,
326 ..
327 } = parse_macro_input!(input as ItemTrait);
328
329 let methods: Vec<TraitItemMethod> = items
330 .iter()
331 .map(|i| {
332 if let TraitItem::Method(m) = i {
333 Some(m)
334 } else {
335 None
336 }
337 })
338 .filter(|i| i.is_some())
339 .map(|x| x.unwrap().clone())
340 .collect();
341 let idents_collected: Vec<_> = methods
342 .iter()
343 .map(|x| {
344 let TraitItemMethod {
345 attrs,
346 sig,
347 default,
348 semi_token,
349 } = x;
350 let Signature {
351 constness,
352 asyncness,
353 unsafety,
354 abi,
355 fn_token,
356 ident,
357 generics,
358 inputs,
360 variadic,
361 ..
363 } = sig;
364
365 let ident_camel = Ident::new(&snake_to_camel(&ident.to_string()), ident.span());
366 let args: Vec<_> = inputs
367 .iter()
368 .map(|i| {
369 if let FnArg::Typed(pat) = i {
370 Some(pat)
371 } else {
372 None
373 }
374 })
375 .filter(|i| i.is_some())
376 .map(|x| {
377 let x = &x.unwrap().pat;
378 quote! {#x,}
379 })
380 .collect();
381 let inputs2: Vec<_> = inputs
382 .iter()
383 .map(|i| {
384 if let FnArg::Typed(pat) = i {
385 Some(pat)
386 } else {
387 None
388 }
389 })
390 .filter(|i| i.is_some())
391 .map(|x| {
392 let x = x.unwrap();
393 quote! {#x,}
394 })
395 .collect();
396 let servant_method = quote! {
397 #(#attrs)*
398 #constness #asyncness #unsafety #abi #fn_token #ident #generics (
399 #inputs #variadic
400 )
401 #default #semi_token
402 };
403 (ident, ident_camel, args, inputs2, servant_method)
404 })
405 .collect();
406 let ident_vec: Vec<_> = idents_collected.iter().map(|i| i.0.clone()).collect();
407 let idents_camel_vec: Vec<_> = idents_collected.iter().map(|i| i.1.clone()).collect();
408 let args_vec: Vec<_> = idents_collected.iter().map(|i| i.2.clone()).collect();
409 let inputs_vec: Vec<_> = idents_collected.iter().map(|i| i.3.clone()).collect();
410 let methods_vec: Vec<_> = idents_collected.iter().map(|i| i.4.clone()).collect();
411
412 let request_ident = Ident::new(&format!("{}Request", ident), ident.span());
413 let request_ident_vect: Vec<_> = idents_collected
414 .iter()
415 .map(|_| request_ident.clone())
416 .collect();
417 let servant_ident = Ident::new(&format!("{}ReportServant", ident), ident.span());
418 let proxy_ident = Ident::new(&format!("{}ReportProxy", ident), ident.span());
419
420 let output1 = if cfg!(any(feature = "adapter", feature = "terminal")) { quote! {
421 #[derive(serde::Serialize, serde::Deserialize)]
422 enum #request_ident {
423 #(#idents_camel_vec { #(#inputs_vec)* },)*
424 }}
425 } else {
426 proc_macro2::TokenStream::new()
427 };
428 let output2 = if cfg!(feature = "adapter") { quote! {
429 #( #attrs )*
430 #vis #unsafety #auto_token #trait_token #ident #generics #colon_token #supertraits {
431 #(#methods_vec)*
432 }
433 pub struct #servant_ident<S> {
434 name: String,
435 entity: S,
436 }
437 impl<S> #servant_ident<S> {
438 pub fn new(name: &str, entity: S) -> Self {
439 Self { name: name.to_string(), entity }
440 }
441 }
442 impl<S> servant::ReportServant for #servant_ident<S>
443 where
444 S: #ident + 'static,
445 {
446 fn name(&self) -> &str {
447 &self.name
448 }
449 fn category(&self) -> &'static str {
450 stringify!(#ident)
451 }
452 fn serve(&mut self, req: Vec<u8>) {
453 let req: #request_ident = bincode::deserialize(&req).unwrap();
454 match req {
455 #(
456 #request_ident_vect::#idents_camel_vec{ #(#args_vec)* } =>
457 self.entity.#ident_vec(#(#args_vec)*),
458 )*
459 }
460 }
461 }}
462 } else {
463 proc_macro2::TokenStream::new()
464 };
465 let output3 = if cfg!(feature = "terminal") { quote!{
466 #[derive(Clone)]
467 pub struct #proxy_ident(servant::Oid, servant::Terminal);
468
469 impl #proxy_ident {
470 pub fn new(name: &str, t: &servant::Terminal) -> Self {
471 let oid = servant::Oid::new(name, stringify!(#ident));
472 Self(oid, t.clone())
473 }
474
475 #(
476 #[allow(unused)]
477 pub async fn #ident_vec(
478 &mut self,
479 #(#inputs_vec)*
480 ) -> servant::ServantResult<()> {
481 let request = #request_ident_vect::#idents_camel_vec { #(#args_vec)* };
482 let response = self
483 .1
484 .report(self.0.clone(), bincode::serialize(&request).unwrap())
485 .await;
486 response
487 }
488 )*
489 }}
490 } else {
491 proc_macro2::TokenStream::new()
492 };
493
494 let output = quote! {
495 #output1
496 #output2
497 #output3
498 };
499 output.into()
500}
501
502#[cfg(feature = "query")]
530#[proc_macro_attribute]
531pub fn query_interface(_attr: TokenStream, input: TokenStream) -> TokenStream {
532 let ItemTrait {
533 attrs,
534 vis,
535 unsafety,
536 auto_token,
537 trait_token,
538 ident,
539 generics,
540 colon_token,
541 supertraits,
542 items,
544 ..
545 } = parse_macro_input!(input as ItemTrait);
546
547 let methods: Vec<TraitItemMethod> = items
548 .iter()
549 .map(|i| {
550 if let TraitItem::Method(m) = i {
551 Some(m)
552 } else {
553 None
554 }
555 })
556 .filter(|i| i.is_some())
557 .map(|x| x.unwrap().clone())
558 .collect();
559 let idents_collected: Vec<_> = methods
560 .iter()
561 .map(|x| {
562 let TraitItemMethod {
563 attrs,
564 sig,
565 default,
566 semi_token,
567 } = x;
568 let Signature {
569 constness,
570 asyncness,
571 unsafety,
572 abi,
573 fn_token,
574 ident,
575 generics,
576 inputs,
578 variadic,
579 output,
580 ..
581 } = sig;
582
583 let output_type = match output.clone() {
584 ReturnType::Default => quote! {()},
585 ReturnType::Type(_, t) => quote! {#t},
586 };
587 let ident_camel = Ident::new(&snake_to_camel(&ident.to_string()), ident.span());
588 let args: Vec<_> = inputs
589 .iter()
590 .map(|i| {
591 if let FnArg::Typed(pat) = i {
592 Some(pat)
593 } else {
594 None
595 }
596 })
597 .filter(|i| i.is_some())
598 .map(|x| {
599 let x = &x.unwrap().pat;
600 quote! {#x,}
601 })
602 .collect();
603 let inputs: Vec<_> = inputs
604 .iter()
605 .map(|i| {
606 if let FnArg::Typed(pat) = i {
607 Some(pat)
608 } else {
609 None
610 }
611 })
612 .filter(|i| i.is_some())
613 .map(|x| {
614 let x = x.unwrap();
615 quote! {#x,}
616 })
617 .collect();
618 let method = quote! {
619 #(#attrs)*
620 #constness #asyncness #unsafety #abi #fn_token #ident #generics (
621 &mut self,
622 #(#inputs)* #variadic
623 ) #output
624 #default #semi_token
625 };
626 (ident, ident_camel, args, inputs, method, output_type)
627 })
628 .collect();
629 let ident_vec: Vec<_> = idents_collected.iter().map(|i| i.0.clone()).collect();
630 let idents_camel_vec: Vec<_> = idents_collected.iter().map(|i| i.1.clone()).collect();
631 let args_vec: Vec<_> = idents_collected.iter().map(|i| i.2.clone()).collect();
632 let inputs_vec: Vec<_> = idents_collected.iter().map(|i| i.3.clone()).collect();
633 let _methods_vec: Vec<_> = idents_collected.iter().map(|i| i.4.clone()).collect();
634 let output_vec: Vec<_> = idents_collected.iter().map(|i| i.5.clone()).collect();
635
636 let request_ident = Ident::new(&format!("{}Request", ident), ident.span());
637 let request_ident_vect: Vec<_> = idents_collected
638 .iter()
639 .map(|_| request_ident.clone())
640 .collect();
641 let servant_ident = Ident::new(&format!("{}Servant", ident), ident.span());
642 let proxy_ident = Ident::new(&format!("{}Proxy", ident), ident.span());
643
644 let output1 = if cfg!(any(feature = "adapter", feature = "terminal")) { quote! {
645 #[derive(serde::Serialize, serde::Deserialize)]
646 enum #request_ident {
647 #(#idents_camel_vec { #(#inputs_vec)* },)*
648 }}
649 } else {
650 proc_macro2::TokenStream::new()
651 };
652 let output2 = if cfg!(feature = "adapter") { quote! {
653 #( #attrs )*
654 #vis #unsafety #auto_token #trait_token #ident #generics #colon_token #supertraits {
655 #(#methods)*
656 }
657 pub struct #servant_ident<S> {
658 entity: S,
659 }
660 impl<S> #servant_ident<S> {
661 pub fn new(entity: S) -> Self {
662 Self { entity }
663 }
664 }
665 impl<S> servant::Servant for #servant_ident<S>
666 where
667 S: #ident + 'static,
668 {
669 fn name(&self) -> &str {
670 ""
671 }
672 fn category(&self) -> &'static str {
673 stringify!(#ident)
674 }
675 fn serve(&mut self, _ctx: Option<servant::Context>, req: Vec<u8>) -> Vec<u8> {
676 let req: #request_ident = bincode::deserialize(&req).unwrap();
677 let reps = match req {
678 #(
679 #request_ident_vect::#idents_camel_vec{ #(#args_vec)* } =>
680 bincode::serialize(&self.entity.#ident_vec(#(#args_vec)*)),
681 )*
682 }
683 .unwrap();
684 reps
685 }
686 }}
687 } else {
688 proc_macro2::TokenStream::new()
689 };
690 let output3 = if cfg!(feature = "terminal") { quote!{
691 #[derive(Clone)]
692 pub struct #proxy_ident(servant::Terminal);
693
694 impl #proxy_ident {
695 pub fn new(t: &servant::Terminal) -> Self {
696 Self(t.clone())
697 }
698
699 #(
700 pub async fn #ident_vec(
701 &mut self,
702 #(#inputs_vec)*
703 ) -> servant::ServantResult<#output_vec> {
704 let request = #request_ident_vect::#idents_camel_vec { #(#args_vec)* };
705 let response = self
706 .0
707 .invoke(None, None, bincode::serialize(&request).unwrap())
708 .await;
709 response.map(|x| bincode::deserialize(&x).unwrap())
710 }
711 )*
712 }}
713 } else {
714 proc_macro2::TokenStream::new()
715 };
716
717 let output = quote! {
718 #output1
719 #output2
720 #output3
721 };
722 output.into()
723}
724
725
726#[cfg(feature = "notify")]
752#[proc_macro_attribute]
753pub fn notify_interface(_attr: TokenStream, input: TokenStream) -> TokenStream {
754 let ItemTrait {
755 attrs,
756 vis,
757 unsafety,
758 auto_token,
759 trait_token,
760 ident,
761 generics,
762 colon_token,
763 supertraits,
764 items,
766 ..
767 } = parse_macro_input!(input as ItemTrait);
768
769 let methods: Vec<TraitItemMethod> = items
770 .iter()
771 .map(|i| {
772 if let TraitItem::Method(m) = i {
773 Some(m)
774 } else {
775 None
776 }
777 })
778 .filter(|i| i.is_some())
779 .map(|x| x.unwrap().clone())
780 .collect();
781 let idents_collected: Vec<_> = methods
782 .iter()
783 .map(|x| {
784 let TraitItemMethod {
785 attrs,
786 sig,
787 default,
788 semi_token,
789 } = x;
790 let Signature {
791 constness,
792 asyncness,
793 unsafety,
794 abi,
795 fn_token,
796 ident,
797 generics,
798 inputs,
800 variadic,
801 ..
803 } = sig;
804
805 let ident_camel = Ident::new(&snake_to_camel(&ident.to_string()), ident.span());
806 let args: Vec<_> = inputs
807 .iter()
808 .map(|i| {
809 if let FnArg::Typed(pat) = i {
810 Some(pat)
811 } else {
812 None
813 }
814 })
815 .filter(|i| i.is_some())
816 .map(|x| {
817 let x = &x.unwrap().pat;
818 quote! {#x,}
819 })
820 .collect();
821 let inputs2: Vec<_> = inputs
822 .iter()
823 .map(|i| {
824 if let FnArg::Typed(pat) = i {
825 Some(pat)
826 } else {
827 None
828 }
829 })
830 .filter(|i| i.is_some())
831 .map(|x| {
832 let x = x.unwrap();
833 quote! {#x,}
834 })
835 .collect();
836 let servant_method = quote! {
837 #(#attrs)*
838 #constness #asyncness #unsafety #abi #fn_token #ident #generics (
839 #inputs #variadic
840 )
841 #default #semi_token
842 };
843 (ident, ident_camel, args, inputs2, servant_method)
844 })
845 .collect();
846 let ident_vec: Vec<_> = idents_collected.iter().map(|i| i.0.clone()).collect();
847 let idents_camel_vec: Vec<_> = idents_collected.iter().map(|i| i.1.clone()).collect();
848 let args_vec: Vec<_> = idents_collected.iter().map(|i| i.2.clone()).collect();
849 let inputs_vec: Vec<_> = idents_collected.iter().map(|i| i.3.clone()).collect();
850 let methods_vec: Vec<_> = idents_collected.iter().map(|i| i.4.clone()).collect();
851
852 let request_ident = Ident::new(&format!("{}Request", ident), ident.span());
853 let request_ident_vect: Vec<_> = idents_collected
854 .iter()
855 .map(|_| request_ident.clone())
856 .collect();
857 let receiver_ident = Ident::new(&format!("{}Receiver", ident), ident.span());
858 let notifier_ident = Ident::new(&format!("{}Notifier", ident), ident.span());
859
860 let output1 = if cfg!(any(feature = "adapter", feature = "terminal")) { quote! {
861 #[derive(serde::Serialize, serde::Deserialize)]
862 enum #request_ident {
863 #(#idents_camel_vec { #(#inputs_vec)* },)*
864 }}
865 } else {
866 proc_macro2::TokenStream::new()
867 };
868 let output2 = if cfg!(feature = "terminal") { quote! {
869 #( #attrs )*
870 #vis #unsafety #auto_token #trait_token #ident #generics #colon_token #supertraits {
871 #(#methods_vec)*
872 }
873 pub struct #receiver_ident<S> {
874 entity: S,
875 }
876 impl<S> #receiver_ident<S> {
877 pub fn new(entity: S) -> Self {
878 Self { entity }
879 }
880 }
881 impl<S> servant::NotifyServant for #receiver_ident<S>
882 where
883 S: #ident + 'static + Send,
884 {
885 fn serve(&mut self, req: Vec<u8>) {
886 let req: #request_ident = bincode::deserialize(&req).unwrap();
887 match req {
888 #(
889 #request_ident_vect::#idents_camel_vec{ #(#args_vec)* } =>
890 self.entity.#ident_vec(#(#args_vec)*),
891 )*
892 }
893 }
894 }}
895 } else {
896 proc_macro2::TokenStream::new()
897 };
898 let output3 = if cfg!(feature = "adapter") { quote!{
899 pub struct #notifier_ident(&'static servant::AdapterRegister);
900
901 impl #notifier_ident {
902 pub fn instance() -> &'static #notifier_ident {
903 static mut NOTIFIER: Option<#notifier_ident> = None;
904 static INIT: std::sync::Once = std::sync::Once::new();
905
906 unsafe {
907 INIT.call_once(|| {
908 NOTIFIER = Some(#notifier_ident(servant::AdapterRegister::instance()));
909 });
910 NOTIFIER.as_ref().unwrap()
911 }
912 }
913
914 #(
915 pub async fn #ident_vec(
916 &self,
917 #(#inputs_vec)*
918 ) {
919 let request = #request_ident_vect::#idents_camel_vec { #(#args_vec)* };
920 self
921 .0
922 .send(bincode::serialize(&request).unwrap())
923 .await
924 }
925 )*
926 }}
927 } else {
928 proc_macro2::TokenStream::new()
929 };
930
931 let output = quote! {
932 #output1
933 #output2
934 #output3
935 };
936 output.into()
937}
938
939fn snake_to_camel(ident_str: &str) -> String {
942 let mut camel_ty = String::new();
943 let chars = ident_str.chars();
944
945 let mut last_char_was_underscore = true;
946 for c in chars {
947 match c {
948 '_' => last_char_was_underscore = true,
949 c if last_char_was_underscore => {
950 camel_ty.extend(c.to_uppercase());
951 last_char_was_underscore = false;
952 }
953 c => camel_ty.extend(c.to_lowercase()),
954 }
955 }
956
957 camel_ty
958}
959
960#[allow(unused)]
961fn snake_to_camel2(ident_str: &str) -> String {
962 let mut camel_ty = String::new();
963 let chars = ident_str.chars();
964
965 let mut last_char_was_underscore = true;
966 for c in chars {
967 match c {
968 '_' => last_char_was_underscore = true,
969 c => camel_ty.push_str(&if last_char_was_underscore {
970 last_char_was_underscore = false;
971 c.to_uppercase().to_string()
972 } else {
973 c.to_lowercase().to_string()
974 }),
975 }
976 }
977
978 camel_ty
979}
980
981#[cfg(test)]
984mod tests {
985 extern crate test_case;
986 use super::*;
987 use test_case::test_case;
988
989 #[test_case("abc_def" => "AbcDef".to_string(); "basic")]
992 #[test_case("abc_def_" => "AbcDef".to_string(); "suffix")]
993 #[test_case("_abc_def"=> "AbcDef".to_string(); "prefix")]
994 #[test_case("abc__def"=> "AbcDef".to_string(); "consecutive")]
995 #[test_case("aBc_dEf"=> "AbcDef".to_string(); "middle")]
996 #[test_case("__abc__def__" => "AbcDef".to_string(); "double middle")]
997 fn test_snake_to_camel(ident_str: &str) -> String {
998 snake_to_camel2(ident_str)
1000 }
1001}