scanner_syn/
contract_descriptor.rs1use 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#[derive(Clone, Default, Debug)]
18pub struct FunctionInfo {
19 pub name: String,
20 pub is_public: bool,
22 pub is_trait_impl: bool,
24 pub is_init: bool,
26 pub is_test: bool,
28 pub is_payable: bool,
30 pub is_view: bool,
32 pub is_mutable: bool,
34 pub is_process: bool,
36 pub is_private_cccalls: bool,
38 pub is_out_of_contract_scope: bool,
40 pub is_event: bool,
42 pub inner_calls: Option<Vec<FunctionInfo>>,
44}
45pub 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
57impl ToTokens for FunctionInfo {
59 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 fn to_token_stream(&self) -> TokenStream {
91 let mut tokens = TokenStream::new();
92 self.to_tokens(&mut tokens);
93 tokens
94 }
95
96 fn into_token_stream(self) -> TokenStream
98 where
99 Self: Sized,
100 {
101 self.to_token_stream()
102 }
103}
104pub trait ContractDescriptor {
106 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
112pub struct DefaultContractDescriptor;
114
115impl 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 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
205impl 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 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 let metadata = self.get_tokens_from_file_path(entry.path());
222 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 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}