use std::collections::HashMap;
use crate::app_ports::select_most_likely_port;
use crate::app_ports::PortsCmd;
use crate::raft_cli_utils::build_flash_command_args;
use crate::raft_cli_utils::get_flash_tool_cmd;
use crate::raft_cli_utils::execute_and_capture_output;
use crate::raft_cli_utils::get_build_folder_name;
use crate::raft_cli_utils::utils_get_sys_type;
use crate::raft_cli_utils::is_wsl;
pub fn flash_raft_app(
build_sys_type: &Option<String>,
app_folder: String,
serial_port: Option<String>,
native_serial_port: bool,
vid: Option<String>,
flash_baud: u32,
flash_tool_opt: Option<String>,
skip_fs: bool,
) -> Result<(), Box<dyn std::error::Error>> {
let sys_type = utils_get_sys_type(build_sys_type, app_folder.clone());
if sys_type.is_err() {
return Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, "Error determining SysType")));
}
let sys_type = sys_type.unwrap();
if is_wsl() && !native_serial_port {
println!("WSL detected: Delegating flash operation to Windows raft.exe for USB serial port access");
return flash_via_windows_raft(
&sys_type,
app_folder,
serial_port,
vid,
flash_baud,
flash_tool_opt,
skip_fs,
);
}
let build_folder = get_build_folder_name(sys_type.clone(), app_folder.clone());
let flash_cmd: String = get_flash_tool_cmd(flash_tool_opt, native_serial_port);
let port = if let Some(port) = serial_port {
port
} else {
let port_cmd = PortsCmd::new_with_vid(vid);
match select_most_likely_port(&port_cmd, native_serial_port) {
Some(p) => p.port_name,
None => {
println!("Error: No suitable port found");
std::process::exit(1);
}
}
};
let flash_cmd_args = build_flash_command_args(build_folder.clone(), &port, flash_baud, skip_fs);
if flash_cmd_args.is_err() {
return Err(Box::new(std::io::Error::new(
std::io::ErrorKind::Other,
"Error extracting flash command arguments",
)));
}
let flash_cmd_args = flash_cmd_args.unwrap();
println!("Flash command: {}", flash_cmd.clone());
println!("Flash command args: {:?}", flash_cmd_args);
println!("Flash command app folder: {}", app_folder.clone());
let (output, success_flag) = if flash_cmd.starts_with("python -m ") {
let module = flash_cmd.strip_prefix("python -m ").unwrap();
let mut args = vec!["-m".to_string(), module.to_string()];
args.extend(flash_cmd_args.clone());
execute_and_capture_output("python".to_string(), &args, app_folder.clone(), HashMap::new())?
} else {
execute_and_capture_output(flash_cmd.clone(), &flash_cmd_args, app_folder.clone(), HashMap::new())?
};
if !success_flag {
if output.contains("ModuleNotFoundError: No module named 'esptool'") {
let err_msg = format!(
"Flash failed: esptool module not found.\n\n\
This error typically occurs when:\n\
1. esptool is not properly installed in the current environment\n\
2. You're in WSL and should let raftcli use Windows for flashing (don't use -n flag)\n\n\
Solutions:\n\
- If in WSL: Run without the -n (native-serial-port) flag to use Windows raft.exe\n\
- Otherwise: Install esptool using: pip install esptool\n\n\
Original error:\n{}", output
);
return Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, err_msg)));
}
let err_msg = format!("Flash executed with errors: {}", output);
return Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, err_msg)));
}
Ok(())
}
fn flash_via_windows_raft(
sys_type: &str,
app_folder: String,
serial_port: Option<String>,
vid: Option<String>,
flash_baud: u32,
flash_tool_opt: Option<String>,
skip_fs: bool,
) -> Result<(), Box<dyn std::error::Error>> {
let mut args = vec!["flash".to_string()];
args.push("-s".to_string());
args.push(sys_type.to_string());
if let Some(port) = serial_port {
args.push("-p".to_string());
args.push(port);
}
if let Some(v) = vid {
args.push("-v".to_string());
args.push(v);
}
args.push("-f".to_string());
args.push(flash_baud.to_string());
if let Some(tool) = flash_tool_opt {
args.push("-t".to_string());
args.push(tool);
}
if skip_fs {
args.push("--no-fs".to_string());
} else {
args.push("--fs".to_string());
}
args.push("-n".to_string());
println!("Executing Windows raft.exe with args: {:?}", args);
let child = std::process::Command::new("raft.exe")
.args(&args)
.current_dir(&app_folder)
.stdin(std::process::Stdio::inherit())
.stdout(std::process::Stdio::inherit())
.stderr(std::process::Stdio::inherit())
.spawn();
match child {
Ok(mut child) => {
match child.wait() {
Ok(status) => {
if status.success() {
Ok(())
} else {
Err(Box::new(std::io::Error::new(
std::io::ErrorKind::Other,
format!("Windows raft.exe flash command failed with exit code: {:?}", status.code()),
)))
}
}
Err(e) => {
Err(Box::new(std::io::Error::new(
std::io::ErrorKind::Other,
format!("Error waiting for raft.exe process: {}", e),
)))
}
}
}
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
Err(Box::new(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Could not find raft.exe (Windows version of raftcli).\n\n\
When using WSL, raftcli needs the Windows version (raft.exe) to access USB serial ports.\n\n\
Please ensure:\n\
1. raftcli is installed on Windows: cargo install raftcli\n\
2. raft.exe is in your Windows PATH\n\
3. You can access Windows executables from WSL (try: raft.exe --version)\n\n\
Alternative: Use the -n flag to attempt flashing with native Linux tools (requires USBIPD or similar)",
)))
} else {
Err(Box::new(e))
}
}
}
}