use clap::Parser;
use std::{path::PathBuf, process::ExitCode, time::Duration};
use trotter::{
error::ResponseErr,
parse::{Gemtext, Symbol},
Actor, UserAgent,
};
#[derive(thiserror::Error, Debug)]
enum TrotErr {
#[error("{0}")]
ActorErr(#[from] trotter::error::ActorError),
#[error("{0}")]
Response(#[from] trotter::error::ResponseErr),
#[error("Expected one of these: archiver, indexer, researcher, webproxy")]
BadUserAgent,
}
#[derive(Parser)]
struct Cli {
url: String,
input: Option<Vec<String>>,
#[clap(short, long)]
cert: Option<PathBuf>,
#[clap(short, long)]
key: Option<PathBuf>,
#[clap(short, long)]
user_agent: Option<String>,
#[clap(short, long)]
timeout: Option<u64>,
#[clap(short, long)]
output: Option<String>,
#[clap(short, long)]
gemtext_only: bool,
#[clap(short, long)]
pretty_print: bool,
}
async fn run() -> Result<(), TrotErr> {
let Cli {
url,
input,
cert,
key,
output,
user_agent,
timeout,
gemtext_only,
pretty_print,
} = Cli::parse();
let mut actor = Actor {
cert,
key,
..Default::default()
};
if let Some(u) = user_agent {
actor.user_agent = Some(match u.as_str() {
"archiver" => UserAgent::Archiver,
"indexer" => UserAgent::Indexer,
"researcher" => UserAgent::Researcher,
"webproxy" => UserAgent::Webproxy,
_ => return Err(TrotErr::BadUserAgent),
})
};
if let Some(t) = timeout {
actor.timeout = Duration::from_secs(t);
}
let response = if let Some(input) = input {
let mut input: String = input.iter().map(|x| format!("{x} ")).collect();
let _ = input.pop();
actor.input(url, input).await?
} else {
actor.get(url).await?
};
if let Some(output) = output {
response.save_to_path(output)?;
return Ok(());
}
let text = if gemtext_only {
response.gemtext()?
} else {
response.text()?
};
if pretty_print && response.is_gemtext() {
for g in Gemtext::parse(&text).inner() {
match g {
Symbol::Text(a) => print!("{a}"),
Symbol::Link(a, b) => print!("\x1b[0;4m{b}\x1b[0m \x1b[2m{a}"),
Symbol::List(a) => print!("โข {a}"),
Symbol::Quote(a) => print!("\x1b[33;3;1mยซ {a} ยป"),
Symbol::Header1(a) => print!("\x1b[32;1mโ {a}"),
Symbol::Header2(a) => print!("\x1b[36;1mโ {a}"),
Symbol::Header3(a) => print!("\x1b[34;1mโ {a}"),
Symbol::Codeblock(a, b) => {
if !a.is_empty() {
print!("\x1b[35;2m{a}\x1b[0m\n")
}
print!("\x1b[35m{b}")
}
}
println!("\x1b[0m");
}
} else {
println!("{text}");
}
Ok(())
}
#[tokio::main]
async fn main() -> ExitCode {
match run().await {
Err(e) => match e {
TrotErr::Response(ResponseErr::UnexpectedStatus(_, status, meta)) => {
println!("{meta}");
ExitCode::from(status.value())
}
_ => {
eprintln!("๐ Trot error :: {e}");
ExitCode::from(1)
}
},
Ok(_) => ExitCode::from(0),
}
}