@@ -7,6 +7,7 @@ use idf_im_lib::{ensure_path, DownloadProgress, ProgressMessage};
77use indicatif:: { ProgressBar , ProgressState , ProgressStyle } ;
88use log:: { debug, error, info, warn} ;
99use rust_i18n:: t;
10+ use std:: collections:: HashMap ;
1011use std:: sync:: mpsc;
1112use std:: thread;
1213use std:: {
@@ -28,181 +29,6 @@ use crate::cli::helpers::{
2829
2930use crate :: cli:: prompts:: * ;
3031
31- async fn download_tools (
32- tools_file : ToolsFile ,
33- selected_chip : Vec < String > ,
34- destination_path : & str ,
35- mirror : Option < & str > ,
36- ) -> Result < Vec < String > > {
37- let tool_name_list: Vec < String > = tools_file
38- . tools
39- . iter ( )
40- . map ( |tool| tool. name . clone ( ) )
41- . collect ( ) ;
42- info ! (
43- "{}: {:?}" ,
44- t!( "wizard.tools_download.progress" ) ,
45- tool_name_list
46- ) ;
47- let list = idf_im_lib:: idf_tools:: filter_tools_by_target ( tools_file. tools , & selected_chip) ;
48-
49- let platform = match idf_im_lib:: idf_tools:: get_platform_identification ( None ) {
50- Ok ( platform) => platform,
51- Err ( err) => {
52- if std:: env:: consts:: OS == "windows" {
53- // All this is for cases when on windows microsoft store creates "pseudolinks" for python
54- let scp = idf_im_lib:: system_dependencies:: get_scoop_path ( ) ;
55- let usable_python = match scp {
56- Some ( path) => {
57- let mut python_path = PathBuf :: from ( path) ;
58- python_path. push ( "python3.exe" ) ;
59- python_path. to_str ( ) . unwrap ( ) . to_string ( )
60- }
61- None => "python3.exe" . to_string ( ) ,
62- } ;
63- match idf_im_lib:: idf_tools:: get_platform_identification ( Some ( & usable_python) ) {
64- Ok ( platform) => platform,
65- Err ( err) => {
66- error ! ( "Unable to identify platform: {}" , err) ;
67- panic ! ( "{}. {:?}" , t!( "wizard.tools_platform_error" ) , err) ;
68- }
69- }
70- } else {
71- panic ! ( "{}. {:?}" , t!( "wizard.tools_platform_error" ) , err) ;
72- }
73- }
74- } ;
75- debug ! ( "Python platform: {}" , platform) ;
76- let download_links = idf_im_lib:: idf_tools:: change_links_donwanload_mirror (
77- idf_im_lib:: idf_tools:: get_download_link_by_platform ( list, & platform) ,
78- // Some("https://dl.espressif.com/github_assets"), // this switches mirror, should be parametrized
79- mirror,
80- )
81- . into_iter ( )
82- . collect :: < Vec < _ > > ( ) ;
83- let mut downloaded_tools: Vec < String > = vec ! [ ] ;
84- for ( tool_name, download_link) in download_links. iter ( ) {
85- info ! ( "{}: {}" , t!( "wizard.tool_download.progress" ) , tool_name) ;
86-
87- let ( progress_tx, progress_rx) = mpsc:: channel ( ) ;
88-
89- let progress_bar = ProgressBar :: new ( download_link. size ) ;
90- progress_bar. set_style ( ProgressStyle :: with_template ( "{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})" ) . unwrap ( )
91- . with_key ( "eta" , |state : & ProgressState , w : & mut dyn Write | write ! ( w, "{:.1}s" , state. eta( ) . as_secs_f64( ) ) . unwrap ( ) )
92- . progress_chars ( "#>-" ) ) ;
93-
94- debug ! ( "Download link: {}" , download_link. url) ;
95- debug ! ( "destination: {}" , destination_path) ;
96-
97- let file_path = Path :: new ( & download_link. url ) ;
98- let filename: & str = file_path. file_name ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) ;
99-
100- let full_file_path = Path :: new ( & destination_path) . join ( Path :: new ( filename) ) ;
101- match idf_im_lib:: verify_file_checksum (
102- & download_link. sha256 ,
103- full_file_path. to_str ( ) . unwrap ( ) ,
104- ) {
105- Ok ( true ) => {
106- downloaded_tools. push ( filename. to_string ( ) ) ; // add it to the list for extraction even if it's already downloaded
107- info ! ( "{}" , t!( "wizard.tool_file.present" ) ) ;
108- progress_bar. finish ( ) ;
109- continue ;
110- }
111- _ => {
112- debug ! ( "{}" , t!( "wizard.tool_file.missing" ) ) ;
113- }
114- }
115- let tn = tool_name. clone ( ) ;
116- let pb = progress_bar. clone ( ) ;
117- let progress_handle = {
118- thread:: spawn ( move || {
119- while let Ok ( progress_msg) = progress_rx. recv ( ) {
120- match progress_msg {
121- DownloadProgress :: Progress ( current, _total) => {
122- pb. set_position ( current) ;
123- }
124- DownloadProgress :: Complete => {
125- pb. finish ( ) ;
126- break ;
127- }
128- DownloadProgress :: Error ( err) => {
129- log:: error!( "Error downloading {}: {}" , tn, err) ;
130- break ;
131- }
132- }
133- }
134- } )
135- } ;
136-
137- match idf_im_lib:: download_file ( & download_link. url , destination_path, Some ( progress_tx) ) . await {
138- Ok ( _) => {
139- downloaded_tools. push ( filename. to_string ( ) ) ;
140- progress_bar. finish ( ) ;
141- info ! ( "{} {}" , t!( "wizard.tool.downloaded" ) , tool_name) ;
142- }
143- Err ( err) => {
144- error ! ( "{}: {}" , t!( "wizard.tool.download_failed" ) , tool_name) ;
145- error ! ( "Error: {:?}" , err) ;
146- return Err ( anyhow ! ( "{}: {}" , t!( "wizard.tool.download_failed" ) , err) ) ;
147- }
148- }
149- progress_handle. join ( ) . map_err ( |e| anyhow:: anyhow!( "Progress reporting thread panicked: {:?}" , e) ) ?;
150- match idf_im_lib:: verify_file_checksum (
151- & download_link. sha256 ,
152- full_file_path. to_str ( ) . unwrap ( ) ,
153- ) {
154- Ok ( true ) => {
155- continue ;
156- }
157- _ => {
158- error ! ( "{}" , t!( "wizard.tool.corupted" ) ) ;
159- match fs:: remove_file ( & full_file_path) {
160- Ok ( _) => {
161- error ! (
162- "{}: {}" ,
163- t!( "wizard.tool.removed" ) ,
164- full_file_path. to_str( ) . unwrap( )
165- ) ;
166- }
167- Err ( err) => {
168- error ! (
169- "{}: {}: {}" ,
170- t!( "wizard.tool.remove_failed" ) ,
171- full_file_path. to_str( ) . unwrap( ) ,
172- err
173- ) ;
174- }
175- } ;
176- }
177- }
178- }
179- Ok ( downloaded_tools)
180- }
181-
182- fn extract_tools ( tools : Vec < String > , source_path : & str , destination_path : & str ) -> anyhow:: Result < ( ) > {
183- let mut failed = false ;
184- for tool in tools. iter ( ) {
185- let mut archive_path = PathBuf :: from ( source_path) ;
186- archive_path. push ( tool) ;
187- let out = idf_im_lib:: decompress_archive ( archive_path. to_str ( ) . unwrap ( ) , destination_path) ;
188- match out {
189- Ok ( _) => {
190- info ! ( "{}: {}" , t!( "wizard.tool.extracted" ) , tool) ;
191- }
192- Err ( err) => {
193- error ! ( "{:?}" , err) ;
194- error ! ( "{}: {}" , t!( "wizard.tool.extract_failed" ) , tool) ;
195- failed = true ;
196- }
197- }
198- }
199- if failed {
200- Err ( anyhow:: anyhow!( "Some tools failed to extract" ) )
201- } else {
202- Ok ( ( ) )
203- }
204- }
205-
20632fn add_to_shell_rc ( content : & str ) -> Result < ( ) , String > {
20733 let shell = env:: var ( "SHELL" ) . unwrap_or_else ( |_| String :: from ( "" ) ) ;
20834 let home = dirs:: home_dir ( ) . unwrap ( ) ;
@@ -352,7 +178,6 @@ pub fn download_idf(config: DownloadConfig) -> Result<(), DownloadError> {
352178 }
353179 }
354180 }
355-
356181}
357182
358183fn setup_directory (
@@ -427,68 +252,57 @@ async fn download_and_extract_tools(
427252 tools : & ToolsFile ,
428253 download_dir : & PathBuf ,
429254 install_dir : & PathBuf ,
430- ) -> anyhow:: Result < ( ) > {
431- let downloaded_tools_list = match download_tools (
432- tools. clone ( ) ,
433- config. target . clone ( ) . unwrap ( ) ,
434- download_dir. to_str ( ) . unwrap ( ) ,
435- config. mirror . as_deref ( ) ,
436- )
437- . await {
438- Ok ( list) => list,
439- Err ( err) => {
440- error ! ( "Failed to download tools: {}" , err) ;
441- return Err ( anyhow ! ( err) ) ;
255+ ) -> anyhow:: Result < HashMap < String , ( String , idf_im_lib:: idf_tools:: Download ) > > {
256+ info ! (
257+ "{}: {:?}" ,
258+ t!( "wizard.tools_download.progress" ) ,
259+ download_dir. display( )
260+ ) ;
261+ let progress_bar = ProgressBar :: new ( 0 ) ;
262+ progress_bar. set_style ( ProgressStyle :: with_template ( "{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})" ) . unwrap ( )
263+ . with_key ( "eta" , |state : & ProgressState , w : & mut dyn Write | write ! ( w, "{:.1}s" , state. eta( ) . as_secs_f64( ) ) . unwrap ( ) )
264+ . progress_chars ( "#>-" ) ) ;
265+
266+ let progress_callback = move |progress : DownloadProgress | match progress {
267+ DownloadProgress :: Progress ( current, total) => {
268+ progress_bar. set_length ( total) ;
269+ progress_bar. set_position ( current) ;
270+ }
271+ DownloadProgress :: Complete => {
272+ progress_bar. finish ( ) ;
273+ }
274+ DownloadProgress :: Error ( err) => {
275+ progress_bar. abandon_with_message ( format ! ( "Error: {}" , err) ) ;
276+ }
277+ DownloadProgress :: Start ( _) => {
278+ progress_bar. set_position ( 0 ) ;
279+ }
280+ DownloadProgress :: Downloaded ( url) => {
281+ if let Some ( filename) = Path :: new ( & url) . file_name ( ) . and_then ( |f| f. to_str ( ) ) {
282+ info ! ( "{} sucessfully downloaded" , filename. to_string( ) ) ;
283+ }
284+ }
285+ DownloadProgress :: Verified ( url) => {
286+ if let Some ( filename) = Path :: new ( & url) . file_name ( ) . and_then ( |f| f. to_str ( ) ) {
287+ info ! ( "{} checksum verified" , filename. to_string( ) ) ;
288+ }
289+ }
290+ DownloadProgress :: Extracted ( url) => {
291+ if let Some ( filename) = Path :: new ( & url) . file_name ( ) . and_then ( |f| f. to_str ( ) ) {
292+ info ! ( "{} sucessfully extracted" , filename. to_string( ) ) ;
293+ }
442294 }
443295 } ;
444296
445- extract_tools (
446- downloaded_tools_list,
447- download_dir. to_str ( ) . unwrap ( ) ,
448- install_dir. to_str ( ) . unwrap ( ) ,
297+ idf_im_lib:: idf_tools:: setup_tools (
298+ tools,
299+ config. target . clone ( ) . unwrap ( ) ,
300+ download_dir,
301+ install_dir,
302+ config. mirror . as_deref ( ) ,
303+ progress_callback,
449304 )
450- }
451-
452- fn get_and_validate_idf_tools_path (
453- config : & mut Settings ,
454- idf_path : & PathBuf ,
455- ) -> Result < PathBuf , String > {
456- let mut idf_tools_path = idf_path. clone ( ) ;
457-
458- if let Some ( file) = config. idf_tools_path . clone ( ) {
459- idf_tools_path. push ( & file) ;
460- } else if config. wizard_all_questions . unwrap_or ( false ) {
461- let name = generic_input (
462- "wizard.idf_tools.prompt" ,
463- "wizard.idf_tools.prompt.failure" ,
464- DEFAULT_IDF_TOOLS_PY_LOCATION ,
465- ) ?;
466-
467- idf_tools_path. push ( & name) ;
468- config. idf_tools_path = Some ( name) ;
469- } else {
470- idf_tools_path. push ( DEFAULT_IDF_TOOLS_PY_LOCATION ) ; // TODO: defaults are in lib now
471- config. idf_tools_path = Some ( DEFAULT_IDF_TOOLS_PY_LOCATION . to_string ( ) ) ;
472- }
473-
474- if fs:: metadata ( & idf_tools_path) . is_err ( ) {
475- warn ! ( "{}" , t!( "wizard.idf_tools.not_found" ) ) ;
476- let idf_tools_py_select = FolderSelect :: with_theme ( & create_theme ( ) )
477- . with_prompt ( t ! ( "wizard.idf_tools.select.prompt" ) )
478- . folder ( idf_path. to_str ( ) . unwrap ( ) )
479- . file ( true )
480- . interact ( )
481- . map_err ( |e| format ! ( "Failed to select: {}" , e) ) ?;
482-
483- if fs:: metadata ( & idf_tools_py_select) . is_ok ( ) {
484- idf_tools_path = PathBuf :: from ( & idf_tools_py_select) ;
485- config. idf_tools_path = Some ( idf_tools_py_select) ;
486- } else {
487- return Err ( t ! ( "wizard.idf_tools.unreachable" ) . to_string ( ) ) ;
488- }
489- }
490-
491- Ok ( idf_tools_path)
305+ . await
492306}
493307
494308pub async fn run_wizzard_run ( mut config : Settings ) -> Result < ( ) , String > {
@@ -583,28 +397,36 @@ pub async fn run_wizzard_run(mut config: Settings) -> Result<(), String> {
583397 let tools = idf_im_lib:: idf_tools:: read_and_parse_tools_file ( & validated_file)
584398 . map_err ( |err| format ! ( "{}: {}" , t!( "wizard.tools_json.unparsable" ) , err) ) ?;
585399
586- match download_and_extract_tools (
400+ let installed_tools_list = match download_and_extract_tools (
587401 & config,
588402 & tools,
589403 & tool_download_directory,
590404 & tool_install_directory,
591405 )
592- . await {
593- Ok ( _) => {
594- info ! ( "{}: {}" , t!( "wizard.tools.downloaded" ) , tools_json_file. display( ) ) ;
406+ . await
407+ {
408+ Ok ( list) => {
409+ info ! (
410+ "{}: {}" ,
411+ t!( "wizard.tools.downloaded" ) ,
412+ tools_json_file. display( )
413+ ) ;
414+ list
595415 }
596416 Err ( err) => {
597417 error ! ( "Failed to download and extract tools: {}" , err) ;
598418 return Err ( err. to_string ( ) ) ;
599419 }
600- }
420+ } ;
601421 match idf_im_lib:: python_utils:: install_python_env (
602- & idf_version,
603- & tool_install_directory,
604- true , //TODO: actually read from config
605- & idf_path,
606- & config. idf_features . clone ( ) . unwrap_or_default ( ) ,
607- ) . await {
422+ & idf_version,
423+ & tool_install_directory,
424+ true , //TODO: actually read from config
425+ & idf_path,
426+ & config. idf_features . clone ( ) . unwrap_or_default ( ) ,
427+ )
428+ . await
429+ {
608430 Ok ( _) => {
609431 info ! ( "Python environment installed" ) ;
610432 }
@@ -613,13 +435,16 @@ pub async fn run_wizzard_run(mut config: Settings) -> Result<(), String> {
613435 return Err ( err. to_string ( ) ) ;
614436 }
615437 } ;
616- let idf_python_env_path = tool_install_directory. join ( "python" ) . join ( & idf_version) . join ( "venv" ) ; //todo: move to config
438+ let idf_python_env_path = tool_install_directory
439+ . join ( "python" )
440+ . join ( & idf_version)
441+ . join ( "venv" ) ; //todo: move to config
617442 ensure_path ( idf_python_env_path. to_str ( ) . unwrap ( ) )
618443 . map_err ( |err| format ! ( "Failed to create Python environment directory: {}" , err) ) ?;
619444
620- let export_paths = idf_im_lib:: idf_tools:: get_tools_export_paths (
445+ let export_paths = idf_im_lib:: idf_tools:: get_tools_export_paths_from_list (
621446 tools,
622- config . target . clone ( ) . unwrap ( ) . clone ( ) ,
447+ installed_tools_list ,
623448 tool_install_directory. to_str ( ) . unwrap ( ) ,
624449 )
625450 . into_iter ( )
0 commit comments