diff --git a/Cargo.lock b/Cargo.lock index 3b18a13..08bdde6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,6 +86,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "byte-unit" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55390dbbf21ce70683f3e926dace00a21da373e35e44a60cafd232e3e9bf2041" + [[package]] name = "byteorder" version = "1.3.4" @@ -323,6 +329,7 @@ name = "gitea-release" version = "0.1.0" dependencies = [ "anyhow", + "byte-unit", "cli-table", "comrak", "git2", diff --git a/Cargo.toml b/Cargo.toml index d4637dd..2ab2c4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] anyhow = "1.0" +byte-unit = "3" cli-table = "0.3" comrak = "0.7" git2 = "0.13" @@ -18,3 +19,6 @@ serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } structopt = { version = "0.3", default-features = false } tokio = { version = "0.2", features = ["macros"] } + +[profile.release] +lto = true diff --git a/TODO.org b/TODO.org index 213e5be..283f018 100644 --- a/TODO.org +++ b/TODO.org @@ -3,7 +3,8 @@ * Commands ** DONE delete CLOSED: [2020-05-30 Sat 12:23] -** TODO download +** DONE download + CLOSED: [2020-05-30 Sat 14:27] ** TODO edit ** DONE info CLOSED: [2020-05-30 Sat 10:52] diff --git a/src/cmd/download.rs b/src/cmd/download.rs new file mode 100644 index 0000000..80c8d12 --- /dev/null +++ b/src/cmd/download.rs @@ -0,0 +1,68 @@ +use crate::{gitea::*, *}; +use anyhow::{anyhow, Result}; +use cli_table::{Cell, Row, Table}; +use std::fs::File; +use std::io::Write; + +pub(crate) async fn run(common: Common, fname: Option) -> Result<()> { + if common.tag.is_none() { + return Err(anyhow!("requires --tag")); + } + let cli = client(&common)?; + let release = get_release_by_tag( + &cli, + &common.server, + &common.owner, + &common.repo, + &common.tag.unwrap(), + ) + .await?; + let attachments = get_attachments_for_release( + &cli, + &common.server, + &common.owner, + &common.repo, + &release.id, + ) + .await?; + + match fname { + None => { + let mut rows: Vec = vec![Row::new(vec![ + Cell::new(&"name", Default::default()), + Cell::new(&"size", Default::default()), + Cell::new(&"url", Default::default()), + ])]; + for attachment in attachments { + rows.push(attachment.row()) + } + + let table = Table::new(rows, Default::default())?; + table.print_stdout()?; + + Ok(()) + } + + Some(fname) => { + let mut url: Option = None; + let fname = fname.into_os_string().into_string().unwrap(); + + for attachment in attachments { + if &fname == &attachment.name { + url = Some(attachment.browser_download_url); + } + } + + if url.is_none() { + return Err(anyhow!("no attachment named {}", fname)); + } + + let data = &cli.get(url.unwrap().as_str()).send().await?.bytes().await?; + let mut fout = File::create(&fname)?; + + fout.write(data)?; + + Ok(()) + } + } +} diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 2a2ce3a..f9de362 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -1,2 +1,3 @@ pub(crate) mod delete; +pub(crate) mod download; pub(crate) mod info; diff --git a/src/gitea.rs b/src/gitea.rs index 030a552..4570bb8 100644 --- a/src/gitea.rs +++ b/src/gitea.rs @@ -85,3 +85,53 @@ pub(crate) async fn get_release_by_tag( Ok(release.unwrap()) } + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Attachment { + pub id: i64, + pub name: String, + pub size: i64, + pub download_count: i64, + pub created_at: String, + pub uuid: String, + pub browser_download_url: String, +} + +impl Attachment { + pub fn row(&self) -> Row { + let size = { + let bytes = byte_unit::Byte::from_bytes(self.size as u128); + let unit = bytes.get_appropriate_unit(false); + unit.to_string() + }; + + Row::new(vec![ + Cell::new(&self.name, Default::default()), + Cell::new(&size, Default::default()), + Cell::new(&self.browser_download_url, Default::default()), + ]) + } +} + +pub(crate) async fn get_attachments_for_release( + cli: &reqwest::Client, + server: &String, + owner: &String, + repo: &String, + id: &i64, +) -> Result> { + let attachments: Vec = cli + .get( + format!( + "{}/api/v1/repos/{}/{}/releases/{}/assets", + server, owner, repo, id + ) + .as_str(), + ) + .send() + .await? + .json() + .await?; + + Ok(attachments) +} diff --git a/src/main.rs b/src/main.rs index 007240b..bed0bb9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,7 @@ pub(crate) struct Common { } // Name your user agent after your app? -static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); +static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); pub(crate) fn client(c: &Common) -> Result { let mut headers = header::HeaderMap::new(); @@ -69,7 +69,7 @@ pub(crate) enum Cmd { common: Common, /// Folder to download release artifacts to #[structopt(short, long)] - fname: PathBuf, + fname: Option, }, /// Edits a release's description, name and other flags Edit { @@ -124,6 +124,7 @@ async fn main() -> Result<()> { match cmd { Cmd::Delete { common } => cmd::delete::run(common).await, + Cmd::Download { common, fname } => cmd::download::run(common, fname).await, Cmd::Info { common, json } => cmd::info::run(common, json).await, _ => Err(anyhow!("not implemented yet")),