use figment::Provider; use std::{ ffi::OsStr, io::Write, ops::{Deref, DerefMut}, os::unix::prelude::OsStrExt, process::{Child, Command, Output, Stdio}, }; pub use config::Config; pub use error::{Error, Result}; mod config; mod error; pub struct Finder { program: String, args: Vec, } impl Finder { pub fn new() -> Result { Self::from_provider(Config::figment()) } /// Extract `Config` from `provider` to construct new `Finder` pub fn from_provider(provider: T) -> Result { Config::extract(&provider) .map(|config| Finder { program: config.program, args: config.args, }) .map_err(Into::into) } pub fn spawn(self) -> Result { Command::new(self.program) .args(self.args) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() .map(FinderChild) .map_err(Into::into) } } pub struct FinderChild(Child); impl FinderChild { pub fn find(mut self, items: V) -> Result where V: IntoIterator, I: AsRef, { let stdin = self.stdin.as_mut().ok_or(Error::Stdin)?; items.into_iter().try_for_each(|item| -> Result<()> { stdin.write_all(item.as_ref().as_bytes())?; stdin.write_all("\n".as_bytes()).map_err(From::from) })?; self.0.wait_with_output().map_err(Into::into) } } impl Deref for FinderChild { type Target = Child; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for FinderChild { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn test_output() { let items = Vec::from(["item1"]); let finder = Finder { program: "fzf".into(), args: ["-1".into()].into(), }; let selected = finder.spawn().unwrap().find(items).unwrap().stdout; assert_eq!(selected.as_slice(), "item1\n".as_bytes()) } #[test] #[ignore] fn test_selection() { let items = Vec::from(["item1", "item2", "item3", "item4"]); let finder = Finder { program: "fzf".into(), args: [].into(), }; let selected = finder.spawn().unwrap().find(items).unwrap().stdout; assert_eq!(selected.as_slice(), "item1\n".as_bytes()) } }