Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ once_cell = "1.21.3"
idf-env = { git = "https://github.com/espressif/idf-env", rev="fd69ab4f550ef35647bb32d1584caa6623cbfc4e" }
fs_extra = { version = "1.3.0", optional = true }
lnk = "0.6.3"
url = "2.5.7"


# GUI-related dependencies (optional)
Expand All @@ -143,7 +144,6 @@ log4rs = { version = "1.3.0", optional = true }
rustpython-vm = { git = "https://github.com/Hahihula/RustPython.git", branch = "test-rust-build", features = ["freeze-stdlib"], optional = true }
rustpython-stdlib = { git = "https://github.com/Hahihula/RustPython.git", branch = "test-rust-build", features = ["ssl-vendor"], optional = true }
os_info = "3.12.0"
url = "2.5.7"
semver = "1.0.27"


Expand Down
3 changes: 0 additions & 3 deletions src-tauri/locales/app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -701,9 +701,6 @@ gui.installation.using_existing:
gui.installation.cloning_from_mirror:
en: "Cloning ESP-IDF %{version} repository from %{mirror}"
cn: "从 %{mirror} 克隆 ESP-IDF %{version} 仓库"
gui.installation.default_mirror:
en: "default mirror"
cn: "默认镜像"
gui.installation.path_not_available:
en: "Installation path not available"
cn: "安装路径不可用"
Expand Down
132 changes: 92 additions & 40 deletions src-tauri/src/cli/prompts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ use idf_im_lib::{idf_features::RequirementsMetadata, settings::Settings};
use idf_im_lib::system_dependencies;
use log::{debug, info};
use rust_i18n::t;

use idf_im_lib::utils::calculate_mirrors_latency;
use crate::cli::helpers::generic_confirm_with_default;


