99 lines
2.2 KiB
Rust

use std::{
fs::File,
io::{self, stdout, Stdout, Write},
path::{Path, PathBuf},
process::ExitCode,
};
use clap::{Parser, Subcommand};
use http::{Method, Uri};
use netutils::proto::http::{Bytes, HttpClient, HttpError};
#[derive(Debug, thiserror::Error)]
enum Error {
#[error("I/O error: {0}")]
IoError(#[from] io::Error),
#[error("HTTP error: {0}")]
HttpError(#[from] HttpError<io::Error>),
}
#[derive(Debug, Parser)]
struct Arguments {
#[clap(short, long)]
output: Option<PathBuf>,
#[clap(subcommand)]
method: RequestMethod,
}
#[derive(Debug, Subcommand)]
enum RequestMethod {
#[clap(arg_required_else_help = true)]
Get {
#[clap(help = "URL to GET")]
url: Uri,
},
}
enum Output {
File(File),
Stdout(Stdout),
}
impl Output {
fn open<P: AsRef<Path>>(path: Option<P>) -> Result<Self, io::Error> {
match path {
Some(path) => File::create(path).map(Self::File),
None => Ok(Self::Stdout(stdout())),
}
}
}
impl Write for Output {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
Self::File(file) => file.write(buf),
Self::Stdout(file) => file.write(buf),
}
}
fn flush(&mut self) -> io::Result<()> {
match self {
Self::File(file) => file.flush(),
Self::Stdout(file) => file.flush(),
}
}
}
fn get(url: Uri, output: Option<PathBuf>) -> Result<(), Error> {
let mut output = Output::open(output)?;
let mut client = HttpClient::default();
let mut buffer = [0; 4096];
let mut response = client.request(Method::GET, url).call::<Bytes>().unwrap();
loop {
let len = response.read(&mut buffer)?;
if len == 0 {
break;
}
output.write_all(&buffer[..len])?;
}
Ok(())
}
fn main() -> ExitCode {
let args = Arguments::parse();
let result = match args.method {
RequestMethod::Get { url } => get(url, args.output),
};
match result {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("Error: {}", error);
ExitCode::FAILURE
}
}
}