scanner_syn/
contract_descriptor.rs

1use proc_macro2::{Ident, Span};
2use std::io::Read;
3use std::iter::IntoIterator;
4
5
6use std::{fs::File, path::Path};
7use syn::{Item, ItemStruct};
8
9use crate::core_impl::*;
10use proc_macro2::TokenStream;
11use quote::quote;
12use syn::__private::ToTokens;
13use syn::visit::Visit;
14use walkdir::WalkDir;
15
16///Function information from the code scanned by ContractDescriptor
17#[derive(Clone, Default, Debug)]
18pub struct FunctionInfo {
19    pub name: String,
20    /// Whether method is exported
21    pub is_public: bool,
22    /// Whether this is a trait implementation.
23    pub is_trait_impl: bool,
24    /// Whether method does not modify the state.
25    pub is_init: bool,
26    /// Whether method is test method
27    pub is_test: bool,
28    /// Whether method accepting $NEAR.
29    pub is_payable: bool,
30    /// Whether method does not modify the state.
31    pub is_view: bool,
32    /// Whether method can modify the state.
33    pub is_mutable: bool,
34    /// Whether method doesn't return a value.
35    pub is_process: bool,
36    /// Whether method can accept calls from self (current account)
37    pub is_private_cccalls: bool,
38    /// Whether `impl` section decorated with `#[near_bindgen]`
39    pub is_out_of_contract_scope: bool,
40    /// Whether method is part of `NearEvent` trait
41    pub is_event: bool,
42    ///functions are being called by this function
43    pub inner_calls: Option<Vec<FunctionInfo>>,
44}
45///Contract information from the code scanned by ContractDescriptor
46pub struct ContractInfo {
47    pub contract_metadata: Vec<ContractDescriptorMeta>,
48    pub contract_name:Option<String>,
49}
50#[derive(Debug)]
51pub struct ContractDescriptorMeta {
52    pub fns: Vec<FunctionInfo>,
53    pub connections: Option<Vec<FunctionInfo>>,
54    pub tokens: Option<TokenStream>,
55}
56
57///Trait for converting tokenstream to extended one
58impl ToTokens for FunctionInfo {
59    ///Function extends TokenStream with FunctionInfo
60    fn to_tokens(&self, tokens: &mut TokenStream) {
61        let name: &str = &self.name;
62        let is_public: bool = self.is_public;
63        let is_trait_impl: bool = self.is_trait_impl;
64        let is_init: bool = self.is_init;
65        let is_payable: bool = self.is_payable;
66        let is_view: bool = self.is_view;
67        let is_mutable: bool = self.is_mutable;
68        let is_process: bool = self.is_process;
69        let is_private_cccalls: bool = self.is_private_cccalls;
70        let is_out_of_contract_scope: bool = self.is_out_of_contract_scope;
71        let is_event: bool = self.is_event;
72        tokens.extend(quote! {
73            FunctionInfo {
74                name: #name,
75                is_public: #is_public,
76                is_trait_impl: #is_trait_impl,
77                is_init: #is_init,
78                is_payable: #is_payable,
79                is_view: #is_view,
80                is_mutable: #is_mutable,
81                is_process: #is_process,
82                is_private_cccalls: #is_private_cccalls,
83                is_out_of_contract_scope: #is_out_of_contract_scope,
84                is_event: #is_event
85            }
86        });
87    }
88
89    ///Converts to TokenStream
90    fn to_token_stream(&self) -> TokenStream {
91        let mut tokens = TokenStream::new();
92        self.to_tokens(&mut tokens);
93        tokens
94    }
95
96    ///gets the token stream
97    fn into_token_stream(self) -> TokenStream
98    where
99        Self: Sized,
100    {
101        self.to_token_stream()
102    }
103}
104///Trait near smart contracts descriptor
105pub trait ContractDescriptor {
106    ///Gets the contract information inside the current crate
107    fn get_contract_info_for_crate(&self,root:Option<&str>,contract_name:Option<String>) -> ContractInfo;
108    fn get_tokens_from_file_path(&self, file_path: &Path) -> ContractDescriptorMeta;
109    fn get_tokens_from_source(&self, src: String) -> ContractDescriptorMeta;
110}
111
112///Default Near contract descriptor
113pub struct DefaultContractDescriptor;
114
115///Implementation of Near contract descriptor
116impl DefaultContractDescriptor {
117    pub fn new() -> Self {
118        Self {}
119    }
120    pub fn version()-> String{
121        String::from("0.0.1")
122    }
123    fn get_inner_calls(
124        &self,
125        fn_name: String,
126        connections: Vec<FunctionInfo>,
127        fns: Vec<FunctionInfo>,
128    ) -> Option<Vec<FunctionInfo>> {
129        let mut def=fn_name;
130        if def=="def_ault"{
131            def=def.replace("def_ault", "default");
132        }
133
134        let con_info = connections
135            .into_iter()
136            .find(|el| def == el.name)
137            .unwrap();
138
139        let mut fn_iter = fns.into_iter();
140
141        let inner_calls = con_info
142            .inner_calls
143            .unwrap()
144            .into_iter()
145            .filter_map(|ic| -> Option<FunctionInfo> {
146                fn_iter.find(|f| f.name == ic.name && !f.is_payable && !f.is_init)
147            })
148            .collect::<Vec<_>>();
149
150        if inner_calls.len() > 0 {
151            Some(inner_calls)
152        } else {
153            None
154        }
155    }
156    fn resolve_call_hierarchy(
157        &self,
158        metadata: ContractDescriptorMeta,
159        fns: Vec<FunctionInfo>,
160    ) -> ContractDescriptorMeta {
161        let iiter = fns;
162        let connections = metadata.connections.unwrap();
163        //print!("{:?}",connections);
164        let result = metadata
165            .fns
166            .iter()
167            .map(|f_info| FunctionInfo {
168                inner_calls: self.get_inner_calls(
169                    f_info.name.clone(),
170                    connections.clone(),
171                    iiter.clone(),
172                ),
173                ..f_info.clone()
174            })
175            .collect::<Vec<FunctionInfo>>();
176
177        ContractDescriptorMeta {
178            fns: result,
179            connections: None,
180            tokens: None,
181        }
182    }
183
184    fn metadata(&self, item: proc_macro2::TokenStream) -> syn::Result<ContractDescriptorMeta> {
185        if let Ok(input) = syn::parse2::<syn::File>(item) {
186            let mut visitor = MetadataVisitor::new();
187            visitor.visit_file(&input);
188            let connections = visitor.get_connections();
189            let fns = visitor.generate_metadata_method().unwrap();
190            syn::Result::Ok(ContractDescriptorMeta {
191                fns,
192                connections: Some(connections),
193                tokens: None,
194            })
195        } else {
196            syn::__private::Err(syn::Error::new(
197                Span::call_site(),
198                "Failed to parse code decorated with `metadataa!{}` macro. Only valid Rust is supported.",
199            ))
200        }
201    }
202    
203}
204
205///Implement contract descriptor trait for DefaultContractDescriptor
206impl ContractDescriptor for DefaultContractDescriptor {
207    fn get_contract_info_for_crate(&self,root:Option<&str>,contract_name:Option<String>) -> ContractInfo {
208        let mut contract_metadata: Vec<ContractDescriptorMeta> = vec![];
209        let mut fns: Vec<FunctionInfo> = vec![];
210        // Walk into every dir to find every `rs` file
211        let root_path=root.unwrap_or(".");
212        for entry in WalkDir::new(root_path).into_iter().filter_map(|e| {
213            let dir = e.unwrap().clone();
214            if !dir.path().to_str().unwrap().contains("test") {
215                return Some(dir);
216            }
217            None
218        }) {
219            if entry.path().extension().map(|s| s == "rs").unwrap_or(false) {
220                //println!("\n{}", entry.path().display());
221                let metadata = self.get_tokens_from_file_path(entry.path());
222                //println!("\n{:?}", metadata.connections);
223                let scoped_fns = metadata.fns.clone();
224                fns.extend(scoped_fns);
225                contract_metadata.push(metadata);
226            }
227        }
228
229        let resolved = contract_metadata
230            .into_iter()
231            .map(|m| self.resolve_call_hierarchy(m, fns.clone()))
232            .collect();
233
234        //println!("\n{:?}", resolved);
235        ContractInfo {
236            contract_metadata: resolved,
237            contract_name,
238        }
239    }
240
241    fn get_tokens_from_file_path(&self, file_path: &Path) -> ContractDescriptorMeta {
242        let mut file = File::open(file_path).expect("Unable to open file");
243        let mut src = String::new();
244        file.read_to_string(&mut src).expect("Unable to read file");
245        self.get_tokens_from_source(src)
246    }
247
248    fn get_tokens_from_source(&self, src: String) -> ContractDescriptorMeta {
249        let syntax = syn::parse_file(&src).expect("Unable to parse file");
250        self.metadata(syntax.to_token_stream()).unwrap()
251    }
252}