fzf-wrapper
A library for integrating the fzf cli tool into your rust program!
This library provides bindings for integrating the fzf cli program into your rust
projects.
NOTE this does mean that the end user must have fzf installed on their system.
fzf version
This crate was developed with fzf v0.40.0 in mind, however there should be no reason why it
shouldn't work above it, and most of the features should work below it. If your program relies
on v0.40.0 features, it might be a good idea to check the version of fzf your program has
access to
Example
Say we're wanting to get the user to select their favourite colour using the power of fuzzy
finding. First we'd create a list of colours, which will be a collection we'll pass to fzf
let colours = vec!;
The next step is to construct an instance of [Fzf] and start it:
let mut fzf = default;
fzf.run.expect;
The code above fetches the default [Fzf] configuration, which runs fzf with no arguments,
and then calls the run() method. This displays fzf to the user.
At the moment all that will be displayed is a blank screen, as we haven't actually told fzf
to display anything. There are two ways to do this, the add_item() method, and the
add_items() method. They are both nearly identical, with the only difference being that
add_items() takes a [Vec] of [String]'s as items, and passes them one by one to
add_item().
let mut fzf = default;
fzf.run.expect;
fzf.add_items.expect;
The only thing left to do is to get what the user selected! This will be returned as an
[Option] containing None if the user exited fzf, or Some(String) with the string being
the item they selected. To get the output we simply call the output() method, which will
blocks execution until the user selects an item with fzf
let mut fzf = default;
fzf.run.expect;
fzf.add_items.expect;
let users_selection = fzf.output.expect;
The code in it's entirety looks like the following.
use Fzf;
This operation of using fzf to select from a predetermined [Vec] of items is so common that
a helper function exists to streamline the work involved. Using it, the code looks like the
following:
use Fzf;
use run_with_output;
Running fzf with arguments
Now, our favourite color picker program is pretty cool, but our fzf interface is a bit
confusing, how are our user's supposed to know what they're picking?
Thankfully fzf provides many different ways to customize it, many of which have been
implemented in this library. So far we have been calling the [Fzf] structs default
implementation, however we can build more complex instances using [FzfBuilder].
We can use two different ways to get an [FzfBuilder], either through it's own new() method,
or the builder() method on [Fzf]. Let's switch out the default call for a builder call.
let fzf = builder.build.unwrap;
let users_selection = run_with_output.expect;
NOTE: It is safe to unwrap the build() call, as every field has a default implementation.
If we run the program now, we'll notice... nothing has changed. This is because the default()
method calls the exact line of code we just replaced it with.
Let's make things interesting! First we should probably give the finder a label. Without a border, the label won't show so lets give it a border too!
Adding a border and border label
If you were using fzf straight from the command line, you would pass it the --border flag
with the name of one of the supported types of borders, however this can lead to errors if the
border doesn't exist. For this reason, fzf-wrapped makes use of enums to ensure that these
choices are always valid! For example, to choose a border we'd call the border() method on
our [FzfBuilder], contain our chosen variant of the [Border] enum.
Adding a rounded border makes our code look like this:
use Fzf;
use Border;
use run_with_output;
And adding a label is even more simple
use Fzf;
use Border;
use run_with_output;
Running this should display an fzf finder with a rounded border, and a centered label
containing "Favourite Colour".
Changing the layout of fzf
Well, now that we've got a border, we may as well change up the layout. This is almost identical to changing the border, as all possible layout's are mapped to an enum.
All we need to add to our builder is the layout() method with our chosen [Layout] variant
use Fzf;
use ;
use run_with_output;
Choosing colours
fzf lets us pick from a few colour themes, but for this one we're going to keep it simple and
use the black and white theme. Similar to borders and the layout, this is selected using an
enum. Adding it to our builder results in the following code:
use Fzf;
use ;
use run_with_output;
If you run the program now, you'll notice that the ui is in black and white!
Adding a header
Our user might still be confused about what they're picking, so to add some more context, fzf
lets us set a header. To do this all we do is call the header() method on our [FzfBuilder]
struct, and pass it anything with the Into<String> trait. We also want the header to appear
above our search field, so we'll call the header_first() method with true.
use Fzf;
use ;
use run_with_output;
Using an argument not supported by fzf_wrapped
If by chance the argument you want to run with fzf is not a method included on the [Fzf]
struct, do not worry! The custom_args() command will let you pass any argument you want! For
example, say we wanted to make our colour picker program not take up the full screen, say only
10% of it. fzf has the --height= flag, however the [Fzf] struct doesn't support it! To
add it all we'll need to do is to call the custom_args() command on our builder, and the
arguments we pass into it will be run with the run() method.
Implementing it would look like:
use Fzf;
use ;
use run_with_output;
And with that, our program is almost done!
All we need to do is print some kind of nice message, and while we're at it, we may as well use some proper error handling.
use Fzf;
use ;
use run_with_output;
Adding Items at runtime of fzf
With the power of this library, you can use fzf to select from a list of items, even if those
items have not been fetched yet. Using the add_item and add_items method adds items to
fzf's list, even while fzf is running. This means that if you're calling information from a
REST api, you can display result's as they come in straight to fzf, or even hide the slight
delay by starting up fzf.
For an example of this, look at my workflows project