use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use std::fs; use anyhow::{Result, Context, bail}; const WEEVIL_VERSION: &str = env!("CARGO_PKG_VERSION"); #[derive(Debug, Serialize, Deserialize)] pub struct ProjectConfig { pub project_name: String, pub weevil_version: String, pub ftc_sdk_path: PathBuf, pub ftc_sdk_version: String, #[serde(default = "default_android_sdk_path")] pub android_sdk_path: PathBuf, } fn default_android_sdk_path() -> PathBuf { PathBuf::new() } impl ProjectConfig { pub fn new(project_name: &str, ftc_sdk_path: PathBuf, android_sdk_path: PathBuf) -> Result { let ftc_sdk_version = crate::sdk::ftc::get_version(&ftc_sdk_path) .unwrap_or_else(|_| "unknown".to_string()); Ok(Self { project_name: project_name.to_string(), weevil_version: WEEVIL_VERSION.to_string(), ftc_sdk_path, ftc_sdk_version, android_sdk_path, }) } pub fn load(project_path: &Path) -> Result { let config_path = project_path.join(".weevil.toml"); if !config_path.exists() { bail!("Not a weevil project (missing .weevil.toml)"); } let contents = fs::read_to_string(&config_path) .context("Failed to read .weevil.toml")?; let mut config: ProjectConfig = toml::from_str(&contents) .context("Failed to parse .weevil.toml")?; // Migrate old configs that don't have android_sdk_path if config.android_sdk_path.as_os_str().is_empty() { let sdk_config = crate::sdk::SdkConfig::new()?; config.android_sdk_path = sdk_config.android_sdk_path; } Ok(config) } pub fn save(&self, project_path: &Path) -> Result<()> { let config_path = project_path.join(".weevil.toml"); let contents = toml::to_string_pretty(self) .context("Failed to serialize config")?; fs::write(&config_path, contents) .context("Failed to write .weevil.toml")?; Ok(()) } pub fn update_sdk_path(&mut self, new_path: PathBuf) -> Result<()> { // Verify the SDK exists crate::sdk::ftc::verify(&new_path)?; // Update version self.ftc_sdk_version = crate::sdk::ftc::get_version(&new_path) .unwrap_or_else(|_| "unknown".to_string()); self.ftc_sdk_path = new_path; Ok(()) } pub fn display(&self) { use colored::*; println!(); println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan()); println!("{}", " Project Configuration".bright_cyan().bold()); println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan()); println!(); println!("{:.<20} {}", "Project Name", self.project_name.bright_white()); println!("{:.<20} {}", "Weevil Version", self.weevil_version.bright_white()); println!(); println!("{:.<20} {}", "FTC SDK Path", self.ftc_sdk_path.display().to_string().bright_white()); println!("{:.<20} {}", "FTC SDK Version", self.ftc_sdk_version.bright_white()); println!("{:.<20} {}", "Android SDK Path", self.android_sdk_path.display().to_string().bright_white()); println!(); } }