Drinks - DRY Links Proof of Concept

Uses links dict in drinks.txt for general links storage
Replaces `{{#drink somedrink}}` placeholders with value from the dict
This commit is contained in:
Andrey Voronkov 2024-02-28 21:16:31 +03:00
parent 5a35144d4f
commit 5e6bacf545
4 changed files with 96 additions and 2 deletions

View File

@ -83,6 +83,8 @@ impl BookBuilder {
self.write_book_toml()?;
self.write_drinks_txt()?;
match MDBook::load(&self.root) {
Ok(book) => Ok(book),
Err(e) => {
@ -108,6 +110,18 @@ impl BookBuilder {
Ok(())
}
fn write_drinks_txt(&self) -> Result<()> {
debug!("Writing drinks.txt");
let drinks_txt = self.root.join("drinks.txt");
let entry = "hello: https://world.org";
File::create(drinks_txt)
.with_context(|| "Couldn't create drinks.txt")?
.write_all(&entry.as_bytes())
.with_context(|| "Unable to write config to drinks.txt")?;
Ok(())
}
fn copy_across_theme(&self) -> Result<()> {
debug!("Copying theme");

View File

@ -24,7 +24,7 @@ use topological_sort::TopologicalSort;
use crate::errors::*;
use crate::preprocess::{
CmdPreprocessor, IndexPreprocessor, LinkPreprocessor, Preprocessor, PreprocessorContext,
CmdPreprocessor, DrinkPreprocessor, IndexPreprocessor, LinkPreprocessor, Preprocessor, PreprocessorContext,
};
use crate::renderer::{CmdRenderer, HtmlHandlebars, MarkdownRenderer, RenderContext, Renderer};
use crate::utils;
@ -432,7 +432,7 @@ fn determine_renderers(config: &Config) -> Vec<Box<dyn Renderer>> {
renderers
}
const DEFAULT_PREPROCESSORS: &[&str] = &["links", "index"];
const DEFAULT_PREPROCESSORS: &[&str] = &["drinks", "links", "index"];
fn is_default_preprocessor(pre: &dyn Preprocessor) -> bool {
let name = pre.name();
@ -533,6 +533,7 @@ fn determine_preprocessors(config: &Config) -> Result<Vec<Box<dyn Preprocessor>>
names.sort();
for name in names {
let preprocessor: Box<dyn Preprocessor> = match name.as_str() {
"drinks" => Box::new(DrinkPreprocessor::new()),
"links" => Box::new(LinkPreprocessor::new()),
"index" => Box::new(IndexPreprocessor::new()),
_ => {

77
src/preprocess/drinks.rs Normal file
View File

@ -0,0 +1,77 @@
use crate::errors::*;
use super::{Preprocessor, PreprocessorContext};
use crate::book::{Book, BookItem, Chapter};
use regex::{Captures, Regex};
use once_cell::sync::Lazy;
use std::io::{BufRead, BufReader};
use std::fs::File;
use std::collections::HashMap;
const SPLITTER: char = ':';
type Dict=HashMap<String, String>;
/// DRY Links - A preprocessor for using centralized links collection:
///
/// - `{{# drink}}` - Insert link from the collection
#[derive(Default)]
pub struct DrinkPreprocessor;
impl DrinkPreprocessor {
pub(crate) const NAME: &'static str = "drinks";
/// Create a new `DrinkPreprocessor`.
pub fn new() -> Self {
DrinkPreprocessor
}
fn replace_drinks(&self, chapter: &mut Chapter, dict: &Dict) -> Result<String, Error> {
static RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(
r"(?x) # insignificant whitespace mode
\{\{\s* # link opening parens and whitespace
\#(drink) # drink marker
\s+ # separating whitespace
(?<drink>[A-z0-9_-]+) # drink name
\}\} # link closing parens",
).unwrap()
});
static NODRINK: Lazy<String> = Lazy::new(|| {
"deadbeef".to_string()
});
let res = RE.replace_all(&chapter.content, |caps: &Captures<'_>| {
dict.get(&caps["drink"]).unwrap_or(&NODRINK)
});
Ok(res.to_string())
}
}
impl Preprocessor for DrinkPreprocessor {
fn name(&self) -> &str {
Self::NAME
}
fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result<Book> {
let path = ctx.root.join("drinks.txt");
let drinks: Dict = {
let reader = BufReader::new(File::open(path).expect("Cannot open drinks dictionary"));
reader.lines().filter_map(|l| {
l.expect("Cannot read line in drinks dictionary").split_once(SPLITTER).map(|(name, value)| (name.trim().to_owned(), value.trim().to_owned()))
}).collect::<HashMap<_, _>>()
};
book.for_each_mut(|section: &mut BookItem| {
if let BookItem::Chapter(ref mut ch) = *section {
ch.content = self
.replace_drinks(ch, &drinks)
.expect("Error converting drinks into links for chapter");
}
});
Ok(book)
}
}

View File

@ -1,10 +1,12 @@
//! Book preprocessing.
pub use self::cmd::CmdPreprocessor;
pub use self::drinks::DrinkPreprocessor;
pub use self::index::IndexPreprocessor;
pub use self::links::LinkPreprocessor;
mod cmd;
mod drinks;
mod index;
mod links;