1use anyhow::{Context, Result};
2use clap::Parser;
3use std::path::PathBuf;
4
5use bookmarks_core::config::{edit_config, print_config};
6use bookmarks_core::open::open_links;
7use bookmarks_core::storage::Storage;
8use bookmarks_core::toml_storage::TomlStorage;
9
10#[derive(Parser, Debug)]
11#[command(name = "bookmarks")]
12#[command(about = "Bookmarks in your filesystem")]
13#[command(version)]
14pub struct Args {
15 #[arg(short = 'f', long = "bookmarks-file", conflicts_with = "global")]
17 pub bookmarks_file: Option<PathBuf>,
18
19 #[arg(short, long, conflicts_with_all = ["bookmarks_file", "local"])]
21 pub global: bool,
22
23 #[arg(short, long, conflicts_with_all = ["bookmarks_file", "global"])]
25 pub local: bool,
26
27 #[arg(short, long)]
29 pub config: bool,
30
31 #[cfg(feature = "app")]
33 #[arg(short = 'a', long)]
34 pub app: bool,
35
36 #[cfg(feature = "webapp")]
38 #[arg(short = 'w', long)]
39 pub webapp: bool,
40
41 pub urls: Vec<String>,
43}
44
45fn resolve_storage(
52 bookmarks_file: Option<PathBuf>,
53 global: bool,
54 local: bool,
55) -> Result<TomlStorage> {
56 if let Some(path) = bookmarks_file {
57 anyhow::ensure!(
58 path.exists(),
59 "bookmarks file not found: {}",
60 path.display()
61 );
62 return Ok(TomlStorage::new(path));
63 }
64
65 if local {
66 let cwd_path = TomlStorage::cwd_path().context("failed to get current directory")?;
67 let storage = TomlStorage::new(cwd_path);
68 storage.init()?;
69 return Ok(storage);
70 }
71
72 if !global
73 && let Some(cwd_path) = TomlStorage::cwd_path()
74 && cwd_path.exists()
75 {
76 return Ok(TomlStorage::new(cwd_path));
77 }
78
79 let storage = TomlStorage::with_default_path()?;
80 storage.init()?;
81 Ok(storage)
82}
83
84pub fn run_cli<I, T>(args: I) -> Result<()>
85where
86 I: IntoIterator<Item = T>,
87 T: Into<std::ffi::OsString> + Clone,
88{
89 let args = Args::parse_from(args);
90
91 let storage = resolve_storage(args.bookmarks_file, args.global, args.local)?;
92
93 #[cfg(feature = "app")]
94 if args.app {
95 return bookmarks_app::run_app(Box::new(storage)).map_err(|e| anyhow::anyhow!("{e}"));
96 }
97
98 #[cfg(feature = "webapp")]
99 if args.webapp {
100 return bookmarks_webapp::run_webapp(Box::new(storage));
101 }
102
103 if args.config {
104 match storage.path() {
105 Some(path) => edit_config(path)?,
106 None => anyhow::bail!("no bookmarks file found to edit"),
107 }
108 return Ok(());
109 }
110
111 let config = storage.load()?;
112
113 if args.urls.is_empty() {
114 print_config(&config);
115 } else {
116 open_links(&args.urls, &config)?;
117 }
118
119 if let Some(path) = storage.path() {
120 println!(
121 "(using {}, use --bookmarks-file to override)",
122 path.display()
123 );
124 }
125
126 Ok(())
127}