diff --git a/examples/star_unstar_a_gist.rs b/examples/star_unstar_a_gist.rs new file mode 100644 index 00000000..69c82d77 --- /dev/null +++ b/examples/star_unstar_a_gist.rs @@ -0,0 +1,72 @@ +use octocrab::Octocrab; + +struct ProgramArguments { + gist_id: String, + star: bool, +} + +/// Rudimentary CLI interface. Tries to be nice to the caller without too +/// much bloat on the example. +fn parse_argv_or_exit() -> ProgramArguments { + const USAGE: &str = r#"Usage: + (--star | --unstar) GIST_ID"#; + let star = if let Some(param) = std::env::args().nth(1) { + if param == "--star" { + true + } else if param == "--unstar" { + false + } else { + eprintln!("error: Need (--star | --unstar) as first argument."); + eprintln!("{}", USAGE); + std::process::exit(1); + } + } else { + eprintln!("error: Need (--star | --unstar) as first argument."); + eprintln!("{}", USAGE); + std::process::exit(1); + }; + + let gist_id = if let Some(gist_id) = std::env::args().nth(2) { + gist_id + } else { + eprintln!("error: Need GIST_ID as second argument."); + eprintln!("{}", USAGE); + std::process::exit(1); + }; + ProgramArguments { gist_id, star } +} + +/// This example tries to demonstrate interacting with a gists' 'stars'. +/// It does so by making a program that takes two CLI arguments: +/// +/// 1) `--star` or `--unstar` to either star/unstar a gist +/// 2) A `GIST_ID` to identify which gist to operate on +/// +/// The example will check if a gist is already starred / unstarred, before +/// performing the operation. +#[tokio::main] +async fn main() -> octocrab::Result<()> { + let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); + let args = parse_argv_or_exit(); + let octocrab = Octocrab::builder().personal_token(token).build()?; + let gists_handler = octocrab.gists(); + let is_starred = gists_handler.is_starred(&args.gist_id).await?; + + if is_starred && args.star { + println!("{gist_id} is already starred.", gist_id = &args.gist_id); + return Ok(()); + } + if !is_starred && !args.star { + println!("{gist_id} is already un-starred.", gist_id = &args.gist_id); + return Ok(()); + } + + if args.star { + gists_handler.star(&args.gist_id).await?; + println!("Starred {gist_id}.", gist_id = &args.gist_id) + } else { + gists_handler.unstar(&args.gist_id).await?; + println!("Un-starred {gist_id}.", gist_id = &args.gist_id) + } + Ok(()) +} diff --git a/src/api/gists.rs b/src/api/gists.rs index 2e28b340..40f7c558 100644 --- a/src/api/gists.rs +++ b/src/api/gists.rs @@ -1,6 +1,7 @@ //! The gist API mod list_commits; +use http::StatusCode; use serde::Serialize; use std::collections::BTreeMap; @@ -122,6 +123,79 @@ impl<'octo> GistsHandler<'octo> { pub fn list_commits(&self, gist_id: impl Into) -> list_commits::ListCommitsBuilder { list_commits::ListCommitsBuilder::new(self, gist_id.into()) } + + /// Check if the given is gist is already starred by the authenticated user. + /// See [GitHub API Documentation][docs] more information about response + /// data. + /// + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// let is_starred: bool = octocrab::instance() + /// .gists() + /// .is_starred("00000000000000000000000000000000") + /// .await?; + /// # Ok(()) + /// # } + /// ``` + /// + /// [docs]: https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#check-if-a-gist-is-starred + pub async fn is_starred(&self, gist_id: impl AsRef) -> Result { + let gist_id = gist_id.as_ref(); + let response = self + .crab + ._get(format!("/gists/{gist_id}/star")) + .await?; + // Gist API returns 204 (NO CONTENT) if a gist is starred + Ok(response.status() == StatusCode::NO_CONTENT) + } + + /// Star the given gist. See [GitHub API Documentation][docs] more + /// information about response data. + /// + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// octocrab::instance() + /// .gists() + /// .star("00000000000000000000000000000000") + /// .await?; + /// # Ok(()) + /// # } + /// ``` + /// + /// [docs]: https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#star-a-gist + pub async fn star(&self, gist_id: impl AsRef) -> Result<()> { + let gist_id = gist_id.as_ref(); + // PUT here returns an empty body, ignore it since it doesn't make + // sense to deserialize it as JSON. + self.crab + ._put(format!("/gists/{gist_id}/star"), None::<&()>) + .await + .map(|_| ()) + } + + /// Unstar the given gist. See [GitHub API Documentation][docs] more + /// information about response data. + /// + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// octocrab::instance() + /// .gists() + /// .unstar("00000000000000000000000000000000") + /// .await?; + /// # Ok(()) + /// # } + /// ``` + /// + /// [docs]: https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#unstar-a-gist + pub async fn unstar(&self, gist_id: impl AsRef) -> Result<()> { + let gist_id = gist_id.as_ref(); + // DELETE here returns an empty body, ignore it since it doesn't make + // sense to deserialize it as JSON. + self.crab + ._delete(format!("/gists/{gist_id}/star"), None::<&()>) + .await + .map(|_| ()) + } } #[derive(Debug)]