Uploading all available versions

This commit is contained in:
2025-08-08 23:13:28 +03:00
parent af84517ce3
commit e1881782fe
+213 -106
View File
@@ -1,19 +1,20 @@
use clap::{Args, Parser, Subcommand}; use clap::{Parser, Subcommand};
use dirs_next::config_dir; use dirs_next::config_dir;
use reqwest::{Client, multipart}; use reqwest::{Client, multipart};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt::{Display, Formatter};
use std::fs;
use std::fs::File; 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::path::{Path, PathBuf};
use std::process::exit; use std::process::exit;
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use std::time::Duration; use std::time::Duration;
use std::{fs, thread};
/// Nuget packages manager /// Nuget packages manager
#[derive(Parser, Debug)] #[derive(Parser, Debug, Clone)]
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
struct CliArgs { struct CliArgs {
/// Path to .nupkg file to send /// Path to .nupkg file to send
@@ -30,7 +31,7 @@ struct CliArgs {
command: Option<Commands>, command: Option<Commands>,
} }
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug, Clone)]
enum Commands { enum Commands {
/// does testing things /// does testing things
Test { Test {
@@ -44,20 +45,25 @@ enum Commands {
}, },
/// Forget personal nuget api key /// Forget personal nuget api key
Logout, Logout,
/// Show current configuration
ShowCfg, ShowCfg,
/// Check newer packets versions
Check,
Publish,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
struct Configuration { struct Configuration {
key: Option<String>, key: Option<String>,
packets: Vec<Packet>, packets: Vec<Packet>,
} }
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Packet { struct Packet {
key: String, key: String,
version: Version, version: Version,
path: String, path: String,
} }
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
struct Version { struct Version {
major: u32, major: u32,
minor: u32, minor: u32,
@@ -79,7 +85,13 @@ impl Version {
let c = u32::from_str(parts.pop().unwrap()).unwrap(); let c = u32::from_str(parts.pop().unwrap()).unwrap();
let b = 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(); 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 a = u32::from_str(parts.pop().unwrap()).unwrap();
let name = parts.join(".").to_string(); let name = parts.join(".").to_string();
println!("{}", name);
let dir = path.parent().unwrap().to_str().unwrap(); let dir = path.parent().unwrap().to_str().unwrap();
Packet { Packet {
key: name, key: name,
@@ -164,14 +176,15 @@ fn try_remember_packet(path: &Path, config: &mut Configuration) {
{ {
Some(packet) => { Some(packet) => {
let version = Version::from_name(&path.file_name().unwrap().to_str().unwrap()); 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; packet.version = version;
} }
} }
None => config.packets.push(Packet::new(path)), None => config.packets.push(Packet::new(path)),
} }
println!("{:?}", config) write_config(config.clone());
} }
fn conv(str: &mut String) { fn conv(str: &mut String) {
@@ -179,100 +192,20 @@ fn conv(str: &mut String) {
str.insert(0, last); str.insert(0, last);
} }
#[tokio::main] async fn send_packet(path: &String, args: CliArgs) -> bool {
async fn main() { let api_key: String;
let args = CliArgs::parse();
let config_folder = config_dir() let config = &mut read_config();
.expect("Could not determine config directory")
.join("numan");
if !Path::exists(&config_folder) { if let Some(key) = args.key {
fs::create_dir_all(&config_folder).unwrap() 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 form = multipart::Form::new().file("", path.clone()).await.unwrap();
let req_thread = tokio::spawn(async { let req_thread = tokio::spawn(async {
let client = Client::new(); let client = Client::new();
@@ -287,7 +220,12 @@ async fn send_packet(path: &String, api_key: String) -> bool {
}); });
let mut ind = "---->---->---->".to_string(); 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() { while !req_thread.is_finished() {
print!("{}", ind); print!("{}", ind);
stdout().flush().unwrap(); stdout().flush().unwrap();
@@ -296,19 +234,188 @@ async fn send_packet(path: &String, api_key: String) -> bool {
conv(&mut ind); conv(&mut ind);
tokio::time::sleep(Duration::from_millis(100)).await; tokio::time::sleep(Duration::from_millis(100)).await;
} }
println!();
let response = req_thread.await.unwrap(); let response = req_thread.await.unwrap();
match response { match response {
Ok(response) => { Ok(response) => {
let status = response.status(); let status = response.status();
println!("Response: [{}] {:?}", status, response.text().await); println!("Response: [{}] {}", status, response.text().await.unwrap());
if !status.is_success() { if !status.is_success() {
return false; // return false;
}
if !args.overlook {
try_remember_packet(&Path::new(&path), config);
} }
true true
} }
Err(error) => { Err(error) => {
println!("Error: {:?}", error.status().unwrap()); println!("Error: {:?}", error.status());
false 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<Packet> {
let mut packets: Vec<Packet> = 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.");
}
}