main.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. use anyhow::{Context, Result};
  2. use clap::{Parser, Subcommand};
  3. use rand::prelude::IndexedRandom;
  4. use reqwest;
  5. use std::{
  6. string::String,
  7. };
  8. use std::fs::File;
  9. use std::io::Write;
  10. mod config;
  11. mod fetch;
  12. mod parse;
  13. // Setup the command line options
  14. #[derive(Parser)]
  15. #[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.")]
  16. struct Cli {
  17. // Update UserAgent versions
  18. // #[arg(short, long, action=clap::ArgAction::SetTrue)]
  19. // update: bool,
  20. #[command(subcommand)]
  21. command: Option<Commands>,
  22. }
  23. #[derive(Subcommand)]
  24. enum Commands {
  25. /// Display the latest UserAgents
  26. Display {},
  27. /// Fetch the latest UserAgents
  28. Fetch {},
  29. }
  30. /// Configuration filename
  31. const CONFIG_FILE: &str = "latest.config";
  32. const FIREFOX_URL: &str = "https://www.whatismybrowser.com/guides/the-latest-user-agent/firefox";
  33. const GCHROME_URL: &str = "https://www.whatismybrowser.com/guides/the-latest-user-agent/chrome";
  34. fn random_user_agent(config: &config::Config) -> String {
  35. // Pick a random user_agent, or default to something "sane"...
  36. if config.user_agents.len() == 0 {
  37. // No sensible defaults.
  38. return String::from(
  39. "Mozilla/5.0 (X11; Linux x86_64; rv:135.0) Gecko/20100101 Firefox/135.0",
  40. );
  41. }
  42. // Ok, do this!
  43. config.user_agents.choose(&mut rand::rng()).unwrap().clone()
  44. }
  45. fn save_html(filename: &str, html: &str) -> Result<()> {
  46. let mut file = File::create(filename)
  47. .with_context(|| format!("Failed to write file [{:?}].", filename))?;
  48. file.write_all(html.as_bytes())
  49. .with_context(|| format!("Failed to write all to [{:?}].", filename))?;
  50. Ok(())
  51. }
  52. fn main() -> Result<()> {
  53. let mut config = config::read_config(CONFIG_FILE)?;
  54. let cli = Cli::parse();
  55. match &cli.command {
  56. Some(Commands::Fetch {}) => {
  57. let ua = random_user_agent(&config);
  58. let client = reqwest::blocking::Client::builder()
  59. .user_agent(&ua)
  60. .build()?;
  61. let result = fetch::fetch(&client, FIREFOX_URL)?;
  62. let filename = fetch::filename_from_url(FIREFOX_URL)?;
  63. save_html(&filename, &result)?;
  64. let mut agents = parse::find_useragents(&result)?;
  65. config.user_agents.clear();
  66. config.user_agents.append(&mut agents);
  67. let result = fetch::fetch(&client, GCHROME_URL)?;
  68. let filename = fetch::filename_from_url(GCHROME_URL)?;
  69. save_html(&filename, &result)?;
  70. agents = parse::find_useragents(&result)?;
  71. config.user_agents.append(&mut agents);
  72. config::write_config(CONFIG_FILE, &config)?;
  73. println!("Ok! {} agents found.", config.user_agents.len());
  74. }
  75. Some(Commands::Display {}) => {
  76. println!("Display...");
  77. for ua in config.user_agents {
  78. println!(" {}", ua);
  79. }
  80. return Ok(());
  81. }
  82. None => {
  83. println!("I didn't see a command. Displaying help.\n");
  84. let _show_help: Cli = Cli::parse_from(["--help"]);
  85. }
  86. }
  87. Ok(())
  88. }