tenseleyflow/gump / 0336f87

Browse files

import from zoxide, autojump, z, z.lua, and fasd

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
0336f8714dbf43d14fe314bff912325d5c8ba6c7
Parents
655b33b
Tree
443746a

3 changed files

StatusFile+-
M Cargo.lock 35 2
M Cargo.toml 1 0
M src/cmd/import.rs 167 40
Cargo.lockmodified
@@ -201,7 +201,16 @@ version = "5.0.1"
201201
 source = "registry+https://github.com/rust-lang/crates.io-index"
202202
 checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
203203
 dependencies = [
204
- "dirs-sys",
204
+ "dirs-sys 0.4.1",
205
+]
206
+
207
+[[package]]
208
+name = "dirs"
209
+version = "6.0.0"
210
+source = "registry+https://github.com/rust-lang/crates.io-index"
211
+checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
212
+dependencies = [
213
+ "dirs-sys 0.5.0",
205214
 ]
206215
 
207216
 [[package]]
@@ -212,10 +221,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
212221
 dependencies = [
213222
  "libc",
214223
  "option-ext",
215
- "redox_users",
224
+ "redox_users 0.4.6",
216225
  "windows-sys 0.48.0",
217226
 ]
218227
 
228
+[[package]]
229
+name = "dirs-sys"
230
+version = "0.5.0"
231
+source = "registry+https://github.com/rust-lang/crates.io-index"
232
+checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
233
+dependencies = [
234
+ "libc",
235
+ "option-ext",
236
+ "redox_users 0.5.2",
237
+ "windows-sys 0.61.2",
238
+]
239
+
219240
 [[package]]
220241
 name = "either"
221242
 version = "1.15.0"
