summaryrefslogtreecommitdiffstats
path: root/main
diff options
context:
space:
mode:
authorFilip Wandzio <contact@philw.dev>2025-12-28 00:45:12 +0100
committerFilip Wandzio <contact@philw.dev>2025-12-28 00:45:12 +0100
commit7995282f65183b0a615c224a3ea13eeb10a1e828 (patch)
tree84705b645f3cc799b8d6cf8af55b67dbc045e82a /main
downloadpdat-7995282f65183b0a615c224a3ea13eeb10a1e828.tar.gz
pdat-7995282f65183b0a615c224a3ea13eeb10a1e828.zip
Kind of Initial CommentHEADmaster
Implement basic way of testing various http methods via simple cli interface. Also write basic config file kind of parser.
Diffstat (limited to '')
-rw-r--r--main/Cargo.toml15
-rw-r--r--main/src/main.rs185
2 files changed, 200 insertions, 0 deletions
diff --git a/main/Cargo.toml b/main/Cargo.toml
new file mode 100644
index 0000000..e91ddbf
--- /dev/null
+++ b/main/Cargo.toml
@@ -0,0 +1,15 @@
1[package]
2name = "pdat"
3version = "0.1.0"
4edition = "2024"
5
6[dependencies]
7clap = {version = "4.5.48", features = ["derive"] }
8colored = "3.0.0"
9reqwest = {version = "0.12.23", features = ["json"] }
10serde_json = {version = "1.0.145"}
11tokio = {version = "1.47.1", features = ["full"]}
12toml = "0.9.7"
13
14
15config = { path = "../config" }
diff --git a/main/src/main.rs b/main/src/main.rs
new file mode 100644
index 0000000..0604673
--- /dev/null
+++ b/main/src/main.rs
@@ -0,0 +1,185 @@
1use clap::Parser;
2use colored::*;
3use config::Config;
4use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
5use serde_json::Value;
6use std::process;
7
8#[derive(Parser)]
9#[command(author, version, about)]
10struct Args {
11 method: String,
12 url: String,
13 #[arg(short = 'H', long)]
14 header: Vec<String>,
15 #[arg(short = 'd', long)]
16 data: Option<String>,
17 #[arg(short = 'c', long)]
18 config: Option<String>,
19 #[arg(long)]
20 indent: Option<usize>,
21}
22
23fn print_colored_json(value: &Value, indent_level: usize, indent_spaces: usize) {
24 let indent = " ".repeat(indent_spaces * indent_level);
25
26 match value {
27 Value::Object(map) => {
28 println!("{}{}", indent, "{".bright_blue());
29 for (key, val) in map {
30 print!(
31 "{}{}: ",
32 " ".repeat(indent_spaces * (indent_level + 1)),
33 key.bright_cyan()
34 );
35 print_colored_json(val, indent_level + 1, indent_spaces);
36 }
37 println!("{}{}", indent, "}".bright_blue());
38 }
39 Value::Array(arr) => {
40 println!("{}{}", indent, "[".bright_blue());
41 for val in arr {
42 print_colored_json(val, indent_level + 1, indent_spaces);
43 }
44 println!("{}{}", indent, "]".bright_blue());
45 }
46 Value::String(s) => println!("{}{}", indent, format!("\"{}\"", s).bright_yellow()),
47 Value::Number(n) => println!("{}{}", indent, n.to_string().bright_magenta()),
48 Value::Bool(b) => println!("{}{}", indent, b.to_string().bright_green()),
49 Value::Null => println!("{}{}", indent, "null".bright_black()),
50 }
51}
52
53fn colorize_status(status_code: u16) -> ColoredString {
54 match status_code {
55 100..=199 => status_code.to_string().bright_blue(),
56 200..=299 => status_code.to_string().green(),
57 300..=399 => status_code.to_string().cyan(),
58 400..=499 => status_code.to_string().yellow(),
59 500..=599 => status_code.to_string().red(),
60 _ => status_code.to_string().normal(),
61 }
62}
63
64fn build_request(
65 client: &reqwest::Client,
66 method: &str,
67 url: &str,
68 headers: HeaderMap,
69) -> Result<reqwest::RequestBuilder, String> {
70 let method = method.to_uppercase();
71 let builder = match method.as_str() {
72 "GET" => Ok(client.get(url)),
73 "POST" => Ok(client.post(url)),
74 "PUT" => Ok(client.put(url)),
75 "DELETE" => Ok(client.delete(url)),
76 "PATCH" => Ok(client.patch(url)),
77 "OPTIONS" => Ok(client.request(reqwest::Method::OPTIONS, url)),
78 "HEAD" => Ok(client.head(url)),
79 other => Err(format!("Unsupported HTTP method: {}", other)),
80 }?;
81 Ok(builder.headers(headers))
82}
83
84#[tokio::main]
85async fn main() {
86 let args = Args::parse();
87 let client = reqwest::Client::new();
88
89 let config = match args.config.as_ref().map(|p| Config::from_file(p)) {
90 Some(Ok(cfg)) => Some(cfg),
91 Some(Err(e)) => {
92 eprintln!("{}: Failed to load config: {}", "Error".red(), e);
93 process::exit(1);
94 }
95 None => None,
96 };
97
98 let mut headers = HeaderMap::new();
99
100 if let Some(cfg) = &config {
101 for header_str in cfg.headers() {
102 match header_str.split_once(':') {
103 Some((key, value)) => {
104 if let (Ok(k), Ok(v)) = (
105 HeaderName::from_bytes(key.trim().as_bytes()),
106 HeaderValue::from_str(value.trim()),
107 ) {
108 headers.insert(k, v);
109 }
110 }
111 None => eprintln!(
112 "{}: Invalid header format in config: {}",
113 "Warning".yellow(),
114 header_str
115 ),
116 }
117 }
118 }
119
120 for header_str in &args.header {
121 match header_str.split_once(':') {
122 Some((key, value)) => {
123 if let (Ok(k), Ok(v)) = (
124 HeaderName::from_bytes(key.trim().as_bytes()),
125 HeaderValue::from_str(value.trim()),
126 ) {
127 headers.insert(k, v);
128 }
129 }
130 None => eprintln!(
131 "{}: Invalid header format in CLI: {}",
132 "Warning".yellow(),
133 header_str
134 ),
135 }
136 }
137
138 let indent_spaces = args
139 .indent
140 .or_else(|| config.as_ref().map(|c| c.indent()))
141 .unwrap_or(2);
142
143 let mut request_builder = match build_request(&client, &args.method, &args.url, headers) {
144 Ok(r) => r,
145 Err(e) => {
146 eprintln!("{}: {}", "Error".red(), e);
147 process::exit(1);
148 }
149 };
150
151 if let Some(json_data) = args.data {
152 match serde_json::from_str::<Value>(&json_data) {
153 Ok(json_value) => request_builder = request_builder.json(&json_value),
154 Err(e) => {
155 eprintln!("{}: Invalid JSON body: {}", "Error".red(), e);
156 process::exit(1);
157 }
158 }
159 }
160
161 let response = match request_builder.send().await {
162 Ok(resp) => resp,
163 Err(e) => {
164 eprintln!("{}: Request failed: {}", "Error".red(), e);
165 process::exit(1);
166 }
167 };
168
169 let status_code = response.status().as_u16();
170 println!("Status: {}", colorize_status(status_code));
171
172 let response_text = match response.text().await {
173 Ok(t) => t,
174 Err(e) => {
175 eprintln!("{}: Failed to read response body: {}", "Error".red(), e);
176 process::exit(1);
177 }
178 };
179
180 if let Ok(json_value) = serde_json::from_str::<Value>(&response_text) {
181 print_colored_json(&json_value, 0, indent_spaces);
182 } else {
183 println!("{}", response_text);
184 }
185}