use clap::Parser;
use reqwest::RequestBuilder;
use serde_json::{Map, Value};
use std::collections::HashMap;
use zero4rs::core::awsv2::pre_signed_url_v2;
#[derive(Parser)]
#[clap(author = "keesh.zhang", version, about = "Amazon aws v2 signture tool")]
pub struct Cli {
#[clap(flatten)]
access_key_id: AccessKeyId,
#[clap(flatten)]
secret_access_key: SecretAccessKey,
#[clap(flatten)]
method: Method,
#[clap(flatten)]
request_url: RequestUrl,
#[clap(flatten)]
expire_seconds: ExpireSeconds,
#[clap(flatten)]
time_format: TimeFormat,
#[clap(flatten)]
body: Body,
}
#[derive(clap::Parser)]
pub struct AccessKeyId {
#[clap(short, long = "access_key_id", help = "*")]
access_key_id: String,
}
#[derive(clap::Parser)]
pub struct SecretAccessKey {
#[clap(short, long = "secret_access_key", help = "*")]
secret_access_key: String,
}
#[derive(clap::Parser)]
pub struct Method {
#[clap(
short, long = "method",
value_parser = clap::builder::PossibleValuesParser::new(["get", "post", "delete", "put", "patch"]),
help = "*",
)]
method: String,
}
#[derive(clap::Parser)]
pub struct RequestUrl {
#[clap(long, short, help = "*")]
request_url: String,
}
#[derive(clap::Parser)]
pub struct ExpireSeconds {
#[clap(long, short, default_value = "0")]
expire_seconds: u32,
}
#[derive(clap::Parser)]
pub struct TimeFormat {
#[clap(
long, short,
value_parser = clap::builder::PossibleValuesParser::new(["%Y-%m-%dT%H:%M:%SZ", "%Y-%m-%dT%H:%M:%S"]),
help = "*",
)]
time_format: String,
}
#[derive(clap::Parser)]
pub struct Body {
#[clap(long, short, default_value = "")]
body: String,
}
fn main() {
let args = Cli::parse();
let new_signed_url = pre_signed_url_v2(
&args.access_key_id.access_key_id,
&args.secret_access_key.secret_access_key,
args.expire_seconds.expire_seconds,
&args.method.method,
&args.request_url.request_url,
&args.time_format.time_format,
);
let request = if &args.method.method == "post" || &args.method.method == "POST" {
reqwest::Client::builder()
.build()
.unwrap()
.post(new_signed_url.clone())
.header("Content-Type", "application/json")
} else {
reqwest::Client::builder()
.build()
.unwrap()
.get(new_signed_url.clone())
.header("Content-Type", "application/json")
};
let mut curl_command = curl(&request);
if (&args.method.method == "post" || &args.method.method == "POST")
&& !args.body.body.is_empty()
{
curl_command = curl_command + " -d '" + &args.body.body + "'";
}
println!("curl_command:\n------------------\n{}\n", curl_command);
}
pub fn curl(req: &RequestBuilder) -> String {
let req_info = format!(r#"{:?}"#, req);
let url_regex = regex::Regex::new(
r#"RequestBuilder \{ method: (\w+), url: Url \{ scheme: "(\w+)", cannot_be_a_base: (.*?), username: (.*?), password: (.*?), host: (.*?), port: (.*?), path: "(.*?)", query: (.*?), fragment: (.*?) \}, headers: (.*?) \}"#,
).unwrap();
let captures = url_regex.captures(&req_info).unwrap();
let method = captures.get(1).map_or("", |m| m.as_str());
let scheme = captures.get(2).map_or("", |m| m.as_str());
let domain = captures.get(6).map_or("", |m| m.as_str());
let port = captures.get(7).map_or("", |m| m.as_str());
let path = captures.get(8).map_or("", |m| m.as_str());
let query = captures.get(9).map_or("", |m| m.as_str());
let headers = captures.get(11).map_or("", |m| m.as_str());
let domain = if domain != "None" {
regex::Regex::new(r#"Some\(Domain\("(.*?)"\)\)"#)
.unwrap()
.captures(domain)
.unwrap()
.get(1)
.map_or("", |m| m.as_str())
} else {
""
};
let port = if port != "None" {
format!(
":{}",
regex::Regex::new(r#"Some\((.*?)\)"#)
.unwrap()
.captures(domain)
.unwrap()
.get(1)
.map_or("", |m| m.as_str())
)
} else {
"".to_string()
};
let query = if query != "None" {
"?".to_owned()
+ regex::Regex::new(r#"Some\("(.*?)"\)"#)
.unwrap()
.captures(query)
.unwrap()
.get(1)
.map_or("", |m| m.as_str())
} else {
"".to_string()
};
let result: Result<Map<String, Value>, _> = serde_json::from_str(headers);
let headers = match result {
Ok(map) => {
let hashmap: HashMap<String, serde_json::Value> = map.into_iter().collect();
let result: Vec<String> = hashmap
.iter()
.map(|(key, value)| format!("-H '{}:{}'", key, value.as_str().unwrap()))
.collect();
result.join(" ")
}
Err(_) => "".to_string(),
};
format!(
"curl -vX {} '{}://{}{}{}{}' {}",
method, scheme, domain, port, path, query, headers
)
}