@@ -275,6 +296,7 @@ dependencies = [
275296
  "chrono",
276297
  "clap",
277298
  "directories",
299
+ "dirs",
278300
  "nucleo",
279301
  "serde",
280302
  "serde_json",
@@ -512,6 +534,17 @@ dependencies = [
512534
  "thiserror 1.0.69",
513535
 ]
514536
 
537
+[[package]]
538
+name = "redox_users"
539
+version = "0.5.2"
540
+source = "registry+https://github.com/rust-lang/crates.io-index"
541
+checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
542
+dependencies = [
543
+ "getrandom 0.2.17",
544
+ "libredox",
545
+ "thiserror 2.0.17",
546
+]
547
+
515548
 [[package]]
516549
 name = "rustix"
517550
 version = "1.1.3"
Cargo.tomlmodified
@@ -18,6 +18,7 @@ chrono = { version = "0.4", features = ["serde"] }
1818
 thiserror = "2"
1919
 nucleo = "0.5"
2020
 serde_json = "1"
21
+dirs = "6"
2122
 
2223
 [dev-dependencies]
2324
 tempfile = "3"
src/cmd/import.rsmodified
@@ -1,13 +1,35 @@
1
+use std::fs;
12
 use std::io::{BufRead, BufReader};
3
+use std::path::PathBuf;
24
 use std::process::{Command, Stdio};
35
 
46
 use crate::db::Database;
57
 
6
-use super::{CmdError, Result};
8
+use super::Result;
79
 
8
-/// Import directories from zoxide database.
10
+/// Import directories from various tools (zoxide, autojump, z, fasd).
911
 pub fn run() -> Result<()> {
10
-    // Try to get entries from zoxide CLI
12
+    let mut db = Database::open()?;
13
+    let mut total_imported = 0;
14
+
15
+    // Try each source
16
+    total_imported += import_zoxide(&mut db)?;
17
+    total_imported += import_autojump(&mut db)?;
18
+    total_imported += import_z(&mut db)?;
19
+    total_imported += import_fasd(&mut db)?;
20
+
21
+    if total_imported > 0 {
22
+        db.save()?;
23
+        println!("Imported {} total entries", total_imported);
24
+    } else {
25
+        println!("No databases found to import from");
26
+    }
27
+
28
+    Ok(())
29
+}
30
+
31
+/// Import from zoxide using its CLI.
32
+fn import_zoxide(db: &mut Database) -> Result<usize> {
1133
     let output = Command::new("zoxide")
1234
         .args(["query", "--list", "--score"])
1335
         .stdout(Stdio::piped())
@@ -16,72 +38,177 @@ pub fn run() -> Result<()> {
1638
 
1739
     let output = match output {
1840
         Ok(o) if o.status.success() => o,
19
-        Ok(_) => {
20
-            return Err(CmdError::Other(
21
-                "zoxide query failed - is zoxide installed and initialized?".to_string(),
22
-            ));
23
-        }
24
-        Err(_) => {
25
-            return Err(CmdError::Other(
26
-                "zoxide not found - please install zoxide first".to_string(),
27
-            ));
28
-        }
41
+        _ => return Ok(0),
2942
     };
3043
 
31
-    let mut db = Database::open()?;
3244
     let mut imported = 0;
33
-    let mut skipped = 0;
34
-
3545
     let reader = BufReader::new(output.stdout.as_slice());
46
+
3647
     for line in reader.lines() {
3748
         let line = line?;
38
-        let line = line.trim();
49
+        if let Some((score, path)) = parse_score_path(&line) {
50
+            if db.import_entry(path, score).is_ok() {
51
+                imported += 1;
52
+            }
53
+        }
54
+    }
55
+
56
+    if imported > 0 {
57
+        println!("  zoxide: {} entries", imported);
58
+    }
59
+    Ok(imported)
60
+}
61
+
62
+/// Import from autojump (~/.local/share/autojump/autojump.txt).
63
+fn import_autojump(db: &mut Database) -> Result<usize> {
64
+    let path = dirs_autojump();
65
+    if !path.exists() {
66
+        return Ok(0);
67
+    }
68
+
69
+    let content = fs::read_to_string(&path)?;
70
+    let mut imported = 0;
3971
 
72
+    // Format: "score\tpath" (tab-separated)
73
+    for line in content.lines() {
74
+        let line = line.trim();
4075
         if line.is_empty() {
4176
             continue;
4277
         }
4378
 
44
-        // Parse "  123.4 /path/to/dir" format
45
-        if let Some((score_str, path)) = parse_zoxide_line(line) {
46
-            let score: f64 = match score_str.parse() {
47
-                Ok(s) => s,
48
-                Err(_) => {
49
-                    skipped += 1;
50
-                    continue;
79
+        if let Some((score_str, path)) = line.split_once('\t') {
80
+            if let Ok(score) = score_str.parse::<f64>() {
81
+                if db.import_entry(path, score).is_ok() {
82
+                    imported += 1;
5183
                 }
52
-            };
84
+            }
85
+        }
86
+    }
5387
 
54
-            // Import with the zoxide score
55
-            if let Err(_) = db.import_entry(path, score) {
56
-                skipped += 1;
57
-            } else {
58
-                imported += 1;
88
+    if imported > 0 {
89
+        println!("  autojump: {} entries", imported);
90
+    }
91
+    Ok(imported)
92
+}
93
+
94
+/// Import from z/z.lua/zsh-z (~/.z).
95
+fn import_z(db: &mut Database) -> Result<usize> {
96
+    // Check both ~/.z and $Z_DATA / $_Z_DATA
97
+    let paths: Vec<PathBuf> = [
98
+        Some(dirs_z()),
99
+        std::env::var("_Z_DATA").map(PathBuf::from).ok(),
100
+        std::env::var("Z_DATA").map(PathBuf::from).ok(),
101
+        std::env::var("ZSHZ_DATA").map(PathBuf::from).ok(),
102
+    ]
103
+    .into_iter()
104
+    .flatten()
105
+    .collect();
106
+
107
+    let mut imported = 0;
108
+
109
+    for path in paths {
110
+        if !path.exists() {
111
+            continue;
112
+        }
113
+
114
+        let content = match fs::read_to_string(&path) {
115
+            Ok(c) => c,
116
+            Err(_) => continue,
117
+        };
118
+
119
+        // Format: "path|score|timestamp" (pipe-separated)
120
+        for line in content.lines() {
121
+            let line = line.trim();
122
+            if line.is_empty() {
123
+                continue;
124
+            }
125
+
126
+            let parts: Vec<&str> = line.split('|').collect();
127
+            if parts.len() >= 2 {
128
+                let path = parts[0];
129
+                if let Ok(score) = parts[1].parse::<f64>() {
130
+                    if db.import_entry(path, score).is_ok() {
131
+                        imported += 1;
132
+                    }
133
+                }
59134
             }
60
-        } else {
61
-            skipped += 1;
62135
         }
63136
     }
64137
 
65
-    db.save()?;
138
+    if imported > 0 {
139
+        println!("  z/z.lua: {} entries", imported);
140
+    }
141
+    Ok(imported)
142
+}
66143
 
67
-    println!("Imported {} entries from zoxide", imported);
68
-    if skipped > 0 {
69
-        println!("Skipped {} entries (parse errors or excluded paths)", skipped);
144
+/// Import from fasd (~/.fasd).
145
+fn import_fasd(db: &mut Database) -> Result<usize> {
146
+    let path = dirs_fasd();
147
+    if !path.exists() {
148
+        return Ok(0);
70149
     }
71150
 
72
-    Ok(())
151
+    let content = fs::read_to_string(&path)?;
152
+    let mut imported = 0;
153
+
154
+    // Format: "path|score|timestamp" (similar to z)
155
+    for line in content.lines() {
156
+        let line = line.trim();
157
+        if line.is_empty() {
158
+            continue;
159
+        }
160
+
161
+        let parts: Vec<&str> = line.split('|').collect();
162
+        if parts.len() >= 2 {
163
+            let path = parts[0];
164
+            if let Ok(score) = parts[1].parse::<f64>() {
165
+                // fasd tracks files too, only import directories
166
+                if std::path::Path::new(path).is_dir() {
167
+                    if db.import_entry(path, score).is_ok() {
168
+                        imported += 1;
169
+                    }
170
+                }
171
+            }
172
+        }
173
+    }
174
+
175
+    if imported > 0 {
176
+        println!("  fasd: {} entries", imported);
177
+    }
178
+    Ok(imported)
73179
 }
74180
 
75
-/// Parse a zoxide output line: "  123.4 /path/to/dir"
76
-fn parse_zoxide_line(line: &str) -> Option<(&str, &str)> {
181
+/// Parse "  123.4 /path/to/dir" format (zoxide output).
182
+fn parse_score_path(line: &str) -> Option<(f64, &str)> {
77183
     let line = line.trim_start();
78184
     let space_idx = line.find(' ')?;
79
-    let score = &line[..space_idx];
185
+    let score_str = &line[..space_idx];
80186
     let path = line[space_idx..].trim_start();
81187
 
82188
     if path.is_empty() {
83189
         return None;
84190
     }
85191
 
192
+    let score = score_str.parse().ok()?;
86193
     Some((score, path))
87194
 }
195
+
196
+fn dirs_autojump() -> PathBuf {
197
+    if let Some(data) = dirs::data_local_dir() {
198
+        data.join("autojump").join("autojump.txt")
199
+    } else {
200
+        PathBuf::from("~/.local/share/autojump/autojump.txt")
201
+    }
202
+}
203
+
204
+fn dirs_z() -> PathBuf {
205
+    dirs::home_dir()
206
+        .map(|h| h.join(".z"))
207
+        .unwrap_or_else(|| PathBuf::from("~/.z"))
208
+}
209
+
210
+fn dirs_fasd() -> PathBuf {
211
+    dirs::home_dir()
212
+        .map(|h| h.join(".fasd"))
213
+        .unwrap_or_else(|| PathBuf::from("~/.fasd"))
214
+}