thinlinelib/analysis.rs
1use entity::Entity;
2use failure::{err_msg, Fallible};
3use glob::glob;
4use language_type::LanguageType;
5use std::{
6 cell::{Ref, RefCell, RefMut}, fmt::{Display, Formatter, Result}, marker::PhantomData,
7 path::PathBuf,
8};
9
10////////////////////////////////////////////////////////////////////////////////
11
12/// Represents a entity description.
13#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub struct Description {
15 pub description: Vec<String>,
16}
17
18impl Description {
19 /// Creates a new Description instance.
20 pub fn new() -> Self {
21 Self {
22 description: Vec::new(),
23 }
24 }
25
26 /// Sets and formats the description.
27 pub fn set(&mut self, description: &str) {
28 self.description = description
29 .split('\n')
30 .map(|desc| {
31 String::from(
32 desc.trim_left()
33 .trim_left_matches('*')
34 .trim_left_matches('/')
35 .trim_left(),
36 )
37 })
38 .filter(|ref desc| !desc.is_empty() && desc.as_str() != "**")
39 .map(|desc| {
40 if desc.chars().next() == Some('#') {
41 desc.replace(" ", "")
42 } else {
43 desc
44 }
45 })
46 .collect();
47 }
48}
49
50////////////////////////////////////////////////////////////////////////////////
51
52/// Represents a parsed function argument.
53#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
54pub struct Argument {
55 pub name: String,
56 pub atype: Option<String>,
57 pub value: Option<String>,
58}
59
60impl Argument {
61 /// Creates a new Argument instance.
62 ///
63 /// # Example
64 ///
65 /// ```
66 /// use thinlinelib::analysis::Argument;
67 ///
68 /// let argument = Argument::new("int1", Some("int"));
69 ///
70 /// assert_eq!(argument.name, "int1");
71 /// assert!(argument.atype.is_some());
72 /// assert_eq!(argument.atype.unwrap(), "int");
73 /// ```
74 pub fn new<S: Into<String>>(name: S, atype: Option<S>) -> Self {
75 Argument {
76 name: name.into(),
77 atype: atype.map(S::into),
78 value: None,
79 }
80 }
81
82 /// Sets a value to the argument.
83 ///
84 /// # Example
85 ///
86 /// ```
87 /// use thinlinelib::analysis::Argument;
88 ///
89 /// let mut argument = Argument::new("arg", Some("std::string"));
90 /// argument.set_value("FirstArg");
91 ///
92 /// assert!(argument.value.is_some());
93 ///
94 /// ```
95 pub fn set_value(&mut self, value: &str) {
96 self.value = Some(String::from(value));
97 }
98}
99
100////////////////////////////////////////////////////////////////////////////////
101
102/// Represents a parsed function type.
103#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
104pub struct Function {
105 pub name: String,
106 pub return_type: Option<String>,
107 pub arguments: Vec<Argument>,
108 pub description: Option<Description>,
109}
110
111impl Function {
112 /// Creates a new Function instance.
113 ///
114 /// # Example
115 ///
116 /// ```
117 /// use thinlinelib::analysis::Function;
118 ///
119 /// let function = Function::new("testFunction");
120 ///
121 /// assert_eq!(function.name, String::from("testFunction"));
122 /// assert!(function.return_type.is_none());
123 /// assert!(function.arguments.is_empty());
124 /// assert!(function.description.is_none());
125 /// ```
126 pub fn new<S: Into<String>>(name: S) -> Self {
127 Self {
128 name: name.into(),
129 return_type: None,
130 arguments: Vec::new(),
131 description: None,
132 }
133 }
134
135 /// Creates the format type for the Function.
136 ///
137 /// # Example
138 ///
139 /// ```
140 /// use thinlinelib::analysis::Function;
141 ///
142 /// let mut function = Function::new("testFunction");
143 /// function.set_return_type("int");
144 ///
145 /// assert_eq!(function.return_type, Some(String::from("int")));
146 ///
147 /// function.set_return_type("");
148 ///
149 /// assert_eq!(function.return_type, None);
150 /// ```
151 pub fn set_return_type(&mut self, ftype: &str) -> Fallible<()> {
152 if ftype.is_empty() {
153 self.return_type = None;
154 } else {
155 let ftype_vec: Vec<&str> = ftype.split('(').collect();
156 self.return_type = Some(String::from(
157 ftype_vec
158 .get(0)
159 .ok_or_else(|| err_msg("Function type can not be parsed from signature."))?
160 .trim_right(),
161 ));
162 }
163
164 Ok(())
165 }
166
167 /// Sets the description for the Function.
168 ///
169 /// # Example
170 ///
171 /// ```
172 /// use thinlinelib::analysis::Function;
173 ///
174 /// let mut function = Function::new("testFunction");
175 /// function.set_description("
176 /// # TESTCASE(check_if_sum_works)
177 /// int test_no = 2;
178 /// #EQ[TL_FCT(no1: test_no, no2: 5) => 7]
179 /// #EQ[TL_FCT(no1: 5, no2: 2) => 7]
180 /// EXPECT_EQ(11, test_int_no1(9, 2));
181 /// ");
182 ///
183 /// assert!(function.description.is_some());
184 /// ```
185 pub fn set_description(&mut self, description: &str) {
186 if self.description.is_none() {
187 self.description = Some(Description::new());
188 }
189
190 if let Some(desc) = &mut self.description {
191 desc.set(description);
192 }
193 }
194
195 /// Sets arguments for the Function.
196 pub fn set_arguments(&mut self, arguments: &[Argument]) {
197 self.arguments = arguments.into();
198 }
199}
200
201////////////////////////////////////////////////////////////////////////////////
202
203/// Represents a parsed enum argument.
204#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
205pub struct Enum {
206 pub name: String,
207 pub etype: Option<String>,
208 pub arguments: Vec<Argument>,
209}
210
211impl Enum {
212 /// Creates a new Enum instance.
213 ///
214 /// # Example
215 ///
216 /// ```
217 /// use thinlinelib::analysis::Enum;
218 ///
219 /// let enumeration = Enum::new("testEnum");
220 ///
221 /// assert_eq!(enumeration.name, String::from("testEnum"));
222 /// assert!(enumeration.etype.is_none());
223 /// assert!(enumeration.arguments.is_empty());
224 /// ```
225 pub fn new<S: Into<String>>(name: S) -> Self {
226 Self {
227 name: name.into(),
228 etype: None,
229 arguments: Vec::new(),
230 }
231 }
232
233 /// Sets arguments for the Enum.
234 ///
235 /// # Example
236 ///
237 /// ```
238 /// use thinlinelib::analysis::{Argument, Enum};
239 ///
240 /// let mut enumeration = Enum::new("testEnum");
241 /// let args = vec![Argument::new("Zero", Some("0")), Argument::new("Two", Some("2"))];
242 /// enumeration.set_arguments(&args);
243 ///
244 /// assert_eq!(enumeration.arguments.len(), 2);
245 /// ```
246 pub fn set_arguments(&mut self, arguments: &[Argument]) {
247 self.arguments = arguments.into();
248 }
249}
250
251////////////////////////////////////////////////////////////////////////////////
252
253#[derive(Default, Clone, Debug)]
254pub struct ProjectFile<T> {
255 pub pf_type: PhantomData<T>,
256 pub path: PathBuf,
257 pub entities: RefCell<Vec<Entity>>,
258}
259
260/// Represents a parsed project file.
261impl<T> ProjectFile<T>
262where
263 T: LanguageType,
264{
265 /// Creates a new ProjectFile instance.
266 ///
267 /// # Example
268 ///
269 /// ```
270 /// use std::path::PathBuf;
271 /// use thinlinelib::analysis::ProjectFile;
272 /// use thinlinelib::language_type::C;
273 ///
274 /// let project_file: ProjectFile<C> = ProjectFile::new("test/project_file");
275 ///
276 /// assert_eq!(project_file.path, PathBuf::from("test/project_file"));
277 /// assert_eq!(project_file.entities().len(), 0);
278 /// ```
279 pub fn new<S: Into<PathBuf>>(path: S) -> Self {
280 ProjectFile {
281 pf_type: PhantomData,
282 path: path.into(),
283 entities: RefCell::new(Vec::new()),
284 }
285 }
286
287 /// Returns a reference to the entities list.
288 ///
289 /// # Example
290 ///
291 /// ```
292 /// use thinlinelib::analysis::ProjectFile;
293 /// use thinlinelib::entity::Entity;
294 /// use thinlinelib::language_type::C;
295 ///
296 /// let project_file: ProjectFile<C> = ProjectFile::new("test/project_file");
297 /// project_file.entities_mut().push(Entity::new("testEntity"));
298 ///
299 /// assert_eq!(project_file.entities().len(), 1);
300 /// ```
301 pub fn entities(&self) -> Ref<Vec<Entity>> {
302 self.entities.borrow()
303 }
304
305 /// Returns a mutable reference to the entities list.
306 ///
307 /// # Example
308 ///
309 /// ```
310 /// use thinlinelib::analysis::ProjectFile;
311 /// use thinlinelib::entity::Entity;
312 /// use thinlinelib::language_type::C;
313 ///
314 /// let project_file: ProjectFile<C> = ProjectFile::new("test/project_file");
315 /// project_file.entities_mut().push(Entity::new("testEntity"));
316 ///
317 /// let mut entities = project_file.entities_mut();
318 /// assert_eq!(entities.len(), 1);
319 ///
320 /// entities.clear();
321 /// assert_eq!(entities.len(), 0);
322 /// ```
323 pub fn entities_mut(&self) -> RefMut<Vec<Entity>> {
324 self.entities.borrow_mut()
325 }
326}
327
328impl<T> Display for ProjectFile<T>
329where
330 T: LanguageType,
331{
332 /// Formats a ProjectFile to be displayed by std output.
333 fn fmt(&self, f: &mut Formatter) -> Result {
334 if let Some(path) = self.path.to_str() {
335 return write!(f, "{}", path);
336 }
337 write!(f, "Unable to stringify filename")
338 }
339}
340
341////////////////////////////////////////////////////////////////////////////////
342
343#[derive(Default, Debug)]
344pub struct Analysis<T>
345where
346 T: LanguageType,
347{
348 pub file_types: &'static [&'static str],
349 pub project_files: RefCell<Vec<ProjectFile<T>>>,
350}
351
352impl<T> Analysis<T>
353where
354 T: LanguageType,
355{
356 /// Creates a new Analysis instance.
357 ///
358 /// # Example
359 ///
360 /// ```
361 /// use thinlinelib::analysis::Analysis;
362 /// use thinlinelib::language_type::{C, LanguageType};
363 ///
364 /// let analysis: Analysis<C> = Analysis::new();
365 ///
366 /// assert_eq!(analysis.file_types, C::file_types());
367 /// assert_eq!(analysis.project_files().len(), 0);
368 /// ```
369 pub fn new() -> Self {
370 Analysis {
371 file_types: T::file_types(),
372 project_files: RefCell::new(Vec::new()),
373 }
374 }
375
376 /// Returns a reference to the collected project files for analysis.
377 pub fn project_files(&self) -> Ref<Vec<ProjectFile<T>>> {
378 self.project_files.borrow()
379 }
380
381 /// Returns a mutable reference to the collected project files for analysis.
382 ///
383 /// # Example
384 ///
385 /// ```
386 /// use thinlinelib::analysis::{Analysis, ProjectFile};
387 /// use thinlinelib::language_type::C;
388 ///
389 /// let analysis: Analysis<C> = Analysis::new();
390 /// let mut project_files = analysis.project_files_mut();
391 /// assert_eq!(project_files.len(), 0);
392 ///
393 /// project_files.push(ProjectFile::new("test/anotherFile"));
394 /// assert_eq!(project_files.len(), 1);
395 /// ```
396 pub fn project_files_mut(&self) -> RefMut<Vec<ProjectFile<T>>> {
397 self.project_files.borrow_mut()
398 }
399
400 /// Collects all the sources within the given project dir.
401 pub fn collect_sources(&self, project_dir: &PathBuf, search_dirs: &[String]) -> Fallible<()> {
402 // Check the given project directory
403 if !project_dir.exists() || !project_dir.is_dir() {
404 return Err(format_err!(
405 "The given project dir '{}' does not exist.",
406 project_dir
407 .to_str()
408 .ok_or_else(|| err_msg("Unable to stringify project dir path."))?
409 ));
410 }
411
412 // Traverse through the files within the specified source directories
413 // and store them for analyzing purposes
414 for src_dir in search_dirs {
415 for ext in self.file_types {
416 for entry in glob(
417 project_dir
418 .join(src_dir)
419 .join("**")
420 .join(String::from("*.") + ext)
421 .to_str()
422 .unwrap_or("."),
423 )? {
424 self.project_files_mut().push(ProjectFile::new(entry?));
425 }
426 }
427 }
428
429 Ok(())
430 }
431
432 /// Extracts function signatures and comments of thinlines parsed files.
433 pub fn extract_entities(&self) -> Fallible<()> {
434 T::extract_entities(&self)
435 }
436}