From e1881782fe9b23288b0bbd5500957d67ae08a472 Mon Sep 17 00:00:00 2001 From: Mikhail Mitrofanov Date: Fri, 8 Aug 2025 23:13:28 +0300 Subject: [PATCH] Uploading all available versions --- src/main.rs | 319 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 213 insertions(+), 106 deletions(-) diff --git a/src/main.rs b/src/main.rs index b7257cb..b3f7a21 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,20 @@ -use clap::{Args, Parser, Subcommand}; +use clap::{Parser, Subcommand}; use dirs_next::config_dir; use reqwest::{Client, multipart}; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; +use std::fmt::{Display, Formatter}; +use std::fs; use std::fs::File; -use std::io::{stdout, BufReader, BufWriter, Read, Write}; +use std::io::{BufReader, BufWriter, Read, Write, stdout}; use std::path::{Path, PathBuf}; use std::process::exit; use std::str::FromStr; use std::string::ToString; use std::time::Duration; -use std::{fs, thread}; /// Nuget packages manager -#[derive(Parser, Debug)] +#[derive(Parser, Debug, Clone)] #[command(version, about, long_about = None)] struct CliArgs { /// Path to .nupkg file to send @@ -30,7 +31,7 @@ struct CliArgs { command: Option, } -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Debug, Clone)] enum Commands { /// does testing things Test { @@ -44,20 +45,25 @@ enum Commands { }, /// Forget personal nuget api key Logout, + /// Show current configuration ShowCfg, + /// Check newer packets versions + Check, + Publish, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] struct Configuration { key: Option, packets: Vec, } -#[derive(Serialize, Deserialize, Debug)] + +#[derive(Serialize, Deserialize, Debug, Clone)] struct Packet { key: String, version: Version, path: String, } -#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] struct Version { major: u32, minor: u32, @@ -79,7 +85,13 @@ impl Version { let c = u32::from_str(parts.pop().unwrap()).unwrap(); let b = u32::from_str(parts.pop().unwrap()).unwrap(); let a = u32::from_str(parts.pop().unwrap()).unwrap(); - Version::new(c, b, a) + Version::new(a, b, c) + } +} + +impl Display for Version { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}.{}.{}", self.major, self.minor, self.patch) } } @@ -113,7 +125,7 @@ impl Packet { let a = u32::from_str(parts.pop().unwrap()).unwrap(); let name = parts.join(".").to_string(); - println!("{}", name); + let dir = path.parent().unwrap().to_str().unwrap(); Packet { key: name, @@ -164,14 +176,15 @@ fn try_remember_packet(path: &Path, config: &mut Configuration) { { Some(packet) => { let version = Version::from_name(&path.file_name().unwrap().to_str().unwrap()); - if packet.version < version { + let more = packet.version < version; + if more { packet.version = version; } } None => config.packets.push(Packet::new(path)), } - println!("{:?}", config) + write_config(config.clone()); } fn conv(str: &mut String) { @@ -179,100 +192,20 @@ fn conv(str: &mut String) { str.insert(0, last); } -#[tokio::main] -async fn main() { - let args = CliArgs::parse(); +async fn send_packet(path: &String, args: CliArgs) -> bool { + let api_key: String; - let config_folder = config_dir() - .expect("Could not determine config directory") - .join("numan"); + let config = &mut read_config(); - if !Path::exists(&config_folder) { - fs::create_dir_all(&config_folder).unwrap() + if let Some(key) = args.key { + api_key = key; + } else if let Some(key) = &config.key { + api_key = (*key).clone(); + } else { + println!("Api key is not defined! Use -k [key]"); + exit(4) } - if let Some(path) = args.path { - let package = Path::new(&path); - if !package.exists() { - println!("File {} does not exist", path); - exit(1); - } - if !package.is_file() { - println!("{} is not a file", path); - exit(2); - } - - if !path.ends_with(".nupkg") { - println!("Invalid file: it is not nuget package"); - exit(3); - } - - let api_key: String; - - let mut config = read_config(); - - if let Some(key) = args.key { - api_key = key; - } else if let Some(key) = &config.key { - api_key = (*key).clone(); - } else { - println!("Api key is not defined! Use -k [key]"); - exit(4) - } - - - let success = send_packet(&path, api_key).await; - - if !args.overlook && success{ - try_remember_packet(&Path::new(&path), &mut config); - } - exit(0); - } - - match &args.command { - Some(Commands::Test { list }) => { - if *list { - println!("Printing testing lists..."); - } else { - println!("Not printing testing lists..."); - } - } - - Some(Commands::Auth { key }) => { - println!("Authenticating... {}", key); - let mut current = read_config(); - - current.key = Some(key.clone()); - - write_config(current); - } - - Some(Commands::ShowCfg) => { - let config = config_path(); - - let mut ctg = String::new(); - - match File::open(config) { - Ok(f) => { - let mut reader = BufReader::new(f); - reader.read_to_string(&mut ctg).unwrap(); - } - _ => { - ctg = "Not configured yet".to_string(); - } - } - println!("{}", ctg); - } - Some(Commands::Logout) => { - let mut config = read_config(); - config.key = None; - write_config(config); - } - None => {} - } -} - -async fn send_packet(path: &String, api_key: String) -> bool { let form = multipart::Form::new().file("", path.clone()).await.unwrap(); let req_thread = tokio::spawn(async { let client = Client::new(); @@ -287,7 +220,12 @@ async fn send_packet(path: &String, api_key: String) -> bool { }); let mut ind = "---->---->---->".to_string(); - print!("{} [{}] https://www.nuget.org/api/v2/package/{}", path, ind, "\x08".repeat(54)); + print!( + "{} [{}] https://www.nuget.org/api/v2/package/{}", + path, + ind, + "\x08".repeat(54) + ); while !req_thread.is_finished() { print!("{}", ind); stdout().flush().unwrap(); @@ -296,19 +234,188 @@ async fn send_packet(path: &String, api_key: String) -> bool { conv(&mut ind); tokio::time::sleep(Duration::from_millis(100)).await; } + println!(); let response = req_thread.await.unwrap(); match response { Ok(response) => { let status = response.status(); - println!("Response: [{}] {:?}", status, response.text().await); + println!("Response: [{}] {}", status, response.text().await.unwrap()); if !status.is_success() { - return false; + // return false; + } + if !args.overlook { + try_remember_packet(&Path::new(&path), config); } true } Err(error) => { - println!("Error: {:?}", error.status().unwrap()); + println!("Error: {:?}", error.status()); false } } } + +#[tokio::main] +async fn main() { + let args = CliArgs::parse(); + let args_clone = args.clone(); + let config_folder = config_dir() + .expect("Could not determine config directory") + .join("numan"); + + if !Path::exists(&config_folder) { + fs::create_dir_all(&config_folder).unwrap() + } + + if let Some(_) = args.path { + single_packet(args_clone).await; + return; + } + + match &args.command { + Some(Commands::Test { list }) => { + if *list { + println!("Printing testing lists..."); + } else { + println!("Not printing testing lists..."); + } + } + + Some(Commands::Auth { key }) => { + regular_key(key); + } + + Some(Commands::ShowCfg) => { + show_config(); + } + Some(Commands::Logout) => { + logout(); + } + Some(Commands::Check) => check(), + Some(Commands::Publish) => { + publish(&args_clone).await; + } + None => {} + } +} + +async fn publish(args: &CliArgs) { + println!("Publishing newer versions"); + let config = read_config(); + for packet in config.packets { + let path = packet.path; + print!("{} ({}) Current: [{}]", packet.key, path, packet.version); + let versions = find_packets(Path::new(&path)); + if let Some(newer) = versions.iter().find(|v| v.version > packet.version) { + println!(" -> [{}]", newer.version); + println!("{:?}", newer); + send_packet( + &format!("{}/{}.{}.nupkg", newer.path, newer.key, newer.version), + args.clone(), + ) + .await; + } else { + println!() + } + } +} + +async fn single_packet(args: CliArgs) { + let args_clone = args.clone(); + let path = args_clone.clone().path.unwrap(); + let package = Path::new(&path); + if !package.exists() { + println!("File {} does not exist", path); + exit(1); + } + if !package.is_file() { + println!("{} is not a file", path); + exit(2); + } + + if !path.ends_with(".nupkg") { + println!("Invalid file: it is not nuget package"); + exit(3); + } + + send_packet(&path, args_clone).await; + + exit(0); +} + +fn regular_key(key: &String) { + println!("Authenticating... {}", key); + let mut current = read_config(); + + current.key = Some(key.clone()); + + write_config(current); +} + +fn show_config() { + let config = config_path(); + + let mut ctg = String::new(); + + match File::open(config) { + Ok(f) => { + let mut reader = BufReader::new(f); + reader.read_to_string(&mut ctg).unwrap(); + } + _ => { + ctg = "Not configured yet".to_string(); + } + } + println!("{}", ctg); +} + +fn logout() { + let mut config = read_config(); + config.key = None; + write_config(config); +} + +fn find_packets(path: &Path) -> Vec { + let mut packets: Vec = Vec::new(); + + for r in path.read_dir().unwrap() { + if let Ok(entry) = r { + if !entry.path().is_file() { + continue; + } + let name = entry.file_name().to_str().unwrap().to_string(); + if !name.ends_with(".nupkg") { + continue; + } + + packets.push(Packet::new(entry.path().as_path())) + } + } + + packets +} + +fn check() { + println!("Checking for newer versions"); + let config = read_config(); + println!("Found {} packets", config.packets.len()); + let mut updates = 0_u32; + for packet in config.packets { + let path = packet.path; + print!("{} ({}) Current: [{}]", packet.key, path, packet.version); + let versions = find_packets(Path::new(&path)); + if let Some(newer) = versions.iter().find(|v| v.version > packet.version) { + println!(" -> [{}]", newer.version); + updates += 1; + } else { + println!(" Lastest") + } + } + + if updates > 0 { + println!("{} Updates found", updates); + } + if updates == 0 { + println!("No newer versions found."); + } +}