Skip to content

Conversation

@alirana01
Copy link
Collaborator

Description

Ping test added for the mirrors. We first query the HEAD with a GET request and if that fails we try a normal GET to verify latency and connection

Testing

Tested the installation via cli, gui using both simple and wizard based flows


Checklist

Before submitting a Pull Request, please ensure the following:

  • All CI checks (GH Actions) pass.
  • Documentation is updated as needed.
  • Tests are updated or added as necessary.
  • Code is well-commented, especially in complex areas.
  • Git history is clean — commits are squashed to the minimum necessary.

@alirana01 alirana01 self-assigned this Nov 12, 2025
@gemini-code-assist
Copy link

Summary of Changes

Hello @alirana01, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to the mirror selection process by integrating latency-based ping checks. The primary goal is to improve the reliability and speed of downloads and installations by ensuring the application connects to the most performant mirrors. This change provides a more intelligent and user-friendly way to manage mirror preferences across both command-line and graphical interfaces.

Highlights

  • Mirror Latency Checks: Implemented a new feature to perform latency checks (ping) on available mirrors (IDF, tools, and PyPI) to determine their responsiveness.
  • Enhanced Mirror Selection: The mirror selection logic in both the CLI and GUI has been updated to utilize these latency scores, automatically prioritizing and displaying the fastest available mirrors.
  • UI/UX Improvements: The GUI now visually presents the latency (ping time) for each mirror and indicates unreachable mirrors with a 'timeout' status, improving user experience during mirror selection.
  • Dependency Updates: Added new Rust dependencies futures and url, and updated the reqwest library to include stream and json features, supporting the new network operations.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a latency check for mirrors, which is a great improvement for user experience. The implementation is solid, but there are several areas with significant code duplication in both the Rust backend and the Vue frontend. I've provided suggestions to refactor this duplicated logic into helper functions to improve maintainability. Additionally, I've identified a potential crash in a GUI command due to an unhandled error and a performance improvement opportunity by parallelizing mirror checks.

Hahihula

This comment was marked as outdated.

Comment on lines 519 to 489

/// Compute the latency map for the tools mirror
pub async fn get_tools_mirror_latency_map(&self) -> Result<HashMap<String, u32>> {
let available_mirrors = crate::get_idf_tools_mirrors_list()
.to_vec()
.iter()
.map(|s| s.to_string())
.collect::<Vec<String>>();
let mirror_latency_map =
crate::utils::calculate_mirror_latency_map(&available_mirrors).await;
Ok(mirror_latency_map)
}

/// Compute the latency map for the IDF mirror
pub async fn get_idf_mirror_latency_map(&self) -> Result<HashMap<String, u32>> {
let available_mirrors = crate::get_idf_mirrors_list()
.to_vec()
.iter()
.map(|s| s.to_string())
.collect::<Vec<String>>();
let mirror_latency_map =
crate::utils::calculate_mirror_latency_map(&available_mirrors).await;
Ok(mirror_latency_map)
}

