/ capsule-cli / src / client.rs
client.rs
  1  use std::{fmt, path::Path};
  2  
  3  use anyhow::Result;
  4  use headers::{Authorization, HeaderMapExt};
  5  use reqwest::{
  6      Url,
  7      header::{HeaderMap, HeaderValue},
  8      multipart,
  9  };
 10  use serde::Deserialize;
 11  
 12  use crate::model::{Claim, Projects, Stats};
 13  
 14  static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
 15  
 16  pub struct Client {
 17      client: reqwest::Client,
 18      host: Url,
 19  }
 20  
 21  impl Client {
 22      pub fn new(host: String, api_key: Option<String>) -> Result<Self> {
 23          let mut headers = HeaderMap::new();
 24  
 25          if let Some(api_key) = api_key {
 26              headers.typed_insert(Authorization::bearer(&api_key)?);
 27              headers.insert("docat-api-key", api_key.parse()?);
 28          }
 29  
 30          Ok(Self {
 31              client: reqwest::Client::builder()
 32                  .user_agent(APP_USER_AGENT)
 33                  .default_headers(headers)
 34                  .build()?,
 35              host: host.parse()?,
 36          })
 37      }
 38  
 39      pub async fn stats(&self) -> Result<Stats> {
 40          let url = self.url(format_args!("stats"))?;
 41          let resp = self.client.get(url).send().await?.error_for_status()?;
 42  
 43          resp.json().await.map_err(Into::into)
 44      }
 45  
 46      pub async fn projects(&self) -> Result<Projects> {
 47          let url = self.url(format_args!("projects"))?;
 48          let resp = self.client.get(url).send().await?.error_for_status()?;
 49  
 50          resp.json().await.map_err(Into::into)
 51      }
 52  
 53      pub async fn push(&self, project: &str, version: &str, docs_path: &Path) -> Result<()> {
 54          let url = self.url(format_args!("{project}/{version}"))?;
 55          let form = multipart::Form::new().file("file", docs_path).await?;
 56  
 57          self.client
 58              .post(url)
 59              .multipart(form)
 60              .send()
 61              .await?
 62              .error_for_status()?;
 63  
 64          Ok(())
 65      }
 66  
 67      pub async fn claim(&self, project: &str) -> Result<Claim> {
 68          let url = self.url(format_args!("{project}/claim"))?;
 69          let resp = self.client.get(url).send().await?.error_for_status()?;
 70  
 71          resp.json().await.map_err(Into::into)
 72      }
 73  
 74      pub async fn push_icon(&self, project: &str, icon_path: &Path) -> Result<()> {
 75          let url = self.url(format_args!("{project}/icon"))?;
 76          let form = multipart::Form::new().file("file", icon_path).await?;
 77  
 78          self.client
 79              .post(url)
 80              .multipart(form)
 81              .send()
 82              .await?
 83              .error_for_status()?;
 84  
 85          Ok(())
 86      }
 87  
 88      pub async fn delete(&self, project: &str, version: &str) -> Result<()> {
 89          let url = self.url(format_args!("{project}/{version}"))?;
 90          self.client.delete(url).send().await?.error_for_status()?;
 91  
 92          Ok(())
 93      }
 94  
 95      pub async fn tag(&self, project: &str, version: &str, tag: &str) -> Result<()> {
 96          let url = self.url(format_args!("{project}/{version}/tags/{tag}"))?;
 97          self.client.put(url).send().await?.error_for_status()?;
 98  
 99          Ok(())
100      }
101  
102      pub async fn rename(&self, project: &str, new_name: &str) -> Result<()> {
103          let url = self.url(format_args!("{project}/rename/{new_name}"))?;
104          self.client.put(url).send().await?.error_for_status()?;
105  
106          Ok(())
107      }
108  
109      pub async fn hide(&self, project: &str, version: &str) -> Result<()> {
110          let url = self.url(format_args!("{project}/{version}/hide"))?;
111          self.client.post(url).send().await?.error_for_status()?;
112  
113          Ok(())
114      }
115  
116      pub async fn show(&self, project: &str, version: &str) -> Result<()> {
117          let url = self.url(format_args!("{project}/{version}/show"))?;
118          self.client.post(url).send().await?.error_for_status()?;
119  
120          Ok(())
121      }
122  
123      fn url(&self, args: fmt::Arguments<'_>) -> Result<Url> {
124          self.host
125              .join(&format!("/api/v1/{args}"))
126              .map_err(Into::into)
127      }
128  }