178 lines
5.2 KiB
Rust
178 lines
5.2 KiB
Rust
|
use std::{
|
||
|
collections::HashMap,
|
||
|
path::{Path, PathBuf},
|
||
|
};
|
||
|
|
||
|
const SUPPORTED_FAMILIES: [&str; 8] = [
|
||
|
"STM32F0",
|
||
|
"STM32F4",
|
||
|
"STM32G0",
|
||
|
"STM32L0",
|
||
|
"STM32L4",
|
||
|
"STM32H7",
|
||
|
"STM32WB55",
|
||
|
"STM32WL55",
|
||
|
];
|
||
|
|
||
|
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";
|
||
|
|
||
|
/// True if the chip named `name` is supported else false
|
||
|
fn is_supported(name: &str) -> bool {
|
||
|
SUPPORTED_FAMILIES
|
||
|
.iter()
|
||
|
.any(|family| name.starts_with(family))
|
||
|
}
|
||
|
|
||
|
/// Get the yaml file names and the associated chip names for supported chips
|
||
|
///
|
||
|
/// Print errors to `stderr` when something is returned by the glob but is not in the returned
|
||
|
/// [`Vec`]
|
||
|
fn supported_chip_yaml_files_with_names() -> Vec<(PathBuf, String)> {
|
||
|
glob::glob("../stm32-data/data/chips/*.yaml")
|
||
|
.expect("bad glob pattern")
|
||
|
.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()) {
|
||
|
if is_supported(name) {
|
||
|
let owned_name = name.to_lowercase();
|
||
|
Some((entry, owned_name))
|
||
|
} else {
|
||
|
eprintln!("{} is not supported", name);
|
||
|
None
|
||
|
}
|
||
|
} else {
|
||
|
eprintln!("{:?} is not a regural 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
|
||
|
///
|
||
|
/// # None
|
||
|
/// Returns none if "cores" is not an array
|
||
|
fn chip_cores(path: &Path) -> Option<Vec<yaml_rust::Yaml>> {
|
||
|
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().cloned()
|
||
|
}
|
||
|
|
||
|
/// Load the list of chips
|
||
|
///
|
||
|
/// # Panic
|
||
|
/// Panics if a file contains yaml syntax errors or if a value does not have a consistent type
|
||
|
pub fn load_chip_list() -> HashMap<String, Vec<String>> {
|
||
|
let mut result = HashMap::new();
|
||
|
for (path, name) in supported_chip_yaml_files_with_names() {
|
||
|
let cores = chip_cores(&path).unwrap_or_else(|| panic!("{}[cores] is not an array", name));
|
||
|
if cores.len() > 1 {
|
||
|
for (i, core) in cores.into_iter().enumerate() {
|
||
|
let core_name = core["name"]
|
||
|
.as_str()
|
||
|
.unwrap_or_else(|| panic!("{}[cores][{}][name] is not a string", name, i));
|
||
|
let key = format!("{}_{}", name, core_name);
|
||
|
let value = vec![format!("stm32-metapac/{}_{}", name, core_name)];
|
||
|
result.insert(key, value);
|
||
|
}
|
||
|
} else {
|
||
|
let value = vec![format!("stm32-metapac/{}", &name)];
|
||
|
result.insert(name, value);
|
||
|
}
|
||
|
}
|
||
|
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: &HashMap<String, Vec<String>>,
|
||
|
) -> String {
|
||
|
let (before, after) = split_cargo_toml_contents(previous_text);
|
||
|
let generated_content = toml::to_string(new_contents).unwrap();
|
||
|
before.to_owned() + SEPARATOR_START + HELP + &generated_content + SEPARATOR_END + after
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod tests {
|
||
|
use super::*;
|
||
|
|
||
|
#[test]
|
||
|
fn stm32f407vg_is_supported() {
|
||
|
assert!(is_supported("STM32F407VG"))
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn abcdef_is_not_supported() {
|
||
|
assert!(!is_supported("ABCDEF"))
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn stm32f407vg_yaml_file_exists() {
|
||
|
assert!(supported_chip_yaml_files_with_names()
|
||
|
.into_iter()
|
||
|
.any(|(path, name)| {
|
||
|
name == "stm32f407vg"
|
||
|
&& path.to_str() == Some("../stm32-data/data/chips/STM32F407VG.yaml")
|
||
|
}))
|
||
|
}
|
||
|
|
||
|
#[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 map = HashMap::from([(String::from("a"), vec![String::from("b")])]);
|
||
|
assert_eq!(generate_cargo_toml_file(initial, &map), expected);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
#[should_panic]
|
||
|
fn does_not_generate_if_separators_are_missing() {
|
||
|
let initial = "\
|
||
|
before
|
||
|
# END GENERATED FEATURES
|
||
|
after
|
||
|
";
|
||
|
|
||
|
let map = HashMap::from([(String::from("a"), vec![String::from("b")])]);
|
||
|
generate_cargo_toml_file(initial, &map);
|
||
|
}
|
||
|
}
|