From 6f9b7c475ef8d95b103f0b98e02d037101888943 Mon Sep 17 00:00:00 2001 From: Jan Range Date: Thu, 23 May 2024 08:37:50 +0200 Subject: [PATCH] add comments --- src/client.rs | 10 ++++++---- src/identifier.rs | 33 ++++++++++++++------------------- src/main.rs | 5 +++++ src/progressbar.rs | 4 ++++ src/request.rs | 9 ++++++++- src/response.rs | 15 +++++++++++++++ 6 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/client.rs b/src/client.rs index 9c2b517..a4f0f19 100644 --- a/src/client.rs +++ b/src/client.rs @@ -13,6 +13,9 @@ pub struct BaseClient { client: Client, } +// This is the base client that will be used to make requests to the API. +// Its acts as a wrapper around the reqwest::blocking::Client and provides +// methods to make GET, POST, PUT, and DELETE requests. impl BaseClient { pub fn new(base_url: &String, api_token: Option<&String>) -> Result { let base_url = Url::parse(base_url).unwrap(); @@ -76,9 +79,9 @@ impl BaseClient { parameters: Option>, context: &RequestType, ) -> Result { + // Process the URL and build the request based on the context let url = self.base_url.join(path).unwrap(); let request = context.to_request(self.client.request(method, url.clone())); - let request = match parameters { Some(parameters) => request.query(¶meters), None => request, @@ -86,14 +89,13 @@ impl BaseClient { print_call(url.to_string()); + // Add the API token if it exists let request = match &self.api_token { Some(api_token) => request.header("X-Dataverse-key", api_token), None => request, }; - let response = request.send(); - - response + request.send() } } diff --git a/src/identifier.rs b/src/identifier.rs index d1e8bc6..32d728f 100644 --- a/src/identifier.rs +++ b/src/identifier.rs @@ -2,6 +2,15 @@ use std::str::FromStr; use serde::{Deserialize, Serialize}; +// We differentiate between persistent identifiers and +// regular identifiers here. This makes it easier to +// handle the two types of identifiers in the codebase +// without having to check for the presence of a persistent +// identifier every time we need to use an identifier. +// +// This way users can supply a general identifier without specifying +// whether it is a persistent identifier or not. The code will +// automatically determine the type of identifier and use it. #[derive(Serialize, Deserialize, Debug)] #[serde(untagged)] pub enum Identifier { @@ -12,25 +21,11 @@ pub enum Identifier { impl FromStr for Identifier { type Err = String; fn from_str(s: &str) -> Result { - if s.starts_with("doi:") { - Ok(Identifier::PeristentId(s.to_owned())) - } else { - match s.parse::() { - Ok(id) => Ok(Identifier::Id(id)), - Err(_) => Err(format!("Invalid identifier: {}", s)), - } - } - } -} - -impl Identifier { - pub fn from_pid_or_id(pid: &Option, id: &Option) -> Self { - if let Some(pid) = pid { - Identifier::PeristentId(pid.to_owned()) - } else if let Some(id) = id { - Identifier::Id(id.to_owned()) - } else { - panic!("Either a persistent identifier or an identifier must be provided") + // If it can be parsed as an integer, it is an id + // Otherwise, it is a persistent id + match s.parse::() { + Ok(_) => Ok(Identifier::Id(s.parse::().unwrap())), + Err(_) => Ok(Identifier::PeristentId(s.to_owned())), } } } diff --git a/src/main.rs b/src/main.rs index a36a171..f6a1866 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,9 @@ static HEADER: &str = r#" --- Dataverse Command Line Interface (DVCLI) --- "#; +// This is the basic overall structure of the CLI +// Subcommands are defined in their respective modules +// and are processed here. #[derive(StructOpt, Debug)] #[structopt(about = "CLI to interact with Dataverse")] enum DVCLI { @@ -39,6 +42,8 @@ fn main() { } } +// This function extracts the base URL and API token from the environment +// variables DVCLI_URL and DVCLI_TOKEN, respectively. fn extract_config_from_env() -> Option<(String, Option)> { let base_url = std::env::var("DVCLI_URL").ok()?; let api_token = std::env::var("DVCLI_TOKEN").ok(); diff --git a/src/progressbar.rs b/src/progressbar.rs index 9fe8cb0..32a9f46 100644 --- a/src/progressbar.rs +++ b/src/progressbar.rs @@ -5,6 +5,8 @@ use std::fs::File; use std::io::{self, Read}; use std::sync::Arc; +// This function is used in the RequestType::Multipart::build_form_request method +// to wrap a file in a progress bar and return a multipart part. pub fn wrap_progressbar( file_path: &str, multi_pb: &MultiProgress, @@ -44,6 +46,8 @@ pub fn wrap_progressbar( Ok(part) } +// A reader that tracks progress and updates a progress bar +// as data is read from it using the Read trait. struct ProgressReader { inner: Box, pb: Arc, diff --git a/src/request.rs b/src/request.rs index 6aa5f55..0df5d51 100644 --- a/src/request.rs +++ b/src/request.rs @@ -3,11 +3,18 @@ use indicatif::MultiProgress; use reqwest::blocking::{multipart, RequestBuilder}; use std::collections::HashMap; +// We distinguish between three types of requests: plain, JSON, and multipart pub enum RequestType { + // A plain request with no body Plain, + + // A JSON request with a JSON body and the + // content type set to application/json JSON { body: String, }, + + // A multipart request with a body and files Multipart { bodies: Option>, files: Option>, @@ -15,7 +22,7 @@ pub enum RequestType { } impl RequestType { - // Convert the request type to a request + // Convert the request type to a request builder pub fn to_request(&self, request: RequestBuilder) -> RequestBuilder { match self { RequestType::Plain => request, diff --git a/src/response.rs b/src/response.rs index f1813b8..e6e72ce 100644 --- a/src/response.rs +++ b/src/response.rs @@ -2,6 +2,9 @@ use atty::Stream; use colored::Colorize; use colored_json::prelude::*; +// We distinguish success and error responses with this enum +// Once the response is parsed, we can check if it's an error or not +// and act accordingly #[derive(Debug, serde::Deserialize, serde::Serialize)] pub enum Status { OK, @@ -34,6 +37,8 @@ impl Status { } } +// This struct acts as a wrapper for the response and +// models the response we expect from Dataverse #[derive(Debug, serde::Deserialize, serde::Serialize)] #[allow(non_snake_case)] pub struct Response { @@ -75,6 +80,10 @@ where } } + // This function is used to redirect the output to the appropriate stream + // If users are redirecting the output to a file, we don't want to print + // the success message but only the JSON response to ensure that the output + // is clean and can be used in other scripts fn redirect_stream(&self, json_str: &str) { if atty::is(Stream::Stdout) { println!("{}", success_message()); @@ -93,6 +102,12 @@ fn success_message() -> String { ) } +// This is a workaround to tackle the issue of having a nested message +// in the response currently caused by the editMetadata endpoint +// +// For more info: +// https://dataverse.zulipchat.com/#narrow/stream/378866-troubleshooting/topic/.E2.9C.94.20Duplicate.20file.20response +// #[derive(Debug, serde::Deserialize, serde::Serialize)] #[serde(untagged)] pub enum Message {