/ src / main.rs
main.rs
 1  //! Entry point for `text_lazy_browser`.
 2  //!
 3  //! `main.rs` stays intentionally thin: argument parsing + handing off to the app runner.
 4  
 5  mod app;
 6  mod bundle;
 7  mod cache;
 8  mod net;
 9  mod parse;
10  mod privacy;
11  mod search;
12  mod util;
13  
14  use clap::Parser;
15  use std::path::PathBuf;
16  use url::Url;
17  
18  /// A tiny, privacy-first text-mode browser.
19  /// It fetches only HTML and downloads images only on explicit request.
20  #[derive(Parser, Debug)]
21  #[command(name = "text_lazy_browser", version, about)]
22  struct Args {
23      /// The URL to fetch and render.
24      #[arg(required_unless_present = "open_bundle")]
25      url: Option<String>,
26  
27      /// Where to save manually downloaded images.
28      #[arg(long, default_value = "downloads")]
29      download_dir: PathBuf,
30  
31      /// Base URL for the (optional) SearXNG instance used by the search UI.
32      #[arg(long, default_value = "https://searx.example.invalid")]
33      searx_url: String,
34  
35      /// Optional proxy URL (e.g. socks5h://127.0.0.1:9050).
36      #[arg(long, value_name = "URL")]
37      proxy: Option<String>,
38  
39      /// Open a saved bundle directory or bundle.json file.
40      #[arg(long, value_name = "PATH")]
41      open_bundle: Option<PathBuf>,
42  
43      /// Cache directory for HTML and downloads.
44      #[arg(long, default_value = "cache")]
45      cache_dir: PathBuf,
46  
47      /// Cache TTL in seconds (0 disables caching).
48      #[arg(long, default_value_t = 3600)]
49      cache_ttl_secs: u64,
50  
51      /// Allowlist for third-party image domains (repeatable).
52      #[arg(long, value_name = "DOMAIN")]
53      allow_domain: Vec<String>,
54  
55      /// Denylist for image domains (repeatable).
56      #[arg(long, value_name = "DOMAIN")]
57      deny_domain: Vec<String>,
58  }
59  
60  fn main() -> anyhow::Result<()> {
61      let args = Args::parse();
62  
63      let start = if let Some(bundle_path) = args.open_bundle {
64          app::StartMode::Bundle(bundle_path)
65      } else {
66          let raw = args.url.expect("url required when no bundle");
67          let url = Url::parse(&raw).map_err(|e| anyhow::anyhow!("Invalid URL {:?}: {}", raw, e))?;
68          app::StartMode::Url(url)
69      };
70  
71      let opts = app::RunOpts {
72          start,
73          download_dir: args.download_dir,
74          searx_url: args.searx_url,
75          proxy: args.proxy,
76          cache_dir: args.cache_dir,
77          cache_ttl_secs: args.cache_ttl_secs,
78          allow_domains: args.allow_domain,
79          deny_domains: args.deny_domain,
80      };
81  
82      app::run(opts)
83  }