diff --git a/Cargo.toml b/Cargo.toml index febccc4..4fd569c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "gt-tool" -version = "1.0.0" +version = "2.2.0" edition = "2024" [dependencies] clap = { version = "4.0.7", features = ["derive", "env"] } +colored = "2.0.0" +itertools = "0.10.0" reqwest = { version = "0.11.13", features = ["json", "stream", "multipart"] } serde = { version = "1.0.152", features = ["derive"] } tokio = { version = "1.24.2", features = ["macros", "rt-multi-thread"] } diff --git a/src/api/release.rs b/src/api/release.rs index 4250d3f..fa2f8c4 100644 --- a/src/api/release.rs +++ b/src/api/release.rs @@ -1,12 +1,9 @@ - use crate::{ Result, - structs::{ - release::{CreateReleaseOption, Release}, - }, + structs::release::{CreateReleaseOption, Release}, }; -pub fn get_release(id: u64) -> Result { +pub fn get_release(_id: u64) -> Result { todo!(); } pub fn get_latest_release() -> Result { @@ -20,7 +17,7 @@ pub async fn list_releases( ) -> Result> { let request_url = format!("{gitea_url}/api/v1/repos/{repo}/releases/"); let req = client.get(request_url).send().await; - let response = req.map_err(|reqwest_err| crate::Error::WrappedReqwestErr(reqwest_err))?; + let response = req.map_err(crate::Error::WrappedReqwestErr)?; if response.status().is_success() { let release_list = response .json::>() @@ -50,22 +47,22 @@ pub async fn create_release( .json(&submission) .send() .await - .map_err(|e| crate::Error::from(e))?; + .map_err(crate::Error::from)?; if response.status().is_success() { let new_release = response .json::() .await - .map_err(|e| crate::Error::from(e))?; + .map_err(crate::Error::from)?; return Ok(new_release); } else if response.status().is_client_error() { let mesg = crate::decode_client_error(response).await?; - return Err(crate::Error::ApiErrorMessage(mesg)) + return Err(crate::Error::ApiErrorMessage(mesg)); } panic!("Reached end of create_release without matching a return path"); } -pub fn edit_release(id: u64) -> Result { +pub fn edit_release(_id: u64) -> Result { todo!(); } -pub fn delete_release(id: u64) -> Result<()> { +pub fn delete_release(_id: u64) -> Result<()> { todo!(); } diff --git a/src/api/release_attachment.rs b/src/api/release_attachment.rs index ae626ef..e5b7b61 100644 --- a/src/api/release_attachment.rs +++ b/src/api/release_attachment.rs @@ -22,8 +22,10 @@ pub async fn create_release_attachment( Ok(false) => return Err(crate::Error::NoSuchFile), Err(e) => { eprintln!("Uh oh! The file-exists check couldn't be done: {e}"); - panic!("TODO: Deal with scenario where the file's existence cannot be checked (e.g.: no permission)"); - }, + panic!( + "TODO: Deal with scenario where the file's existence cannot be checked (e.g.: no permission)" + ); + } } println!("Uploading file {}", &file); @@ -44,7 +46,7 @@ pub async fn create_release_attachment( let attachment_desc = response .json::() .await - .map_err(|e| crate::Error::from(e))?; + .map_err(crate::Error::from)?; return Ok(attachment_desc); } else if response.status().is_client_error() { let mesg = crate::decode_client_error(response).await?; diff --git a/src/lib.rs b/src/lib.rs index 42cac0a..70a11a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,13 +10,11 @@ pub struct ApiError { url: String, } -pub (crate) async fn decode_client_error(response: reqwest::Response) -> Result { +pub(crate) async fn decode_client_error(response: reqwest::Response) -> Result { response .json::() .await - .map_err(|reqwest_err| { - crate::Error::WrappedReqwestErr(reqwest_err) - }) + .map_err(crate::Error::WrappedReqwestErr) } #[derive(Debug)] diff --git a/src/main.rs b/src/main.rs index 555b843..1c98e8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ - use std::path; use gt_tool::cli::Args; @@ -22,10 +21,7 @@ async fn main() -> Result<(), gt_tool::Error> { headers.append("Authorization", token.parse().unwrap()); } let client = reqwest::Client::builder() - .user_agent(format!( - "gt-tools-agent-{}", - env!("CARGO_PKG_VERSION") - )) + .user_agent(format!("gt-tools-agent-{}", env!("CARGO_PKG_VERSION"))) .default_headers(headers) .build()?; @@ -33,9 +29,15 @@ async fn main() -> Result<(), gt_tool::Error> { gt_tool::cli::Commands::ListReleases => { let releases = gt_tool::api::release::list_releases(&client, &args.gitea_url, &args.repo).await?; - for release in releases { - println!("{:?}", release); - } + // Print in reverse order so the newest items are closest to the + // user's command prompt. Otherwise the newest item scrolls off the + // screen and can't be seen. + itertools::Itertools::intersperse( + releases.iter().rev().map(|release| release.colorized()), + String::from(""), + ) + .map(|release| println!("{}", release)) + .fold((), |_, _| ()); } gt_tool::cli::Commands::CreateRelease { name, @@ -52,13 +54,8 @@ async fn main() -> Result<(), gt_tool::Error> { tag_name, target_commitish, }; - gt_tool::api::release::create_release( - &client, - &args.gitea_url, - &args.repo, - submission, - ) - .await?; + gt_tool::api::release::create_release(&client, &args.gitea_url, &args.repo, submission) + .await?; } gt_tool::cli::Commands::UploadRelease { tag_name, @@ -88,8 +85,10 @@ async fn main() -> Result<(), gt_tool::Error> { Ok(false) => return Err(gt_tool::Error::NoSuchFile), Err(e) => { eprintln!("Uh oh! The file-exists check couldn't be done: {e}"); - panic!("TODO: Deal with scenario where the file's existence cannot be checked (e.g.: no permission)"); - }, + panic!( + "TODO: Deal with scenario where the file's existence cannot be checked (e.g.: no permission)" + ); + } } } for file in files { @@ -143,5 +142,5 @@ fn match_release_by_tag(tag: &String, releases: Vec) -> Option } } } - return release; + release } diff --git a/src/structs/release.rs b/src/structs/release.rs index 1f8a33b..9ed537e 100644 --- a/src/structs/release.rs +++ b/src/structs/release.rs @@ -1,3 +1,4 @@ +use colored::Colorize; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Serialize)] @@ -19,6 +20,36 @@ pub struct Release { author: Author, } +impl Release { + pub fn colorized(&self) -> String { + let tag = "Tag:".green().bold(); + let name = "Name:".green(); + let published = "Published:".bright_green(); + let created = "Created:".green().dimmed(); + let author = "Author:".blue(); + let body = if !self.body.is_empty() { + &self.body.white() + } else { + &String::from("(empty body)").dimmed() + }; + + format!( + "{tag} {} +{name} {} + {} +{published} {} ({created} {}) +{author} {} ({})", + self.tag_name.bold(), + self.name, + body, + self.published_at, + self.created_at.dimmed(), + self.author.login, + self.author.email, + ) + } +} + #[derive(Debug, Deserialize, Serialize)] pub struct Author { id: usize,