1use std::convert;
2use std::env;
3use std::fs;
5use std::io;
6use std::ops::Deref;
7use std::process::Command;
8use std::path::{Path, PathBuf};
9use std::result;
10
11#[derive(Debug)]
12pub enum Error {
13 IoError(io::Error),
14 IoErrorContext(io::Error, ErrorContext),
15}
16
17#[derive(Debug)]
18pub enum ErrorContext {
19 ReadDir(PathBuf),
20}
21
22#[derive(Copy, Clone, Debug)]
23pub enum TrackChanges { Accept, Reject, All }
24
25impl TrackChanges {
26 fn display(&self) -> &'static str {
27 match *self {
28 TrackChanges::Accept => "accept",
29 TrackChanges::Reject => "reject",
30 TrackChanges::All => "all",
31 }
32 }
33}
34
35#[derive(Copy, Clone, Debug)]
36pub enum EmailObfuscation { None, Javascript, References }
37
38impl EmailObfuscation {
39 fn display(&self) -> &'static str {
40 match *self {
41 EmailObfuscation::None => "none",
42 EmailObfuscation::Javascript => "javascript",
43 EmailObfuscation::References => "references",
44 }
45 }
46}
47
48pub type URL = str;
49
50#[derive(Clone, Debug)]
51pub enum PandocOption<'a> {
52 To(OutputFormatExt), DataDir(&'a Path), Strict, ParseRaw, Smart, OldDashes, BaseHeaderLevel(u32), IndentedCodeClasses(&'a str), Filter(&'a Path), Normalize, PreserveTabs, TabStop(u32), TrackChanges(TrackChanges), ExtractMedia(&'a Path), Standalone, Template(&'a Path), Meta(&'a str, Option<&'a str>), Var(&'a str, Option<&'a str>), PrintDefaultTemplate(&'a str), PrintDefaultDataFile(&'a Path), NoWrap, Columns(u32), TableOfContents, TableOfContentsDepth(u32), NoHighlight, HighlightStyle(&'a str), IncludeInHeader(&'a Path), IncludeBeforeBody(&'a Path), IncludeAfterBody(&'a Path), SelfContained, Offline, Html5, HtmlQTags, Ascii, ReferenceLinks, AtxHeaders, Chapters, NumberSections, NumberOffset(&'a [u32]), NoTexLigatures, Listings, Incremental, SlideLevel(u32), SectionDivs, DefaultImageExtension(&'a str), EmailObfuscation(EmailObfuscation), IdPrefix(&'a str), TitlePrefix(&'a str), Css(&'a URL), ReferenceOdt(&'a Path), ReferenceDocx(&'a Path), EpubStylesheet(&'a Path), EpubCoverImage(&'a Path), EpubMetadata(&'a Path), EpubEmbedFont(&'a Path), EpubChapterLevel(u32), LatexEngine(&'a Path), LatexEngineOpt(&'a str), Bibliography(&'a Path), Csl(&'a Path), CitationAbbreviations(&'a Path), Natbib, Biblatex, LatexMathML(Option<&'a URL>), AsciiMathML(Option<&'a URL>), MathML(Option<&'a URL>), MimeTex(Option<&'a URL>), WebTex(Option<&'a URL>), JsMath(Option<&'a URL>), MathJax(Option<&'a URL>), Katex(Option<&'a URL>), KatexStylesheet(&'a URL), GladTex, Trace, DumpArgs, IgnoreArgs, Verbose, }
130
131#[derive(Copy, Clone, Debug)]
132#[allow(non_camel_case_types)]
133pub enum OutputFormat {
134 asciidoc, beamer, commonmark, context, docbook, docx, dokuwiki,
135 dzslides, epub, epub3, fb2, haddock, html, html5, icml, json,
136 latex, man, markdown, markdown_github, markdown_mmd,
137 markdown_phpextra, markdown_strict, mediawiki, native, odt,
138 opendocument, opml, org, plain, revealjs, rst, rtf, s5,
139 slideous, slidy, texinfo, textile
140}
141
142#[derive(Clone, Debug)]
143pub enum OutputFormatExt {
144 Fmt(OutputFormat),
145 FmtExt(OutputFormat, Vec<String>),
146}
147
148impl OutputFormatExt {
149 fn render(&self) -> String {
150 match *self {
151 OutputFormatExt::Fmt(ref s) => s.to_string(),
152 OutputFormatExt::FmtExt(ref s, ref ext) => {
153 let mut s = s.to_string();
154 for e in ext {
155 s.push_str("+");
156 s.push_str(e);
157 }
158 s
159 }
160 }
161 }
162}
163
164macro_rules! cases {
165 ($x:expr => $($id:ident),*) => {
166 match $x {
167 $($id => stringify!($id)),*
168 }
169 }
170}
171
172impl Deref for OutputFormat {
173 type Target = str;
174
175 fn deref(&self) -> &str {
176 use OutputFormat::*;
177 cases!(*self => asciidoc, beamer, commonmark, context, docbook, docx, dokuwiki,
178 dzslides, epub, epub3, fb2, haddock, html, html5, icml, json,
179 latex, man, markdown, markdown_github, markdown_mmd,
180 markdown_phpextra, markdown_strict, mediawiki, native, odt,
181 opendocument, opml, org, plain, revealjs, rst, rtf, s5,
182 slideous, slidy, texinfo, textile)
183 }
184}
185
186impl<'a> PandocOption<'a> {
187 fn apply<'c>(&self, pandoc: &'c mut Command) -> &'c mut Command {
188 use PandocOption::*;
189 match *self {
190
191 NumberOffset(nums) => {
192 let nums = nums.iter()
193 .fold(String::new(),
194 |b, n| {
195 if b.len() == 0 {
196 format!("{}", n)
197 } else {
198 format!("{}, {}", b, n)
199 }
200 });
201 pandoc.args(&[&format!("--number-offset={}", nums)])
202 }
203
204 To(ref f) => pandoc.args(&["-t", &f.render()]),
205 DataDir(dir) => pandoc.args(&[&format!("--data-dir={}", dir.display())]),
206 Strict => pandoc.args(&["--strict"]),
207 ParseRaw => pandoc.args(&["--parse-raw"]),
208 Smart => pandoc.args(&["--smart"]),
209 OldDashes => pandoc.args(&["--old-dashes"]),
210 BaseHeaderLevel(n) => pandoc.args(&[&format!("--base-header-level={}", n)]),
211 IndentedCodeClasses(s) => pandoc.args(&[&format!("--indented-code-classes={}", s)]),
212 Filter(program) => pandoc.args(&[&format!("--filter={}", program.display())]),
213 Normalize => pandoc.args(&["--normalize"]),
214 PreserveTabs => pandoc.args(&["--preserve-tabs"]),
215 TabStop(n) => pandoc.args(&[&format!("--tab-stop={}", n)]),
216 TrackChanges(ref v) => pandoc.args(&[&format!("--track-changes={}", v.display())]),
217 ExtractMedia(p) => pandoc.args(&[&format!("--extract-media={}", p.display())]),
218 Standalone => pandoc.args(&["--standalone"]),
219 Template(p) => pandoc.args(&[&format!("--template={}", p.display())]),
220 Meta(k, Some(v)) => pandoc.args(&["-M", &format!("{}:{}", k, v)]),
221 Meta(k, None) => pandoc.args(&["-M", k]),
222 Var(k, Some(v)) => pandoc.args(&["-V", &format!("{}:{}", k, v)]),
223 Var(k, None) => pandoc.args(&["-V", k]),
224 PrintDefaultTemplate(f) => pandoc.args(&[&format!("--print-default-template={}", f)]),
225 PrintDefaultDataFile(f) => pandoc.args(&[&format!("--print-default-data-file={}", f.display())]),
226 NoWrap => pandoc.args(&["--no-wrap"]),
227 Columns(n) => pandoc.args(&[&format!("--columns={}", n)]),
228 TableOfContents => pandoc.args(&["--table-of-contents"]),
229 TableOfContentsDepth(d) => pandoc.args(&[&format!("--toc-depth={}", d)]),
230 NoHighlight => pandoc.args(&["--no-highlight"]),
231 HighlightStyle(s) => pandoc.args(&[&format!("--highlight-style={}", s)]),
232 IncludeInHeader(p) => pandoc.args(&[&format!("--include-in-header={}", p.display())]),
233 IncludeBeforeBody(p) => pandoc.args(&[&format!("--include-before-body={}", p.display())]),
234 IncludeAfterBody(p) => pandoc.args(&[&format!("--include-after-body={}", p.display())]),
235 SelfContained => pandoc.args(&["--self-contained"]),
236 Offline => pandoc.args(&["--offline"]),
237 Html5 => pandoc.args(&["--html5"]),
238 HtmlQTags => pandoc.args(&["--html-q-tags"]),
239 Ascii => pandoc.args(&["--ascii"]),
240 ReferenceLinks => pandoc.args(&["--reference-links"]),
241 AtxHeaders => pandoc.args(&["--atx-headers"]),
242 Chapters => pandoc.args(&["--chapters"]),
243 NumberSections => pandoc.args(&["--number-sections"]),
244 NoTexLigatures => pandoc.args(&["--no-tex-ligatures"]),
245 Listings => pandoc.args(&["--listings"]),
246 Incremental => pandoc.args(&["--incremental"]),
247 SlideLevel(n) => pandoc.args(&[format!("--slide-level={}", n)]),
248 SectionDivs => pandoc.args(&["--section-divs"]),
249 DefaultImageExtension(s) => pandoc.args(&[format!("--default-image-extension={}", s)]),
250 EmailObfuscation(o) => pandoc.args(&[format!("--email-obfuscation={}", o.display())]),
251 IdPrefix(s) => pandoc.args(&[format!("--id-prefix={}", s)]),
252 TitlePrefix(s) => pandoc.args(&[format!("--title-prefix={}", s)]),
253 Css(url) => pandoc.args(&[format!("--css={}", url)]),
254 ReferenceOdt(file) => pandoc.args(&[format!("--reference-odt={}", file.display())]),
255 ReferenceDocx(file) => pandoc.args(&[&format!("--reference-docx={}", file.display())]),
256 EpubStylesheet(file) => pandoc.args(&[&format!("--epub-stylesheet={}", file.display())]),
257 EpubCoverImage(file) => pandoc.args(&[&format!("--epub-cover-image={}", file.display())]),
258 EpubMetadata(file) => pandoc.args(&[&format!("--epub-metadata={}", file.display())]),
259 EpubEmbedFont(file) => pandoc.args(&[&format!("--epub-embed-font={}", file.display())]),
260 EpubChapterLevel(num) => pandoc.args(&[&format!("--epub-chapter-level={}", num)]),
261 LatexEngine(program) => pandoc.args(&[&format!("--latex-engine={}", program.display())]),
262 LatexEngineOpt(s) => pandoc.args(&[&format!("--latex-engine-opt={}", s)]),
263 Bibliography(file) => pandoc.args(&[&format!("--bibliography={}", file.display())]),
264 Csl(file) => pandoc.args(&[&format!("--csl={}", file.display())]),
265 CitationAbbreviations(f) => pandoc.args(&[&format!("--citation-abbreviations={}", f.display())]),
266 Natbib => pandoc.args(&["--natbib"]),
267 Biblatex => pandoc.args(&["--biblatex"]),
268 LatexMathML(Some(url)) => pandoc.args(&[&format!("--latexmathml={}", url)]),
269 AsciiMathML(Some(url)) => pandoc.args(&[&format!("--asciimathml={}", url)]),
270 MathML(Some(url)) => pandoc.args(&[&format!("--mathml={}", url)]),
271 MimeTex(Some(url)) => pandoc.args(&[&format!("--mimetex={}", url)]),
272 WebTex(Some(url)) => pandoc.args(&[&format!("--webtex={}", url)]),
273 JsMath(Some(url)) => pandoc.args(&[&format!("--jsmath={}", url)]),
274 MathJax(Some(url)) => pandoc.args(&[&format!("--mathjax={}", url)]),
275 Katex(Some(url)) => pandoc.args(&[&format!("--katex={}", url)]),
276 LatexMathML(None) => pandoc.args(&["--latexmathml"]),
277 AsciiMathML(None) => pandoc.args(&["--asciimathml"]),
278 MathML(None) => pandoc.args(&["--mathml"]),
279 MimeTex(None) => pandoc.args(&["--mimetex["]),
280 WebTex(None) => pandoc.args(&["--webtex["]),
281 JsMath(None) => pandoc.args(&["--jsmath["]),
282 MathJax(None) => pandoc.args(&["--mathjax["]),
283 Katex(None) => pandoc.args(&["--katex["]),
284 KatexStylesheet(url) => pandoc.args(&[&format!("--katex-stylesheet={}", url)]),
285 GladTex => pandoc.args(&["--gladtex"]),
286 Trace => pandoc.args(&["--trace"]),
287 DumpArgs => pandoc.args(&["--dump-args"]),
288 IgnoreArgs => pandoc.args(&["--ignore-args"]),
289 Verbose => pandoc.args(&["--verbose"]),
290 }
291 }
292}
293
294impl convert::From<io::Error> for Error {
295 fn from(e: io::Error) -> Self {
296 Error::IoError(e)
297 }
298}
299
300pub type Result<X> = result::Result<X, Error>;
301
302fn read_dir<P: AsRef<Path>>(path: P) -> Result<fs::ReadDir> {
303 let path = path.as_ref();
304 fs::read_dir(path).map_err(|e| {
305 Error::IoErrorContext(e, ErrorContext::ReadDir(path.to_path_buf()))
306 })
307}
308
309pub fn run_pandoc(src_dir: &str, target_name: &str, opts: &[PandocOption]) -> Result<()> {
310 let src_dir_path = &format!("src/{}", src_dir);
311 let mut src_paths = Vec::new();
312 for entry in try!(read_dir(src_dir_path)) {
313 let entry = try!(entry);
314 if is_skipped_md(&entry) { continue; }
315 if let Some("md") = entry.path().extension().and_then(|p|p.to_str()) {
316 src_paths.push(entry.path());
317 }
318 }
319 let tgt_path = &format!("{}.html", target_name);
320 let mut pandoc = Command::new("pandoc");
321 for o in opts {
322 o.apply(&mut pandoc);
323 }
324 pandoc.args(&["-o", tgt_path]);
325 src_paths.sort();
326 for p in src_paths {
327 pandoc.arg(p);
328 }
329
330 let command = format!("{:?}", pandoc);
331 println!("command: {}", command);
332 match pandoc.output() {
333 Ok(ref output) if output.status.success() => {}
334 Ok(ref output) => {
335 panic!("something went wrong running pandoc; \
336 command: {} current_dir: {} exit status: {:?} stdout: {} stderr: {}",
337 command,
338 env::current_dir().unwrap().display(),
339 output.status.code(),
340 String::from_utf8_lossy(&output.stdout),
341 String::from_utf8_lossy(&output.stderr),
342 );
343 }
344 Err(e) => {
345 panic!("something went wrong running pandoc; \
346 command: {} current_dir: {} err: {} PATH: {:?}",
347 command,
348 env::current_dir().unwrap().display(),
349 e,
350 env::var_os("PATH"));
351 }
352 }
353
354 Ok(())
355}
356
357fn is_skipped_md(entry: &fs::DirEntry) -> bool {
358 {
359 match entry.path().file_name().and_then(|p|p.to_str()) {
360 Some("mod.md") | Some("lib.md") => true,
361 _ => false,
362 }
363 }
364}
365
366
367#[test]
368fn it_works() {
369}