Skip to main content

bookmarks/
cli.rs

1use anyhow::Result;
2use clap::Parser;
3use std::path::PathBuf;
4
5use crate::config::{edit_config, print_config};
6use crate::open::open_links;
7use crate::storage::Storage;
8use crate::toml_storage::TomlStorage;
9
10#[derive(Parser, Debug)]
11#[command(name = "bookmarks")]
12#[command(about = "Bookmarks in your terminal")]
13#[command(version)]
14pub struct Args {
15    /// Path to bookmarks file (overrides cwd and global)
16    #[arg(short = 'f', long = "bookmarks-file")]
17    pub bookmarks_file: Option<PathBuf>,
18
19    /// Configure bookmarks
20    #[arg(short, long)]
21    pub config: bool,
22
23    /// Open the desktop app
24    #[cfg(feature = "app")]
25    #[arg(short = 'a', long)]
26    pub app: bool,
27
28    /// Open the webapp
29    #[cfg(feature = "webapp")]
30    #[arg(short = 'w', long)]
31    pub webapp: bool,
32
33    /// Things to open
34    pub links: Vec<String>,
35}
36
37/// Resolve which bookmarks file to use and ensure it exists:
38/// 1. --bookmarks-file flag (explicit, must exist)
39/// 2. bookmarks.toml in cwd (local, must exist)
40/// 3. ~/.config/bookmarks/bookmarks.toml (global, auto-created)
41fn resolve_storage(bookmarks_file: Option<PathBuf>) -> Result<TomlStorage> {
42    if let Some(path) = bookmarks_file {
43        anyhow::ensure!(path.exists(), "bookmarks file not found: {}", path.display());
44        return Ok(TomlStorage::new(path));
45    }
46
47    if let Some(cwd_path) = TomlStorage::cwd_path() {
48        if cwd_path.exists() {
49            return Ok(TomlStorage::new(cwd_path));
50        }
51    }
52
53    let storage = TomlStorage::with_default_path()?;
54    storage.init()?;
55    Ok(storage)
56}
57
58pub fn run<I, T>(args: I) -> Result<()>
59where
60    I: IntoIterator<Item = T>,
61    T: Into<std::ffi::OsString> + Clone,
62{
63    let args = Args::parse_from(args);
64
65    let storage = resolve_storage(args.bookmarks_file)?;
66
67    #[cfg(feature = "app")]
68    if args.app {
69        return crate::app::run(Box::new(storage)).map_err(|e| anyhow::anyhow!("{e}"));
70    }
71
72    #[cfg(feature = "webapp")]
73    if args.webapp {
74        return crate::webapp::run(Box::new(storage));
75    }
76
77    if args.config {
78        if let Some(path) = storage.path() {
79            edit_config(path)?;
80        }
81        return Ok(());
82    }
83
84    let config = storage.load()?;
85
86    if args.links.is_empty() {
87        print_config(&config);
88    } else {
89        open_links(&args.links, &config)?;
90    }
91
92    if let Some(path) = storage.path() {
93        println!("(using {}, use --bookmarks-file to override)", path.display());
94    }
95
96    Ok(())
97}