From 041f68503121e9cece206045d65d332c2f6a0b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20Kaan=20G=C3=9CM=C3=9C=C5=9E?= <96421894+Tahinli@users.noreply.github.com> Date: Fri, 22 Nov 2024 22:31:19 +0300 Subject: [PATCH] feat: :sparkles: user interactions -> read_package, read_all_packages, install_package, delete_package --- client/src/file.rs | 101 +++++++++++++++++++++++++++++++++--- client/src/lib.rs | 1 + client/src/main.rs | 3 ++ client/src/test.rs | 3 ++ client/src/user.rs | 94 +++++++++++++++++++++++++++++++++ server/src/package/utils.rs | 55 +++++++++++++++++--- 6 files changed, 243 insertions(+), 14 deletions(-) create mode 100644 client/src/user.rs diff --git a/client/src/file.rs b/client/src/file.rs index 22af5c2..3e10382 100644 --- a/client/src/file.rs +++ b/client/src/file.rs @@ -1,22 +1,27 @@ use tokio::{ - fs::{read_dir, remove_file, File}, - io::AsyncWriteExt, + fs::{read_dir, remove_file, DirBuilder, File, OpenOptions}, + io::{AsyncReadExt, AsyncWriteExt}, }; -const FILE_LOCATION: &str = "./packages/"; +const PACKAGE_PATH: &str = "./packages/"; pub async fn save_package(package_name: String, package_data: &[u8]) -> Result<(), std::io::Error> { - let file_location = format!("{}{}", FILE_LOCATION, package_name); + let file_location = format!("{}{}", PACKAGE_PATH, package_name); + if let Err(_) = File::open(PACKAGE_PATH).await { + DirBuilder::new().create(PACKAGE_PATH).await?; + } let mut package_file = File::create(file_location).await?; - package_file.write_all(package_data).await + package_file.write_all(package_data).await?; + save_metadata(package_name).await } pub async fn delete_package(package_name: String) -> Result<(), std::io::Error> { - remove_file(format!("{}{}", FILE_LOCATION, package_name)).await + remove_file(format!("{}{}", PACKAGE_PATH, package_name)).await?; + delete_metadata(package_name).await } pub async fn list_packages() -> Option> { - let mut folder_elements = read_dir(FILE_LOCATION).await.ok()?; + let mut folder_elements = read_dir(PACKAGE_PATH).await.ok()?; let mut packages = vec![]; loop { match folder_elements.next_entry().await.ok()? { @@ -30,3 +35,85 @@ pub async fn list_packages() -> Option> { } Some(packages) } + +async fn search_metadata(package_name: String) -> Result, std::io::Error> { + let file_location = format!("{}{}", PACKAGE_PATH, "metadata.txt"); + let mut file = match File::open(file_location.clone()).await { + Ok(file) => file, + Err(_) => { + return Ok(None); + } + }; + let mut file_data = String::default(); + _ = file.read_to_string(&mut file_data).await?; + + for (index, line) in file_data.lines().enumerate() { + let line = line.trim_end(); + if line == package_name { + return Ok(Some(index)); + } + } + Ok(None) +} + +async fn search_and_retrieve_metadata( + package_name: String, +) -> Result<(Option, Vec), std::io::Error> { + let file_location = format!("{}{}", PACKAGE_PATH, "metadata.txt"); + let mut file = match File::open(file_location.clone()).await { + Ok(file) => file, + Err(_) => { + return Ok((None, vec![])); + } + }; + let mut file_data = String::default(); + _ = file.read_to_string(&mut file_data).await?; + let mut lines = vec![]; + let mut target_index = None; + for (index, line) in file_data.lines().enumerate() { + if line.trim_end() == package_name { + target_index = Some(index); + } + lines.push(line.to_string()); + } + Ok((target_index, lines)) +} + +async fn save_metadata(package_name: String) -> Result<(), std::io::Error> { + let searched = search_metadata(package_name.to_owned()).await?; + if searched.is_none() { + let file_location = format!("{}{}", PACKAGE_PATH, "metadata.txt"); + let mut file = OpenOptions::new() + .append(true) + .create(true) + .write(true) + .open(file_location) + .await?; + file.write_all(package_name.as_bytes()).await?; + file.write_all(b"\n").await?; + } + Ok(()) +} + +async fn delete_metadata(package_name: String) -> Result<(), std::io::Error> { + let (target_index, mut file_data) = search_and_retrieve_metadata(package_name).await?; + let target_index = match target_index { + Some(target_index) => target_index, + None => return Err(std::io::ErrorKind::NotFound.into()), + }; + + let file_location = format!("{}{}", PACKAGE_PATH, "metadata.txt"); + let mut file = OpenOptions::new() + .append(false) + .create(false) + .write(true) + .open(file_location) + .await?; + file_data.remove(target_index); + + if file_data.is_empty() { + file.set_len(0).await + } else { + file.write_all(&file_data.concat().as_bytes()).await + } +} diff --git a/client/src/lib.rs b/client/src/lib.rs index 9565a17..2a7fb9a 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -2,3 +2,4 @@ pub mod file; pub mod package; pub mod request; mod test; +pub mod user; diff --git a/client/src/main.rs b/client/src/main.rs index 7e3d561..6c42af2 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -1,4 +1,7 @@ +use rust_package_manager_client::user::user_interaction; + #[tokio::main] async fn main() { println!("Hello, world!"); + user_interaction().await } diff --git a/client/src/test.rs b/client/src/test.rs index ba2f611..14641d8 100644 --- a/client/src/test.rs +++ b/client/src/test.rs @@ -40,6 +40,7 @@ async fn test_save_package() { use crate::file::save_package; let saved_or_not = save_package("test_package".to_string(), &package_data.unwrap()[..]).await; + println!("{:#?}", saved_or_not); assert_eq!(saved_or_not.is_ok(), true); } @@ -54,10 +55,12 @@ async fn test_delete_package() { use crate::file::save_package; let saved_or_not = save_package("test_package".to_string(), &package_data.unwrap()[..]).await; + println!("{:#?}", saved_or_not); assert_eq!(saved_or_not.is_ok(), true); use crate::file::delete_package; let deleted_or_not = delete_package("test_package".to_string()).await; + println!("{:#?}", deleted_or_not); assert_eq!(deleted_or_not.is_ok(), true); } diff --git a/client/src/user.rs b/client/src/user.rs new file mode 100644 index 0000000..d55bc39 --- /dev/null +++ b/client/src/user.rs @@ -0,0 +1,94 @@ +use std::env; + +fn env_collector() -> Vec { + let mut env_values = env::args().collect::>(); + env_values.remove(0); + env_values +} + +pub async fn user_interaction() { + let env_values = env_collector(); + for (i, env_value) in env_values.iter().enumerate() { + match env_value.as_str() { + "read_all_packages" => { + read_all_packages().await; + return; + } + "read_package" => { + let package_name = match env_values.get(i + 1) { + Some(package_name) => package_name, + None => { + eprintln!("Length is not enough"); + return; + } + }; + read_package(package_name).await; + return; + } + "install_package" => { + let package_name = match env_values.get(i + 1) { + Some(package_name) => package_name, + None => { + eprintln!("Length is not enough"); + return; + } + }; + install_package(package_name).await; + return; + } + "delete_package" => { + let package_name = match env_values.get(i + 1) { + Some(package_name) => package_name, + None => { + eprintln!("Length is not enough"); + return; + } + }; + delete_package(package_name).await; + return; + } + _ => { + eprintln!("Need an Argument"); + return; + } + } + } +} + +async fn read_all_packages() { + let packages = crate::request::read_all_packages().await; + match packages { + Ok(packages) => { + for package in packages { + println!("{}", package.get_name()); + } + } + Err(err_val) => eprintln!("Error: Read All Packages | {}", err_val), + } +} + +async fn read_package(package_name: &String) { + match crate::request::read_package(package_name.to_owned()).await { + Some(package) => println!("{:#?}", package), + None => eprintln!("Error: Package Name is Invalid"), + } +} + +async fn install_package(package_name: &String) { + match crate::request::download_package(package_name.to_owned()).await { + Some(package_data) => { + match crate::file::save_package(package_name.to_owned(), &package_data).await { + Ok(_) => println!("{} is Installed", package_name), + Err(err_val) => eprintln!("Error: Save Package | {}", err_val), + } + } + None => eprintln!("Error: Download Package"), + } +} + +async fn delete_package(package_name: &String) { + match crate::file::delete_package(package_name.to_owned()).await { + Ok(_) => println!("{} is Deleted", package_name), + Err(err_val) => eprintln!("Error: Delete Package | {}", err_val), + } +} diff --git a/server/src/package/utils.rs b/server/src/package/utils.rs index b8e9b7e..81968f3 100644 --- a/server/src/package/utils.rs +++ b/server/src/package/utils.rs @@ -1,5 +1,8 @@ use axum::extract::Multipart; -use tokio::{fs::File, io::AsyncWriteExt}; +use tokio::{ + fs::{DirBuilder, File}, + io::AsyncWriteExt, +}; use tokio_util::io::ReaderStream; use crate::{database, routing, PACKAGE_PATH}; @@ -62,24 +65,62 @@ pub async fn delete_package(package_name: String) -> Option { pub async fn download_package(package_name: String) -> Option> { let package = crate::package::utils::read_package(package_name).await?; - let package_file_stream = package.serve().await.ok()?; + let package_file_stream = match package.serve().await { + Ok(package_file_stream) => package_file_stream, + Err(err_val) => { + eprintln!("Error: Download | File Stream | {}", err_val); + return None; + } + }; Some(package_file_stream) } pub async fn upload_package(mut package_file: Multipart) -> Option { - let package_file_part = package_file.next_field().await.ok()??; + let package_file_part = match package_file.next_field().await { + Ok(field_unchecked) => field_unchecked?, + Err(err_val) => { + eprintln!("Error: Upload | Multipart | {}", err_val); + return None; + } + }; let package_file_name = package_file_part.name()?.to_string(); let file_location = format!("{}/{}", PACKAGE_PATH, package_file_name); + if let Err(_) = File::open(PACKAGE_PATH).await { + if let Err(err_val) = DirBuilder::new().create(PACKAGE_PATH).await { + eprintln!("Error: Upload | Create Directory | {}", err_val); + return None; + } + } - let package_file_data = package_file_part.bytes().await.ok()?; + let package_file_data = match package_file_part.bytes().await { + Ok(package_file_data) => package_file_data, + Err(err_val) => { + eprintln!("Error: Upload | Multipart Bytes | {}", err_val); + return None; + } + }; let mut package = crate::package::utils::read_package(package_file_name).await?; - let mut file_descriptor = File::create(&file_location).await.ok()?; - file_descriptor.write_all(&package_file_data).await.ok()?; + let mut file_descriptor = match File::create(&file_location).await { + Ok(file_descriptor) => file_descriptor, + Err(err_val) => { + eprintln!( + "Error: Upload | File Descriptor | {} |{}", + file_location, err_val + ); + return None; + } + }; + if let Err(err_val) = file_descriptor.write_all(&package_file_data).await { + eprintln!("Error: Upload | File Descriptor Write | {}", err_val); + return None; + } package.set_location(&file_location.to_string()); - package.set_hash().await.ok()?; + if let Err(err_val) = package.set_hash().await { + eprintln!("Error: Hash | {}", err_val); + } let package = crate::package::utils::update_package(package.get_name(), package).await?; Some(package)