Implements template-based project creation allowing teams to start with
professional example code instead of empty projects.
Features:
- Two templates: 'basic' (minimal) and 'testing' (45-test showcase)
- Template variable substitution ({{PROJECT_NAME}}, etc.)
- Template validation with helpful error messages
- `weevil new --list-templates` command
- Template files embedded in binary at compile time
Technical details:
- Templates stored in templates/basic/ and templates/testing/
- Files ending in .template have variables replaced
- Uses include_dir! macro to embed templates in binary
- Returns file count for user feedback
Testing template includes:
- 3 complete subsystems (MotorCycler, WallApproach, TurnController)
- Hardware abstraction layer with mock implementations
- 45 comprehensive tests (unit, integration, system)
- Professional documentation (DESIGN_AND_TEST_PLAN.md, etc.)
Usage:
weevil new my-robot # basic template
weevil new my-robot --template testing # testing showcase
weevil new --list-templates # show available templates
This enables FTC teams to learn from working code and best practices
rather than starting from scratch.
200 lines
5.8 KiB
Rust
200 lines
5.8 KiB
Rust
use clap::{Parser, Subcommand};
|
|
use colored::*;
|
|
use anyhow::Result;
|
|
use weevil::version::WEEVIL_VERSION;
|
|
|
|
mod commands;
|
|
mod sdk;
|
|
mod project;
|
|
mod templates;
|
|
|
|
use sdk::proxy::ProxyConfig;
|
|
|
|
#[derive(Parser)]
|
|
#[command(name = "weevil")]
|
|
#[command(author = "Eric Ratliff <eric@nxlearn.net>")]
|
|
#[command(version = WEEVIL_VERSION)]
|
|
#[command(
|
|
about = "FTC robotics project generator - bores into complexity, emerges with clean code",
|
|
long_about = None
|
|
)]
|
|
struct Cli {
|
|
/// Use this HTTP/HTTPS proxy for all downloads
|
|
#[arg(long, value_name = "URL", global = true)]
|
|
proxy: Option<String>,
|
|
|
|
/// Skip proxy entirely — go direct even if HTTPS_PROXY is set
|
|
#[arg(long, global = true)]
|
|
no_proxy: bool,
|
|
|
|
#[command(subcommand)]
|
|
command: Commands,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum Commands {
|
|
/// Create a new FTC robot project
|
|
New {
|
|
/// Name of the robot project
|
|
name: Option<String>,
|
|
|
|
/// Path to FTC SDK (optional, will auto-detect or download)
|
|
#[arg(long)]
|
|
ftc_sdk: Option<String>,
|
|
|
|
/// Path to Android SDK (optional, will auto-detect or download)
|
|
#[arg(long)]
|
|
android_sdk: Option<String>,
|
|
|
|
/// Template to use (basic, testing)
|
|
#[arg(long, short = 't', value_name = "TEMPLATE")]
|
|
template: Option<String>,
|
|
|
|
/// List available templates
|
|
#[arg(long, conflicts_with = "name")]
|
|
list_templates: bool,
|
|
},
|
|
|
|
/// Check system health and diagnose issues
|
|
Doctor,
|
|
|
|
/// Setup development environment (system or project)
|
|
Setup {
|
|
/// Path to project directory (optional - without it, sets up system)
|
|
path: Option<String>,
|
|
},
|
|
|
|
/// Remove Weevil-installed SDKs and dependencies
|
|
Uninstall {
|
|
/// Show what would be removed without actually removing anything
|
|
#[arg(long)]
|
|
dry_run: bool,
|
|
|
|
/// Remove only specific items by number (use --dry-run first to see the list)
|
|
#[arg(long, value_name = "NUM", num_args = 1..)]
|
|
only: Option<Vec<usize>>,
|
|
},
|
|
|
|
/// Upgrade an existing project to the latest generator version
|
|
Upgrade {
|
|
/// Path to the project directory
|
|
path: String,
|
|
},
|
|
|
|
/// Build and deploy project to Control Hub
|
|
Deploy {
|
|
/// Path to the project directory
|
|
path: String,
|
|
|
|
/// Force USB connection
|
|
#[arg(long)]
|
|
usb: bool,
|
|
|
|
/// Force WiFi connection
|
|
#[arg(long)]
|
|
wifi: bool,
|
|
|
|
/// Custom IP address
|
|
#[arg(short, long)]
|
|
ip: Option<String>,
|
|
},
|
|
|
|
/// Manage SDKs (FTC and Android)
|
|
Sdk {
|
|
#[command(subcommand)]
|
|
command: SdkCommands,
|
|
},
|
|
|
|
/// Show or update project configuration
|
|
Config {
|
|
/// Path to the project directory
|
|
path: String,
|
|
|
|
/// Set FTC SDK path for this project
|
|
#[arg(long, value_name = "PATH")]
|
|
set_sdk: Option<String>,
|
|
},
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum SdkCommands {
|
|
/// Install required SDKs
|
|
Install,
|
|
|
|
/// Show SDK status and locations
|
|
Status,
|
|
|
|
/// Update SDKs to latest versions
|
|
Update,
|
|
}
|
|
|
|
fn main() -> Result<()> {
|
|
// Enable colors on Windows
|
|
#[cfg(windows)]
|
|
colored::control::set_virtual_terminal(true).ok();
|
|
|
|
let cli = Cli::parse();
|
|
|
|
print_banner();
|
|
|
|
// Resolve proxy once at the top — every network-touching command uses it.
|
|
let proxy = ProxyConfig::resolve(cli.proxy.as_deref(), cli.no_proxy)?;
|
|
|
|
match cli.command {
|
|
Commands::New { name, ftc_sdk, android_sdk, template, list_templates } => {
|
|
if list_templates {
|
|
commands::new::list_templates()
|
|
} else if let Some(project_name) = name {
|
|
commands::new::create_project(
|
|
&project_name,
|
|
ftc_sdk.as_deref(),
|
|
android_sdk.as_deref(),
|
|
template.as_deref(),
|
|
&proxy
|
|
)
|
|
} else {
|
|
anyhow::bail!("Project name is required. Use --list-templates to see available templates.");
|
|
}
|
|
}
|
|
Commands::Doctor => {
|
|
commands::doctor::run_diagnostics()
|
|
}
|
|
Commands::Setup { path } => {
|
|
commands::setup::setup_environment(path.as_deref(), &proxy)
|
|
}
|
|
Commands::Uninstall { dry_run, only } => {
|
|
commands::uninstall::uninstall_dependencies(dry_run, only)
|
|
}
|
|
Commands::Upgrade { path } => {
|
|
commands::upgrade::upgrade_project(&path)
|
|
}
|
|
Commands::Deploy { path, usb, wifi, ip } => {
|
|
commands::deploy::deploy_project(&path, usb, wifi, ip.as_deref())
|
|
}
|
|
Commands::Sdk { command } => match command {
|
|
SdkCommands::Install => commands::sdk::install_sdks(&proxy),
|
|
SdkCommands::Status => commands::sdk::show_status(),
|
|
SdkCommands::Update => commands::sdk::update_sdks(&proxy),
|
|
},
|
|
Commands::Config { path, set_sdk } => {
|
|
if let Some(sdk_path) = set_sdk {
|
|
commands::config::set_sdk(&path, &sdk_path)
|
|
} else {
|
|
commands::config::show_config(&path)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn print_banner() {
|
|
println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan());
|
|
println!(
|
|
"{}",
|
|
format!(" 🪲 Weevil - FTC Project Generator v{}", WEEVIL_VERSION)
|
|
.bright_cyan()
|
|
.bold()
|
|
);
|
|
println!("{}", " Nexus Workshops LLC".bright_cyan());
|
|
println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan());
|
|
println!();
|
|
} |