rune_parser/
lib.rs

1pub mod types;
2pub mod parser;
3pub mod post_processing;
4pub mod scanner;
5pub mod validation;
6
7use post_processing::{ link_user_definitions, parse_define_statements };
8use scanner::Scanner;
9use std::{ fs::ReadDir, path::Path, process::exit };
10use types::Definitions;
11use validation::validate_struct_indexes;
12
13const ALLOCATION_SIZE: usize = 0x40;
14
15#[derive(Debug, Clone)]
16pub struct RuneFileDescription {
17    pub relative_path: String,
18    pub file_name:     String,
19    pub definitions:   Definitions
20}
21
22#[derive(Debug)]
23pub enum RuneParserError {
24    InvalidInputPath,
25}
26
27pub fn parser_rune_files(input_path: &Path) -> Result<Vec<RuneFileDescription>, RuneParserError> {
28    if !input_path.exists() || !input_path.is_dir() {
29        if !input_path.exists() {
30            println!("Input path \"{0}\" does not exist!", input_path.as_os_str().to_str().unwrap());
31        } else if !input_path.is_dir() {
32            println!("Input path \"{0}\" is not a directory!", input_path.as_os_str().to_str().unwrap());
33        }
34
35        return Err(RuneParserError::InvalidInputPath)
36    }
37
38    // Get rune files from folder
39    // ———————————————————————————
40
41    // Create a vector with allocated space for 64 rune files, which should be more than plenty for most projects
42    let mut rune_file_list: Vec<String> = Vec::with_capacity(ALLOCATION_SIZE);
43
44    get_rune_files(input_path, &mut rune_file_list);
45
46    if rune_file_list.is_empty() {
47        println!("Could not parse any rune files from folder");
48        return Ok(Vec::new())
49    }
50
51    // Print all found files
52    println!("\nFound the following rune files:");
53    for i in 0..rune_file_list.len() {
54        println!("    {0}", rune_file_list[i]);
55    }
56
57    // Process rune files
58    // ———————————————————
59
60    let mut definitions_list: Vec<RuneFileDescription> = Vec::with_capacity(ALLOCATION_SIZE);
61
62    for filepath in rune_file_list {
63        let file_path: &Path = Path::new(&filepath);
64
65        let file = match std::fs::read_to_string(file_path) {
66            Err(error) => panic!("Error in reading file to string. Got error {0}", error),
67            Ok(path)  => path
68        };
69
70        let tokens = match Scanner::new(file.chars()).scan_all() {
71            Err(e) => {
72                eprintln!("Error while scanning file {}: {:#?}", filepath, e);
73                exit(-1);
74            }
75            Ok(t) => t,
76        };
77
78        let types: Definitions = match parser::parse_tokens(&mut tokens.into_iter().peekable()) {
79            Err(e) => {
80                eprintln!("Error while parsing file {}: {:#?}", filepath, e);
81                exit(-1);
82            }
83            Ok(t) => t,
84        };
85
86        // Get isolated file name (without .rune extension)
87        let file_name: String = file_path.file_name().unwrap().to_str().unwrap().strip_suffix(".rune").unwrap().to_string();
88
89        // Get relative path (from input path)
90        let relative_path: String = filepath.strip_prefix(input_path.to_str().unwrap()).unwrap()
91                                            .strip_prefix("/").unwrap()
92                                            .strip_suffix(file_path.file_name().unwrap().to_str().unwrap()).unwrap().to_string();
93
94        definitions_list.push(
95            RuneFileDescription {
96                relative_path: relative_path,
97                file_name:     file_name,
98                definitions:   types,
99            }
100        );
101    }
102
103    // Post-processing
104    // ————————————————
105
106    // Parse and resolve define statements
107    parse_define_statements(&mut definitions_list);
108
109    // Parse and link user defined data types across files
110    link_user_definitions(&mut definitions_list);
111
112    // Validate parsed data structures
113    // ————————————————————————————————
114
115    validate_struct_indexes(&definitions_list);
116
117    // Return list
118    // ————————————
119
120    Ok(definitions_list)
121}
122
123fn get_rune_files(folder_path: &Path, mut rune_file_list: &mut Vec<String>) {
124
125    let folder_iterator: ReadDir = match folder_path.read_dir() {
126        Err(error)  => panic!("Could not read \"{0}\" directory. Got error {1}", folder_path.as_os_str().to_str().unwrap(), error),
127        Ok(value) =>  value
128    };
129
130    for item in folder_iterator {
131        match item {
132            Err(error) => println!("Got an error {0} in one of the items in \"{1}\" directory", error, folder_path.as_os_str().to_str().unwrap()),
133            Ok(item) => {
134                match item.file_type() {
135                    Err(error) => println!("Got error {0} in getting file type of file \"{1}\"", error, item.file_name().to_str().unwrap()),
136                    Ok(file_type) => {
137                        if file_type.is_dir() {
138
139                            // Subfolder
140                            // ——————————
141
142                            println!("Found subdirectory named {0:?}", item.file_name());
143
144                            let subfolder_string: String = format!("{0}/{1}", folder_path.as_os_str().to_str().unwrap(), item.file_name().to_str().unwrap());
145
146                            let subfolder_path: &Path = Path::new(&subfolder_string);
147
148                            // Recursively call function to parse files in subfolder
149                            get_rune_files(subfolder_path, &mut rune_file_list);
150
151                        } else if file_type.is_file() {
152
153                            // Rune file
154                            // ——————————
155
156                            let file_string = item.file_name().into_string().expect("Could not parse os string!");
157
158                            if file_string.ends_with(".rune") {
159                                rune_file_list.push(format!("{0}/{1}", folder_path.as_os_str().to_str().unwrap(), file_string));
160                            }
161
162                        } else {
163                            /* Nothing - Ignore anything that is not a subfolder or a .rune file */
164                        }
165                    }
166                }
167            }
168        };
169    }
170}