/// Compute the latency map for the PyPI mirror
pub async fn get_pypi_mirror_latency_map(&self) -> Result<HashMap<String, u32>> {
let available_mirrors = crate::get_pypi_mirrors_list()
.to_vec()
.iter()
.map(|s| s.to_string())
.collect::<Vec<String>>();
let mirror_latency_map =
crate::utils::calculate_mirror_latency_map(&available_mirrors).await;
Ok(mirror_latency_map)
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@Hahihula I think i made it redundant by adding these functions here or I may have miss understood something

Copy link
Collaborator

Choose a reason for hiding this comment

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

so why do you add them?

@alirana01 alirana01 requested a review from Hahihula November 24, 2025 14:59
Copy link
Collaborator

@Hahihula Hahihula left a comment

Choose a reason for hiding this comment

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

you also use u32::MAX for unreachable on rust side and 0 on javascript side. avoid using magical numbers if possible. Usage of Option<> or enum will be better variant here.
You also have a lot of faulty code as well as code which will work only on happy path scenarios. Avoid using unwrap if you are not 100% sure it's ok to do so.
A lot of the logic is AntiIdiomatic.
Also, please turn off the automatic formatting features of your IDE and avoid copy&paste from LLMs, which changes the formatting of all the code. Unnecessary changes in code formatting are masking the history of changes in git. Please do not reformat part of code you otherwise did not touch.
You also completely ignored some of my comments from the last time. For example the error you have here with the stripping of port number from the url. I expect that after the fix you will also have test which covers this case.
The UI side logic can also be significantly simplified. There is also the issue of not easily understandable code with the converting u32::MAX to 0 and using positive and negative infinity alongside syntactic null and undefined. But the idea of bootstrapping the store after the app starts with values needed later is good and we should use it more broadly for example for getting OS params in one place.
I also think the functions mirror_entries_to_display and url_from_display_line really shouldn't exist at all

use url::Url;
use zstd::{decode_all, Decoder};

use crate::{
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please avoid needles reformatting/rearranging of imports as it masks history

}

/// Turn `(url, latency)` tuples into display strings like `https://... (123 ms)` or `(... timeout)`.
pub fn mirror_entries_to_display(entries: &[MirrorEntry]) -> Vec<String> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

as this is formating fo cli part only, it should not be part of common library shared between cli and gui

.unwrap()
.0
.clone();
let mirror = match best_mirror.is_empty() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

here, you select the mirror to be either the best or the default mirror, than emit in in the message and never use it!


let mirror = settings.idf_mirror.clone().unwrap_or_default();
let mut available_mirrors = idf_im_lib::get_idf_mirrors_list().to_vec();
let mirror = settings.idf_mirror.clone().unwrap_or_default();
Copy link
Collaborator

Choose a reason for hiding this comment

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

another unnecessary formating change... please avoid

}

/// Return URL -> score (lower is better). Unreachable mirrors get u32::MAX.
pub async fn calculate_mirror_latency_map(mirrors: &[String]) -> HashMap<String, u32> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

the function as it's written now will introduce potentially multiple 10s of second wait for timeout as it checks the mirrors sequentially, it also creates new reqwest::Client for every mirror. Creating a client is expensive (allocating connection pools, TLS configuration).

