Add board presets, devices --clear, and test/UX fixes
Board presets: - anvil new --board mega (uno, mega, nano, nano-old, leonardo, micro) - anvil new --list-boards shows presets with compatible clones - FQBN and baud rate flow into .anvil.toml via template variables - Defaults to uno when --board is omitted Devices --clear: - anvil devices --clear deletes .anvil.local, reverts to auto-detect
This commit is contained in:
@@ -4,6 +4,8 @@ use serde::Deserialize;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
pub mod presets;
|
||||
|
||||
/// Information about a detected serial port.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PortInfo {
|
||||
|
||||
134
src/board/presets.rs
Normal file
134
src/board/presets.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
/// Board presets for common Arduino boards.
|
||||
///
|
||||
/// Each preset provides a known-good FQBN and default baud rate.
|
||||
/// Users can always override the FQBN in .anvil.toml after creation.
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BoardPreset {
|
||||
/// Short name used on the command line (e.g. "uno", "mega")
|
||||
pub name: &'static str,
|
||||
/// Fully Qualified Board Name for arduino-cli
|
||||
pub fqbn: &'static str,
|
||||
/// Default serial baud rate
|
||||
pub baud: u32,
|
||||
/// Human-readable description
|
||||
pub description: &'static str,
|
||||
/// Common clones and compatible boards
|
||||
pub also_known_as: &'static str,
|
||||
/// Required core (for display in help text)
|
||||
pub core: &'static str,
|
||||
}
|
||||
|
||||
/// Built-in board presets.
|
||||
///
|
||||
/// Only includes boards whose core is installed by `anvil setup`
|
||||
/// (arduino:avr). Users targeting other platforms can set the FQBN
|
||||
/// manually in .anvil.toml.
|
||||
pub const PRESETS: &[BoardPreset] = &[
|
||||
BoardPreset {
|
||||
name: "uno",
|
||||
fqbn: "arduino:avr:uno",
|
||||
baud: 115200,
|
||||
description: "Arduino Uno (ATmega328P)",
|
||||
also_known_as: "SparkFun RedBoard, Elegoo Uno R3, DFRobot DFRduino, SunFounder Uno",
|
||||
core: "arduino:avr",
|
||||
},
|
||||
BoardPreset {
|
||||
name: "mega",
|
||||
fqbn: "arduino:avr:mega:cpu=atmega2560",
|
||||
baud: 115200,
|
||||
description: "Arduino Mega 2560",
|
||||
also_known_as: "Elegoo Mega R3, Robotdyn Mega, SunFounder Mega",
|
||||
core: "arduino:avr",
|
||||
},
|
||||
BoardPreset {
|
||||
name: "nano",
|
||||
fqbn: "arduino:avr:nano:cpu=atmega328",
|
||||
baud: 115200,
|
||||
description: "Arduino Nano (new bootloader)",
|
||||
also_known_as: "Elegoo Nano, Makerfire Nano V3",
|
||||
core: "arduino:avr",
|
||||
},
|
||||
BoardPreset {
|
||||
name: "nano-old",
|
||||
fqbn: "arduino:avr:nano:cpu=atmega328old",
|
||||
baud: 115200,
|
||||
description: "Arduino Nano (old bootloader)",
|
||||
also_known_as: "pre-2018 Nano clones, most cheap Nano boards",
|
||||
core: "arduino:avr",
|
||||
},
|
||||
BoardPreset {
|
||||
name: "leonardo",
|
||||
fqbn: "arduino:avr:leonardo",
|
||||
baud: 115200,
|
||||
description: "Arduino Leonardo (ATmega32U4)",
|
||||
also_known_as: "SparkFun Pro Micro (Leonardo mode), Freetronics LeoStick",
|
||||
core: "arduino:avr",
|
||||
},
|
||||
BoardPreset {
|
||||
name: "micro",
|
||||
fqbn: "arduino:avr:micro",
|
||||
baud: 115200,
|
||||
description: "Arduino Micro (ATmega32U4)",
|
||||
also_known_as: "SparkFun Pro Micro, Adafruit ItsyBitsy 32u4",
|
||||
core: "arduino:avr",
|
||||
},
|
||||
];
|
||||
|
||||
/// Default board used when --board is not specified.
|
||||
pub const DEFAULT_PRESET: &str = "uno";
|
||||
|
||||
/// Look up a board preset by short name (case-insensitive).
|
||||
pub fn find_preset(name: &str) -> Option<&'static BoardPreset> {
|
||||
let lower = name.to_lowercase();
|
||||
PRESETS.iter().find(|p| p.name == lower)
|
||||
}
|
||||
|
||||
/// List all preset names for display.
|
||||
pub fn preset_names() -> Vec<&'static str> {
|
||||
PRESETS.iter().map(|p| p.name).collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_find_preset_uno() {
|
||||
let p = find_preset("uno").unwrap();
|
||||
assert_eq!(p.fqbn, "arduino:avr:uno");
|
||||
assert_eq!(p.baud, 115200);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_preset_case_insensitive() {
|
||||
assert!(find_preset("Mega").is_some());
|
||||
assert!(find_preset("NANO").is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_preset_unknown() {
|
||||
assert!(find_preset("esp32").is_none());
|
||||
assert!(find_preset("stm32").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_preset_exists() {
|
||||
assert!(find_preset(DEFAULT_PRESET).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_presets_have_avr_core() {
|
||||
for p in PRESETS {
|
||||
assert_eq!(p.core, "arduino:avr", "{} should use arduino:avr core", p.name);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_preset_names() {
|
||||
let names = preset_names();
|
||||
assert!(names.contains(&"uno"));
|
||||
assert!(names.contains(&"mega"));
|
||||
assert!(names.contains(&"nano"));
|
||||
}
|
||||
}
|
||||
@@ -274,6 +274,31 @@ pub fn set_port(port: Option<&str>, project_dir: Option<&str>) -> Result<()> {
|
||||
|
||||
// -- Helpers --------------------------------------------------------------
|
||||
|
||||
/// Delete .anvil.local from the given project directory.
|
||||
pub fn clear_port(project_dir: Option<&str>) -> Result<()> {
|
||||
let project_path = resolve_project_dir(project_dir)?;
|
||||
require_anvil_project(&project_path)?;
|
||||
|
||||
let local_file = project_path.join(".anvil.local");
|
||||
if !local_file.exists() {
|
||||
println!(
|
||||
"{} No .anvil.local file found -- nothing to clear.",
|
||||
"--".bright_black()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fs::remove_file(&local_file)
|
||||
.context("Failed to delete .anvil.local")?;
|
||||
|
||||
println!(
|
||||
"{} Removed .anvil.local -- port will be auto-detected.",
|
||||
"ok".green()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_project_dir(project_dir: Option<&str>) -> Result<PathBuf> {
|
||||
match project_dir {
|
||||
Some(dir) => Ok(PathBuf::from(dir)),
|
||||
|
||||
@@ -2,6 +2,7 @@ use anyhow::{Result, bail};
|
||||
use colored::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::board::presets::{self, BoardPreset};
|
||||
use crate::templates::{TemplateManager, TemplateContext};
|
||||
use crate::version::ANVIL_VERSION;
|
||||
|
||||
@@ -28,7 +29,52 @@ pub fn list_templates() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_project(name: &str, template: Option<&str>) -> Result<()> {
|
||||
pub fn list_boards() -> Result<()> {
|
||||
println!("{}", "Available board presets:".bright_cyan().bold());
|
||||
println!();
|
||||
|
||||
for preset in presets::PRESETS {
|
||||
let marker = if preset.name == presets::DEFAULT_PRESET {
|
||||
" (default)"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
println!(
|
||||
" {}{}",
|
||||
preset.name.bright_white().bold(),
|
||||
marker.bright_cyan()
|
||||
);
|
||||
println!(" {}", preset.description);
|
||||
if !preset.also_known_as.is_empty() {
|
||||
println!(" Also: {}", preset.also_known_as.bright_black());
|
||||
}
|
||||
println!(" FQBN: {}", preset.fqbn.bright_black());
|
||||
println!();
|
||||
}
|
||||
|
||||
println!(
|
||||
" {}",
|
||||
"Usage: anvil new <project-name> --board mega".bright_yellow()
|
||||
);
|
||||
println!();
|
||||
println!(
|
||||
" {}",
|
||||
"For boards not listed here, create a project and edit the".bright_black()
|
||||
);
|
||||
println!(
|
||||
" {}",
|
||||
"fqbn value in .anvil.toml to any valid arduino-cli FQBN.".bright_black()
|
||||
);
|
||||
println!();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_project(
|
||||
name: &str,
|
||||
template: Option<&str>,
|
||||
board: Option<&str>,
|
||||
) -> Result<()> {
|
||||
// Validate project name
|
||||
validate_project_name(name)?;
|
||||
|
||||
@@ -52,6 +98,25 @@ pub fn create_project(name: &str, template: Option<&str>) -> Result<()> {
|
||||
bail!("Invalid template");
|
||||
}
|
||||
|
||||
// Resolve board preset
|
||||
let preset: &BoardPreset = match board {
|
||||
Some(b) => {
|
||||
match presets::find_preset(b) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
println!(
|
||||
"{}",
|
||||
format!("Unknown board preset: '{}'", b).red().bold()
|
||||
);
|
||||
println!();
|
||||
list_boards()?;
|
||||
bail!("Invalid board preset");
|
||||
}
|
||||
}
|
||||
}
|
||||
None => presets::find_preset(presets::DEFAULT_PRESET).unwrap(),
|
||||
};
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
format!("Creating Arduino project: {}", name)
|
||||
@@ -59,6 +124,14 @@ pub fn create_project(name: &str, template: Option<&str>) -> Result<()> {
|
||||
.bold()
|
||||
);
|
||||
println!("{}", format!("Template: {}", template_name).bright_cyan());
|
||||
println!(
|
||||
"{}",
|
||||
format!("Board: {} ({})", preset.name, preset.description).bright_cyan()
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
format!("FQBN: {}", preset.fqbn).bright_black()
|
||||
);
|
||||
println!();
|
||||
|
||||
// Create project directory
|
||||
@@ -69,6 +142,8 @@ pub fn create_project(name: &str, template: Option<&str>) -> Result<()> {
|
||||
let context = TemplateContext {
|
||||
project_name: name.to_string(),
|
||||
anvil_version: ANVIL_VERSION.to_string(),
|
||||
fqbn: preset.fqbn.to_string(),
|
||||
baud: preset.baud,
|
||||
};
|
||||
|
||||
let file_count = TemplateManager::extract(template_name, &project_path, &context)?;
|
||||
|
||||
@@ -47,6 +47,8 @@ pub fn run_refresh(project_dir: Option<&str>, force: bool) -> Result<()> {
|
||||
let context = TemplateContext {
|
||||
project_name: config.project.name.clone(),
|
||||
anvil_version: ANVIL_VERSION.to_string(),
|
||||
fqbn: config.build.fqbn.clone(),
|
||||
baud: config.monitor.baud,
|
||||
};
|
||||
|
||||
// Extract template into a temp directory so we can compare
|
||||
|
||||
33
src/main.rs
33
src/main.rs
@@ -31,9 +31,17 @@ enum Commands {
|
||||
#[arg(long, short = 't', value_name = "TEMPLATE")]
|
||||
template: Option<String>,
|
||||
|
||||
/// Board preset (uno, mega, nano, leonardo, micro)
|
||||
#[arg(long, short = 'b', value_name = "BOARD")]
|
||||
board: Option<String>,
|
||||
|
||||
/// List available templates
|
||||
#[arg(long, conflicts_with = "name")]
|
||||
list_templates: bool,
|
||||
|
||||
/// List available board presets
|
||||
#[arg(long, conflicts_with = "name")]
|
||||
list_boards: bool,
|
||||
},
|
||||
|
||||
/// Check system health and diagnose issues
|
||||
@@ -45,13 +53,17 @@ enum Commands {
|
||||
/// List connected boards and serial ports
|
||||
Devices {
|
||||
/// Save a port to .anvil.local for this project
|
||||
#[arg(long, conflicts_with = "get")]
|
||||
#[arg(long, conflicts_with_all = ["get", "clear"])]
|
||||
set: bool,
|
||||
|
||||
/// Show the saved port for this project
|
||||
#[arg(long, conflicts_with = "set")]
|
||||
#[arg(long, conflicts_with_all = ["set", "clear"])]
|
||||
get: bool,
|
||||
|
||||
/// Remove .anvil.local (revert to auto-detect)
|
||||
#[arg(long, conflicts_with_all = ["set", "get"])]
|
||||
clear: bool,
|
||||
|
||||
/// Port name (e.g. COM3, /dev/ttyUSB0). Auto-detects if omitted with --set.
|
||||
port_or_dir: Option<String>,
|
||||
|
||||
@@ -80,18 +92,23 @@ fn main() -> Result<()> {
|
||||
print_banner();
|
||||
|
||||
match cli.command {
|
||||
Commands::New { name, template, list_templates } => {
|
||||
if list_templates {
|
||||
Commands::New { name, template, board, list_templates, list_boards } => {
|
||||
if list_boards {
|
||||
commands::new::list_boards()
|
||||
} else if list_templates {
|
||||
commands::new::list_templates()
|
||||
} else if let Some(project_name) = name {
|
||||
commands::new::create_project(
|
||||
&project_name,
|
||||
template.as_deref(),
|
||||
board.as_deref(),
|
||||
)
|
||||
} else {
|
||||
anyhow::bail!(
|
||||
"Project name required.\n\
|
||||
Usage: anvil new <n>\n\
|
||||
Usage: anvil new <name>\n\
|
||||
Usage: anvil new <name> --board mega\n\
|
||||
List boards: anvil new --list-boards\n\
|
||||
List templates: anvil new --list-templates"
|
||||
);
|
||||
}
|
||||
@@ -102,7 +119,7 @@ fn main() -> Result<()> {
|
||||
Commands::Setup => {
|
||||
commands::setup::run_setup()
|
||||
}
|
||||
Commands::Devices { set, get, port_or_dir, dir } => {
|
||||
Commands::Devices { set, get, clear, port_or_dir, dir } => {
|
||||
if set {
|
||||
commands::devices::set_port(
|
||||
port_or_dir.as_deref(),
|
||||
@@ -112,6 +129,10 @@ fn main() -> Result<()> {
|
||||
commands::devices::get_port(
|
||||
dir.as_deref().or(port_or_dir.as_deref()),
|
||||
)
|
||||
} else if clear {
|
||||
commands::devices::clear_port(
|
||||
dir.as_deref().or(port_or_dir.as_deref()),
|
||||
)
|
||||
} else {
|
||||
commands::devices::scan_devices()
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ static BASIC_TEMPLATE: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates/basic")
|
||||
pub struct TemplateContext {
|
||||
pub project_name: String,
|
||||
pub anvil_version: String,
|
||||
pub fqbn: String,
|
||||
pub baud: u32,
|
||||
}
|
||||
|
||||
pub struct TemplateManager;
|
||||
@@ -140,6 +142,8 @@ fn substitute_variables(text: &str, context: &TemplateContext) -> String {
|
||||
text.replace("{{PROJECT_NAME}}", &context.project_name)
|
||||
.replace("{{ANVIL_VERSION}}", &context.anvil_version)
|
||||
.replace("{{ANVIL_VERSION_CURRENT}}", ANVIL_VERSION)
|
||||
.replace("{{FQBN}}", &context.fqbn)
|
||||
.replace("{{BAUD}}", &context.baud.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -172,10 +176,15 @@ mod tests {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "my_project".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:mega:cpu=atmega2560".to_string(),
|
||||
baud: 9600,
|
||||
};
|
||||
let input = "Name: {{PROJECT_NAME}}, Version: {{ANVIL_VERSION}}";
|
||||
let input = "Name: {{PROJECT_NAME}}, Version: {{ANVIL_VERSION}}, FQBN: {{FQBN}}, Baud: {{BAUD}}";
|
||||
let output = substitute_variables(input, &ctx);
|
||||
assert_eq!(output, "Name: my_project, Version: 1.0.0");
|
||||
assert_eq!(
|
||||
output,
|
||||
"Name: my_project, Version: 1.0.0, FQBN: arduino:avr:mega:cpu=atmega2560, Baud: 9600"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -190,6 +199,8 @@ mod tests {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "test_proj".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
let count = TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -220,6 +231,8 @@ mod tests {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "my_sensor".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
|
||||
@@ -3,10 +3,10 @@ name = "{{PROJECT_NAME}}"
|
||||
anvil_version = "{{ANVIL_VERSION}}"
|
||||
|
||||
[build]
|
||||
fqbn = "arduino:avr:uno"
|
||||
fqbn = "{{FQBN}}"
|
||||
warnings = "more"
|
||||
include_dirs = ["lib/hal", "lib/app"]
|
||||
extra_flags = ["-Werror"]
|
||||
|
||||
[monitor]
|
||||
baud = 115200
|
||||
baud = {{BAUD}}
|
||||
|
||||
@@ -15,6 +15,8 @@ fn test_basic_template_extracts_all_expected_files() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "test_proj".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
let count = TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -27,6 +29,8 @@ fn test_template_creates_sketch_directory() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "blink".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -55,6 +59,8 @@ fn test_template_creates_hal_files() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "sensor".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -86,6 +92,8 @@ fn test_template_creates_app_header() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "my_sensor".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -110,6 +118,8 @@ fn test_template_creates_test_infrastructure() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "blink".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -146,6 +156,8 @@ fn test_template_test_file_references_correct_app() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "motor_ctrl".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -165,6 +177,8 @@ fn test_template_cmake_references_correct_project() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "my_bot".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -184,6 +198,8 @@ fn test_template_creates_dot_files() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "blink".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -212,6 +228,8 @@ fn test_template_creates_readme() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "blink".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -233,6 +251,8 @@ fn test_template_creates_valid_config() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "blink".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -312,6 +332,8 @@ fn test_full_project_structure() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "full_test".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -361,6 +383,8 @@ fn test_no_unicode_in_template_output() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "ascii_test".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -408,6 +432,8 @@ fn test_unknown_template_fails() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "test".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
let result = TemplateManager::extract("nonexistent", tmp.path(), &ctx);
|
||||
@@ -431,6 +457,8 @@ fn test_template_creates_self_contained_scripts() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "standalone".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -453,6 +481,8 @@ fn test_build_sh_reads_anvil_toml() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "toml_reader".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -478,6 +508,8 @@ fn test_upload_sh_reads_anvil_toml() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "uploader".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -511,6 +543,8 @@ fn test_monitor_sh_reads_anvil_toml() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "serial_mon".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -536,6 +570,8 @@ fn test_scripts_have_shebangs() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "shebangs".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -558,6 +594,8 @@ fn test_scripts_no_anvil_binary_dependency() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "no_anvil_dep".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -605,6 +643,8 @@ fn test_gitignore_excludes_build_cache() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "gitcheck".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -630,6 +670,8 @@ fn test_readme_documents_self_contained_workflow() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "docs_check".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -663,6 +705,8 @@ fn test_scripts_tolerate_missing_toml_keys() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "grep_safe".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
@@ -698,6 +742,8 @@ fn test_bat_scripts_no_unescaped_parens_in_echo() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "parens_test".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
|
||||
@@ -757,6 +803,8 @@ fn test_scripts_read_anvil_local_for_port() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "local_test".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
|
||||
@@ -777,6 +825,8 @@ fn test_anvil_toml_template_has_no_port() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "no_port".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
|
||||
@@ -806,6 +856,8 @@ fn test_bat_scripts_call_detect_port_ps1() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "ps1_test".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
|
||||
@@ -826,6 +878,8 @@ fn test_detect_port_ps1_is_valid() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "ps1_valid".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
|
||||
@@ -856,6 +910,8 @@ fn test_refresh_freshly_extracted_is_up_to_date() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "fresh_proj".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
|
||||
@@ -884,6 +940,8 @@ fn test_refresh_detects_modified_script() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "mod_proj".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
|
||||
@@ -951,6 +1009,8 @@ fn test_scripts_read_vid_pid_from_anvil_local() {
|
||||
let ctx = TemplateContext {
|
||||
project_name: "vidpid_test".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
|
||||
@@ -963,3 +1023,43 @@ fn test_scripts_read_vid_pid_from_anvil_local() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Board preset tests
|
||||
// ==========================================================================
|
||||
|
||||
#[test]
|
||||
fn test_board_preset_fqbn_in_config() {
|
||||
// Creating a project with --board mega should set the FQBN in .anvil.toml
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = TemplateContext {
|
||||
project_name: "mega_test".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "arduino:avr:mega:cpu=atmega2560".to_string(),
|
||||
baud: 115200,
|
||||
};
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
|
||||
let config = ProjectConfig::load(tmp.path()).unwrap();
|
||||
assert_eq!(
|
||||
config.build.fqbn, "arduino:avr:mega:cpu=atmega2560",
|
||||
".anvil.toml should contain mega FQBN"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_board_preset_custom_fqbn_in_config() {
|
||||
// Even arbitrary FQBNs should work through the template
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = TemplateContext {
|
||||
project_name: "custom_board".to_string(),
|
||||
anvil_version: "1.0.0".to_string(),
|
||||
fqbn: "esp32:esp32:esp32".to_string(),
|
||||
baud: 9600,
|
||||
};
|
||||
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
|
||||
|
||||
let config = ProjectConfig::load(tmp.path()).unwrap();
|
||||
assert_eq!(config.build.fqbn, "esp32:esp32:esp32");
|
||||
assert_eq!(config.monitor.baud, 9600);
|
||||
}
|
||||
Reference in New Issue
Block a user