diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a73097..47b80f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - After generating the project the tool now checks the rust version, espflash version and probe-rs version (#88) - Be more helpful in case of common linker errors (#94) - Support for `ELIF` conditions (#96) -- Display help text (#100) +- Display help text (#100, #103) ### Changed - Update `probe-rs run` arguments (#90) @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The UI no longer allows selecting options with missing requirements, and does not allow deselecting options that are required by other options. (#101) - Options can now declare negative requirements (e.g. `!alloc` can not be enabled if `alloc` is used) (#101) +- Template settings are now described in a template-specific `yaml` file (#103) ### Fixed diff --git a/Cargo.lock b/Cargo.lock index f8ce7cd..0b2a46f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,6 +422,8 @@ dependencies = [ "quote", "ratatui", "rhai", + "serde", + "serde_yaml", "taplo", "update-informer", "walkdir", @@ -843,6 +845,16 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown 0.15.1", +] + [[package]] name = "indoc" version = "2.0.5" @@ -1531,6 +1543,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1959,6 +1984,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 4b7d8ef..74b629e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,8 @@ ratatui = { version = "0.29.0", features = ["crossterm", "unstable"] } rhai = "1.20.1" taplo = "0.13.2" update-informer = "1.1.0" +serde = { version = "1", features = ["derive"] } +serde_yaml = "0.9" [build-dependencies] quote = "1.0.38" diff --git a/build.rs b/build.rs index 8f12e1d..5f44607 100644 --- a/build.rs +++ b/build.rs @@ -31,6 +31,10 @@ fn main() { continue; } + if path.file_name() == Some(std::ffi::OsStr::new("template.yaml")) { + continue; + } + println!("{:?} {}", path, relative_path); let content = std::fs::read_to_string(path).unwrap(); diff --git a/src/main.rs b/src/main.rs index 60bc8cc..52b5405 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,12 +4,14 @@ use std::{ fs, path::{Path, PathBuf}, process::{self, Command}, + sync::LazyLock, time::Duration, }; use clap::Parser; use env_logger::{Builder, Env}; use esp_metadata::Chip; +use serde::{Deserialize, Serialize}; use taplo::formatter::Options; use update_informer::{registry, Check}; @@ -17,13 +19,16 @@ mod check; mod template_files; mod tui; -#[derive(Clone, Copy)] +#[derive(Clone, Serialize, Deserialize)] pub struct GeneratorOption { - name: &'static str, - display_name: &'static str, - help: &'static str, - requires: &'static [&'static str], - chips: &'static [Chip], + name: String, + display_name: String, + #[serde(default)] + help: String, + #[serde(default)] + requires: Vec, + #[serde(default)] + chips: Vec, } impl GeneratorOption { @@ -32,42 +37,44 @@ impl GeneratorOption { } } -#[derive(Clone, Copy)] +#[derive(Clone, Serialize, Deserialize)] pub struct GeneratorOptionCategory { - name: &'static str, - display_name: &'static str, - help: &'static str, - options: &'static [GeneratorOptionItem], + name: String, + display_name: String, + #[serde(default)] + help: String, + #[serde(default)] + options: Vec, } impl GeneratorOptionCategory { fn options(&self) -> Vec { let mut res = Vec::new(); - for option in self.options { + for option in self.options.iter() { res.extend(option.options()); } res } } -#[derive(Clone, Copy)] +#[derive(Clone, Serialize, Deserialize)] pub enum GeneratorOptionItem { Category(GeneratorOptionCategory), Option(GeneratorOption), } impl GeneratorOptionItem { - fn title(&self) -> String { + fn title(&self) -> &str { match self { - GeneratorOptionItem::Category(category) => category.display_name.to_string(), - GeneratorOptionItem::Option(option) => option.display_name.to_string(), + GeneratorOptionItem::Category(category) => category.display_name.as_str(), + GeneratorOptionItem::Option(option) => option.display_name.as_str(), } } - fn name(&self) -> String { + fn name(&self) -> &str { match self { - GeneratorOptionItem::Category(category) => category.name.to_string(), - GeneratorOptionItem::Option(option) => option.name.to_string(), + GeneratorOptionItem::Category(category) => category.name.as_str(), + GeneratorOptionItem::Option(option) => option.name.as_str(), } } @@ -82,135 +89,37 @@ impl GeneratorOptionItem { matches!(self, GeneratorOptionItem::Category(_)) } - fn chips(&self) -> &'static [Chip] { + fn chips(&self) -> &[Chip] { match self { GeneratorOptionItem::Category(_) => &[], - GeneratorOptionItem::Option(option) => option.chips, + GeneratorOptionItem::Option(option) => option.chips.as_slice(), } } - fn requires(&self) -> &[&str] { + fn requires(&self) -> &[String] { match self { GeneratorOptionItem::Category(_) => &[], - GeneratorOptionItem::Option(option) => option.requires, + GeneratorOptionItem::Option(option) => option.requires.as_slice(), } } fn help(&self) -> &str { match self { - GeneratorOptionItem::Category(category) => category.help, - GeneratorOptionItem::Option(option) => option.help, + GeneratorOptionItem::Category(category) => &category.help, + GeneratorOptionItem::Option(option) => &option.help, } } } -static OPTIONS: &[GeneratorOptionItem] = &[ - GeneratorOptionItem::Option(GeneratorOption { - name: "alloc", - display_name: "Enable allocations via the `esp-alloc` crate.", - help: "", - requires: &[], - chips: &[], - }), - GeneratorOptionItem::Option(GeneratorOption { - name: "wifi", - display_name: "Enable Wi-Fi via the `esp-wifi` crate.", - help: "Requires `alloc`. Not available on ESP32-H2.", - requires: &["alloc"], - chips: &[ - Chip::Esp32, - Chip::Esp32c2, - Chip::Esp32c3, - Chip::Esp32c6, - Chip::Esp32s2, - Chip::Esp32s3, - ], - }), - GeneratorOptionItem::Option(GeneratorOption { - name: "ble", - display_name: "Enable BLE via the `esp-wifi` crate.", - help: "Requires `alloc`. Not available on ESP32-S2.", - requires: &["alloc"], - chips: &[ - Chip::Esp32, - Chip::Esp32c2, - Chip::Esp32c3, - Chip::Esp32c6, - Chip::Esp32h2, - Chip::Esp32s3, - ], - }), - GeneratorOptionItem::Option(GeneratorOption { - name: "embassy", - display_name: "Add `embassy` framework support.", - help: "", - requires: &[], - chips: &[], - }), - GeneratorOptionItem::Option(GeneratorOption { - name: "probe-rs", - display_name: "Enable `defmt` and flashes using `probe-rs` instead of `espflash`.", - help: "", - requires: &[], - chips: &[], - }), - GeneratorOptionItem::Category(GeneratorOptionCategory { - name: "optional", - display_name: "Options", - help: "", - options: &[ - GeneratorOptionItem::Option(GeneratorOption { - name: "wokwi", - display_name: "Add support for Wokwi simulation using VS Code Wokwi extension.", - help: "", - requires: &[], - chips: &[ - Chip::Esp32, - Chip::Esp32c3, - Chip::Esp32c6, - Chip::Esp32h2, - Chip::Esp32s2, - Chip::Esp32s3, - ], - }), - GeneratorOptionItem::Option(GeneratorOption { - name: "dev-container", - display_name: "Add support for VS Code Dev Containers and GitHub Codespaces.", - help: "", - requires: &[], - chips: &[], - }), - GeneratorOptionItem::Option(GeneratorOption { - name: "ci", - display_name: "Add GitHub Actions support with some basic checks.", - help: "", - requires: &[], - chips: &[], - }), - ], - }), - GeneratorOptionItem::Category(GeneratorOptionCategory { - name: "editor", - display_name: "Optional editor config files for rust-analyzer", - help: "", - options: &[ - GeneratorOptionItem::Option(GeneratorOption { - name: "helix", - display_name: "Add rust-analyzer settings for Helix Editor", - help: "", - requires: &[], - chips: &[], - }), - GeneratorOptionItem::Option(GeneratorOption { - name: "vscode", - display_name: "Add rust-analyzer settings for Visual Studio Code", - help: "", - requires: &[], - chips: &[], - }), - ], - }), -]; +#[derive(Clone, Serialize, Deserialize)] +struct Template { + options: Vec, +} + +static TEMPLATE: LazyLock