const list = (urls || []).map((url) => ({
value: url,
label: url,
ping: this.normalizePingValue(latencyMap ? latencyMap[url] : undefined),
Copy link
Collaborator

Choose a reason for hiding this comment

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

as this is the only place you use normalizePingValue the function is there only for converting hardcoded undefined to hardcoded null...

let default_mirror = rust_i18n::t!("gui.installation.default_mirror").to_string();
let mirror = settings.idf_mirror.as_deref().unwrap_or(&default_mirror);
let default_mirror_str = rust_i18n::t!("gui.installation.default_mirror").to_string();
let default_mirror = settings
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is not only default mirror but potentially also user selected preferred mirror

@alirana01 alirana01 requested a review from Hahihula November 28, 2025 09:50
Copy link
Collaborator

@Hahihula Hahihula left a comment

Choose a reason for hiding this comment

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

You completely ignored my recommendation from last review:
I also think the functions mirror_entries_to_display and url_from_display_line really shouldn't exist at all
Could you please really pay attention to the review remarks?

pub wizard_all_questions: Option<bool>,
pub mirror: Option<String>,
pub tools_mirror: Option<String>,
pub idf_mirror: Option<String>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a breaking API change. Will force to upgrade all integrations. Docker images and actions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

reverted back!

self.tools_json_file.map(Into::into),
),
("mirror".to_string(), self.mirror.map(Into::into)),
// map CLI flag `-m|--mirror` to settings field `tools_mirror`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Mapping the CLI --mirror argument to the tools_mirror setting is inconsistent and unjustified. You apply this remapping only for this single argument, which means it behaves differently from all other CLI options. This breaks the established logic for how settings are populated.

Additionally, the CLI is not the only source of configuration. By introducing this one-off mapping, you are decoupling settings in a way that disrupts the overall configuration flow. This is not acceptable.

I already raised this concern in the previous iteration of the review, yet the issue not only remains unresolved, but it has been made worse.

On top of that, the renaming you introduced constitutes a breaking change to the application’s interface. All related changes must be reverted. If such a renaming is ever considered, it must be evaluated and delivered in a dedicated PR, not mixed in with unrelated work.

Do not bundle multiple unrelated changes into the same PR. While it may seem convenient to merge, it significantly increases the complexity and risk when a rollback is needed. My priority is not how easy it is to merge your changes — it’s how difficult it will be to revert them if something breaks. Please follow proper PR scoping and revert the unrelated interface changes 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.

reverted

let latency_map = calculate_mirror_latency_map(&mirrors.to_vec()).await;
let mut entries: Vec<MirrorEntry> = Vec::new();
for (key, value) in latency_map.iter() {
entries.push((key.clone(), value.clone()));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why are you building a new structure by cloning everything? Please prefer iterator-based construction via collect() for simplicity and zero-cost abstraction. Also you wouldn't need mutable entries

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed here


/// Sort mirrors by measured latency (ascending), using None for timeouts.
pub async fn sorted_mirror_entries(mirrors: &[&str]) -> Vec<MirrorEntry> {
let latency_map = calculate_mirror_latency_map(&mirrors.to_vec()).await;
Copy link
Collaborator

Choose a reason for hiding this comment

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

&mirrors.to_vec() converts a slice to a vec and back to a slice reference

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed at required places

use idf_im_lib::utils::{calculate_mirror_latency_map};

/// A tuple containing a mirror URL and its measured latency.
pub type MirrorEntry = (String, Option<u32>);
Copy link
Collaborator

Choose a reason for hiding this comment

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

you should use struct instead of tuple so you are not later accessing it by mystique e.1
also use macro to derive Ord to make the sorting cleaner

something like this:

impl PartialOrd for MirrorEntry {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.latency.partial_cmp(&other.latency)
    }
}

impl Ord for MirrorEntry {
    fn cmp(&self, other: &Self) -> Ordering {
        self.partial_cmp(other).unwrap_or(Ordering::Equal)  // Or handle None explicitly
    }
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

#[tauri::command]
pub async fn start_simple_setup(app_handle: tauri::AppHandle) -> Result<(), String> {
let app_state = app_handle.state::<crate::gui::app_state::AppState>();
app_state::set_is_simple_installation(&app_handle, true)?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

as mentioned above this true will always get overwritten by start_simple_setup to false

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

use anyhow::{Result};

use crate::gui;
use crate::gui::{self, app_state};
Copy link
Collaborator

Choose a reason for hiding this comment

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

You added app_state to the import list here, but it is never used. Introducing unused imports is unnecessary noise and should be caught before the PR is submitted.

It would be extremely helpful if you reviewed the GitHub diff yourself before requesting a review. Basic self-review should catch obvious issues like unused imports, leftover code, or incomplete refactoring. This is a matter of professional courtesy and respect for the reviewer’s time.

Please ensure you perform a thorough pass over your changes before assigning the PR for review.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed

}
}

pub async fn get_best_mirror(mirror_latency_map: &HashMap<String, Option<u32>>) -> Option<String> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This String + Option pairing is fundamentally the same data structure as the tuple you’re using in the CLI component. As mentioned in the previous review round, you should introduce a proper struct to represent a mirror entry and implement Ord (as previously suggested).

If you define this struct in the shared library layer, you can use it consistently in both the CLI and GUI code. This will simplify the implementation, remove the need for ad-hoc comparisons, and ensure that both parts of the application follow the exact same logic when selecting the best mirror.

Please refactor accordingly to avoid duplicating logic and relying on loosely structured tuples or paired values.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

moved original struct to the library now

entries.push((key.clone(), value.clone()));
}

entries.sort_by_key(|e| e.1);
Copy link
Collaborator

Choose a reason for hiding this comment

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

much more readable way to write this is
entries.sort_by_key(|(url, latency)| latency);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed here since now it is part of shared library as a struct

ui::send_message,
utils::is_path_empty_or_nonexistent,
};
use std::path::PathBuf;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please avoid reformatting imports or modifying code that is unrelated to the scope of your changes. Splitting and rearranging imports without necessity introduces noise, obscures git history, and makes the review process significantly harder.

This is the third time I’ve had to raise this concern on this PR, and it is becoming a recurring issue. Going forward, do not make non-essential formatting or structural changes unless they are directly required for the feature or fix you are implementing.

Thank you for addressing this properly in future submissions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

cleaned up

@alirana01
Copy link
Collaborator Author

@Hahihula
#271 (comment)

Just to add clarification to the comment I cannot figure out a way other than to separate the call from the command and the function the command invoked from frontend should not have been called in the backend at first place I guess we can try to refactor code in another PR where we make sure that all such functions are broken down further to avoid conflicting changes later in the code when we need to add new things or features. The addition of the new command that will be invoked by the frontend will avoid this flaw.

@alirana01 alirana01 requested a review from Hahihula December 2, 2025 11:43
Copy link
Collaborator

@Hahihula Hahihula left a comment

Choose a reason for hiding this comment

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

It's still fundamentally not working, you have terrible flaws in logic as well as code repetition and antiidiomatic approach, but it's much better than last time.

}

