Skip to main content

crepe_bordeaux/
lib.rs

1use anyhow::Result;
2use arboard::Clipboard;
3use std::env;
4use std::fmt::{Display, Error, Formatter};
5use std::fs;
6use std::path::{Path, PathBuf};
7
8pub fn copy(content: &str, register: Option<&str>, verbose: bool) -> Result<()> {
9    match register {
10        Some(filename) => copy_to_file(content, filename, verbose),
11        None => match Clipboard::new() {
12            Ok(mut clipboard) => {
13                clipboard.set_text(content.to_owned())?;
14                if verbose {
15                    println!("{}", &content);
16                    println!("copied successfully to system clipboard");
17                }
18                Ok(())
19            }
20            Err(_) => copy_to_file(content, "default", verbose),
21        },
22    }
23}
24
25pub fn paste(register: Option<&str>, verbose: bool) -> Result<()> {
26    match register {
27        Some(filename) => {
28            let path = get_register_path(filename)?;
29            paste_from_file(path, verbose)
30        }
31        None => match Clipboard::new() {
32            Ok(mut clipboard) => {
33                match clipboard.get_text() {
34                    Ok(text) => print!("{}", text),
35                    Err(_) => print!(""),
36                }
37                Ok(())
38            }
39            Err(_) => {
40                let path = get_register_path("default")?;
41                paste_from_file(path, verbose)
42            }
43        },
44    }
45}
46
47fn get_register_path(filename: &str) -> Result<PathBuf> {
48    let mut path = get_register_dir()?;
49    path.push(Path::new(filename).file_name().unwrap()); // escaping so a string like "../../foo" won't leave the cb directory.
50    path.set_extension("txt");
51    Ok(path)
52}
53
54pub fn get_register_dir() -> Result<PathBuf> {
55    let mut path;
56    match env::var("CB_DIR") {
57        Ok(cb_dir_str) => {
58            path = PathBuf::from(cb_dir_str);
59        }
60        Err(_) => {
61            path = env::temp_dir();
62            path.push("crepe-bordeaux");
63        }
64    };
65    if !path.is_dir() {
66        fs::create_dir_all(&path)?;
67    }
68    Ok(path)
69}
70
71fn get_register_paths() -> Result<Vec<PathBuf>> {
72    return Ok(fs::read_dir(get_register_dir()?)?
73        .map(|res| res.map(|e| e.path()))
74        .filter_map(|x| x.ok())
75        .filter(|x| x.is_file())
76        .filter(|x| x.extension().is_some_and(|extension| extension == "txt"))
77        .filter(|x| x.file_stem().is_some_and(|file_stem| !file_stem.is_empty()))
78        .collect::<Vec<PathBuf>>());
79}
80
81fn copy_to_file(content: &str, filename: &str, verbose: bool) -> Result<()> {
82    let path = get_register_path(filename)?;
83    fs::write(path, content)?;
84    if verbose {
85        println!("{}", &content);
86        println!("copied successfully to register: {}", filename);
87    }
88    Ok(())
89}
90
91fn paste_from_file(filepath: PathBuf, verbose: bool) -> Result<()> {
92    match filepath.is_file() {
93        true => {
94            let x = fs::read_to_string(filepath)?;
95            println!("{}", x);
96            Ok(())
97        }
98        false => {
99            if verbose {
100                eprintln!("register not found");
101            } else {
102                print!("");
103            }
104            Ok(())
105        }
106    }
107}
108
109pub fn list() -> Result<()> {
110    let register_paths = get_register_paths()?;
111    let mut register_names = register_paths
112        .iter()
113        .map(|x| String::from(x.file_stem().unwrap().to_str().unwrap()))
114        .collect::<Vec<String>>();
115
116    register_names.sort();
117
118    print!("{}", StringVec(register_names));
119
120    Ok(())
121}
122
123pub fn dump() -> Result<()> {
124    let mut register_paths = get_register_paths()?;
125
126    register_paths.sort();
127
128    let register_contents = register_paths
129        .iter()
130        .filter_map(|path| fs::read_to_string(path).ok())
131        .collect::<Vec<String>>();
132
133    println!("{}", StringVec(register_contents));
134
135    Ok(())
136}
137
138pub fn clear(register: Option<&str>) -> Result<()> {
139    match register {
140        Some(filename) => {
141            let path = get_register_path(filename)?;
142            fs::remove_file(path)?;
143            Ok(())
144        }
145        None => match Clipboard::new() {
146            Ok(mut clipboard) => {
147                clipboard.clear()?;
148                Ok(())
149            }
150            Err(_) => {
151                let path = get_register_path("default")?;
152                fs::remove_file(path)?;
153                Ok(())
154            }
155        },
156    }
157}
158
159pub fn clear_all() -> Result<()> {
160    let register_paths = get_register_paths()?;
161    for path in register_paths {
162        fs::remove_file(path)?;
163    }
164
165    if let Ok(mut clipboard) = Clipboard::new() {
166        clipboard.clear()?;
167    };
168
169    Ok(())
170}
171
172struct StringVec(Vec<String>);
173
174impl Display for StringVec {
175    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
176        let mut buffer = String::new();
177
178        for item in &self.0 {
179            buffer.push_str(item);
180            buffer.push('\n');
181        }
182
183        write!(f, "{}", buffer.trim())
184    }
185}