Skip to main content

bear_cli/
runner.rs

1use anyhow::{Result, anyhow};
2use clap::Parser;
3
4use crate::bear::{join_tags, maybe_push, maybe_push_bool, open_bear_action};
5use crate::cli::Cli;
6use crate::cli::Commands;
7use crate::config::{encode_file, load_token, resolve_database_path, save_token};
8use crate::db::BearDb;
9
10pub fn run() -> Result<()> {
11    let cli = Cli::parse();
12    let db = match &cli.command {
13        Commands::OpenNote(_)
14        | Commands::Tags
15        | Commands::OpenTag(_)
16        | Commands::Search(_)
17        | Commands::Untagged(_)
18        | Commands::Todo(_)
19        | Commands::Today(_)
20        | Commands::Locked(_) => Some(BearDb::open(resolve_database_path(
21            cli.database.as_deref(),
22        )?)?),
23        _ => None,
24    };
25
26    match cli.command {
27        Commands::Auth(cmd) => {
28            save_token(&cmd.token)?;
29            println!("Saved API token.");
30        }
31        Commands::OpenNote(cmd) => {
32            let note = db
33                .as_ref()
34                .expect("db available for read command")
35                .find_note(cmd.id.as_deref(), cmd.title.as_deref(), cmd.exclude_trashed)?;
36            println!("{}", note.text);
37        }
38        Commands::Tags => {
39            for tag in db.as_ref().expect("db available for read command").tags()? {
40                println!("{tag}");
41            }
42        }
43        Commands::OpenTag(cmd) => {
44            for note in db
45                .as_ref()
46                .expect("db available for read command")
47                .notes_for_tags(&split_csv(&cmd.name), false)?
48            {
49                println!("{}\t{}", note.identifier, note.title);
50            }
51        }
52        Commands::Search(cmd) => {
53            for note in db.as_ref().expect("db available for read command").search(
54                cmd.term.as_deref(),
55                cmd.tag.as_deref(),
56                false,
57            )? {
58                println!("{}\t{}", note.identifier, note.title);
59            }
60        }
61        Commands::Untagged(cmd) => {
62            for note in db
63                .as_ref()
64                .expect("db available for read command")
65                .untagged(cmd.search.as_deref())?
66            {
67                println!("{}\t{}", note.identifier, note.title);
68            }
69        }
70        Commands::Todo(cmd) => {
71            for note in db
72                .as_ref()
73                .expect("db available for read command")
74                .todo(cmd.search.as_deref())?
75            {
76                println!("{}\t{}", note.identifier, note.title);
77            }
78        }
79        Commands::Today(cmd) => {
80            for note in db
81                .as_ref()
82                .expect("db available for read command")
83                .today(cmd.search.as_deref())?
84            {
85                println!("{}\t{}", note.identifier, note.title);
86            }
87        }
88        Commands::Locked(cmd) => {
89            for note in db
90                .as_ref()
91                .expect("db available for read command")
92                .locked(cmd.search.as_deref())?
93            {
94                println!("{}\t{}", note.identifier, note.title);
95            }
96        }
97        Commands::Create(cmd) => {
98            let mut query = Vec::new();
99            maybe_push(&mut query, "title", cmd.title);
100            maybe_push(&mut query, "text", cmd.text);
101            maybe_push(&mut query, "tags", join_tags(&cmd.tag));
102            maybe_push_bool(&mut query, "open_note", cmd.open_note);
103            maybe_push_bool(&mut query, "new_window", cmd.new_window);
104            maybe_push_bool(&mut query, "float", cmd.float);
105            maybe_push_bool(&mut query, "show_window", cmd.show_window);
106            maybe_push_bool(&mut query, "pin", cmd.pin);
107            maybe_push_bool(&mut query, "edit", cmd.edit);
108            maybe_push_bool(&mut query, "timestamp", cmd.timestamp);
109            maybe_push(&mut query, "type", cmd.kind);
110            maybe_push(&mut query, "url", cmd.url);
111
112            if let Some(file) = cmd.file {
113                let filename = cmd
114                    .filename
115                    .or_else(|| {
116                        file.file_name()
117                            .map(|name| name.to_string_lossy().into_owned())
118                    })
119                    .ok_or_else(|| {
120                        anyhow!("--filename is required when the file path has no file name")
121                    })?;
122                let encoded = encode_file(&file)?;
123                maybe_push(&mut query, "filename", Some(filename));
124                maybe_push(&mut query, "file", Some(encoded));
125            }
126
127            open_bear_action("create", &query)?;
128        }
129        Commands::AddText(cmd) => {
130            let mut query = Vec::new();
131            maybe_push(&mut query, "id", cmd.id);
132            maybe_push(&mut query, "title", cmd.title);
133            maybe_push(&mut query, "text", cmd.text);
134            maybe_push(&mut query, "header", cmd.header);
135            maybe_push(&mut query, "mode", Some(cmd.mode));
136            maybe_push(&mut query, "tags", join_tags(&cmd.tag));
137            maybe_push_bool(&mut query, "exclude_trashed", cmd.exclude_trashed);
138            maybe_push_bool(&mut query, "new_line", cmd.new_line);
139            maybe_push_bool(&mut query, "open_note", cmd.open_note);
140            maybe_push_bool(&mut query, "new_window", cmd.new_window);
141            maybe_push_bool(&mut query, "show_window", cmd.show_window);
142            maybe_push_bool(&mut query, "edit", cmd.edit);
143            maybe_push_bool(&mut query, "timestamp", cmd.timestamp);
144            open_bear_action("add-text", &query)?;
145        }
146        Commands::AddFile(cmd) => {
147            let mut query = Vec::new();
148            maybe_push(&mut query, "id", cmd.id);
149            maybe_push(&mut query, "title", cmd.title);
150            maybe_push(&mut query, "header", cmd.header);
151            maybe_push(&mut query, "mode", Some(cmd.mode));
152            maybe_push_bool(&mut query, "open_note", cmd.open_note);
153            maybe_push_bool(&mut query, "new_window", cmd.new_window);
154            maybe_push_bool(&mut query, "show_window", cmd.show_window);
155            maybe_push_bool(&mut query, "edit", cmd.edit);
156            maybe_push(
157                &mut query,
158                "filename",
159                cmd.filename.or_else(|| {
160                    cmd.file
161                        .file_name()
162                        .map(|name| name.to_string_lossy().into_owned())
163                }),
164            );
165            maybe_push(&mut query, "file", Some(encode_file(&cmd.file)?));
166            open_bear_action("add-file", &query)?;
167        }
168        Commands::GrabUrl(cmd) => {
169            let mut query = Vec::new();
170            maybe_push(&mut query, "url", Some(cmd.url));
171            maybe_push(&mut query, "tags", join_tags(&cmd.tag));
172            maybe_push_bool(&mut query, "pin", cmd.pin);
173            maybe_push_bool(&mut query, "wait", cmd.wait);
174            open_bear_action("grab-url", &query)?;
175        }
176        Commands::Trash(cmd) => {
177            let mut query = Vec::new();
178            maybe_push(&mut query, "id", cmd.id);
179            maybe_push(&mut query, "search", cmd.search);
180            maybe_push_bool(&mut query, "show_window", cmd.show_window);
181            open_bear_action("trash", &query)?;
182        }
183        Commands::Archive(cmd) => {
184            let mut query = Vec::new();
185            maybe_push(&mut query, "id", cmd.id);
186            maybe_push(&mut query, "search", cmd.search);
187            maybe_push_bool(&mut query, "show_window", cmd.show_window);
188            open_bear_action("archive", &query)?;
189        }
190        Commands::RenameTag(cmd) => {
191            let mut query = Vec::new();
192            maybe_push(&mut query, "name", Some(cmd.name));
193            maybe_push(&mut query, "new_name", Some(cmd.new_name));
194            maybe_push_bool(&mut query, "show_window", cmd.show_window);
195            open_bear_action("rename-tag", &query)?;
196        }
197        Commands::DeleteTag(cmd) => {
198            let mut query = Vec::new();
199            maybe_push(&mut query, "name", Some(cmd.name));
200            maybe_push_bool(&mut query, "show_window", cmd.show_window);
201            open_bear_action("delete-tag", &query)?;
202        }
203        Commands::Raw(cmd) => {
204            let mut params = cmd.params;
205            let has_token = params.iter().any(|(key, _)| key == "token");
206            if !has_token {
207                if let Some(token) = cmd.token {
208                    params.push(("token".into(), token));
209                } else if cmd.use_saved_token {
210                    if let Some(token) = load_token()? {
211                        params.push(("token".into(), token));
212                    }
213                }
214            }
215            open_bear_action(&cmd.action, &params)?;
216        }
217    }
218
219    Ok(())
220}
221
222fn split_csv(input: &str) -> Vec<String> {
223    input
224        .split(',')
225        .map(str::trim)
226        .filter(|part| !part.is_empty())
227        .map(ToOwned::to_owned)
228        .collect()
229}