skim_pick/
lib.rs

1use std::{borrow::Cow, sync::Arc};
2
3use crossbeam_channel::unbounded;
4use skim::{prelude::SkimOptionsBuilder, Skim, SkimItem, SkimOptions};
5
6pub trait SkimPick {
7    fn pick<T: ToString + Send + Sync + 'static>(
8        self,
9        items: impl IntoIterator<Item = T>,
10    ) -> Option<T>;
11}
12impl SkimPick for SkimOptions<'_> {
13    fn pick<T: ToString + Send + Sync + 'static>(
14        self,
15        items: impl IntoIterator<Item = T>,
16    ) -> Option<T> {
17        pick_with_options(items, self)
18    }
19}
20impl SkimPick for SkimOptionsBuilder<'_> {
21    fn pick<T: ToString + Send + Sync + 'static>(
22        mut self,
23        items: impl IntoIterator<Item = T>,
24    ) -> Option<T> {
25        pick_with_options(items, self.build().ok()?)
26    }
27}
28
29pub fn pick_with_options<T: ToString + Send + Sync + 'static>(
30    items: impl IntoIterator<Item = T>,
31    options: SkimOptions,
32) -> Option<T> {
33    let (tx, rx) = unbounded();
34    for item in items {
35        let item: Arc<dyn SkimItem> = Arc::new(Item(Some(item)));
36        tx.send(item).ok()?
37    }
38    drop(tx);
39
40    let choice = Skim::run_with(&options, Some(rx))?;
41    let mut choice = choice.selected_items.into_iter().next()?;
42    let item = Arc::get_mut(&mut choice)?
43        .as_any_mut()
44        .downcast_mut::<Item<_>>()?;
45    item.0.take()
46}
47
48pub fn pick<T: ToString + Send + Sync + 'static>(items: impl IntoIterator<Item = T>) -> Option<T> {
49    let config = SkimOptions::default();
50    pick_with_options(items, config)
51}
52
53struct Item<T>(Option<T>);
54impl<T: ToString + Send + Sync + 'static> SkimItem for Item<T> {
55    fn text(&self) -> Cow<str> {
56        if let Some(ref v) = self.0 {
57            Cow::Owned(v.to_string())
58        } else {
59            Cow::Borrowed("")
60        }
61    }
62}