Log in to access Rust Adventure videos!

Lesson Details

To rename the temporary file, we need a file name to move it to inside of our garden directory.

use edit::{edit_file, Builder};
use std::{fs, io::Write, path::PathBuf};

pub fn write(
    garden_path: PathBuf,
    title: Option<String>,
) -> Result<(), std::io::Error> {
    let (mut file, filepath) = Builder::new()
        .suffix(".md")
        .rand_bytes(5)
        .tempfile_in(&garden_path)?
        .keep()?;
    dbg!(&filepath);
    let template =
        format!("# {}", title.as_deref().unwrap_or(""));
    file.write_all(template.as_bytes())?;
    edit_file(&filepath)?;
    let contents = fs::read_to_string(&filepath)?;

    let document_title = title.or_else(|| {
        contents.lines().find(|v| v.starts_with("# ")).map(
            |line| {
                line.trim_start_matches("# ").to_string()
            },
        )
    });

    let filename = match document_title {
        Some(raw_title) => slug::slugify(raw_title),
        None => {
            todo!("ask for filename");
        }
    };

    let mut dest = garden_path.join(filename);
    dest.set_extension("md");
    fs::rename(filepath, &dest)?;
		dbg!(dest);

    Ok(())
}

We’ll start out by matching on the document_title, which could be Some or None.

If its Some, then we’ll use the slug crate to slugify the title into something without spaces.

If its None, we’ll use a todo!(), macro to panic if we hit that branch. The todo! macro is nice because it short-circuits type inference, meaning that we don’t need to return random values to get our program to compile.

We get to say “We’ll do this later”.

let filename = match document_title {
    Some(raw_title) => slug::slugify(raw_title),
    None => {
        todo!("ask for filename");
    }
};
 cargo add slug
    Updating crates.io index
      Adding slug v0.1.4 to dependencies.
    Updating crates.io index

If there ends up being no title, then we’ll see our program panic

thread 'main' panicked at 'not yet implemented: ask for filename', src/lib.rs:31:13

Renaming files

After getting the appropriate title, renaming the file from the temporary location to the designed final location is straightforward.

Take the garden_path which is a PathBuf, and join it with the filename.

Then set the extension for the file to .md.

and use std::fs::rename to rename the original temporary file path to the new destination.

let mut dest = garden_path.join(filename);
dest.set_extension("md");
fs::rename(filepath, &dest)?;
dbg!(dest);

fs::rename is another function that returns an io::Error for its error, so we get to use ? again.

Running

and now when we run

❯ cargo run -- write
   Compiling garden v0.1.0 (/rust-adventure/digital-garden)
    Finished dev [unoptimized + debuginfo] target(s) in 0.57s
     Running `target/debug/garden write`
[src/lib.rs:13] &filepath = "/Users/chris/garden/.tmpWNu0x.md"
[src/lib.rs:38] dest = "/Users/chris/garden/some-file.md"

and the file contents are in that file

❯ cat /Users/chris/garden/some-file.md
# some file