Skip to content

Commit

Permalink
Using Options to pick from multiple potential values
Browse files Browse the repository at this point in the history
After getting the user's input, we need to
figure out a filename to write the file out
as. We can either use the title the user
passed in, or we can try to use heuristics
to find a title from the markdown content.

We can combine the user's title from the cli
arguments (an `Option`) with the logic we
use as a heuristic for the markdown (also
an `Option`).

```rust
// use `title` if the user passed it in,
// otherwise try to find a heading in the markdown
let document_title = title.or_else(|| {
    contents
        .lines()
        .find(|v| v.starts_with("# "))
        // markdown headings are required to have `# ` with
        // at least one space
        .map(|maybe_line| maybe_line.trim_start_matches("# ").to_string())
});
```

`option.or_else()` returns the original
option if it is a `Some` value, or executes
the function to return a different `Option`
if not. So `document_title` is an
`Option<String>` either way and we've
covered all of the possible scenarios:

1. the user has passed in a title
2. the user has written content with a heading
3. the user has done neither of these

Which leaves us to our markdown heuristic.

Markdown headings are required to have `# `
with at least one space, so we can turn
`contents` into an iterator and `find` the
first line that starts with `# `.

If we find one, we want to trim `# ` off of
the heading to get just the heading content,
so we can use `map` to operate on the value
inside of the `Option` returned by `find` if
it exists.

```rust
contents
    .lines()
    .find(|v| v.starts_with("# "))
    .map(|maybe_line| maybe_line.trim_start_matches("# ").to_string())
```

Now this code works fine, but it exposes a bug
in our file handling.

If we `write_all` to the `file`, that moves the
internal cursor to the end of that content. So
when we run our code now, `contents` is
skipping the first `# ` bytes, which means our
heuristic will only find the second heading in
the file.

To fix this, we can bring in the `std::io::Seek`
trait, and seek to the beginning of the file.

```rust
let mut contents = String::new();
file.seek(SeekFrom::Start(0))?;
file.read_to_string(&mut contents)?;
```
  • Loading branch information
ChristopherBiscardi committed Jan 16, 2021
1 parent 8e69a12 commit d9dcc0e
Showing 1 changed file with 15 additions and 2 deletions.
17 changes: 15 additions & 2 deletions src/write.rs
@@ -1,6 +1,6 @@
use color_eyre::{eyre::WrapErr, Result};
use edit::{edit_file, Builder};
use std::io::{Read, Write};
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::PathBuf;

const TEMPLATE: &[u8; 2] = b"# ";
Expand All @@ -19,8 +19,21 @@ pub fn write(garden_path: PathBuf, title: Option<String>) -> Result<()> {
edit_file(filepath)?;
// Read the user's changes back from the file into a string
let mut contents = String::new();
file.seek(SeekFrom::Start(0))?;
file.read_to_string(&mut contents)?;

dbg!(contents, title);
// use `title` if the user passed it in,
// otherwise try to find a heading in the markdown
let document_title = title.or_else(|| {
dbg!("here");
contents
.lines()
.find(|v| dbg!(v).starts_with("# "))
// markdown headings are required to have `# ` with
// at least one space
.map(|maybe_line| maybe_line.trim_start_matches("# ").to_string())
});

dbg!(contents, document_title);
todo!()
}

0 comments on commit d9dcc0e

Please sign in to comment.