use anyhow::{Context, Result}; use clap::{Parser, Subcommand}; use rand::prelude::IndexedRandom; use reqwest; use std::{ string::String, }; use std::fs::File; use std::io::Write; mod config; mod fetch; mod parse; // Setup the command line options #[derive(Parser)] #[command(about = "Retrieve the latest UserAgent strings", long_about=None, arg_required_else_help = true, after_help = "This is very specific to the website's HTML.\nIf it changes, this program might no longer work.")] struct Cli { // Update UserAgent versions // #[arg(short, long, action=clap::ArgAction::SetTrue)] // update: bool, #[command(subcommand)] command: Option, } #[derive(Subcommand)] enum Commands { /// Display the latest UserAgents Display {}, /// Fetch the latest UserAgents Fetch {}, } /// Configuration filename const CONFIG_FILE: &str = "latest.config"; const FIREFOX_URL: &str = "https://www.whatismybrowser.com/guides/the-latest-user-agent/firefox"; const GCHROME_URL: &str = "https://www.whatismybrowser.com/guides/the-latest-user-agent/chrome"; fn random_user_agent(config: &config::Config) -> String { // Pick a random user_agent, or default to something "sane"... if config.user_agents.len() == 0 { // No sensible defaults. return String::from( "Mozilla/5.0 (X11; Linux x86_64; rv:135.0) Gecko/20100101 Firefox/135.0", ); } // Ok, do this! config.user_agents.choose(&mut rand::rng()).unwrap().clone() } fn save_html(filename: &str, html: &str) -> Result<()> { let mut file = File::create(filename) .with_context(|| format!("Failed to write file [{:?}].", filename))?; file.write_all(html.as_bytes()) .with_context(|| format!("Failed to write all to [{:?}].", filename))?; Ok(()) } fn main() -> Result<()> { let mut config = config::read_config(CONFIG_FILE)?; let cli = Cli::parse(); match &cli.command { Some(Commands::Fetch {}) => { let ua = random_user_agent(&config); let client = reqwest::blocking::Client::builder() .user_agent(&ua) .build()?; let result = fetch::fetch(&client, FIREFOX_URL)?; let filename = fetch::filename_from_url(FIREFOX_URL)?; save_html(&filename, &result)?; let mut agents = parse::find_useragents(&result)?; config.user_agents.clear(); config.user_agents.append(&mut agents); let result = fetch::fetch(&client, GCHROME_URL)?; let filename = fetch::filename_from_url(GCHROME_URL)?; save_html(&filename, &result)?; agents = parse::find_useragents(&result)?; config.user_agents.append(&mut agents); config::write_config(CONFIG_FILE, &config)?; println!("Ok! {} agents found.", config.user_agents.len()); } Some(Commands::Display {}) => { println!("Display..."); for ua in config.user_agents { println!(" {}", ua); } return Ok(()); } None => { println!("I didn't see a command. Displaying help.\n"); let _show_help: Cli = Cli::parse_from(["--help"]); } } Ok(()) }