diff --git a/desktop/src-tauri/src/cli.rs b/desktop/src-tauri/src/cli.rs index f741cab..87638dc 100644 --- a/desktop/src-tauri/src/cli.rs +++ b/desktop/src-tauri/src/cli.rs @@ -1,10 +1,10 @@ -use std::fs::File; -use std::io::Read; +use std::io::{Read, Write}; use std::path::PathBuf; +use std::{fs::File, path::Path}; use clap::Clap; use indicatif::{ProgressBar, ProgressStyle}; -use libnspire::{PID, PID_CX2, VID}; +use libnspire::{dir::EntryType, PID, PID_CX2, VID}; #[derive(Clap, Debug)] #[clap(author, about, version)] @@ -16,11 +16,12 @@ struct Opt { #[derive(Clap, Debug)] enum SubCommand { Upload(Upload), + Download(Download), Mkdir(Mkdir), Rmdir(Rmdir), + Ls(Ls), /// View license information License, - // Download(Download), } /// Upload files to the calculator @@ -33,30 +34,37 @@ struct Upload { dest: String, } -// Create a directory +/// Download files from the calculator +#[derive(Clap, Debug)] +struct Download { + /// Files to download + #[clap(required = true)] + files: Vec, + /// Destination path + #[clap(required = true, parse(from_os_str))] + dest: PathBuf, +} + +/// Create a directory #[derive(Clap, Debug)] struct Mkdir { #[clap(required = true)] - path: String + path: String, } -// Delete a directory +/// Delete a directory #[derive(Clap, Debug)] struct Rmdir { #[clap(required = true)] - path: String + path: String, } -// /// Download files from the calculator -// #[derive(Clap, Debug)] -// struct Download { -// /// Files to download -// #[clap(required = true)] -// files: Vec, -// /// Destination path -// #[clap(parse(from_os_str))] -// dest: PathBuf, -// } +/// List the contents of a directory +#[derive(Clap, Debug)] +struct Ls { + #[clap(required = true)] + path: String, +} fn get_dev() -> Option> { rusb::devices() @@ -106,20 +114,80 @@ pub fn run() -> bool { if dest.ends_with('/') { dest.remove(dest.len() - 1); } - let res = handle - .write_file(&format!("{}/{}", dest, name), &buf, &mut |remaining| { - bar.set_position((buf.len() - remaining) as u64) - }); + let res = handle.write_file(&format!("{}/{}", dest, name), &buf, &mut |remaining| { + bar.set_position((buf.len() - remaining) as u64) + }); - match res { - Ok(_) => { - println!("Upload {}: Ok", dest); - } - Err(error) => { - bar.abandon_with_message(&format!("Failed: {}", error)); + match res { + Ok(_) => { + println!("Upload {}: Ok", dest); + } + Err(error) => { + bar.abandon_with_message(&format!("Failed: {}", error)); + } + } + bar.finish(); + } + } else { + eprintln!("Couldn't find any device"); + } + } + SubCommand::Download(Download { dest, files }) => { + if let Some(handle) = get_dev() { + for file in files { + let attr = handle.file_attr(&file); + match attr { + Ok(attr) => { + let path = Path::new(&file); + let dest_path = Path::join(&dest, path.file_name().unwrap().to_str().unwrap()); + match File::create(dest_path) { + Ok(mut dest_file) => { + let mut buf = vec![0u8; attr.size() as usize]; + + let bar = ProgressBar::new(buf.len() as u64); + bar.set_style(ProgressStyle::default_bar().template("{spinner:.green} {msg} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})")); + bar.set_message(&format!( + "Download {}", + path.file_name().unwrap().to_str().unwrap() + )); + bar.enable_steady_tick(100); + + let len = buf.len(); + + let res = handle.read_file(&file, &mut buf, &mut |remaining| { + bar.set_position((len - remaining) as u64); + }); + + match res { + Ok(_) => { + bar.set_message("Writing file to disk"); + + match dest_file.write_all(&buf) { + Ok(_) => { + bar.finish_with_message("Transfer completed"); + } + Err(error) => { + bar.abandon_with_message(&format!( + "Failed to write file to disk: {}", + error + )); + } + } + } + Err(error) => { + bar.abandon_with_message(&format!("Failed to transfer file: {}", error)) + } + } + } + Err(error) => { + eprintln!("Failed to open destination file: {}", error); + } } } - bar.finish(); + Err(error) => { + eprintln!("Failed to read file info: {}", error); + } + } } } else { eprintln!("Couldn't find any device"); @@ -153,6 +221,27 @@ pub fn run() -> bool { eprintln!("Couldn't find any device"); } } + SubCommand::Ls(Ls { path }) => { + if let Some(handle) = get_dev() { + match handle.list_dir(&path) { + Ok(dir_list) => { + for i in 0..dir_list.len() { + let item = dir_list.get(i).unwrap(); + let mut item_name = String::from(item.name().to_str().unwrap()); + if item.entry_type() == EntryType::Directory { + item_name += "/"; + } + println!("{}", item_name); + } + } + Err(error) => { + eprintln!("Failed to list directory: {}", error); + } + } + } else { + eprintln!("Couldn't find any device"); + } + } SubCommand::License => { println!("{}", include_str!("../../LICENSE")); println!(include_str!("NOTICE.txt"), env!("CARGO_PKG_REPOSITORY"));