If we don’t have a title from the user’s CLI arguments, or from the content of the file, then we should ask the user for a title to use.
rprompt
is one crate that can handle this interaction for us.
❯ cargo add rprompt
Updating crates.io index
Adding rprompt v2.0.2 to dependencies.
Updating crates.io index
We’ll write a new function called ask_for_filename
to encapsulate our usage of rprompt
.
prompt_reply
will ask the user for input using our string, then return that value to us. If prompt_reply
fails, it returns an io::Result
which contains the io::Error
we’ve been using over and over in this workshop.
fn ask_for_filename() -> io::Result<String> {
rprompt::prompt_reply(
"Enter filename
> ",
)
}
Then we can use it instead of our todo!
macro. Similar to Option::map
, Result
also has a map
function we can use to modify the Ok
inner value. There’s no trait that dictates the map function implementations, its a convention to be able to map
over types that contain values. This works for Options, Results, Iterators, and even third party crates implement this pattern.
Then we can use ?
to return the error if there is one.
This all means that filename
is a slugified filename, ready to be our destination file.
let filename = match document_title {
Some(raw_title) => slug::slugify(raw_title),
None => ask_for_filename()
.map(|title| slug::slugify(title))?,
};
Our application now asks for input from the user if we don’t have a title.
❯ cargo run -- write
Compiling garden v0.1.0 (/rust-adventure/digital-garden)
Finished dev [unoptimized + debuginfo] target(s) in 1.08s
Running `target/debug/garden write`
[src/lib.rs:17] &filepath = "/Users/chris/garden/.tmpclNu1.md"
Enter filename
> Some new File
[src/lib.rs:41] dest = "/Users/chris/garden/some-new-file.md"