working
This commit is contained in:
parent
002cca571c
commit
bc3560f1f8
1 changed files with 98 additions and 53 deletions
151
src/tui.rs
151
src/tui.rs
|
|
@ -1,4 +1,6 @@
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
|
use crate::{downloader, md5_utils, mirrors, wget_list};
|
||||||
|
#[cfg(feature = "tui")]
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{self, Event, KeyCode},
|
event::{self, Event, KeyCode},
|
||||||
execute,
|
execute,
|
||||||
|
|
@ -13,16 +15,14 @@ use ratatui::{
|
||||||
widgets::{Block, Borders, List, ListItem, ListState},
|
widgets::{Block, Borders, List, ListItem, ListState},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
use std::collections::HashMap;
|
use std::{
|
||||||
#[cfg(feature = "tui")]
|
collections::HashMap,
|
||||||
use std::io::{self, stdout};
|
io::{self, stdout},
|
||||||
#[cfg(feature = "tui")]
|
path::PathBuf,
|
||||||
use std::path::PathBuf;
|
sync::{Arc, Mutex},
|
||||||
#[cfg(feature = "tui")]
|
thread,
|
||||||
use std::time::{Duration, Instant};
|
time::Duration,
|
||||||
|
};
|
||||||
#[cfg(feature = "tui")]
|
|
||||||
use crate::{downloader, mirrors, wget_list};
|
|
||||||
|
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
fn init_environment() -> PathBuf {
|
fn init_environment() -> PathBuf {
|
||||||
|
|
@ -51,28 +51,22 @@ fn select_mirrors_tui(mirrors: Vec<String>) -> Vec<String> {
|
||||||
terminal
|
terminal
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
let size = f.size();
|
let size = f.size();
|
||||||
let block = Block::default()
|
let items: Vec<ListItem> = mirrors
|
||||||
.title("Select mirrors (space to toggle, Enter to confirm)")
|
.iter()
|
||||||
.borders(Borders::ALL);
|
.enumerate()
|
||||||
f.render_widget(block, size);
|
.map(|(i, mirror)| {
|
||||||
|
let prefix = if selected[i] { "[x] " } else { "[ ] " };
|
||||||
let chunks = Layout::default()
|
ListItem::new(format!("{}{}", prefix, mirror))
|
||||||
.direction(Direction::Vertical)
|
})
|
||||||
.margin(2)
|
.collect();
|
||||||
.constraints(vec![Constraint::Length(3); mirrors.len()])
|
let list = List::new(items)
|
||||||
.split(size);
|
.block(
|
||||||
|
Block::default()
|
||||||
for (i, mirror) in mirrors.iter().enumerate() {
|
.borders(Borders::ALL)
|
||||||
let mut style = Style::default();
|
.title("Select mirrors"),
|
||||||
if Some(i) == state.selected() {
|
)
|
||||||
style = style.bg(Color::Red).fg(Color::White);
|
.highlight_symbol(">> ");
|
||||||
}
|
f.render_stateful_widget(list, size, &mut state);
|
||||||
let prefix = if selected[i] { "[x] " } else { "[ ] " };
|
|
||||||
let list_item = ListItem::new(format!("{}{}", prefix, mirror)).style(style);
|
|
||||||
let list =
|
|
||||||
List::new(vec![list_item]).block(Block::default().borders(Borders::ALL));
|
|
||||||
f.render_widget(list, chunks[i]);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
@ -115,8 +109,8 @@ fn select_mirrors_tui(mirrors: Vec<String>) -> Vec<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
fn download_packages(lfs_sources: &PathBuf) {
|
fn download_packages_tui(lfs_sources: &PathBuf) {
|
||||||
let mirrors_list = mirrors::fetch_mirrors().unwrap_or_else(|_| vec![]);
|
let mirrors_list = mirrors::fetch_mirrors().unwrap_or_default();
|
||||||
let selected_mirrors = select_mirrors_tui(mirrors_list);
|
let selected_mirrors = select_mirrors_tui(mirrors_list);
|
||||||
if selected_mirrors.is_empty() {
|
if selected_mirrors.is_empty() {
|
||||||
println!("⚠️ No mirrors selected!");
|
println!("⚠️ No mirrors selected!");
|
||||||
|
|
@ -130,7 +124,7 @@ fn download_packages(lfs_sources: &PathBuf) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut md5_map = HashMap::new();
|
let mut md5_map = HashMap::new();
|
||||||
if let Ok(md5_content) = crate::md5_utils::get_md5sums() {
|
if let Ok(md5_content) = md5_utils::get_md5sums() {
|
||||||
for line in md5_content.lines() {
|
for line in md5_content.lines() {
|
||||||
if let Some((hash, filename)) = line.split_once(' ') {
|
if let Some((hash, filename)) = line.split_once(' ') {
|
||||||
md5_map.insert(filename.to_string(), hash.to_string());
|
md5_map.insert(filename.to_string(), hash.to_string());
|
||||||
|
|
@ -138,28 +132,75 @@ fn download_packages(lfs_sources: &PathBuf) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for file in wget_list {
|
let download_state: Arc<Mutex<Vec<(String, String)>>> = Arc::new(Mutex::new(
|
||||||
let mut downloaded = false;
|
wget_list
|
||||||
for mirror in &selected_mirrors {
|
.iter()
|
||||||
print!("⬇️ Downloading {} from {} ... ", file, mirror);
|
.map(|f| (f.clone(), "Pending".into()))
|
||||||
io::stdout().flush().unwrap();
|
.collect(),
|
||||||
|
));
|
||||||
|
|
||||||
let start = Instant::now();
|
let download_state_clone = Arc::clone(&download_state);
|
||||||
if downloader::download_files(&file, lfs_sources, Some(mirror.clone()), Some(&md5_map))
|
let mirrors_clone = selected_mirrors.clone();
|
||||||
|
let lfs_sources = lfs_sources.clone();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
for file in &wget_list {
|
||||||
|
let mut status = "Failed".to_string();
|
||||||
|
for mirror in &mirrors_clone {
|
||||||
|
if downloader::download_files(
|
||||||
|
file,
|
||||||
|
&lfs_sources,
|
||||||
|
Some(mirror.clone()),
|
||||||
|
Some(&md5_map),
|
||||||
|
)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
println!("✅ done in {:?}", start.elapsed());
|
status = format!("Downloaded from {}", mirror);
|
||||||
downloaded = true;
|
break;
|
||||||
break;
|
}
|
||||||
} else {
|
}
|
||||||
println!("⚠️ failed, trying next mirror...");
|
let mut state = download_state_clone.lock().unwrap();
|
||||||
|
if let Some(entry) = state.iter_mut().find(|(f, _)| f == file) {
|
||||||
|
entry.1 = status.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !downloaded {
|
});
|
||||||
println!("❌ Failed to download {}", file);
|
|
||||||
|
let mut stdout = stdout();
|
||||||
|
execute!(stdout, EnterAlternateScreen).unwrap();
|
||||||
|
enable_raw_mode().unwrap();
|
||||||
|
let backend = CrosstermBackend::new(stdout);
|
||||||
|
let mut terminal = Terminal::new(backend).unwrap();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
terminal
|
||||||
|
.draw(|f| {
|
||||||
|
let size = f.size();
|
||||||
|
let items: Vec<ListItem> = {
|
||||||
|
let state = download_state.lock().unwrap();
|
||||||
|
state
|
||||||
|
.iter()
|
||||||
|
.map(|(f, s)| ListItem::new(format!("{}: {}", f, s)))
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
let list = List::new(items).block(
|
||||||
|
Block::default()
|
||||||
|
.title("Downloading Packages")
|
||||||
|
.borders(Borders::ALL),
|
||||||
|
);
|
||||||
|
f.render_widget(list, size);
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let state = download_state.lock().unwrap();
|
||||||
|
if state.iter().all(|(_, s)| s != "Pending") {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disable_raw_mode().unwrap();
|
||||||
|
execute!(terminal.backend_mut(), LeaveAlternateScreen).unwrap();
|
||||||
println!("🎉 All downloads finished!");
|
println!("🎉 All downloads finished!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,15 +263,19 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Enter => match state.selected() {
|
KeyCode::Enter => match state.selected() {
|
||||||
Some(0) => lfs_sources = Some(init_environment()),
|
Some(0) => {
|
||||||
|
lfs_sources = Some(init_environment());
|
||||||
|
}
|
||||||
Some(1) => {
|
Some(1) => {
|
||||||
if let Some(ref path) = lfs_sources {
|
if let Some(ref path) = lfs_sources {
|
||||||
download_packages(path);
|
download_packages_tui(path);
|
||||||
} else {
|
} else {
|
||||||
println!("⚠️ Please initialize environment first!");
|
println!("⚠️ Please initialize environment first!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(2) => println!("🔍 Status selected! (TODO)"),
|
Some(2) => {
|
||||||
|
println!("🔍 Status selected! (TODO)");
|
||||||
|
}
|
||||||
Some(3) | _ => break,
|
Some(3) | _ => break,
|
||||||
},
|
},
|
||||||
KeyCode::Esc => break,
|
KeyCode::Esc => break,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue