use anyhow::{Context, Result, anyhow};
use chrono::Local;
use std::env;
use std::fs;
use std::io::Write;
fn main() -> Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
return Err(anyhow!("Usage: {} <file_path>", args[0]));
}
let file_path = &args[1];
let content =
fs::read_to_string(file_path).context(format!("Failed to read file: {}", file_path))?;
let mut lines = content.lines();
let url = lines
.next()
.ok_or_else(|| anyhow!("File is empty, expected URL on first line"))?
.trim();
let second_line = lines.next().unwrap_or("");
if !second_line.trim().is_empty() {
return Err(anyhow!("Second line should be empty"));
}
let json_content: String = lines.collect::<Vec<&str>>().join("\n");
let json_content = json_content.trim();
if json_content.is_empty() {
return Err(anyhow!("No JSON content found after the second line"));
}
let _json_value: serde_json::Value =
serde_json::from_str(json_content).context("Failed to parse JSON content")?;
let client = reqwest::blocking::Client::builder()
.build()
.context("Failed to create HTTP client")?;
let content_length = json_content.as_bytes().len();
let request = client
.post(url)
.header("Content-Type", "application/json")
.header("User-Agent", "yuan/0.2.1")
.header("Accept", "*/*")
.header("Content-Length", content_length)
.body(json_content.to_string())
.build()
.context("Failed to build HTTP request")?;
let timestamp = Local::now().format("%Y%m%d-%H%M%S");
let output_filename = format!("yuan-post-json-{}.txt", timestamp);
let mut log_content = String::new();
log_content.push_str(&format!("> POST {} HTTP/1.1\n", request.url()));
log_content.push_str(&format!(
"> Host: {}\n",
request.url().host_str().unwrap_or("")
));
for (name, value) in request.headers() {
log_content.push_str(&format!(
"> {}: {}\n",
name,
value.to_str().unwrap_or("<binary>")
));
}
if !request.headers().contains_key("content-length") {
log_content.push_str(&format!("> Content-Length: {}\n", content_length));
}
log_content.push_str(">\n");
log_content.push_str(&format!("{}\n\n", json_content));
let response = client
.execute(request)
.context("Failed to send HTTP request")?;
log_content.push_str(&format!(
"< HTTP/1.1 {} {}\n",
response.status().as_u16(),
response.status().canonical_reason().unwrap_or("")
));
for (name, value) in response.headers() {
log_content.push_str(&format!(
"< {}: {}\n",
name,
value.to_str().unwrap_or("<binary>")
));
}
log_content.push_str("<\n");
let response_text = response.text().context("Failed to read response body")?;
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
log_content.push_str(
&serde_json::to_string_pretty(&response_json).unwrap_or(response_text.clone()),
);
log_content.push('\n');
}
let mut file = fs::File::create(&output_filename)
.context(format!("Failed to create output file: {}", output_filename))?;
file.write_all(log_content.as_bytes())
.context("Failed to write to output file")?;
println!("Request and response logged to: {}", output_filename);
Ok(())
}