1#![allow(clippy::new_ret_no_self)]
16
17use crate::serde_hex;
18#[cfg(not(feature = "std"))]
19use alloc::{
20 format,
21 vec,
22 vec::Vec,
23};
24use core::marker::PhantomData;
25use tetsy_scale_info::{
26 form::{
27 Form,
28 MetaForm,
29 PortableForm,
30 },
31 meta_type,
32 IntoPortable,
33 Registry,
34 TypeInfo,
35};
36use serde::{
37 de::DeserializeOwned,
38 Deserialize,
39 Serialize,
40};
41
42#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(bound(
45 serialize = "F::Type: Serialize, F::String: Serialize",
46 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
47))]
48pub struct ContractSpec<F: Form = MetaForm> {
49 constructors: Vec<ConstructorSpec<F>>,
51 messages: Vec<MessageSpec<F>>,
53 events: Vec<EventSpec<F>>,
55 docs: Vec<F::String>,
57}
58
59impl IntoPortable for ContractSpec {
60 type Output = ContractSpec<PortableForm>;
61
62 fn into_portable(self, registry: &mut Registry) -> Self::Output {
63 ContractSpec {
64 constructors: self
65 .constructors
66 .into_iter()
67 .map(|constructor| constructor.into_portable(registry))
68 .collect::<Vec<_>>(),
69 messages: self
70 .messages
71 .into_iter()
72 .map(|msg| msg.into_portable(registry))
73 .collect::<Vec<_>>(),
74 events: self
75 .events
76 .into_iter()
77 .map(|event| event.into_portable(registry))
78 .collect::<Vec<_>>(),
79 docs: registry.map_into_portable(self.docs),
80 }
81 }
82}
83
84impl<F> ContractSpec<F>
85where
86 F: Form,
87{
88 pub fn constructors(&self) -> &[ConstructorSpec<F>] {
90 &self.constructors
91 }
92
93 pub fn messages(&self) -> &[MessageSpec<F>] {
95 &self.messages
96 }
97
98 pub fn events(&self) -> &[EventSpec<F>] {
100 &self.events
101 }
102
103 pub fn docs(&self) -> &[F::String] {
105 &self.docs
106 }
107}
108
109pub enum Valid {}
111pub enum Invalid {}
113
114pub struct ContractSpecBuilder<S = Invalid> {
116 spec: ContractSpec,
118 marker: PhantomData<fn() -> S>,
120}
121
122impl ContractSpecBuilder<Invalid> {
123 pub fn constructors<C>(self, constructors: C) -> ContractSpecBuilder<Valid>
125 where
126 C: IntoIterator<Item = ConstructorSpec>,
127 {
128 debug_assert!(self.spec.constructors.is_empty());
129 ContractSpecBuilder {
130 spec: ContractSpec {
131 constructors: constructors.into_iter().collect::<Vec<_>>(),
132 ..self.spec
133 },
134 marker: Default::default(),
135 }
136 }
137}
138
139impl<S> ContractSpecBuilder<S> {
140 pub fn messages<M>(self, messages: M) -> Self
142 where
143 M: IntoIterator<Item = MessageSpec>,
144 {
145 debug_assert!(self.spec.messages.is_empty());
146 Self {
147 spec: ContractSpec {
148 messages: messages.into_iter().collect::<Vec<_>>(),
149 ..self.spec
150 },
151 ..self
152 }
153 }
154
155 pub fn events<E>(self, events: E) -> Self
157 where
158 E: IntoIterator<Item = EventSpec>,
159 {
160 debug_assert!(self.spec.events.is_empty());
161 Self {
162 spec: ContractSpec {
163 events: events.into_iter().collect::<Vec<_>>(),
164 ..self.spec
165 },
166 ..self
167 }
168 }
169
170 pub fn docs<D>(self, docs: D) -> Self
172 where
173 D: IntoIterator<Item = &'static str>,
174 {
175 debug_assert!(self.spec.docs.is_empty());
176 Self {
177 spec: ContractSpec {
178 docs: docs.into_iter().collect::<Vec<_>>(),
179 ..self.spec
180 },
181 ..self
182 }
183 }
184}
185
186impl ContractSpecBuilder<Valid> {
187 pub fn done(self) -> ContractSpec {
189 assert!(
190 !self.spec.constructors.is_empty(),
191 "must have at least one constructor"
192 );
193 assert!(
194 !self.spec.messages.is_empty(),
195 "must have at least one message"
196 );
197 self.spec
198 }
199}
200
201impl ContractSpec {
202 pub fn new() -> ContractSpecBuilder {
204 ContractSpecBuilder {
205 spec: Self {
206 constructors: Vec::new(),
207 messages: Vec::new(),
208 events: Vec::new(),
209 docs: Vec::new(),
210 },
211 marker: PhantomData,
212 }
213 }
214}
215
216#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
218#[serde(bound(
219 serialize = "F::Type: Serialize, F::String: Serialize",
220 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
221))]
222pub struct ConstructorSpec<F: Form = MetaForm> {
223 pub name: Vec<F::String>,
227 pub selector: Selector,
229 pub args: Vec<MessageParamSpec<F>>,
231 pub docs: Vec<F::String>,
233}
234
235impl IntoPortable for ConstructorSpec {
236 type Output = ConstructorSpec<PortableForm>;
237
238 fn into_portable(self, registry: &mut Registry) -> Self::Output {
239 ConstructorSpec {
240 name: registry.map_into_portable(self.name),
241 selector: self.selector,
242 args: self
243 .args
244 .into_iter()
245 .map(|arg| arg.into_portable(registry))
246 .collect::<Vec<_>>(),
247 docs: registry.map_into_portable(self.docs),
248 }
249 }
250}
251
252impl<F> ConstructorSpec<F>
253where
254 F: Form,
255{
256 pub fn name(&self) -> &[F::String] {
260 &self.name
261 }
262
263 pub fn selector(&self) -> &Selector {
265 &self.selector
266 }
267
268 pub fn args(&self) -> &[MessageParamSpec<F>] {
270 &self.args
271 }
272
273 pub fn docs(&self) -> &[F::String] {
275 &self.docs
276 }
277}
278
279pub struct ConstructorSpecBuilder<Selector> {
287 spec: ConstructorSpec,
288 marker: PhantomData<fn() -> Selector>,
289}
290
291impl ConstructorSpec {
292 fn from_name_segments(
294 segments: Vec<&'static str>,
295 ) -> ConstructorSpecBuilder<Missing<state::Selector>> {
296 ConstructorSpecBuilder {
297 spec: Self {
298 name: segments,
299 selector: Selector::default(),
300 args: Vec::new(),
301 docs: Vec::new(),
302 },
303 marker: PhantomData,
304 }
305 }
306
307 pub fn from_name(
309 name: &'static str,
310 ) -> ConstructorSpecBuilder<Missing<state::Selector>> {
311 Self::from_name_segments(vec![name])
312 }
313
314 pub fn from_trait_and_name(
316 trait_name: &'static str,
317 constructor_name: &'static str,
318 ) -> ConstructorSpecBuilder<Missing<state::Selector>> {
319 Self::from_name_segments(vec![trait_name, constructor_name])
320 }
321}
322
323impl ConstructorSpecBuilder<Missing<state::Selector>> {
324 pub fn selector(self, selector: [u8; 4]) -> ConstructorSpecBuilder<state::Selector> {
326 ConstructorSpecBuilder {
327 spec: ConstructorSpec {
328 selector: selector.into(),
329 ..self.spec
330 },
331 marker: PhantomData,
332 }
333 }
334}
335
336impl<S> ConstructorSpecBuilder<S> {
337 pub fn args<A>(self, args: A) -> Self
339 where
340 A: IntoIterator<Item = MessageParamSpec>,
341 {
342 let mut this = self;
343 debug_assert!(this.spec.args.is_empty());
344 this.spec.args = args.into_iter().collect::<Vec<_>>();
345 this
346 }
347
348 pub fn docs<D>(self, docs: D) -> Self
350 where
351 D: IntoIterator<Item = &'static str>,
352 {
353 let mut this = self;
354 debug_assert!(this.spec.docs.is_empty());
355 this.spec.docs = docs.into_iter().map(str::trim).collect::<Vec<_>>();
356 this
357 }
358}
359
360impl ConstructorSpecBuilder<state::Selector> {
361 pub fn done(self) -> ConstructorSpec {
363 self.spec
364 }
365}
366
367#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
369#[serde(bound(
370 serialize = "F::Type: Serialize, F::String: Serialize",
371 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
372))]
373#[serde(rename_all = "camelCase")]
374pub struct MessageSpec<F: Form = MetaForm> {
375 name: Vec<F::String>,
380 selector: Selector,
382 mutates: bool,
384 payable: bool,
386 args: Vec<MessageParamSpec<F>>,
388 return_type: ReturnTypeSpec<F>,
390 docs: Vec<F::String>,
392}
393
394pub struct Missing<S>(PhantomData<fn() -> S>);
397
398mod state {
399 pub struct Selector;
404 pub struct Mutates;
406 pub struct IsPayable;
408 pub struct Returns;
410}
411
412impl MessageSpec {
413 fn from_name_segments(
415 segments: Vec<&'static str>,
416 ) -> MessageSpecBuilder<
417 Missing<state::Selector>,
418 Missing<state::Mutates>,
419 Missing<state::IsPayable>,
420 Missing<state::Returns>,
421 > {
422 MessageSpecBuilder {
423 spec: Self {
424 name: segments,
425 selector: Selector::default(),
426 mutates: false,
427 payable: false,
428 args: Vec::new(),
429 return_type: ReturnTypeSpec::new(None),
430 docs: Vec::new(),
431 },
432 marker: PhantomData,
433 }
434 }
435
436 pub fn from_name(
438 name: &'static str,
439 ) -> MessageSpecBuilder<
440 Missing<state::Selector>,
441 Missing<state::Mutates>,
442 Missing<state::IsPayable>,
443 Missing<state::Returns>,
444 > {
445 Self::from_name_segments(vec![name])
446 }
447
448 pub fn from_trait_and_name(
450 trait_name: &'static str,
451 message_name: &'static str,
452 ) -> MessageSpecBuilder<
453 Missing<state::Selector>,
454 Missing<state::Mutates>,
455 Missing<state::IsPayable>,
456 Missing<state::Returns>,
457 > {
458 Self::from_name_segments(vec![trait_name, message_name])
459 }
460}
461
462impl<F> MessageSpec<F>
463where
464 F: Form,
465{
466 pub fn name(&self) -> &[F::String] {
471 &self.name
472 }
473
474 pub fn selector(&self) -> &Selector {
476 &self.selector
477 }
478
479 pub fn mutates(&self) -> bool {
481 self.mutates
482 }
483
484 pub fn payable(&self) -> bool {
486 self.payable
487 }
488
489 pub fn args(&self) -> &[MessageParamSpec<F>] {
491 &self.args
492 }
493
494 pub fn return_type(&self) -> &ReturnTypeSpec<F> {
496 &self.return_type
497 }
498
499 pub fn docs(&self) -> &[F::String] {
501 &self.docs
502 }
503}
504
505#[allow(clippy::type_complexity)]
513pub struct MessageSpecBuilder<Selector, Mutates, IsPayable, Returns> {
514 spec: MessageSpec,
515 marker: PhantomData<fn() -> (Selector, Mutates, IsPayable, Returns)>,
516}
517
518impl<M, P, R> MessageSpecBuilder<Missing<state::Selector>, M, P, R> {
519 pub fn selector(
521 self,
522 selector: [u8; 4],
523 ) -> MessageSpecBuilder<state::Selector, M, P, R> {
524 MessageSpecBuilder {
525 spec: MessageSpec {
526 selector: selector.into(),
527 ..self.spec
528 },
529 marker: PhantomData,
530 }
531 }
532}
533
534impl<S, P, R> MessageSpecBuilder<S, Missing<state::Mutates>, P, R> {
535 pub fn mutates(self, mutates: bool) -> MessageSpecBuilder<S, state::Mutates, P, R> {
537 MessageSpecBuilder {
538 spec: MessageSpec {
539 mutates,
540 ..self.spec
541 },
542 marker: PhantomData,
543 }
544 }
545}
546
547impl<S, M, R> MessageSpecBuilder<S, M, Missing<state::IsPayable>, R> {
548 pub fn payable(
550 self,
551 is_payable: bool,
552 ) -> MessageSpecBuilder<S, M, state::IsPayable, R> {
553 MessageSpecBuilder {
554 spec: MessageSpec {
555 payable: is_payable,
556 ..self.spec
557 },
558 marker: PhantomData,
559 }
560 }
561}
562
563impl<M, S, P> MessageSpecBuilder<S, M, P, Missing<state::Returns>> {
564 pub fn returns(
566 self,
567 return_type: ReturnTypeSpec,
568 ) -> MessageSpecBuilder<S, M, P, state::Returns> {
569 MessageSpecBuilder {
570 spec: MessageSpec {
571 return_type,
572 ..self.spec
573 },
574 marker: PhantomData,
575 }
576 }
577}
578
579impl<S, M, P, R> MessageSpecBuilder<S, M, P, R> {
580 pub fn args<A>(self, args: A) -> Self
582 where
583 A: IntoIterator<Item = MessageParamSpec>,
584 {
585 let mut this = self;
586 debug_assert!(this.spec.args.is_empty());
587 this.spec.args = args.into_iter().collect::<Vec<_>>();
588 this
589 }
590
591 pub fn docs<D>(self, docs: D) -> Self
593 where
594 D: IntoIterator<Item = &'static str>,
595 {
596 let mut this = self;
597 debug_assert!(this.spec.docs.is_empty());
598 this.spec.docs = docs.into_iter().collect::<Vec<_>>();
599 this
600 }
601}
602
603impl
604 MessageSpecBuilder<state::Selector, state::Mutates, state::IsPayable, state::Returns>
605{
606 pub fn done(self) -> MessageSpec {
608 self.spec
609 }
610}
611
612impl IntoPortable for MessageSpec {
613 type Output = MessageSpec<PortableForm>;
614
615 fn into_portable(self, registry: &mut Registry) -> Self::Output {
616 MessageSpec {
617 name: registry.map_into_portable(self.name),
618 selector: self.selector,
619 mutates: self.mutates,
620 payable: self.payable,
621 args: self
622 .args
623 .into_iter()
624 .map(|arg| arg.into_portable(registry))
625 .collect::<Vec<_>>(),
626 return_type: self.return_type.into_portable(registry),
627 docs: registry.map_into_portable(self.docs),
628 }
629 }
630}
631
632#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
634#[serde(bound(
635 serialize = "F::Type: Serialize, F::String: Serialize",
636 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
637))]
638pub struct EventSpec<F: Form = MetaForm> {
639 name: F::String,
641 args: Vec<EventParamSpec<F>>,
643 docs: Vec<F::String>,
645}
646
647pub struct EventSpecBuilder {
649 spec: EventSpec,
650}
651
652impl EventSpecBuilder {
653 pub fn args<A>(self, args: A) -> Self
655 where
656 A: IntoIterator<Item = EventParamSpec>,
657 {
658 let mut this = self;
659 debug_assert!(this.spec.args.is_empty());
660 this.spec.args = args.into_iter().collect::<Vec<_>>();
661 this
662 }
663
664 pub fn docs<D>(self, docs: D) -> Self
666 where
667 D: IntoIterator<Item = &'static str>,
668 {
669 let mut this = self;
670 debug_assert!(this.spec.docs.is_empty());
671 this.spec.docs = docs.into_iter().collect::<Vec<_>>();
672 this
673 }
674
675 pub fn done(self) -> EventSpec {
677 self.spec
678 }
679}
680
681impl IntoPortable for EventSpec {
682 type Output = EventSpec<PortableForm>;
683
684 fn into_portable(self, registry: &mut Registry) -> Self::Output {
685 EventSpec {
686 name: self.name.into_portable(registry),
687 args: self
688 .args
689 .into_iter()
690 .map(|arg| arg.into_portable(registry))
691 .collect::<Vec<_>>(),
692 docs: registry.map_into_portable(self.docs),
693 }
694 }
695}
696
697impl EventSpec {
698 pub fn new(name: &'static str) -> EventSpecBuilder {
700 EventSpecBuilder {
701 spec: Self {
702 name,
703 args: Vec::new(),
704 docs: Vec::new(),
705 },
706 }
707 }
708}
709
710impl<F> EventSpec<F>
711where
712 F: Form,
713{
714 pub fn name(&self) -> &F::String {
716 &self.name
717 }
718
719 pub fn args(&self) -> &[EventParamSpec<F>] {
721 &self.args
722 }
723
724 pub fn docs(&self) -> &[F::String] {
726 &self.docs
727 }
728}
729
730#[derive(Debug, Default, PartialEq, Eq, derive_more::From)]
732pub struct Selector([u8; 4]);
733
734impl serde::Serialize for Selector {
735 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
736 where
737 S: serde::Serializer,
738 {
739 serde_hex::serialize(&self.0, serializer)
740 }
741}
742
743impl<'de> serde::Deserialize<'de> for Selector {
744 fn deserialize<D>(d: D) -> Result<Self, D::Error>
745 where
746 D: serde::Deserializer<'de>,
747 {
748 let mut arr = [0; 4];
749 serde_hex::deserialize_check_len(d, serde_hex::ExpectedLen::Exact(&mut arr[..]))?;
750 Ok(arr.into())
751 }
752}
753
754impl Selector {
755 pub fn to_bytes(&self) -> &[u8] {
757 &self.0
758 }
759}
760
761pub type DisplayName<F> = tetsy_scale_info::Path<F>;
779
780#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
798#[serde(bound(
799 serialize = "F::Type: Serialize, F::String: Serialize",
800 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
801))]
802#[serde(rename_all = "camelCase")]
803pub struct TypeSpec<F: Form = MetaForm> {
804 #[serde(rename = "type")]
806 ty: F::Type,
807 display_name: DisplayName<F>,
809}
810
811impl IntoPortable for TypeSpec {
812 type Output = TypeSpec<PortableForm>;
813
814 fn into_portable(self, registry: &mut Registry) -> Self::Output {
815 TypeSpec {
816 ty: registry.register_type(&self.ty),
817 display_name: self.display_name.into_portable(registry),
818 }
819 }
820}
821
822impl TypeSpec {
823 pub fn with_name_str<T>(display_name: &'static str) -> Self
835 where
836 T: TypeInfo + 'static,
837 {
838 Self::with_name_segs::<T, _>(display_name.split("::"))
839 }
840
841 pub fn with_name_segs<T, S>(segments: S) -> Self
854 where
855 T: TypeInfo + 'static,
856 S: IntoIterator<Item = &'static str>,
857 {
858 Self {
859 ty: meta_type::<T>(),
860 display_name: DisplayName::from_segments(segments)
861 .expect("display name is invalid"),
862 }
863 }
864
865 pub fn new<T>() -> Self
867 where
868 T: TypeInfo + 'static,
869 {
870 Self {
871 ty: meta_type::<T>(),
872 display_name: DisplayName::default(),
873 }
874 }
875}
876
877impl<F> TypeSpec<F>
878where
879 F: Form,
880{
881 pub fn ty(&self) -> &F::Type {
883 &self.ty
884 }
885
886 pub fn display_name(&self) -> &DisplayName<F> {
888 &self.display_name
889 }
890}
891
892#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
894#[serde(bound(
895 serialize = "F::Type: Serialize, F::String: Serialize",
896 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
897))]
898pub struct EventParamSpec<F: Form = MetaForm> {
899 name: F::String,
901 indexed: bool,
903 #[serde(rename = "type")]
905 ty: TypeSpec<F>,
906 docs: Vec<F::String>,
908}
909
910impl IntoPortable for EventParamSpec {
911 type Output = EventParamSpec<PortableForm>;
912
913 fn into_portable(self, registry: &mut Registry) -> Self::Output {
914 EventParamSpec {
915 name: self.name.into_portable(registry),
916 indexed: self.indexed,
917 ty: self.ty.into_portable(registry),
918 docs: registry.map_into_portable(self.docs),
919 }
920 }
921}
922
923impl EventParamSpec {
924 pub fn new(name: &'static str) -> EventParamSpecBuilder {
926 EventParamSpecBuilder {
927 spec: Self {
928 name,
929 indexed: false,
931 ty: TypeSpec::new::<()>(),
933 docs: vec![],
935 },
936 }
937 }
938}
939
940impl<F> EventParamSpec<F>
941where
942 F: Form,
943{
944 pub fn name(&self) -> &F::String {
946 &self.name
947 }
948
949 pub fn indexed(&self) -> bool {
951 self.indexed
952 }
953
954 pub fn ty(&self) -> &TypeSpec<F> {
956 &self.ty
957 }
958
959 pub fn docs(&self) -> &[F::String] {
961 &self.docs
962 }
963}
964
965pub struct EventParamSpecBuilder {
967 spec: EventParamSpec,
969}
970
971impl EventParamSpecBuilder {
972 pub fn of_type(self, spec: TypeSpec) -> Self {
974 let mut this = self;
975 this.spec.ty = spec;
976 this
977 }
978
979 pub fn indexed(self, is_indexed: bool) -> Self {
981 let mut this = self;
982 this.spec.indexed = is_indexed;
983 this
984 }
985
986 pub fn docs<D>(self, docs: D) -> Self
988 where
989 D: IntoIterator<Item = &'static str>,
990 {
991 debug_assert!(self.spec.docs.is_empty());
992 Self {
993 spec: EventParamSpec {
994 docs: docs.into_iter().collect::<Vec<_>>(),
995 ..self.spec
996 },
997 }
998 }
999
1000 pub fn done(self) -> EventParamSpec {
1002 self.spec
1003 }
1004}
1005
1006#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
1008#[serde(transparent)]
1009#[serde(bound(
1010 serialize = "F::Type: Serialize, F::String: Serialize",
1011 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
1012))]
1013pub struct ReturnTypeSpec<F: Form = MetaForm> {
1014 #[serde(rename = "type")]
1015 opt_type: Option<TypeSpec<F>>,
1016}
1017
1018impl IntoPortable for ReturnTypeSpec {
1019 type Output = ReturnTypeSpec<PortableForm>;
1020
1021 fn into_portable(self, registry: &mut Registry) -> Self::Output {
1022 ReturnTypeSpec {
1023 opt_type: self
1024 .opt_type
1025 .map(|opt_type| opt_type.into_portable(registry)),
1026 }
1027 }
1028}
1029
1030impl ReturnTypeSpec {
1031 pub fn new<T>(ty: T) -> Self
1041 where
1042 T: Into<Option<TypeSpec>>,
1043 {
1044 Self {
1045 opt_type: ty.into(),
1046 }
1047 }
1048}
1049
1050impl<F> ReturnTypeSpec<F>
1051where
1052 F: Form,
1053{
1054 pub fn opt_type(&self) -> Option<&TypeSpec<F>> {
1056 self.opt_type.as_ref()
1057 }
1058}
1059
1060#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
1062#[serde(bound(
1063 serialize = "F::Type: Serialize, F::String: Serialize",
1064 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
1065))]
1066pub struct MessageParamSpec<F: Form = MetaForm> {
1067 name: F::String,
1069 #[serde(rename = "type")]
1071 ty: TypeSpec<F>,
1072}
1073
1074impl IntoPortable for MessageParamSpec {
1075 type Output = MessageParamSpec<PortableForm>;
1076
1077 fn into_portable(self, registry: &mut Registry) -> Self::Output {
1078 MessageParamSpec {
1079 name: self.name.into_portable(registry),
1080 ty: self.ty.into_portable(registry),
1081 }
1082 }
1083}
1084
1085impl MessageParamSpec {
1086 pub fn new(name: &'static str) -> MessageParamSpecBuilder {
1088 MessageParamSpecBuilder {
1089 spec: Self {
1090 name,
1091 ty: TypeSpec::new::<()>(),
1093 },
1094 }
1095 }
1096}
1097
1098impl<F> MessageParamSpec<F>
1099where
1100 F: Form,
1101{
1102 pub fn name(&self) -> &F::String {
1104 &self.name
1105 }
1106
1107 pub fn ty(&self) -> &TypeSpec<F> {
1109 &self.ty
1110 }
1111}
1112
1113pub struct MessageParamSpecBuilder {
1115 spec: MessageParamSpec,
1117}
1118
1119impl MessageParamSpecBuilder {
1120 pub fn of_type(self, ty: TypeSpec) -> Self {
1122 let mut this = self;
1123 this.spec.ty = ty;
1124 this
1125 }
1126
1127 pub fn done(self) -> MessageParamSpec {
1129 self.spec
1130 }
1131}