diff --git a/crates/aiken-project/src/github/mod.rs b/crates/aiken-project/src/github/mod.rs index c426b23eb..390aa570b 100644 --- a/crates/aiken-project/src/github/mod.rs +++ b/crates/aiken-project/src/github/mod.rs @@ -1 +1,2 @@ +pub mod payload; pub mod repo; diff --git a/crates/aiken-project/src/github/payload/branch.rs b/crates/aiken-project/src/github/payload/branch.rs new file mode 100644 index 000000000..ff720ef8b --- /dev/null +++ b/crates/aiken-project/src/github/payload/branch.rs @@ -0,0 +1,8 @@ +use super::commit::Commit; +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct Branch { + pub name: String, + pub commit: Commit, +} diff --git a/crates/aiken-project/src/github/payload/commit.rs b/crates/aiken-project/src/github/payload/commit.rs new file mode 100644 index 000000000..6d7efb863 --- /dev/null +++ b/crates/aiken-project/src/github/payload/commit.rs @@ -0,0 +1,6 @@ +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct Commit { + pub sha: String, +} diff --git a/crates/aiken-project/src/github/payload/mod.rs b/crates/aiken-project/src/github/payload/mod.rs new file mode 100644 index 000000000..b123361cd --- /dev/null +++ b/crates/aiken-project/src/github/payload/mod.rs @@ -0,0 +1,5 @@ +pub mod branch; +pub mod commit; +pub mod release; +pub mod repo; +pub mod tag; diff --git a/crates/aiken-project/src/github/payload/release.rs b/crates/aiken-project/src/github/payload/release.rs new file mode 100644 index 000000000..e26d3647e --- /dev/null +++ b/crates/aiken-project/src/github/payload/release.rs @@ -0,0 +1,6 @@ +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct Release { + pub tag_name: String, +} diff --git a/crates/aiken-project/src/github/payload/repo.rs b/crates/aiken-project/src/github/payload/repo.rs new file mode 100644 index 000000000..18b86c77b --- /dev/null +++ b/crates/aiken-project/src/github/payload/repo.rs @@ -0,0 +1,6 @@ +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct Repo { + pub default_branch: String, +} diff --git a/crates/aiken-project/src/github/payload/tag.rs b/crates/aiken-project/src/github/payload/tag.rs new file mode 100644 index 000000000..a99548b4b --- /dev/null +++ b/crates/aiken-project/src/github/payload/tag.rs @@ -0,0 +1,6 @@ +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct Tag { + pub name: String, +} diff --git a/crates/aiken-project/src/github/repo.rs b/crates/aiken-project/src/github/repo.rs index e96c51102..28b7c94b1 100644 --- a/crates/aiken-project/src/github/repo.rs +++ b/crates/aiken-project/src/github/repo.rs @@ -1,22 +1,145 @@ -use reqwest::{blocking::Client, header::USER_AGENT, Error}; -use serde::Deserialize; +use super::payload::{branch::Branch, release::Release, repo::Repo as RepoInfo, tag::Tag}; +use reqwest::{ + blocking::{Client, Response}, + header::USER_AGENT, + Error, +}; +use std::time::Duration; -#[derive(Deserialize)] -pub struct LatestRelease { - pub tag_name: String, +enum Get { + Releases, + Tags, + Branches, + Info, } +enum Query { + Param(T), + All, +} + +const ALL: Query = Query::All; + +// #region Github repo's RELEASES +pub struct LatestRelease {} impl LatestRelease { - pub fn of>(repo: Repo) -> Result { - Ok({ - Client::new() - .get(format!( - "https://api.github.com/repos/{}/releases/latest", - repo.as_ref() - )) - .header(USER_AGENT, "aiken") - .send()? - .json::()? - }) + pub fn of>(repo: Repo) -> Result { + http_get(repo, Get::Releases, Query::Param("latest"))?.json::() + } +} + +pub struct Releases {} +impl Releases { + pub fn of>(repo: Repo) -> Result, Error> { + http_get(repo, Get::Releases, ALL)?.json::>() + } +} +// #endregion + +// #region Github repo's TAGS +pub struct Tags {} +impl Tags { + pub fn of>(repo: Repo) -> Result, Error> { + http_get(repo, Get::Tags, ALL)?.json::>() + } +} +// #endregion + +// #region Github repo's BRANCHES +pub struct MainBranch {} +impl MainBranch { + pub fn of>(repo: Repo) -> Result { + http_get( + repo, + Get::Branches, + Query::Param("master"), // Github will try to redirect this to `main` + )? + .json::() + } +} + +pub struct DefaultBranch {} +impl DefaultBranch { + pub fn of>(repo: Repo) -> Result { + http_get( + &repo, + Get::Branches, + Query::Param(Info::of(&repo)?.default_branch), + )? + .json::() + } +} + +pub struct Branches {} +impl Branches { + pub fn of>(repo: Repo) -> Result, Error> { + http_get(repo, Get::Branches, ALL)?.json::>() + } +} +// #endregion + +// #region Github repo's INFO +pub struct Info {} +impl Info { + pub fn of>(repo: Repo) -> Result { + http_get(repo, Get::Info, ALL)?.json::() + } +} +// #endregion + +/// Sends an HTTP GET Request to Github API. +/// +/// # Parameters +/// +/// - `repo` must be in this format `owner/repo`. +/// It accepts `str`, `String`, or `impl AsRef`. +/// +/// - `param` is used to query a certain `release`, `tag`, or `branch`. +/// +/// # Example +/// +/// `http_get("owner/repo", Get::Branches, Query::Param("branch_name"))` +/// means it queries for the branch `branch_name`. +fn http_get, Param: AsRef>( + repo: Repo, + get: Get, + param: Query, +) -> Result { + let mut url = format!("https://api.github.com/repos/{}", repo.as_ref()); + let path; + url.push_str(match get { + Get::Releases => { + path = format_path("/releases", param); + path.as_ref() + } + Get::Tags => { + path = match param { + Query::All => format_path("/tags", Query::All), + param => format_path("/releases/tags", param), + }; + path.as_ref() + } + Get::Branches => { + path = format_path("/branches", param); + path.as_ref() + } + Get::Info => "", + }); + + Client::builder() + .timeout(Duration::from_secs(5)) // it's not cool waiting for too long + .build()? + .get(url) + .header(USER_AGENT, "aiken-lang") + .send() +} + +fn format_path, Param: AsRef>( + path: Path, + param: Query, +) -> impl AsRef { + match param { + Query::Param(param) => format!("{}/{}", path.as_ref(), param.as_ref()), + _ => format!("{}", path.as_ref()), } } diff --git a/crates/aiken/src/cmd/packages/add.rs b/crates/aiken/src/cmd/packages/add.rs index c15e8f638..5d7b5a425 100644 --- a/crates/aiken/src/cmd/packages/add.rs +++ b/crates/aiken/src/cmd/packages/add.rs @@ -1,6 +1,7 @@ use aiken_project::{ config::{Config, Dependency, Platform}, error::Warning, + github::repo::Info, package_name::PackageName, pretty, }; @@ -9,6 +10,7 @@ use owo_colors::{OwoColorize, Stream::Stderr}; use std::{path::PathBuf, process, str::FromStr}; #[derive(clap::Args)] +#[clap(disable_version_flag = true)] /// Add a new project package as dependency pub struct Args { /// Package name, in the form of {owner}/{repository}. @@ -20,7 +22,7 @@ pub struct Args { pub package: String, /// The package version, as a git commit hash, a tag or a branch name. #[clap(long)] - pub version: String, + pub version: Option, #[clap(hide = true, long)] pub overwrite: bool, @@ -31,7 +33,10 @@ pub fn exec(args: Args) -> miette::Result<()> { let dependency = Dependency { name: PackageName::from_str(&args.package)?, - version: args.version, + version: args.version.unwrap_or(match Info::of(&args.package) { + Ok(repo) => repo.default_branch, + _ => "main".to_string(), + }), source: Platform::Github, }; diff --git a/crates/aiken/src/cmd/packages/upgrade.rs b/crates/aiken/src/cmd/packages/upgrade.rs index 4c6812014..5a4feee27 100644 --- a/crates/aiken/src/cmd/packages/upgrade.rs +++ b/crates/aiken/src/cmd/packages/upgrade.rs @@ -1,18 +1,19 @@ use super::add; #[derive(clap::Args)] +#[clap(disable_version_flag = true)] /// Change the version of an installed dependency pub struct Args { /// Package name, in the form of {owner}/{repository}. /// - /// For example → 'add aiken-lang/stdlib' + /// For example → 'packages upgrade aiken-lang/stdlib' /// /// Note that by default, this assumes the package is located /// on Github. package: String, /// The package version, as a git commit hash, a tag or a branch name. #[clap(long)] - version: String, + version: Option, } pub fn exec(args: Args) -> miette::Result<()> {