thoth_cli/
cli.rs

1use crate::{
2    get_save_backup_file_path, load_textareas, save_textareas, EditorClipboard, ThemeMode,
3    ThothConfig,
4};
5use anyhow::{bail, Result};
6use std::{
7    fs::File,
8    io::{BufRead, BufReader, Write},
9};
10
11use std::env;
12
13use clap::{Parser, Subcommand};
14
15use crate::get_save_file_path;
16#[derive(Parser)]
17#[command(author = env!("CARGO_PKG_AUTHORS"), version = env!("CARGO_PKG_VERSION"), about, long_about = None, rename_all = "snake_case")]
18pub struct Cli {
19    #[command(subcommand)]
20    pub command: Option<Commands>,
21}
22
23#[derive(Subcommand)]
24#[command(rename_all = "snake_case")]
25pub enum Commands {
26    /// Add a new block to the scratchpad
27    Add {
28        /// Name of the block to be added
29        name: String,
30        /// Contents to be associated with the named block
31        content: Option<String>,
32    },
33    /// List all of the blocks within your thoth scratchpad
34    List,
35    /// Load backup file as the main thoth markdown file
36    LoadBackup,
37    /// Read the contents of the clipboard backup file
38    ReadClipboard,
39    /// Delete a block by name
40    Delete {
41        /// The name of the block to be deleted
42        name: String,
43    },
44    /// View (STDOUT) the contents of the block by name
45    View {
46        /// The name of the block to be used
47        name: String,
48    },
49    /// Copy the contents of a block to the system clipboard
50    Copy {
51        /// The name of the block to be used
52        name: String,
53    },
54    /// Set the theme to light or dark mode
55    Theme {
56        /// The theme to set: 'light' or 'dark'
57        mode: String,
58    },
59    /// Get the current theme
60    GetTheme,
61}
62
63pub fn set_theme(mode: &str) -> Result<()> {
64    let mode_lowercase = mode.to_lowercase();
65
66    let mut config = ThothConfig::load()?;
67
68    match mode_lowercase.as_str() {
69        "light" => {
70            config.set_theme(ThemeMode::Light)?;
71            println!("Theme set to light mode");
72        }
73        "dark" => {
74            config.set_theme(ThemeMode::Dark)?;
75            println!("Theme set to dark mode");
76        }
77        _ => {
78            bail!("Invalid theme mode. Use 'light' or 'dark'");
79        }
80    }
81
82    Ok(())
83}
84
85pub fn get_theme() -> Result<()> {
86    let config = ThothConfig::load()?;
87
88    match config.theme {
89        ThemeMode::Light => println!("Current theme: light"),
90        ThemeMode::Dark => println!("Current theme: dark"),
91    }
92
93    Ok(())
94}
95
96pub fn read_clipboard_backup() -> Result<()> {
97    let file_path = crate::get_clipboard_backup_file_path();
98    if !file_path.exists() {
99        println!("No clipboard backup file found at {}", file_path.display());
100        return Ok(());
101    }
102
103    let content = std::fs::read_to_string(&file_path)?;
104    if content.is_empty() {
105        println!("Clipboard backup file exists but is empty.");
106    } else {
107        println!("{}", content);
108    }
109    Ok(())
110}
111
112pub fn add_block(name: &str, content: &str) -> Result<()> {
113    let mut file = std::fs::OpenOptions::new()
114        .append(true)
115        .create(true)
116        .open(get_save_file_path())?;
117
118    writeln!(file, "# {}", name)?;
119    writeln!(file, "{}", content)?;
120    writeln!(file)?;
121
122    println!("Block '{}' added successfully.", name);
123    Ok(())
124}
125
126pub fn list_blocks() -> Result<()> {
127    let file = File::open(get_save_file_path())?;
128    let reader = BufReader::new(file);
129
130    for line in reader.lines() {
131        let line = line?;
132
133        if let Some(strip) = line.strip_prefix("# ") {
134            println!("{}", strip);
135        }
136    }
137
138    Ok(())
139}
140
141pub fn replace_from_backup() -> Result<()> {
142    let (backup_textareas, backup_textareas_titles) = load_textareas(get_save_backup_file_path())?;
143    save_textareas(
144        &backup_textareas,
145        &backup_textareas_titles,
146        get_save_file_path(),
147    )
148}
149
150pub fn view_block(name: &str) -> Result<()> {
151    let file = File::open(get_save_file_path())?;
152    let reader = BufReader::new(file);
153    let mut blocks = Vec::new();
154    let mut current_block = Vec::new();
155    let mut current_name = String::new();
156
157    for line in reader.lines() {
158        let line = line?;
159        if let Some(strip) = line.strip_prefix("# ") {
160            if !current_name.is_empty() {
161                blocks.push((current_name, current_block));
162                current_block = Vec::new();
163            }
164            current_name = strip.to_string();
165        } else {
166            current_block.push(line);
167        }
168    }
169
170    if !current_name.is_empty() {
171        blocks.push((current_name, current_block));
172    }
173
174    for (block_name, block_content) in blocks {
175        if block_name == name {
176            for line in block_content {
177                println!("{}", line);
178            }
179        }
180    }
181    Ok(())
182}
183
184pub fn copy_block(name: &str) -> Result<()> {
185    let file = File::open(get_save_file_path())?;
186    let reader = BufReader::new(file);
187    let mut blocks = Vec::new();
188    let mut current_block = Vec::new();
189    let mut current_name = String::new();
190    let mut matched_name: Option<String> = None;
191
192    for line in reader.lines() {
193        let line = line?;
194        if let Some(strip) = line.strip_prefix("# ") {
195            if !current_name.is_empty() {
196                blocks.push((current_name, current_block));
197                current_block = Vec::new();
198            }
199            current_name = strip.to_string();
200        } else {
201            current_block.push(line);
202        }
203    }
204
205    if !current_name.is_empty() {
206        blocks.push((current_name, current_block));
207    }
208
209    for (block_name, block_content) in blocks {
210        if block_name == name {
211            let result_ctx = EditorClipboard::new();
212
213            if result_ctx.is_err() {
214                bail!("Failed to create clipboard context for copy block");
215            }
216
217            let mut ctx = result_ctx.unwrap();
218
219            let is_success = ctx.set_contents(block_content.join("\n"));
220
221            if is_success.is_err() {
222                bail!(format!(
223                    "Failed to copy contents of block {} to system clipboard",
224                    block_name
225                ));
226            }
227            matched_name = Some(block_name);
228            break;
229        }
230    }
231    match matched_name {
232        Some(name) => println!("Successfully copied contents from block {}", name),
233        None => println!("Didn't find the block. Please try again. You can use `thoth list` to find the name of all blocks")
234    };
235
236    Ok(())
237}
238
239pub fn delete_block(name: &str) -> Result<()> {
240    let file = File::open(get_save_file_path())?;
241    let reader = BufReader::new(file);
242    let mut blocks = Vec::new();
243    let mut current_block = Vec::new();
244    let mut current_name = String::new();
245
246    for line in reader.lines() {
247        let line = line?;
248        if let Some(strip) = line.strip_prefix("# ") {
249            if !current_name.is_empty() {
250                blocks.push((current_name, current_block));
251                current_block = Vec::new();
252            }
253            current_name = strip.to_string();
254        } else {
255            current_block.push(line);
256        }
257    }
258
259    if !current_name.is_empty() {
260        blocks.push((current_name, current_block));
261    }
262
263    let mut file = File::create(get_save_file_path())?;
264    let mut deleted = false;
265
266    for (block_name, block_content) in blocks {
267        if block_name != name {
268            writeln!(file, "# {}", block_name)?;
269            for line in block_content {
270                writeln!(file, "{}", line)?;
271            }
272            writeln!(file)?;
273        } else {
274            deleted = true;
275        }
276    }
277
278    if deleted {
279        println!("Block '{}' deleted successfully.", name);
280    } else {
281        println!("Block '{}' not found.", name);
282    }
283
284    Ok(())
285}