Skip to content

Commit

Permalink
add comments
Browse files Browse the repository at this point in the history
  • Loading branch information
JR-1991 committed May 23, 2024
1 parent 1d3cd1b commit 6f9b7c4
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 24 deletions.
10 changes: 6 additions & 4 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, reqwest::Error> {
let base_url = Url::parse(base_url).unwrap();
Expand Down Expand Up @@ -76,24 +79,23 @@ impl BaseClient {
parameters: Option<HashMap<String, String>>,
context: &RequestType,
) -> Result<reqwest::blocking::Response, reqwest::Error> {
// 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(&parameters),
None => request,
};

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()
}
}

Expand Down
33 changes: 14 additions & 19 deletions src/identifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -12,25 +21,11 @@ pub enum Identifier {
impl FromStr for Identifier {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.starts_with("doi:") {
Ok(Identifier::PeristentId(s.to_owned()))
} else {
match s.parse::<i64>() {
Ok(id) => Ok(Identifier::Id(id)),
Err(_) => Err(format!("Invalid identifier: {}", s)),
}
}
}
}

impl Identifier {
pub fn from_pid_or_id(pid: &Option<String>, id: &Option<i64>) -> 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::<i64>() {
Ok(_) => Ok(Identifier::Id(s.parse::<i64>().unwrap())),
Err(_) => Ok(Identifier::PeristentId(s.to_owned())),
}
}
}
5 changes: 5 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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<String>)> {
let base_url = std::env::var("DVCLI_URL").ok()?;
let api_token = std::env::var("DVCLI_TOKEN").ok();
Expand Down
4 changes: 4 additions & 0 deletions src/progressbar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<dyn Read + Send>,
pb: Arc<ProgressBar>,
Expand Down
9 changes: 8 additions & 1 deletion src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,26 @@ 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<HashMap<String, String>>,
files: Option<HashMap<String, String>>,
},
}

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,
Expand Down
15 changes: 15 additions & 0 deletions src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<T> {
Expand Down Expand Up @@ -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());
Expand All @@ -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 {
Expand Down

0 comments on commit 6f9b7c4

Please sign in to comment.