I’m writing a Rust function for getting a title based on the first line of a file.
The files are written in Markdown, and the first line should be a heading that starts with one or more hashes, followed by some text. Examples:
# This is a top-level heading
## This is a second-level heading
#### Let's jump straight to the fourth-level heading
I want to throw away the leading hashes, discard any leading/trailing whitespace, and return the remaining string. Example outputs:
"This is a top-level heading"
"This is a second-level heading"
"Let's jump straight to the fourth-level heading"
Assume that, for now, I’m not worried about edge cases like a first line that’s only whitespace and hashes, or a file whose first line is pathologically long.
This is the program I’ve written to do it:
use std::fs;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
/// Get the title of a Markdown file.
///
/// Reads the first line of a Markdown file, strips any hashes and
/// leading/trailing whitespace, and returns the title.
fn title_string(path: PathBuf) -> String {
// Read the first line of the file into `title`.
let file = match fs::File::open(&path) {
Ok(file) => file,
Err(_) => panic!("Unable to read title from {:?}", &path),
};
let mut buffer = BufReader::new(file);
let mut first_line = String::new();
let _ = buffer.read_line(&mut first_line);
// Where do the leading hashes stop?
let mut last_hash = 0;
for (idx, c) in first_line.chars().enumerate() {
if c != '#' {
last_hash = idx;
break
}
}
// Trim the leading hashes and any whitespace
let first_line: String = first_line.drain(last_hash..).collect();
let first_line = String::from(first_line.trim());
first_line
}
fn main() {
let title = title_string(PathBuf::from("./example.md"));
println!("The title is '{}'", title);
}
I’m fairly new to Rust, and I’m sure I’m doing stuff that isn’t as optimal or idiomatic as it could be. Particular questions:
- Is this idiomatic Rust?
- Is there a better way to strip leading characters from a string? I looked in the documentation for
std::string::String
and couldn’t see anything. TheString::from()
feels a bit inefficient. - Is there anything unsafe that could easily crash (the
panic!
aside)?