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 }