use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use anyhow::{bail, Context, Result};
use ignore::WalkBuilder;
fn find_markdown_files(root: &Path) -> Vec<PathBuf> {
WalkBuilder::new(root)
.build()
.filter_map(|entry| entry.ok())
.filter(|entry| {
entry
.file_type()
.is_some_and(|file_type| file_type.is_file())
&& entry
.path()
.extension()
.and_then(|extension| extension.to_str())
.is_some_and(|extension| extension.eq_ignore_ascii_case("md"))
})
.map(|entry| entry.into_path())
.collect()
}
pub fn pick_markdown_file(root: &Path) -> Result<Option<PathBuf>> {
let mut files = find_markdown_files(root);
if files.is_empty() {
bail!("No Markdown files found below {}", root.display());
}
files.sort();
let mut fzf = Command::new("fzf")
.arg("--prompt=mdpick> ")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.context(
"Failed to run fzf; mdpick requires fzf to be installed, \
see https://github.com/junegunn/fzf",
)?;
{
let stdin = fzf
.stdin
.as_mut()
.expect("fzf stdin is piped and available");
for file in &files {
writeln!(stdin, "{}", file.display())?;
}
}
let output = fzf
.wait_with_output()
.context("Failed to read the file selected in fzf")?;
if !output.status.success() {
return Ok(None);
}
let selection = String::from_utf8(output.stdout)
.context("fzf produced non-UTF-8 output")?
.trim()
.to_string();
if selection.is_empty() {
return Ok(None);
}
Ok(Some(PathBuf::from(selection)))
}