impl Ord for MirrorEntry {
fn cmp(&self, other: &Self) -> Ordering {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Much better approach! But still ... Option already implements the Ord Trait. So it can be simplified like this:

match (self.latency, other.latency) {
    (Some(a), Some(b)) => a.cmp(&b),
    (Some(_), None) => Ordering::Less,
    (None, Some(_)) => Ordering::Greater,
    (None, None) => Ordering::Equal,
}

This 1) avoids unwrap() 2) ensures all possible cases are covered (match will not let you have unimplemented variants) 3) is clearly readable 4) pattern matching is the rust way of doing things ;-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

}
}
}
if head_latency_failed {
Copy link
Collaborator

Choose a reason for hiding this comment

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

you can use else here

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah but why go over the loop once again if the head failed in that attempt why not break it right there.

}

/// Return URL -> score (lower is better). Unreachable mirrors get None.
pub async fn calculate_mirror_latency_map(mirrors: &[&str]) -> HashMap<String, Option<u32>> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Also as you now have the MirrorEntry type, with ordering implemented, I would expect you to realize that you can make select_single_mirror and select_best_mirror much better. If you use Vec in the calculate_mirror_latency_map function.
Is more idiomatic and more efficient, it's also more readable as you know you are working with MirrorEntry also thsi way you will avoind new alocation of the string keys.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

)?)
// Only measure mirror latency if we actually need a value (or wizard wants to ask)
if interactive && (wizard_all || needs_value) {
let mut entries = calculate_mirror_latency_map(candidates).await.into_iter()
Copy link
Collaborator

Choose a reason for hiding this comment

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

after you convert the calculate_mirror_latency_map so it returns Vec you can than
just entries.sort(); without the unnecessary rearranging everything with unnecessary allocations

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

}
}

pub async fn get_best_mirror(mirror_latency_map: HashMap<String, Option<u32>>) -> Option<String> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This function will also benefit from converting mirror_latency_map to Vec

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removing this as we dont need it anymore as moving to MirrorEntry vector the original latency calculation returns it sorted

value: mirror,
label: mirror,
this.sortMirrorsByPing(this.idf_mirrors);
this.loading_idfs = this.mirrorsStore.loading_idf_urls || this.mirrorsStore.loading_idf_latency;
Copy link
Collaborator

Choose a reason for hiding this comment

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

the loading should be also reactive computed properties:

loading_idfs = mirrorsStore.loading_idf_urls || mirrorsStore.loading_idf_latency
``

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

src/store.js Outdated

