sylvia_derive/parser/attributes/
mod.rs1use data::DataFieldParams;
5use features::SylviaFeatures;
6use payload::PayloadFieldParam;
7use proc_macro_error::emit_error;
8use syn::spanned::Spanned;
9use syn::{Attribute, MetaList, PathSegment};
10
11pub mod attr;
12pub mod custom;
13pub mod data;
14pub mod error;
15pub mod features;
16pub mod messages;
17pub mod msg;
18pub mod override_entry_point;
19pub mod payload;
20
21pub use attr::{MsgAttrForwarding, VariantAttrForwarding};
22pub use custom::Custom;
23pub use error::ContractErrorAttr;
24pub use messages::{ContractMessageAttr, Customs};
25pub use msg::{MsgAttr, MsgType};
26pub use override_entry_point::{FilteredOverrideEntryPoints, OverrideEntryPoint};
27
28#[derive(Clone, Copy, Debug, PartialEq)]
31pub enum SylviaAttribute {
32 Custom,
33 Error,
34 Messages,
35 Msg,
36 OverrideEntryPoint,
37 VariantAttrs,
38 MsgAttrs,
39 Payload,
40 Data,
41 Features,
42}
43
44impl SylviaAttribute {
45 pub fn new(attr: &Attribute) -> Option<Self> {
46 let segments = &attr.path().segments;
47 if segments.len() == 2 && segments[0].ident == "sv" {
48 Self::match_attribute(&segments[1])
49 } else {
50 None
51 }
52 }
53
54 fn match_attribute(segment: &PathSegment) -> Option<Self> {
55 match segment.ident.to_string().as_str() {
56 "custom" => Some(Self::Custom),
57 "error" => Some(Self::Error),
58 "messages" => Some(Self::Messages),
59 "msg" => Some(Self::Msg),
60 "override_entry_point" => Some(Self::OverrideEntryPoint),
61 "attr" => Some(Self::VariantAttrs),
62 "msg_attr" => Some(Self::MsgAttrs),
63 "payload" => Some(Self::Payload),
64 "data" => Some(Self::Data),
65 "features" => Some(Self::Features),
66 _ => None,
67 }
68 }
69}
70
71#[derive(Default)]
74pub struct ParsedSylviaAttributes {
75 pub custom_attr: Option<Custom>,
76 pub error_attrs: Option<ContractErrorAttr>,
77 pub messages_attrs: Vec<ContractMessageAttr>,
78 pub msg_attr: Option<MsgAttr>,
79 pub override_entry_point_attrs: Vec<OverrideEntryPoint>,
80 pub variant_attrs_forward: Vec<VariantAttrForwarding>,
81 pub msg_attrs_forward: Vec<MsgAttrForwarding>,
82 pub sv_features: SylviaFeatures,
83 pub data: Option<DataFieldParams>,
84 pub payload: Option<PayloadFieldParam>,
85}
86
87impl ParsedSylviaAttributes {
88 pub fn new<'a>(attrs: impl Iterator<Item = &'a Attribute>) -> Self {
89 let mut result = Self::default();
90 for attr in attrs {
91 let sylvia_attr = SylviaAttribute::new(attr);
92 let attr_content = attr.meta.require_list();
93
94 if let (Some(sylvia_attr), Ok(attr)) = (sylvia_attr, &attr_content) {
95 result.match_attribute(&sylvia_attr, attr);
96 } else if sylvia_attr == Some(SylviaAttribute::Data) {
97 result.data = Some(DataFieldParams::default());
99 } else if sylvia_attr == Some(SylviaAttribute::Payload) {
100 emit_error!(
101 attr.span(), "Missing parameters for `sv::payload`";
102 note = "Expected `#[sv::payload(raw)]`"
103 );
104 }
105 }
106
107 if let Some(attr) = result.variant_attrs_forward.first() {
108 let msg_type = result.msg_attr.as_ref().map(MsgAttr::msg_type);
109 if let Some(MsgType::Instantiate) = msg_type {
110 emit_error!(
111 attr.span, "The attribute `sv::attr` is not supported for `instantiate`";
112 note = "Message `instantiate` is a structure, use `#[sv::msg_attr] instead`";
113 );
114 } else if let Some(MsgType::Migrate) = msg_type {
115 emit_error!(
116 attr.span, "The attribute `sv::attr` is not supported for `migrate`";
117 note = "Message `migrate` is a structure, use `#[sv::msg_attr] instead`";
118 );
119 }
120 }
121
122 result
123 }
124
125 fn match_attribute(&mut self, attribute_type: &SylviaAttribute, attr: &MetaList) {
126 match attribute_type {
127 SylviaAttribute::Custom => {
128 if self.custom_attr.is_none() {
129 if let Ok(custom_attr) = Custom::new(attr) {
130 self.custom_attr = Some(custom_attr);
131 }
132 } else {
133 emit_error!(
134 attr, "The attribute `sv::custom` is redefined";
135 note = attr.span() => "Previous definition of the attribute `sv::custom`";
136 note = "Only one `sv::custom` attribute can exist on a single sylvia entity"
137 );
138 }
139 }
140 SylviaAttribute::Error => {
141 if self.error_attrs.is_none() {
142 if let Ok(error_attr) = ContractErrorAttr::new(attr) {
143 self.error_attrs = Some(error_attr);
144 }
145 } else {
146 emit_error!(
147 attr, "The attribute `sv::error` is redefined";
148 note = attr.span() => "Previous definition of the attribute `sv::error`";
149 note = "Only one `sv::error` attribute can exist on a single method"
150 );
151 }
152 }
153 SylviaAttribute::Messages => {
154 if let Ok(contract) = ContractMessageAttr::new(attr) {
155 self.messages_attrs.push(contract);
156 }
157 }
158 SylviaAttribute::Msg => {
159 if self.msg_attr.is_none() {
160 if let Ok(msg_attr) = MsgAttr::new(attr) {
161 self.msg_attr = Some(msg_attr);
162 }
163 } else {
164 emit_error!(
165 attr, "The attribute `sv::msg` is redefined";
166 note = attr.span() => "Previous definition of the attribute `sv::msg`";
167 note = "Only one `sv::msg` attribute can exist on a single method"
168 );
169 }
170 }
171 SylviaAttribute::OverrideEntryPoint => {
172 if let Ok(override_entry_point) = OverrideEntryPoint::new(attr) {
173 self.override_entry_point_attrs.push(override_entry_point)
174 }
175 }
176 SylviaAttribute::VariantAttrs => {
177 self.variant_attrs_forward
178 .push(VariantAttrForwarding::new(attr));
179 }
180 SylviaAttribute::MsgAttrs => {
181 if let Ok(message_attrs) = MsgAttrForwarding::new(attr) {
182 self.msg_attrs_forward.push(message_attrs);
183 }
184 }
185 SylviaAttribute::Payload => {
186 if let Ok(payload) = PayloadFieldParam::new(attr) {
187 self.payload = Some(payload);
188 }
189 }
190 SylviaAttribute::Data => {
191 if let Ok(data) = DataFieldParams::new(attr) {
192 self.data = Some(data);
193 }
194 }
195 SylviaAttribute::Features => {
196 if let Ok(features) = SylviaFeatures::new(attr) {
197 self.sv_features = features;
198 }
199 }
200 }
201 }
202}