/ src / build_process.rs
build_process.rs
  1  pub mod build_process_handler {
  2  
  3      use file_handling::file_handling::file_handling::{
  4          collect_asset_directories, handle_config_details_update, handle_storage, modify_pubspec,
  5      };
  6      use markdown_backend_utilities::{
  7          data_processing::merge_json::merge_json,
  8          file::{file_name_extractor, is_emacs_file::is_emacs_file},
  9      };
 10      use markdown_element_lib::markdown_line_element::MarkdownLineElement;
 11      use serde_json::Value;
 12      use std::collections::HashMap;
 13      use std::fs;
 14      use std::io::BufRead;
 15      use std::path::Path;
 16  
 17      pub fn build_handler(target_location: &Path, output_dir: &Path, build_command: &str) {
 18          // Check target_location exists
 19          if !target_location.exists() {
 20              panic!("Target location does not exist: {:?}", target_location);
 21          }
 22  
 23          let build_dir_string = &format!(
 24              "{}{}",
 25              output_dir.to_string_lossy(),
 26              "/assets/processed_data/"
 27          );
 28          let build_dir: &Path = Path::new(&build_dir_string);
 29  
 30          // Ensure we are not setting the output directory inside itself
 31          if target_location == build_dir {
 32              panic!("Target location cannot be 'processed_data/' itself.");
 33          }
 34  
 35          // Remove existing build
 36          if build_dir.exists() {
 37              fs::remove_dir_all(&build_dir).expect(
 38                  format!(
 39                      "Failed to delete existing processed_data directory in {:#?}",
 40                      target_location
 41                  )
 42                  .as_str(),
 43              );
 44          }
 45  
 46          // Create new build direcory
 47          fs::create_dir_all(&build_dir).expect("Failed to create output directory");
 48  
 49          let mut config_details: HashMap<String, HashMap<String, String>> = HashMap::new();
 50          let mut additional_config_details: HashMap<String, HashMap<String, Vec<String>>> =
 51              HashMap::new();
 52          additional_config_details.insert(
 53              "actionWidgetCallbackValuesListsMap".to_string(),
 54              HashMap::new(),
 55          );
 56          additional_config_details.insert("actionWidgetStringsListsMap".to_string(), HashMap::new());
 57          // Recursively processes md tree at target_location into toml tree at build_dir for Flutter to build with
 58          process_directory(
 59              &target_location,
 60              &target_location,
 61              &build_dir,
 62              &mut config_details,
 63              &mut additional_config_details,
 64          );
 65  
 66          // Serialize config_details into JSON format
 67          let config_details_json = serde_json::to_string(&config_details)
 68              .expect("Failed to serialize config_details to JSON");
 69          let additional_config_details_json = serde_json::to_string(&additional_config_details)
 70              .expect("Failed to serialize additional_config_details to JSON");
 71  
 72          // Define the output path for the JSON file
 73          let config_details_output_path = build_dir.join("nexus_key_config.json");
 74  
 75          // Write the serialized JSON to the file
 76          fs::write(
 77              config_details_output_path,
 78              serde_json::to_string(&merge_json(
 79                  &config_details_json,
 80                  &additional_config_details_json,
 81              ))
 82              .unwrap(),
 83          )
 84          .expect("Failed to write config_details JSON to file");
 85  
 86          // Get list of all subdirectories to put in
 87          // assets file (pubspec.yaml)because Flutter
 88          // is annoying with its build system
 89          let mut list_of_nested_directory_locations: Vec<String> = vec![];
 90          collect_asset_directories(
 91              build_dir,
 92              "processed_data",
 93              &mut list_of_nested_directory_locations,
 94          );
 95  
 96          // Write over the existing pubspec.yaml with
 97          // the old assets locations removed and new
 98          // ones added
 99          modify_pubspec(
100              &output_dir.join("pubspec.yaml"),
101              list_of_nested_directory_locations,
102          );
103  
104          // build_and_launch_flutter(output_dir, build_command);
105          println!(
106              "build_and_launch_flutter(\noutput_dir={},\nbuild_command={}",
107              output_dir.display(),
108              build_command
109          );
110      }
111  
112      fn process_directory(
113          // Original Dir is needed to prevent copying the same substring showing where the root is every single time when using input_dir as key
114          original_dir: &Path,
115          input_dir: &Path,
116          output_dir: &Path,
117          config_details: &mut HashMap<String, HashMap<String, String>>,
118          additional_config_details: &mut HashMap<String, HashMap<String, Vec<String>>>,
119      ) {
120          if input_dir
121              .file_name()
122              .map_or(false, |name| name == "markdown_to_flutter_build_output")
123          {
124              return;
125          }
126          let output_key: String = input_dir
127              .strip_prefix(original_dir)
128              .map(|p| p.to_string_lossy().into_owned())
129              .unwrap_or_else(|_| input_dir.to_string_lossy().into_owned());
130  
131          if let Some(map) = additional_config_details.get_mut("actionWidgetStringsListsMap") {
132              map.insert(output_key.to_string(), vec![]);
133          }
134          if let Some(map) = additional_config_details.get_mut("actionWidgetCallbackValuesListsMap") {
135              map.insert(output_key.to_string(), vec![]);
136          }
137  
138          // Get and go through files in directory
139          for entry in fs::read_dir(input_dir).expect("Failed to read directory") {
140              // Get file
141              let entry = entry.expect("Failed to get directory entry");
142              // File path
143              let path = entry.path();
144  
145              // Ensure we are not processing anything inside processed_data itself
146              if path.starts_with(&output_dir) {
147                  continue;
148              }
149  
150              // Get the text after the last slash
151              // Path => String
152              let file_name = file_name_extractor::extract_file_name_from_path(
153                  path.to_string_lossy().to_string(),
154              );
155  
156              // Where the file is going to be saved
157              let output_path = output_dir.join(&file_name);
158  
159              // Check if directory
160              if path.is_dir() {
161                  // Make directory
162                  fs::create_dir_all(&output_path).expect(&format!(
163                      "Failed to create output directory: {:#?}",
164                      &output_path
165                  ));
166  
167                  // Recurse for current directory
168                  process_directory(
169                      &original_dir,
170                      &path,
171                      &output_path,
172                      config_details,
173                      additional_config_details,
174                  );
175              }
176              // Is a file
177              else if let Some(extension) = path.extension() {
178                  // Not a file to be ignored
179                  if !(extension == "md" || extension == "json") || is_emacs_file(&path) {
180                      continue;
181                  }
182  
183                  let content = match fs::read_to_string(&path) {
184                      Ok(file_content) => file_content,
185                      Err(e) => {
186                          if (extension == "json") {
187                              panic!(
188                                  "JSON formatting error in file: {:#?}\nERR MSG:\n{}",
189                                  path, e
190                              );
191                          } else {
192                              panic!("Error opening md file: {}", e);
193                          }
194                      }
195                  };
196  
197                  match extension.to_str().unwrap() {
198                      "md" => {
199                          let parsed_lines = MarkdownLineElement::parse_markdown(&content);
200                          handle_config_details_update(
201                              &parsed_lines,
202                              config_details,
203                              &output_key,
204                              &file_name,
205                          );
206  
207                          handle_storage(
208                              &file_name,
209                              &parsed_lines,
210                              output_path.with_extension("json"),
211                          );
212                      }
213                      "json" => {
214                          let parsed_json: Value = serde_json::from_str(&content)
215                              .expect(&format!("Failed to parse JSON file: {:#?}", output_dir));
216  
217                          for (section, items) in
218                              parsed_json.as_object().expect("JSON is not an object")
219                          {
220                              if let Some(array) = items.as_array() {
221                                  println!("Section: {}", section);
222  
223                                  for item in array {
224                                      if let Some(obj) = item.as_object() {
225                                          println!("HERR {:#?}", obj);
226  
227                                          if let Some(map) = additional_config_details
228                                              .get_mut("actionWidgetCallbackValuesListsMap")
229                                          {
230                                              if let Some(val) = map.get_mut(&output_key) {
231                                                  if let Some(location) =
232                                                      obj.get("location").and_then(|v| v.as_str())
233                                                  {
234                                                      val.push(location.to_string());
235                                                  }
236                                              }
237                                          }
238  
239                                          if let Some(map) = additional_config_details
240                                              .get_mut("actionWidgetStringsListsMap")
241                                          {
242                                              if let Some(val) = map.get_mut(&output_key) {
243                                                  if let Some(title) =
244                                                      obj.get("title").and_then(|v| v.as_str())
245                                                  {
246                                                      val.push(title.to_string());
247                                                  }
248                                              }
249                                          }
250                                      }
251                                  }
252                              }
253                          }
254  
255                          fs::write(output_path.with_extension("json"), content)
256                              .expect("Failed to write config JSON to file");
257                      }
258                      _ => {
259                          panic!("What the fuck? We're only supposed to have md and json files!")
260                      }
261                  };
262              }
263          }
264      }
265  }