//! FIXME discuss about which errors to print and when to panic use std::path::Path; const SEPARATOR_START: &str = "# BEGIN GENERATED FEATURES\n"; const SEPARATOR_END: &str = "# END GENERATED FEATURES\n"; const HELP: &str = "# Generated by stm32-gen-features. DO NOT EDIT.\n"; /// Get the list of all the chips and their supported cores /// /// Print errors to `stderr` when something is returned by the glob but is not in the returned /// [`Vec`] /// /// This function is slow because all the yaml files are parsed. pub fn chip_names_and_cores() -> Vec<(String, Vec)> { glob::glob("../stm32-data/data/chips/*.json") .unwrap() .filter_map(|entry| entry.map_err(|e| eprintln!("{:?}", e)).ok()) .filter_map(|entry| { if let Some(name) = entry.file_stem().and_then(|stem| stem.to_str()) { Some((name.to_lowercase(), chip_cores(&entry))) } else { eprintln!("{:?} is not a regular file", entry); None } }) .collect() } /// Get the list of the cores of a chip by its associated file /// /// # Panic /// Panics if the file does not exist or if it contains yaml syntax errors. /// Panics if "cores" is not an array. fn chip_cores(path: &Path) -> Vec { let file_contents = std::fs::read_to_string(path).unwrap(); let doc = &yaml_rust::YamlLoader::load_from_str(&file_contents).unwrap()[0]; doc["cores"] .as_vec() .unwrap_or_else(|| panic!("{:?}:[cores] is not an array", path)) .iter() .enumerate() .map(|(i, core)| { core["name"] .as_str() .unwrap_or_else(|| panic!("{:?}:[cores][{}][name] is not a string", path, i)) .to_owned() }) .collect() } /// Generate data needed in `../embassy-stm32/Cargo.toml` /// /// Print errors to `stderr` when something is returned by the glob but is not in the returned /// [`Vec`] /// /// # Panic /// Panics if a file contains yaml syntax errors or if a value does not have a consistent type pub fn embassy_stm32_needed_data(names_and_cores: &[(String, Vec)]) -> String { let mut result = String::new(); for (chip_name, cores) in names_and_cores { if cores.len() > 1 { for core_name in cores.iter() { result += &format!( "{chip}-{core} = [ \"stm32-metapac/{chip}-{core}\" ]\n", chip = chip_name, core = core_name ); } } else { result += &format!("{chip} = [ \"stm32-metapac/{chip}\" ]\n", chip = chip_name); } } result } /// Generate data needed in `../stm32-metapac/Cargo.toml` /// /// Print errors to `stderr` when something is returned by the glob but is not in the returned /// [`Vec`] /// /// # Panic /// Panics if a file contains yaml syntax errors or if a value does not have a consistent type pub fn stm32_metapac_needed_data(names_and_cores: &[(String, Vec)]) -> String { let mut result = String::new(); for (chip_name, cores) in names_and_cores { if cores.len() > 1 { for core_name in cores { result += &format!("{}-{} = []\n", chip_name, core_name); } } else { result += &format!("{} = []\n", chip_name); } } result } /// Get contents before and after generated contents /// /// # Panic /// Panics when a separator cound not be not found fn split_cargo_toml_contents(contents: &str) -> (&str, &str) { let (before, remainder) = contents .split_once(SEPARATOR_START) .unwrap_or_else(|| panic!("missing \"{}\" tag", SEPARATOR_START)); let (_, after) = remainder .split_once(SEPARATOR_END) .unwrap_or_else(|| panic!("missing \"{}\" tag", SEPARATOR_END)); (before, after) } /// Generates new contents for Cargo.toml /// /// # Panic /// Panics when a separator cound not be not found pub fn generate_cargo_toml_file(previous_text: &str, new_contents: &str) -> String { let (before, after) = split_cargo_toml_contents(previous_text); before.to_owned() + SEPARATOR_START + HELP + new_contents + SEPARATOR_END + after } #[cfg(test)] mod tests { use super::*; #[test] #[ignore] fn stm32f407vg_yaml_file_exists() { assert!(chip_names_and_cores() .as_slice() .into_iter() .any(|(name, _)| { name == "stm32f407vg" })) } #[test] fn keeps_text_around_separators() { let initial = "\ before # BEGIN GENERATED FEATURES # END GENERATED FEATURES after "; let expected = "\ before # BEGIN GENERATED FEATURES # Generated by stm32-gen-features. DO NOT EDIT. a = [\"b\"] # END GENERATED FEATURES after "; let new_contents = String::from("a = [\"b\"]\n"); assert_eq!(generate_cargo_toml_file(initial, &new_contents), expected); } #[test] #[should_panic] fn does_not_generate_if_separators_are_missing() { let initial = "\ before # END GENERATED FEATURES after "; let new_contents = String::from("a = [\"b\"]\n"); generate_cargo_toml_file(initial, &new_contents); } }