1#![deny(warnings)]
4#![warn(missing_docs)]
5
6use std::io::{self, Write};
7
8use contract::NearItemTrait;
9use syn::{
10 Attribute, FnArg, ImplItem, ImplItemMethod, ItemEnum, ItemImpl, ItemStruct, Lit, Meta,
11 MetaList, MetaNameValue, NestedMeta, Path, PathArguments, Type, Visibility,
12};
13
14pub mod contract;
15pub mod md;
16pub mod ts;
17
18pub trait NearImpl {
29 fn get_trait_name(&self) -> Option<String>;
31
32 fn get_impl_name(&self) -> Option<String>;
35
36 fn exported_methods(&self) -> Vec<&ImplItemMethod>;
41
42 fn bindgen_methods(&self) -> Option<Vec<&ImplItemMethod>>;
46
47 fn join_attrs(&self, item_trait: Option<&NearItemTrait>) -> Vec<Attribute>;
50}
51
52impl NearImpl for ItemImpl {
53 fn get_trait_name(&self) -> Option<String> {
54 if let Some((_excl, trait_path, _for)) = &self.trait_ {
55 let trait_name = join_path(trait_path);
56 Some(trait_name)
57 } else {
58 None
59 }
60 }
61
62 fn get_impl_name(&self) -> Option<String> {
63 if let Type::Path(type_path) = &*self.self_ty {
64 Some(join_path(&type_path.path))
65 } else {
66 None
67 }
68 }
69
70 fn exported_methods(&self) -> Vec<&ImplItemMethod> {
71 let mut methods = Vec::new();
72 for impl_item in self.items.iter() {
73 if let ImplItem::Method(method) = impl_item {
74 if method.is_exported(self) {
75 methods.push(method);
76 }
77 }
78 }
79
80 methods
81 }
82
83 fn bindgen_methods(&self) -> Option<Vec<&ImplItemMethod>> {
84 let methods = self.exported_methods();
85 if self.is_bindgen() && methods.len() > 0 {
86 return Some(methods);
87 }
88
89 None
90 }
91
92 fn join_attrs(&self, item_trait: Option<&NearItemTrait>) -> Vec<Attribute> {
93 if let Some(base_trait) = item_trait {
94 let mut attrs = self.attrs.clone();
95 attrs.extend(base_trait.attrs.clone());
96 return attrs;
97 } else {
98 self.attrs.clone()
99 }
100 }
101}
102
103pub trait NearMethod {
105 fn is_public(&self) -> bool;
107
108 fn is_mut(&self) -> bool;
110
111 fn is_init(&self) -> bool;
113
114 fn is_payable(&self) -> bool;
116
117 fn is_private(&self) -> bool;
119
120 fn is_exported(&self, input: &ItemImpl) -> bool;
122
123 fn join_attrs(&self, item_trait: Option<&NearItemTrait>) -> Vec<Attribute>;
126}
127
128impl NearMethod for ImplItemMethod {
129 fn is_public(self: &ImplItemMethod) -> bool {
130 match self.vis {
131 Visibility::Public(_) => true,
132 _ => false,
133 }
134 }
135
136 fn is_mut(&self) -> bool {
137 if let Some(FnArg::Receiver(r)) = self.sig.inputs.iter().next() {
138 r.mutability.is_some()
139 } else {
140 false
141 }
142 }
143
144 fn is_init(&self) -> bool {
145 has_attr(&self.attrs, "init")
146 }
147
148 fn is_payable(&self) -> bool {
149 has_attr(&self.attrs, "payable")
150 }
151
152 fn is_private(self: &ImplItemMethod) -> bool {
153 has_attr(&self.attrs, "private")
154 }
155
156 fn is_exported(&self, input: &ItemImpl) -> bool {
157 (self.is_public() || input.trait_.is_some()) && !self.is_private()
158 }
159
160 fn join_attrs(&self, item_trait: Option<&NearItemTrait>) -> Vec<Attribute> {
161 let name = self.sig.ident.to_string();
162 if let Some(base_trait) = item_trait {
163 if let Some(base_method) = base_trait.get(&name) {
164 let mut attrs = self.attrs.clone();
165 attrs.extend(base_method.attrs.clone());
166 return attrs;
167 }
168 self.attrs.clone()
169 } else {
170 self.attrs.clone()
171 }
172 }
173}
174
175pub trait NearBindgen {
177 fn is_bindgen(&self) -> bool;
191}
192
193impl<I: NearAttributable> NearBindgen for I {
194 fn is_bindgen(&self) -> bool {
195 has_attr(&self.attrs(), "near_bindgen")
196 }
197}
198
199pub trait NearSerde {
201 fn is_serialize(&self) -> bool;
203
204 fn is_deserialize(&self) -> bool;
206
207 fn is_serde(&self) -> bool;
209}
210
211impl<I: NearAttributable> NearSerde for I {
212 fn is_serialize(&self) -> bool {
213 derives(&self.attrs(), "Serialize")
214 }
215
216 fn is_deserialize(&self) -> bool {
217 derives(&self.attrs(), "Deserialize")
218 }
219
220 fn is_serde(&self) -> bool {
221 self.is_serialize() || self.is_deserialize()
222 }
223}
224
225pub trait NearAttributable {
227 fn attrs(&self) -> &Vec<Attribute>;
229}
230
231impl NearAttributable for ItemImpl {
232 fn attrs(&self) -> &Vec<Attribute> {
233 &self.attrs
234 }
235}
236
237impl NearAttributable for ItemStruct {
238 fn attrs(&self) -> &Vec<Attribute> {
239 &self.attrs
240 }
241}
242
243impl NearAttributable for ItemEnum {
244 fn attrs(&self) -> &Vec<Attribute> {
245 &self.attrs
246 }
247}
248
249fn has_attr(attrs: &Vec<Attribute>, attr_name: &str) -> bool {
252 for attr in attrs {
253 if is_ident(&attr.path, attr_name) {
254 return true;
255 }
256 }
257 false
258}
259
260fn derives(attrs: &Vec<Attribute>, macro_name: &str) -> bool {
263 for attr in attrs {
264 if attr.path.is_ident("derive") {
265 if let Ok(Meta::List(MetaList { nested, .. })) = attr.parse_meta() {
266 for elem in nested {
267 if let NestedMeta::Meta(Meta::Path(path)) = elem {
268 if is_ident(&path, macro_name) {
269 return true;
270 }
271 }
272 }
273 }
274 }
275 }
276 false
277}
278
279fn is_ident(path: &Path, ident: &str) -> bool {
280 fn last_segment_is_ident(path: &Path, ident: &str) -> bool {
281 let segments = &path.segments;
282 let len = segments.len();
283 len >= 2
284 && segments[len - 1].arguments == PathArguments::None
285 && segments[len - 1].ident.to_string() == ident
286 }
287
288 path.is_ident(ident) || last_segment_is_ident(path, ident)
289}
290
291fn join_path(path: &syn::Path) -> String {
303 path.segments
304 .iter()
305 .map(|seg| seg.ident.to_string())
306 .collect::<Vec<String>>()
307 .join("::")
308}
309
310pub fn get_docs(attrs: &Vec<Attribute>) -> Vec<String> {
312 let mut docs = Vec::new();
313 for attr in attrs {
314 if attr.path.is_ident("doc") {
315 if let Ok(Meta::NameValue(MetaNameValue {
316 lit: Lit::Str(lit), ..
317 })) = attr.parse_meta()
318 {
319 docs.push(lit.value());
320 } else {
321 panic!("not expected");
322 }
323 }
324 }
325
326 docs
327}
328
329pub fn write_docs<W: Write, F: Fn(String) -> String>(
333 file: &mut W,
334 attrs: &Vec<Attribute>,
335 mapf: F,
336) -> io::Result<()> {
337 for attr in attrs {
338 if attr.path.is_ident("doc") {
339 if let Ok(Meta::NameValue(MetaNameValue {
340 lit: Lit::Str(lit), ..
341 })) = attr.parse_meta()
342 {
343 writeln!(file, "{}", mapf(lit.value()))?;
344 } else {
345 panic!("not expected");
346 }
347 }
348 }
349
350 Ok(())
351}
352
353#[cfg(test)]
354mod tests {
355
356 mod has_attr {
357
358 use proc_macro2::TokenStream;
359 use quote::quote;
360 use syn::{Attribute, ItemImpl};
361
362 use crate::has_attr;
363
364 fn impl_attrs(tokens: TokenStream) -> Vec<Attribute> {
365 let item = syn::parse2::<ItemImpl>(tokens).unwrap();
366 item.attrs
367 }
368
369 #[test]
370 fn it_should_return_true_when_attr_is_defined() {
371 let attrs = impl_attrs(quote! {
372 #[near_bindgen]
373 impl Contract { }
374 });
375 assert!(has_attr(&attrs, "near_bindgen"));
376 }
377
378 #[test]
379 fn it_should_return_false_when_attr_is_not_defined() {
380 let attrs = impl_attrs(quote! {
381 #[not_near_bindgen]
382 #[::near_bindgen]
383 #[near_bindgen::not]
384 impl Contract { }
385 });
386 assert!(!has_attr(&attrs, "near_bindgen"));
387 }
388
389 #[test]
390 fn it_should_return_true_when_attr_is_defined_multiple_times() {
391 let attrs = impl_attrs(quote! {
392 #[near_bindgen]
393 #[near_bindgen]
394 #[maybe_near_bindgen]
395 impl Contract { }
396 });
397 assert!(has_attr(&attrs, "near_bindgen"));
398 }
399
400 #[test]
401 fn it_should_return_true_when_attr_is_defined_using_qualifiers() {
402 let attrs = impl_attrs(quote! {
403 #[near_sdk::near_bindgen]
404 #[::near_sdk::near_bindgen]
405 impl Contract { }
406 });
407 assert!(has_attr(&attrs, "near_bindgen"));
408 }
409 }
410
411 mod derives {
412
413 use proc_macro2::TokenStream;
414 use quote::quote;
415 use syn::{Attribute, ItemStruct};
416
417 use crate::derives;
418
419 fn struct_attrs(tokens: TokenStream) -> Vec<Attribute> {
420 let item = syn::parse2::<ItemStruct>(tokens).unwrap();
421 item.attrs
422 }
423
424 #[test]
425 fn it_should_return_true_when_struct_is_derived() {
426 let attrs = struct_attrs(quote! {
427 #[derive(Serialize)]
428 struct Data { }
429 });
430 assert!(derives(&attrs, "Serialize"));
431 }
432
433 #[test]
434 fn it_should_return_true_when_struct_is_derived_with_qualifiers() {
435 let attrs = struct_attrs(quote! {
436 #[derive(::near_sdk::serde::Serialize)]
437 struct Data { }
438 });
439 assert!(derives(&attrs, "Serialize"));
440 }
441
442 #[test]
443 fn it_should_return_false_when_struct_is_not_derived() {
444 let attrs = struct_attrs(quote! {
445 #[derive()]
446 #[derive(::Serialize)]
447 #[derive=Serialize]
448 #[derive(Serialize=1)]
449 struct Data { }
450 });
451 assert!(!derives(&attrs, "Serialize"));
452 }
453 }
454}