pub async fn select_target() -> Result<Vec<String>, String> {
let mut available_targets = idf_im_lib::idf_versions::get_avalible_targets().await?;
available_targets.insert(0, "all".to_string());
Expand Down Expand Up @@ -182,48 +183,99 @@ pub fn check_and_install_python(
Ok(())
}

pub fn select_mirrors(mut config: Settings) -> Result<Settings, String> {
if (config.wizard_all_questions.unwrap_or_default()
|| config.idf_mirror.is_none()
|| config.is_default("idf_mirror"))
&& config.non_interactive == Some(false)
{
config.idf_mirror = Some(generic_select(
"wizard.idf.mirror",
&idf_im_lib::get_idf_mirrors_list()
.iter()
.map(|&s| s.to_string())
.collect(),
)?)
async fn select_single_mirror<FGet, FSet>(
config: &mut Settings,
field_name: &str, // e.g. "idf_mirror"
get_value: FGet, // e.g. |c: &Settings| &c.idf_mirror
set_value: FSet, // e.g. |c: &mut Settings, v| c.idf_mirror = Some(v)
candidates: &[&str], // list of mirror URLs
wizard_key: &str, // e.g. "wizard.idf.mirror"
log_prefix: &str, // e.g. "IDF", "Tools", "PyPI"
) -> Result<(), String>
where
FGet: Fn(&Settings) -> &Option<String>,
FSet: Fn(&mut Settings, String),
{
// Interactive by default when non_interactive is None
let interactive = !config.non_interactive.unwrap_or_default();
let wizard_all = config.wizard_all_questions.unwrap_or_default();
let current = get_value(config);
let needs_value = current.is_none() || config.is_default(field_name);

// Only measure mirror latency if we actually need a value (or wizard wants to ask)
if interactive && (wizard_all || needs_value) {
let entries = calculate_mirrors_latency(candidates).await;
let display = entries.iter().map(|e| {
if e.latency.is_none() {
format!("{} (timeout)", e.url)
} else {
format!("{} ({:?} ms)", e.url, e.latency.unwrap())
}
})
.collect::<Vec<String>>();
let selected = generic_select(wizard_key, &display)?;
let url = selected.split(" (").next().unwrap_or(&selected).to_string();
set_value(config, url);
} else if needs_value {
let entries = calculate_mirrors_latency(candidates).await;
if let Some(entry) = entries.first() {
if entry.latency.is_some() {
// The first entry is best mirror to select
info!("Selected {log_prefix} mirror: {} ({:?} ms)", entry.url, entry.latency.unwrap());
set_value(config, entry.url.clone());
}
} else {
// If the first entry is timeout or None there are no good mirrors to select try logging a proper message and return an error
info!("No good {log_prefix} mirrors found, please check your internet connection and try again");
return Err(format!("No good {log_prefix} mirrors found, please check your internet connection and try again"));
}
}

if (config.wizard_all_questions.unwrap_or_default()
|| config.mirror.is_none()
|| config.is_default("mirror"))
&& config.non_interactive == Some(false)
{
config.mirror = Some(generic_select(
"wizard.tools.mirror",
&idf_im_lib::get_idf_tools_mirrors_list()
.iter()
.map(|&s| s.to_string())
.collect(),
)?)
}
Ok(())
}

if (config.wizard_all_questions.unwrap_or_default()
|| config.pypi_mirror.is_none()
|| config.is_default("pypi_mirror"))
&& config.non_interactive == Some(false)
{
config.pypi_mirror = Some(generic_select(
"wizard.pypi.mirror",
&idf_im_lib::get_pypi_mirrors_list()
.iter()
.map(|&s| s.to_string())
.collect(),
)?)
}
pub async fn select_mirrors(mut config: Settings) -> Result<Settings, String> {
// IDF mirror
let idf_candidates = idf_im_lib::get_idf_mirrors_list();

select_single_mirror(
&mut config,
"idf_mirror",
|c: &Settings| &c.idf_mirror,
|c: &mut Settings, v| c.idf_mirror = Some(v),
idf_candidates,
"wizard.idf.mirror",
"IDF",
)
.await?;

// Tools mirror
let tools_candidates = idf_im_lib::get_idf_tools_mirrors_list();

select_single_mirror(
&mut config,
"mirror",
|c: &Settings| &c.mirror,
|c: &mut Settings, v| c.mirror = Some(v),
tools_candidates,
"wizard.tools.mirror",
"Tools",
)
.await?;

// PyPI mirror
let pypi_candidates = idf_im_lib::get_pypi_mirrors_list();

select_single_mirror(
&mut config,
"pypi_mirror",
|c: &Settings| &c.pypi_mirror,
|c: &mut Settings, v| c.pypi_mirror = Some(v),
pypi_candidates,
"wizard.pypi.mirror",
"PyPI",
)
.await?;

Ok(config)
}
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/src/cli/wizard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ pub async fn run_wizzard_run(mut config: Settings) -> Result<(), String> {
config = select_targets_and_versions(config).await?;

// mirrors select
config = select_mirrors(config)?;
config = select_mirrors(config).await?;

config = select_installation_path(config)?;

Expand Down
72 changes: 72 additions & 0 deletions src-tauri/src/gui/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
use std::sync::Mutex;
use tauri::AppHandle;
use idf_im_lib::settings::Settings;
use idf_im_lib::utils::MirrorEntry;

use tauri::Manager; // dep: fork = "0.1"

Expand All @@ -20,6 +21,77 @@ pub struct AppState {
pub wizard_data: Mutex<WizardData>,
pub settings: Mutex<Settings>,
pub is_installing: Mutex<bool>,
pub is_simple_installation: Mutex<bool>,
pub idf_mirror_latency_entries: Mutex<Option<Vec<MirrorEntry>>>,
pub tools_mirror_latency_entries: Mutex<Option<Vec<MirrorEntry>>>,
pub pypi_mirror_latency_entries: Mutex<Option<Vec<MirrorEntry>>>,
}

pub fn set_idf_mirror_latency_entries(app_handle: &AppHandle, entries: &Vec<MirrorEntry>) -> Result<(), String> {
let app_state = app_handle.state::<AppState>();
let mut idf_mirror_latency_entries = app_state.idf_mirror_latency_entries.lock().map_err(|_| "Lock error".to_string())?;
*idf_mirror_latency_entries = Some(entries.clone());
Ok(())
}

pub fn set_tools_mirror_latency_entries(app_handle: &AppHandle, entries: &Vec<MirrorEntry>) -> Result<(), String> {
let app_state = app_handle.state::<AppState>();
let mut tools_mirror_latency_entries = app_state.tools_mirror_latency_entries.lock().map_err(|_| "Lock error".to_string())?;
*tools_mirror_latency_entries = Some(entries.clone());
Ok(())
}

pub fn set_pypi_mirror_latency_entries(app_handle: &AppHandle, entries: &Vec<MirrorEntry>) -> Result<(), String> {
let app_state = app_handle.state::<AppState>();
let mut pypi_mirror_latency_entries = app_state.pypi_mirror_latency_entries.lock().map_err(|_| "Lock error".to_string())?;
*pypi_mirror_latency_entries = Some(entries.clone());
Ok(())
}

pub fn get_idf_mirror_latency_entries(app_handle: &AppHandle) -> Option<Vec<MirrorEntry>> {
let app_state = app_handle.state::<AppState>();
app_state.idf_mirror_latency_entries.lock().map(|guard| guard.clone()).unwrap_or_else(|_| {
error!("Failed to acquire idf_mirror_latency_entries lock, returning None");
None
})
}

pub fn get_tools_mirror_latency_entries(app_handle: &AppHandle) -> Option<Vec<MirrorEntry>> {
let app_state = app_handle.state::<AppState>();
app_state.tools_mirror_latency_entries.lock().map(|guard| guard.clone()).unwrap_or_else(|_| {
error!("Failed to acquire tools_mirror_latency_entries lock, returning None");
None
})
}

pub fn get_pypi_mirror_latency_entries(app_handle: &AppHandle) -> Option<Vec<MirrorEntry>> {
let app_state = app_handle.state::<AppState>();
app_state.pypi_mirror_latency_entries.lock().map(|guard| guard.clone()).unwrap_or_else(|_| {
error!("Failed to acquire pypi_mirror_latency_entries lock, returning None");
None
})
}

pub fn set_is_simple_installation(app_handle: &AppHandle, is_simple: bool) -> Result<(), String> {
let app_state = app_handle.state::<AppState>();
let mut simple_installation = app_state
.is_simple_installation
.lock()
.map_err(|_| "Lock error".to_string())?;
*simple_installation = is_simple;
Ok(())
}

pub fn is_simple_installation(app_handle: &AppHandle) -> bool {
let app_state = app_handle.state::<AppState>();
app_state
.is_simple_installation
.lock()
.map(|guard| *guard)
.unwrap_or_else(|_| {
error!("Failed to acquire is_simple_installation lock, assuming false");
false
})
}

/// Gets the current settings from the app state
Expand Down
12 changes: 8 additions & 4 deletions src-tauri/src/gui/commands/idf_tools.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::gui::{app_state::{get_settings_non_blocking, update_settings}, ui::{emit_installation_event, emit_log_message, send_message, send_tools_message, InstallationProgress, InstallationStage, MessageLevel, ProgressBar}};
use crate::gui::{app_state::{get_settings_non_blocking, update_settings}, ui::{InstallationProgress, InstallationStage, MessageLevel, ProgressBar, emit_installation_event, emit_log_message, send_message, send_tools_message}, utils::{get_mirror_to_use, MirrorType}};
use anyhow::{anyhow, Context, Result};

use idf_im_lib::{
Expand Down Expand Up @@ -99,6 +99,7 @@ pub async fn setup_tools(
offline_archive_dir: Option<&Path>,
) -> Result<Vec<String>> {
info!("Setting up tools...");
let is_simple_installation = crate::gui::app_state::is_simple_installation(&app_handle);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_simple_installation will always evaluate to false in this context. The current logic sets the flag to true in start_simple_setup, but start_simple_setup delegates to start_installation, which then sets it back to false. Since setup_tools is executed after this reset, the flag is never actually true, and the entire conditional flow dependent on is_simple_installation is effectively dead code.

This indicates a fundamental flaw in the implementation and strongly suggests the feature was not tested. Please revisit the logic, correct the control flow, and verify the behaviour before resubmitting.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

separate command added that will be called from gui avoiding this


let version_path = idf_path
.parent()
Expand Down Expand Up @@ -316,13 +317,15 @@ pub async fn setup_tools(
}
};

let tools_mirror_to_use = get_mirror_to_use(&app_handle, MirrorType::IDFTools, settings, is_simple_installation).await;

// Use the library's setup_tools function
let installed_tools_list = idf_tools::setup_tools(
&tools,
settings.target.clone().unwrap_or_default(),
&PathBuf::from(&tool_setup.download_dir),
&PathBuf::from(&tool_setup.install_dir),
settings.mirror.as_deref(),
Some(&tools_mirror_to_use),
progress_callback,
)
.await
Expand Down Expand Up @@ -365,7 +368,8 @@ pub async fn setup_tools(
idf_version,
features_for_version
);

let pypi_mirror_to_use = get_mirror_to_use(&app_handle, MirrorType::PyPI, settings, is_simple_installation).await;

// Install Python environment
match idf_im_lib::python_utils::install_python_env(
&paths,
Expand All @@ -374,7 +378,7 @@ pub async fn setup_tools(
true, //TODO: actually read from config
&features_for_version,
offline_archive_dir, // Offline archive directory
&settings.pypi_mirror, // PyPI mirror
&Some(pypi_mirror_to_use), // PyPI mirror
).await {
Ok(_) => {
info!("Python environment installed");
Expand Down
Loading
Loading