computeLatencyInBackground() {
const now = Date.now();
// IDF
Copy link
Collaborator

Choose a reason for hiding this comment

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

i would really like if you can avoid this repetitive code ... maybe you can use something like:

const MIRROR_TYPES = ['idf', 'tools', 'pypi'];
state: () => {
    const base = {};
    for (const type of MIRROR_TYPES) {
      base[`${type}_urls`] = [];
      base[`${type}_latency_map`] = {};
      base[`selected_${type}`] = '';
      base[`loading_${type}_urls`] = false;
      base[`loading_${type}_latency`] = false;
      base[`${type}_last_updated`] = 0;
    }
    return {
      ...base,
      latency_ttl_ms: 15 * 60 * 1000,
    };
  },

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

good idea fixed

Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you please do the same for the rest of the useMirrorsStore ? You have the same repetitive patern all over it. My previos comment is clear hint how to achieve much less repetitive code.

this.refreshListFromStore('pypi');
// Subscribe to store updates and refresh progressively per-type
const unsub = this.mirrorsStore.$subscribe(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

you should avoid the $subscribe...use reactive computed properties
you can also avoid so much code repetition
Please read more about Vuejs reactivity https://vuejs.org/guide/essentials/reactivity-fundamentals.html

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

updated

let mirror = settings.idf_mirror.as_deref().unwrap_or(&default_mirror_str);
let mut mirror_to_use: String = mirror.to_string();

if is_simple_installation && mirror == &default_mirror_str {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Care to explain this to me?
you are reading value from the localisation file:
let default_mirror_str = rust_i18n::t!("gui.installation.default_mirror").to_string();
and comparing it to this value?
How is this exactly supposed to work and in which languages?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🙈🔫


#[cfg(not(target_os = "windows"))]
#[tauri::command]
pub async fn start_installation_gui_cmd(app_handle: AppHandle) -> Result<(), String> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

You are unnecessarily adding another level of indirection...
this function should never exist...
You are obviously trying to achieve the default value... this is usually done in Rust by implementing the default trait for the structure
Please refactor your code according to this

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

@alirana01
Copy link
Collaborator Author

So now the app state on rust side holds the mirror latencies and whenever they are needed anywhere that is checked first to avoid triggering the latency check again. Rest of the logic should be working fine @Hahihula

@alirana01 alirana01 requested a review from Hahihula December 3, 2025 16:02
}
Err(e) => {
warn!("{}", e.to_string());
head_latency_failed = true;
Copy link
Collaborator

Choose a reason for hiding this comment

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

the break should be here

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

Copy link
Collaborator

@Hahihula Hahihula left a comment

Choose a reason for hiding this comment

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

Ok, much better than last time. No more big logical errors. Still not mergable, but we are near.

let entries = calculate_mirrors_latency(candidates).await;
if let Some(entry) = entries.first() {
if entry.latency.is_none() {
info!("Selected {log_prefix} mirror: {} (timeout)", entry.url);
Copy link
Collaborator

Choose a reason for hiding this comment

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

You just selected for the user mirror which timeouted and threat it as normal situation, with logging info ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed returning error if the top entry is none as that means some connection issue as vectors are sorted by latency and None (timeout) should be at the end not first entry

}
}

pub async fn get_mirror_to_use(mirror_type: MirrorType, settings: &Settings, is_simple_installation: bool, app_handle: &AppHandle) -> String {
Copy link
Collaborator

Choose a reason for hiding this comment

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

every other function accepting app_handle has it as firt argument, can you pleare acomodate your to that custom for sake of readibility?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

this.get_available_tools_mirrors();
this.get_available_pypi_mirrors();
// Ensure background bootstrap runs if user navigates directly here before app init
if (
Copy link
Collaborator

Choose a reason for hiding this comment

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

this means that if you have mirror of one type but not the others becaus for example network disconected before you finished measuring, you will not refetch them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

src/store.js Outdated

computeLatencyInBackground() {
const now = Date.now();
// IDF
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you please do the same for the rest of the useMirrorsStore ? You have the same repetitive patern all over it. My previos comment is clear hint how to achieve much less repetitive code.

},
});

export const useMirrorsStore = defineStore("mirrors", {
Copy link
Collaborator

Choose a reason for hiding this comment

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

try to deduplicate the code in this store

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

@alirana01 alirana01 requested a review from Hahihula December 9, 2025 10:18
Copy link
Collaborator

@Hahihula Hahihula left a comment

Choose a reason for hiding this comment

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

the Rust part seems to be ok, let's just cleanup the javascript part

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§");
Copy link
Collaborator

Choose a reason for hiding this comment

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

typo?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed


<script>
import { ref } from "vue";
import { } from "vue";
Copy link
Collaborator

Choose a reason for hiding this comment

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

?? What should this be?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

originally i see ref import from master but not sure if its even used there but i reverted back to that to avoid any issues

buildMirrorList(type) {
const urls = type === 'idf' ? this.mirrorsStore.idf_urls : type === 'tools' ? this.mirrorsStore.tools_urls : this.mirrorsStore.pypi_urls;
const entries = type === 'idf' ? this.mirrorsStore.idf_entries : type === 'tools' ? this.mirrorsStore.tools_entries : this.mirrorsStore.pypi_entries;
if (Array.isArray(entries) && entries.length > 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

you can use entries?.length > 0

Copy link
Collaborator Author

@alirana01 alirana01 Dec 10, 2025

Choose a reason for hiding this comment

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

Simplified with new simplified code

this.selected_idf_mirror = idf_mirrors.selected;
this.loading_idfs = false;
return false;
buildMirrorList(type) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

this function can be simplified using try catch block

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

simplified not exactly try catch but kindly take a look if it is okay now

this.selected_pypi_mirror = pypi_mirrors.selected;
this.loading_pypi = false;
return false;
getBestMirror(list) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

using reduce this function is oneliner

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

},
maybeAutoSelectBest(type) {
if (!this.autoSelect[type]) return;
const list = type === 'idf' ? this.idf_mirrors : type === 'tools' ? this.tools_mirrors : this.pypi_mirrors;
Copy link
Collaborator

Choose a reason for hiding this comment

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

this can be written more readably like: const list = this[${type}_mirrors];

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

const list = type === 'idf' ? this.idf_mirrors : type === 'tools' ? this.tools_mirrors : this.pypi_mirrors;
const best = this.getBestMirror(list);
if (!best) return;
const selectedKey = type === 'idf' ? 'selected_idf_mirror' : type === 'tools' ? 'selected_tools_mirror' : 'selected_pypi_mirror';
Copy link
Collaborator

Choose a reason for hiding this comment

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

const storeKey = selected_${type}_mirror;

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

},
isDefaultMirror(mirror, type) {
return mirror === this.defaultMirrors[type];
const selectedFromStore = type === 'idf' ? this.mirrorsStore.selected_idf : type === 'tools' ? this.mirrorsStore.selected_tools : this.mirrorsStore.selected_pypi;
Copy link
Collaborator

Choose a reason for hiding this comment

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

similar as abow this can be more readable.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

});

export const useMirrorsStore = defineStore("mirrors", {
state: () => ({
Copy link
Collaborator

Choose a reason for hiding this comment

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

Unified the structure for all mirror types could make the store much more readable and maintainable.
for example like this:

mirrors: {
      idf: {
        urls: [],
        entries: [],
        selected: "",
        loading_urls: false,
        loading_latency: false,
        last_updated: 0,
      },
      tools: {
        urls: [],
        entries: [],
        selected: "",
        loading_urls: false,
        loading_latency: false,
        last_updated: 0,
      },
      pypi: {
        urls: [],
        entries: [],
        selected: "",
        loading_urls: false,
        loading_latency: false,
        last_updated: 0,
      },
    },

it will also simplyfy the code in the rest of the store and the component...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

@alirana01 alirana01 requested a review from Hahihula December 10, 2025 12:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants