yazi_plugin/external/
fd.rs1use std::{path::Path, process::Stdio};
2
3use anyhow::Result;
4use tokio::{io::{AsyncBufReadExt, BufReader}, process::{Child, Command}, sync::mpsc::{self, UnboundedReceiver}};
5use yazi_fs::{File, FsUrl};
6use yazi_shared::url::{AsUrl, UrlBuf, UrlLike};
7use yazi_vfs::VfsFile;
8
9pub struct FdOpt {
10 pub cwd: UrlBuf,
11 pub hidden: bool,
12 pub subject: String,
13 pub args: Vec<String>,
14}
15
16pub fn fd(opt: FdOpt) -> Result<UnboundedReceiver<File>> {
17 let mut child = spawn("fd", &opt).or_else(|_| spawn("fdfind", &opt))?;
18
19 let mut it = BufReader::new(child.stdout.take().unwrap()).lines();
20 let (tx, rx) = mpsc::unbounded_channel();
21
22 tokio::spawn(async move {
23 while let Ok(Some(line)) = it.next_line().await {
24 if Path::new(&line).is_absolute() {
25 continue;
26 }
27 let Ok(url) = opt.cwd.try_join(line) else {
28 continue;
29 };
30 if let Ok(file) = File::new(url).await {
31 tx.send(file).ok();
32 }
33 }
34 child.wait().await.ok();
35 });
36 Ok(rx)
37}
38
39fn spawn(program: &str, opt: &FdOpt) -> std::io::Result<Child> {
40 Command::new(program)
41 .arg("--base-directory")
42 .arg(&*opt.cwd.as_url().unified_path())
43 .arg("--regex")
44 .arg(if opt.hidden { "--hidden" } else { "--no-hidden" })
45 .args(&opt.args)
46 .arg(&opt.subject)
47 .kill_on_drop(true)
48 .stdout(Stdio::piped())
49 .stderr(Stdio::null())
50 .spawn()
51}