1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use anyhow::Context;
use anyhow::Result;
use mdbook::book::Book;
use mdbook::book::Chapter;
use mdbook::preprocess::{Preprocessor, PreprocessorContext};
use regex::Captures;
use regex::Regex;
use std::path::Path;
use std::process::Command;
use crate::utils::map_chapter;
pub struct CmdRun;
impl Preprocessor for CmdRun {
fn name(&self) -> &str {
"cmdrun"
}
fn supports_renderer(&self, renderer: &str) -> bool {
renderer == "html"
}
fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result<Book> {
map_chapter(&mut book, &mut CmdRun::run_on_chapter)?;
Ok(book)
}
}
impl CmdRun {
fn run_on_chapter(chapter: &mut Chapter) -> Result<()> {
let working_dir = match &chapter.path {
None => String::new(),
Some(pathbuf) => {
let pathbuf = Path::new("src").join(pathbuf);
match pathbuf.parent() {
None => String::new(),
Some(parent) => match parent.to_str() {
None => String::new(),
Some(s) => String::from(s),
},
}
}
};
chapter.content = CmdRun::run_on_content(&chapter.content, &working_dir)?;
Ok(())
}
pub fn run_on_content(content: &str, working_dir: &str) -> Result<String> {
let re = Regex::new(r"<!--[ ]*cmdrun (.*)-->\n").unwrap();
let mut err = None;
let content = re
.replace_all(content, |caps: &Captures| {
match CmdRun::run_cmdrun(caps[1].to_string(), working_dir) {
Ok(s) => s,
Err(e) => {
err = Some(e);
String::new()
}
}
})
.to_string();
match err {
None => Ok(content),
Some(err) => Err(err),
}
}
pub fn run_cmdrun(command: String, working_dir: &str) -> Result<String> {
let output = Command::new("sh")
.args(["-c", &command])
.current_dir(working_dir)
.output()
.with_context(|| "Fail to run shell")?;
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
Ok(stdout)
}
}