fzf_wrapped/lib.rs
1//! # fzf-wrapper
2//!
3//! A library for integrating the `fzf` cli tool into your rust program!
4//!
5//! This library provides bindings for integrating the `fzf` cli program into your rust
6//! projects.
7//!
8//! **NOTE** this does mean that the end user must have `fzf` installed on their system.
9//!
10//! #### fzf version
11//!
12//! This crate was developed with `fzf` v0.40.0 in mind, however there should be no reason why it
13//! shouldn't work above it, and most of the features should work below it. If your program relies
14//! on v0.40.0 features, it might be a good idea to check the version of `fzf` your program has
15//! access to
16//!
17//! ## Example
18//!
19//! Say we're wanting to get the user to select their favourite colour using the power of fuzzy
20//! finding. First we'd create a list of colours, which will be a collection we'll pass to `fzf`
21//!
22//! ```rust
23//! let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
24//! ```
25//!
26//! The next step is to construct an instance of [`Fzf`] and start it:
27//!
28//! ```rust
29//! let mut fzf = Fzf::default();
30//!
31//! fzf.run().expect("Failed to start fzf");
32//! ```
33//!
34//! The code above fetches the default [`Fzf`] configuration, which runs `fzf` with no arguments,
35//! and then calls the `run()` method. This displays `fzf` to the user.
36//!
37//! At the moment all that will be displayed is a blank screen, as we haven't actually told `fzf`
38//! to display anything. There are two ways to do this, the `add_item()` method, and the
39//! `add_items()` method. They are both nearly identical, with the only difference being that
40//! `add_items()` takes a [`Vec`] of [`String`]'s as items, and passes them one by one to
41//! `add_item()`.
42//!
43//! ```rust
44//! # let mut fzf = Fzf::default();
45//! # fzf.run().expect("Failed to start fzf");
46//! fzf.add_items(colours).expect("Failed to add items");
47//! ```
48//!
49//! The only thing left to do is to get what the user selected! This will be returned as an
50//! [`Option`] containing `None` if the user exited `fzf`, or `Some(String)` with the string being
51//! the item they selected. To get the output we simply call the `output()` method, which will
52//! blocks execution until the user selects an item with `fzf`
53//!
54//! ```rust
55//! # let mut fzf = Fzf::default();
56//! # fzf.run().expect("Failed to start fzf");
57//! # fzf.add_items(colours).expect("Failed to add items");
58//! let users_selection = fzf.output().expect("Failed to get the user's output");
59//! ```
60//!
61//! The code in it's entirety looks like the following.
62//!
63//! ```rust
64//! use fzf_wrapped::Fzf;
65//!
66//! fn main() {
67//! let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
68//!
69//! let mut fzf = Fzf::default();
70//! fzf.run().expect("Failed to start fzf");
71//!
72//! fzf.add_items(colours).expect("Failed to add items");
73//!
74//! let users_selection = fzf.output().expect("Failed to get the user's output");
75//! }
76//! ```
77//!
78//! This operation of using `fzf` to select from a predetermined [`Vec`] of items is so common that
79//! a helper function exists to streamline the work involved. Using it, the code looks like the
80//! following:
81//!
82//! ```rust
83//! use fzf_wrapped::Fzf;
84//! use fzf_wrapped::run_with_output;
85//!
86//! fn main() {
87//! let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
88//!
89//! let users_selection = run_with_output(Fzf::default(), colours).expect("Something went wrong!");
90//! }
91//! ```
92//!
93//! ## Running `fzf` with arguments
94//!
95//! Now, our favourite color picker program is pretty cool, but our `fzf` interface is a bit
96//! confusing, how are our user's supposed to know what they're picking?
97//!
98//! Thankfully `fzf` provides many different ways to customize it, many of which have been
99//! implemented in this library. So far we have been calling the [`Fzf`] structs default
100//! implementation, however we can build more complex instances using [`FzfBuilder`].
101//!
102//! We can use two different ways to get an [`FzfBuilder`], either through it's own `new()` method,
103//! or the `builder()` method on [`Fzf`]. Let's switch out the default call for a builder call.
104//!
105//! ```rust
106//! let fzf = Fzf::builder().build().unwrap();
107//!
108//! let users_selection = run_with_output(fzf, colours).expect("Something went wrong!");
109//! ```
110//!
111//! **NOTE**: It is safe to unwrap the `build()` call, as every field has a default implementation.
112//!
113//! If we run the program now, we'll notice... nothing has changed. This is because the `default()`
114//! method calls the exact line of code we just replaced it with.
115//!
116//! Let's make things interesting! First we should probably give the finder a label. Without a
117//! border, the label won't show so lets give it a border too!
118//!
119//! ### Adding a border and border label
120//!
121//! If you were using `fzf` straight from the command line, you would pass it the `--border` flag
122//! with the name of one of the supported types of borders, however this can lead to errors if the
123//! border doesn't exist. For this reason, `fzf-wrapped` makes use of enums to ensure that these
124//! choices are always valid! For example, to choose a border we'd call the `border()` method on
125//! our [`FzfBuilder`], contain our chosen variant of the [`Border`] enum.
126//!
127//! Adding a rounded border makes our code look like this:
128//!
129//! ```rust
130//! use fzf_wrapped::Fzf;
131//! use fzf_wrapped::Border;
132//! use fzf_wrapped::run_with_output;
133//!
134//! fn main() {
135//! let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
136//!
137//! let fzf = Fzf::builder()
138//! .border(Border::Rounded)
139//! .build()
140//! .unwrap();
141//!
142//! let users_selection = run_with_output(fzf, colours);
143//! }
144//! ```
145//!
146//! And adding a label is even more simple
147//!
148//! ```rust
149//! use fzf_wrapped::Fzf;
150//! use fzf_wrapped::Border;
151//! use fzf_wrapped::run_with_output;
152//!
153//! fn main() {
154//! let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
155//!
156//! let fzf = Fzf::builder()
157//! .border(Border::Rounded)
158//! .border_label("Favourite Colour")
159//! .build()
160//! .unwrap();
161//!
162//! let users_selection = run_with_output(fzf, colours);
163//! }
164//! ```
165//!
166//! Running this should display an `fzf` finder with a rounded border, and a centered label
167//! containing "Favourite Colour".
168//!
169//! ### Changing the layout of `fzf`
170//!
171//! Well, now that we've got a border, we may as well change up the layout. This is almost
172//! identical to changing the border, as all possible layout's are mapped to an enum.
173//!
174//! All we need to add to our builder is the `layout()` method with our chosen [`Layout`] variant
175//!
176//! ```rust
177//! use fzf_wrapped::Fzf;
178//! use fzf_wrapped::{Border, Layout};
179//! use fzf_wrapped::run_with_output;
180//!
181//! fn main() {
182//! let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
183//!
184//! let fzf = Fzf::builder()
185//! .layout(Layout::Reverse)
186//! .border(Border::Rounded)
187//! .border_label("Favourite Colour")
188//! .build()
189//! .unwrap();
190//!
191//! let users_selection = run_with_output(fzf, colours);
192//! }
193//! ```
194//!
195//! ### Choosing colours
196//!
197//! `fzf` lets us pick from a few colour themes, but for this one we're going to keep it simple and
198//! use the black and white theme. Similar to borders and the layout, this is selected using an
199//! enum. Adding it to our builder results in the following code:
200//!
201//! ```rust
202//! use fzf_wrapped::Fzf;
203//! use fzf_wrapped::{Border, Color, Layout};
204//! use fzf_wrapped::run_with_output;
205//!
206//! fn main() {
207//! let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
208//!
209//! let fzf = Fzf::builder()
210//! .layout(Layout::Reverse)
211//! .border(Border::Rounded)
212//! .border_label("Favourite Colour")
213//! .color(Color::Bw)
214//! .build()
215//! .unwrap();
216//!
217//! let users_selection = run_with_output(fzf, colours);
218//! }
219//! ```
220//!
221//! If you run the program now, you'll notice that the ui is in black and white!
222//!
223//! ### Adding a header
224//!
225//! Our user might still be confused about what they're picking, so to add some more context, `fzf`
226//! lets us set a header. To do this all we do is call the `header()` method on our [`FzfBuilder`]
227//! struct, and pass it anything with the `Into<String>` trait. We also want the header to appear
228//! above our search field, so we'll call the `header_first()` method with `true`.
229//!
230//! ```rust
231//! use fzf_wrapped::Fzf;
232//! use fzf_wrapped::{Border, Color, Layout};
233//! use fzf_wrapped::run_with_output;
234//!
235//! fn main() {
236//! let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
237//!
238//! let fzf = Fzf::builder()
239//! .layout(Layout::Reverse)
240//! .border(Border::Rounded)
241//! .border_label("Favourite Colour")
242//! .color(Color::Bw)
243//! .header("Pick your favourite colour")
244//! .header_first(true)
245//! .build()
246//! .unwrap();
247//!
248//! let users_selection = run_with_output(fzf, colours);
249//! }
250//! ```
251//!
252//! ### Using an argument not supported by `fzf_wrapped`
253//!
254//! If by chance the argument you want to run with `fzf` is not a method included on the [`Fzf`]
255//! struct, do not worry! The `custom_args()` command will let you pass any argument you want! For
256//! example, say we wanted to make our colour picker program not take up the full screen, say only
257//! 10% of it. `fzf` has the `--height=` flag, however the [`Fzf`] struct doesn't support it! To
258//! add it all we'll need to do is to call the `custom_args()` command on our builder, and the
259//! arguments we pass into it will be run with the `run()` method.
260//!
261//! Implementing it would look like:
262//!
263//! ```rust
264//! use fzf_wrapped::Fzf;
265//! use fzf_wrapped::{Border, Color, Layout};
266//! use fzf_wrapped::run_with_output;
267//!
268//! fn main() {
269//! let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
270//!
271//! let fzf = Fzf::builder()
272//! .layout(Layout::Reverse)
273//! .border(Border::Rounded)
274//! .border_label("Favourite Colour")
275//! .color(Color::Bw)
276//! .header("Pick your favourite colour")
277//! .header_first(true)
278//! .custom_args(vec!["--height=10".to_string()])
279//! .build()
280//! .unwrap();
281//!
282//! let users_selection = run_with_output(fzf, colours);
283//!
284//! if let Some(colour) = users_selection {
285//! println!("{} is an awesome colour!", colour);
286//! }
287//! }
288//! ```
289//!
290//! And with that, our program is almost done!
291//!
292//! All we need to do is print some kind of nice message, and while we're at it, we may as well use
293//! some proper error handling.
294//!
295//! ```rust
296//! use fzf_wrapped::Fzf;
297//! use fzf_wrapped::{Border, Color, Layout};
298//! use fzf_wrapped::run_with_output;
299//!
300//! fn main() {
301//! let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
302//!
303//! let fzf = Fzf::builder()
304//! .layout(Layout::Reverse)
305//! .border(Border::Rounded)
306//! .border_label("Favourite Colour")
307//! .color(Color::Bw)
308//! .header("Pick your favourite colour")
309//! .header_first(true)
310//! .custom_args(vec!["--height=10".to_string()])
311//! .build()
312//! .unwrap();
313//!
314//! let users_selection = run_with_output(fzf, colours);
315//!
316//! if let Some(colour) = users_selection {
317//! println!("{} is an awesome colour!", colour);
318//! }
319//! }
320//! ```
321//!
322//! ## Adding Items at runtime of `fzf`
323//!
324//! With the power of this library, you can use `fzf` to select from a list of items, even if those
325//! items have not been fetched yet. Using the `add_item` and `add_items` method adds items to
326//! `fzf`'s list, even while fzf is running. This means that if you're calling information from a
327//! REST api, you can display result's as they come in straight to `fzf`, or even hide the slight
328//! delay by starting up `fzf`.
329//!
330//! For an example of this, look at my [workflows](https://github.com/danielronalds/workflows) project
331
332
333mod options;
334pub use options::*;
335mod fzf;
336pub use fzf::*;
337
338/// Runs the given [`Fzf`] struct and returns the user's selection as a [`String`]
339///
340/// # Parameters
341///
342/// - `items` The items to to display in `fzf`
343///
344/// # Returns
345///
346/// An option containing either the users selection as a [`String`], or `None` if the user quit `fzf`
347pub fn run_with_output(fzf: Fzf, items: impl IntoIterator<Item = impl Into<String>>) -> Option<String> {
348 let mut fzf = fzf;
349 fzf.run().ok()?;
350 fzf.add_items(items).ok()?;
351 fzf.output()
352}