1use itertools::Itertools;
4
5use std::io::Write;
6use std::path::{Path, PathBuf};
7use std::rc::Rc;
8use std::str;
9
10#[cfg(windows)]
12const PANDOC_PATH: &[&str] = &[
13 concat!(env!("LOCALAPPDATA"), r#"\Pandoc\"#),
15];
16#[cfg(not(windows))]
18const PANDOC_PATH: &[&str] = &[];
19
20#[cfg(windows)]
22const LATEX_PATH: &[&str] = &[
23 r#"C:\Program Files (x86)\MiKTeX 2.9\miktex\bin"#,
24 r#"C:\Program Files\MiKTeX 2.9\miktex\bin"#,
25];
26#[cfg(not(windows))]
28const LATEX_PATH: &[&str] = &[r"/usr/local/bin", r"/usr/local/texlive/2015/bin/i386-linux"];
29
30#[cfg(windows)]
32const PATH_DELIMIT: &str = ";";
33
34#[cfg(not(windows))]
36const PATH_DELIMIT: &str = ":";
37
38use std::env;
39use std::process::Command;
40
41#[derive(PartialEq, Copy, Clone, Debug)]
42pub enum TrackChanges {
43 Accept,
44 Reject,
45 All,
46}
47
48impl std::fmt::Display for TrackChanges {
49 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
50 match *self {
51 TrackChanges::Accept => write!(fmt, "accept"),
52 TrackChanges::Reject => write!(fmt, "reject"),
53 TrackChanges::All => write!(fmt, "all"),
54 }
55 }
56}
57
58#[derive(PartialEq, Copy, Clone, Debug)]
59pub enum EmailObfuscation {
60 None,
61 Javascript,
62 References,
63}
64
65impl std::fmt::Display for EmailObfuscation {
66 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
67 match *self {
68 EmailObfuscation::None => write!(fmt, "none"),
69 EmailObfuscation::Javascript => write!(fmt, "javascript"),
70 EmailObfuscation::References => write!(fmt, "references"),
71 }
72 }
73}
74
75pub type URL = String;
76
77#[derive(PartialEq, Clone, Debug)]
78pub enum Tld {
79 Chapter,
80 Section,
81 Part,
82}
83
84#[derive(PartialEq, Clone, Debug)]
85#[non_exhaustive]
86pub enum PandocOption {
87 DataDir(PathBuf),
89 Defaults(PathBuf),
91 Strict,
93 ParseRaw,
95 Smart,
97 OldDashes,
99 #[deprecated(note = "replaced by ShiftHeadingLevelBy")]
101 BaseHeaderLevel(u32),
102 ShiftHeadingLevelBy(i32),
104 IndentedCodeClasses(String),
106 Filter(PathBuf),
108 LuaFilter(PathBuf),
110 Normalize,
112 PreserveTabs,
114 TabStop(u32),
116 TrackChanges(TrackChanges),
118 ExtractMedia(PathBuf),
120 Standalone,
122 Template(PathBuf),
124 Meta(String, Option<String>),
126 Var(String, Option<String>),
128 PrintDefaultTemplate(String),
130 PrintDefaultDataFile(PathBuf),
132 NoWrap,
134 Columns(u32),
136 TableOfContents,
138 TableOfContentsDepth(u32),
140 NoHighlight,
142 HighlightStyle(String),
144 IncludeInHeader(PathBuf),
146 IncludeBeforeBody(PathBuf),
148 IncludeAfterBody(PathBuf),
150 SelfContained,
152 Offline,
154 Html5,
156 HtmlQTags,
158 Ascii,
160 ReferenceLinks,
162 AtxHeaders,
164 TopLevelDivision(Tld),
166 NumberSections,
168 NumberOffset(Vec<u32>),
170 NoTexLigatures,
172 Listings,
174 Incremental,
176 SlideLevel(u32),
178 SectionDivs,
180 DefaultImageExtension(String),
182 EmailObfuscation(EmailObfuscation),
184 IdPrefix(String),
186 TitlePrefix(String),
188 Css(URL),
190 ReferenceOdt(PathBuf),
192 #[deprecated(note = "replaced by ReferenceDoc")]
194 ReferenceDocx(PathBuf),
195 ReferenceDoc(PathBuf),
197 EpubStylesheet(PathBuf),
199 EpubCoverImage(PathBuf),
201 EpubMetadata(PathBuf),
203 EpubEmbedFont(PathBuf),
205 EpubChapterLevel(u32),
207 PdfEngine(PathBuf),
209 PdfEngineOpt(String),
211 Citeproc,
213 Bibliography(PathBuf),
215 Csl(PathBuf),
217 CitationAbbreviations(PathBuf),
219 Natbib,
221 Biblatex,
223 LatexMathML(Option<URL>),
225 AsciiMathML(Option<URL>),
227 MathML(Option<URL>),
229 MimeTex(Option<URL>),
231 WebTex(Option<URL>),
233 JsMath(Option<URL>),
235 MathJax(Option<URL>),
237 Katex(Option<URL>),
239 KatexStylesheet(URL),
241 GladTex,
243 Trace,
245 DumpArgs,
247 IgnoreArgs,
249 Verbose,
251 ResourcePath(Vec<PathBuf>),
253 RuntimeSystem(Vec<PandocRuntimeSystemOption>),
270 Sandbox,
279 EOL(String),
282}
283
284#[derive(PartialEq, Clone, Debug)]
285#[non_exhaustive]
286pub enum PandocRuntimeSystemOption {
287 MaximumHeapMemory(String),
289}
290
291impl PandocOption {
292 fn apply<'a>(&self, pandoc: &'a mut Command) -> &'a mut Command {
293 use crate::PandocOption::*;
294 use crate::Tld::*;
295 match *self {
296 NumberOffset(ref nums) => {
297 let nums = nums.iter().fold(String::new(), |b, n| {
298 if b.is_empty() {
299 format!("{}", n)
300 } else {
301 format!("{}, {}", b, n)
302 }
303 });
304 pandoc.args(&[&format!("--number-offset={}", nums)])
305 }
306 DataDir(ref dir) => pandoc.args(&[&format!("--data-dir={}", dir.display())]),
307 Defaults(ref p) => pandoc.args(&[&format!("--defaults={}", p.display())]),
308 Strict => pandoc.args(&["--strict"]),
309 ParseRaw => pandoc.args(&["--parse-raw"]),
310 Smart => pandoc.args(&["--smart"]),
311 OldDashes => pandoc.args(&["--old-dashes"]),
312 #[allow(deprecated)]
313 BaseHeaderLevel(n) => pandoc.args(&[&format!("--base-header-level={}", n)]),
314 ShiftHeadingLevelBy(n) => pandoc.args(&[&format!("--shift-heading-level-by={}", n)]),
315 IndentedCodeClasses(ref s) => pandoc.args(&[&format!("--indented-code-classes={}", s)]),
316 Filter(ref program) => pandoc.args(&[&format!("--filter={}", program.display())]),
317 LuaFilter(ref script) => pandoc.args(&[&format!("--lua-filter={}", script.display())]),
318 Normalize => pandoc.args(&["--normalize"]),
319 PreserveTabs => pandoc.args(&["--preserve-tabs"]),
320 TabStop(n) => pandoc.args(&[&format!("--tab-stop={}", n)]),
321 TrackChanges(ref v) => pandoc.args(&[&format!("--track-changes={}", v)]),
322 ExtractMedia(ref p) => pandoc.args(&[&format!("--extract-media={}", p.display())]),
323 Standalone => pandoc.args(&["--standalone"]),
324 Template(ref p) => pandoc.args(&[&format!("--template={}", p.display())]),
325 Meta(ref k, Some(ref v)) => pandoc.args(&["-M", &format!("{}:{}", k, v)]),
326 Meta(ref k, None) => pandoc.args(&["-M", k]),
327 Var(ref k, Some(ref v)) => pandoc.args(&["-V", &format!("{}:{}", k, v)]),
328 Var(ref k, None) => pandoc.args(&["-V", k]),
329 PrintDefaultTemplate(ref f) => {
330 pandoc.args(&[&format!("--print-default-template={}", f)])
331 }
332 PrintDefaultDataFile(ref f) => {
333 pandoc.args(&[&format!("--print-default-data-file={}", f.display())])
334 }
335 NoWrap => pandoc.args(&["--wrap=none"]),
336 Columns(n) => pandoc.args(&[&format!("--columns={}", n)]),
337 TableOfContents => pandoc.args(&["--table-of-contents"]),
338 TableOfContentsDepth(d) => pandoc.args(&[&format!("--toc-depth={}", d)]),
339 NoHighlight => pandoc.args(&["--no-highlight"]),
340 HighlightStyle(ref s) => pandoc.args(&[&format!("--highlight-style={}", s)]),
341 IncludeInHeader(ref p) => {
342 pandoc.args(&[&format!("--include-in-header={}", p.display())])
343 }
344 IncludeBeforeBody(ref p) => {
345 pandoc.args(&[&format!("--include-before-body={}", p.display())])
346 }
347 IncludeAfterBody(ref p) => {
348 pandoc.args(&[&format!("--include-after-body={}", p.display())])
349 }
350 SelfContained => pandoc.args(&["--self-contained"]),
351 Offline => pandoc.args(&["--offline"]),
352 Html5 => pandoc.args(&["--html5"]),
353 HtmlQTags => pandoc.args(&["--html-q-tags"]),
354 Ascii => pandoc.args(&["--ascii"]),
355 ReferenceLinks => pandoc.args(&["--reference-links"]),
356 AtxHeaders => pandoc.args(&["--atx-headers"]),
357 TopLevelDivision(Chapter) => pandoc.args(&["--top-level-division=chapter"]),
358 TopLevelDivision(Section) => pandoc.args(&["--top-level-division=section"]),
359 TopLevelDivision(Part) => pandoc.args(&["--top-level-division=part"]),
360 NumberSections => pandoc.args(&["--number-sections"]),
361 NoTexLigatures => pandoc.args(&["--no-tex-ligatures"]),
362 Listings => pandoc.args(&["--listings"]),
363 Incremental => pandoc.args(&["--incremental"]),
364 SlideLevel(n) => pandoc.args(&[format!("--slide-level={}", n)]),
365 SectionDivs => pandoc.args(&["--section-divs"]),
366 DefaultImageExtension(ref s) => {
367 pandoc.args(&[format!("--default-image-extension={}", s)])
368 }
369 EmailObfuscation(o) => pandoc.args(&[format!("--email-obfuscation={}", o)]),
370 IdPrefix(ref s) => pandoc.args(&[format!("--id-prefix={}", s)]),
371 TitlePrefix(ref s) => pandoc.args(&[format!("--title-prefix={}", s)]),
372 Css(ref url) => pandoc.args(&[format!("--css={}", url)]),
373 ReferenceOdt(ref file) => pandoc.args(&[format!("--reference-odt={}", file.display())]),
374 #[allow(deprecated)]
375 ReferenceDocx(ref file) => {
376 pandoc.args(&[&format!("--reference-docx={}", file.display())])
377 }
378 ReferenceDoc(ref file) => {
379 pandoc.args(&[&format!("--reference-doc={}", file.display())])
380 }
381 EpubStylesheet(ref file) => {
382 pandoc.args(&[&format!("--epub-stylesheet={}", file.display())])
383 }
384 EpubCoverImage(ref file) => {
385 pandoc.args(&[&format!("--epub-cover-image={}", file.display())])
386 }
387 EpubMetadata(ref file) => {
388 pandoc.args(&[&format!("--epub-metadata={}", file.display())])
389 }
390 EpubEmbedFont(ref file) => {
391 pandoc.args(&[&format!("--epub-embed-font={}", file.display())])
392 }
393 EpubChapterLevel(num) => pandoc.args(&[&format!("--epub-chapter-level={}", num)]),
394 PdfEngine(ref program) => {
395 pandoc.args(&[&format!("--pdf-engine={}", program.display())])
396 }
397 PdfEngineOpt(ref s) => pandoc.args(&[&format!("--pdf-engine-opt={}", s)]),
398 Citeproc => pandoc.args(&["--citeproc"]),
399 Bibliography(ref file) => pandoc.args(&[&format!("--bibliography={}", file.display())]),
400 Csl(ref file) => pandoc.args(&[&format!("--csl={}", file.display())]),
401 CitationAbbreviations(ref f) => {
402 pandoc.args(&[&format!("--citation-abbreviations={}", f.display())])
403 }
404 Natbib => pandoc.args(&["--natbib"]),
405 Biblatex => pandoc.args(&["--biblatex"]),
406 LatexMathML(Some(ref url)) => pandoc.args(&[&format!("--latexmathml={}", url)]),
407 AsciiMathML(Some(ref url)) => pandoc.args(&[&format!("--asciimathml={}", url)]),
408 MathML(Some(ref url)) => pandoc.args(&[&format!("--mathml={}", url)]),
409 MimeTex(Some(ref url)) => pandoc.args(&[&format!("--mimetex={}", url)]),
410 WebTex(Some(ref url)) => pandoc.args(&[&format!("--webtex={}", url)]),
411 JsMath(Some(ref url)) => pandoc.args(&[&format!("--jsmath={}", url)]),
412 MathJax(Some(ref url)) => pandoc.args(&[&format!("--mathjax={}", url)]),
413 Katex(Some(ref url)) => pandoc.args(&[&format!("--katex={}", url)]),
414 LatexMathML(None) => pandoc.args(&["--latexmathml"]),
415 AsciiMathML(None) => pandoc.args(&["--asciimathml"]),
416 MathML(None) => pandoc.args(&["--mathml"]),
417 MimeTex(None) => pandoc.args(&["--mimetex"]),
418 WebTex(None) => pandoc.args(&["--webtex"]),
419 JsMath(None) => pandoc.args(&["--jsmath"]),
420 MathJax(None) => pandoc.args(&["--mathjax"]),
421 Katex(None) => pandoc.args(&["--katex"]),
422 KatexStylesheet(ref url) => pandoc.args(&[&format!("--katex-stylesheet={}", url)]),
423 GladTex => pandoc.args(&["--gladtex"]),
424 Trace => pandoc.args(&["--trace"]),
425 DumpArgs => pandoc.args(&["--dump-args"]),
426 IgnoreArgs => pandoc.args(&["--ignore-args"]),
427 Verbose => pandoc.args(&["--verbose"]),
428 ResourcePath(ref paths) => {
429 let delimiter = if cfg!(windows) { ";" } else { ":" };
430 let paths = paths
431 .iter()
432 .map(|path| path.display().to_string())
433 .join(delimiter);
434 pandoc.args(&[&format!("--resource-path={}", paths)])
435 }
436 RuntimeSystem(ref rts_options) => {
437 pandoc.args(&["+RTS"]);
438 for option in rts_options {
439 match option {
440 PandocRuntimeSystemOption::MaximumHeapMemory(ref s) => {
441 pandoc.args(&[&format!("-M{}", s)]);
442 }
443 }
444 }
445 pandoc.args(&["-RTS"])
446 }
447 Sandbox => pandoc.args(&["--sandbox"]),
448 EOL(ref eol) => pandoc.args(&[&format!("--eol={}", eol)]),
449 }
450 }
451}
452
453#[derive(Debug, Clone)]
455pub enum DocumentClass {
456 Article,
458 Report,
460 Book,
462}
463
464pub use crate::DocumentClass::*;
465
466impl std::fmt::Display for DocumentClass {
467 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
468 match *self {
469 Article => write!(fmt, "article"),
470 Report => write!(fmt, "report"),
471 Book => write!(fmt, "book"),
472 }
473 }
474}
475
476#[derive(Debug, Clone)]
478#[non_exhaustive]
479pub enum OutputFormat {
480 Native,
482 Json,
484 Plain,
486 Markdown,
488 MarkdownStrict,
490 MarkdownPhpextra,
492 MarkdownGithub,
494 Commonmark,
496 CommonmarkX,
498 Rst,
500 Html,
502 Html5,
504 Latex,
506 Beamer,
508 Context,
510 Pdf,
512 Man,
514 MediaWiki,
516 Dokuwiki,
518 Textile,
520 Org,
522 Texinfo,
524 Opml,
526 Docbook,
528 OpenDocument,
530 Odt,
532 Docx,
534 Haddock,
536 Rtf,
538 Epub,
540 Epub3,
542 Fb2,
544 Asciidoc,
546 Icml,
548 Slidy,
550 Slideous,
552 Dzslides,
554 Revealjs,
556 S5,
558 Lua(String),
560 Other(String),
562}
563
564impl std::fmt::Display for OutputFormat {
565 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
566 use crate::OutputFormat::*;
567 match self {
568 Native => write!(fmt, "native"),
569 Json => write!(fmt, "json"),
570 Plain => write!(fmt, "plain"),
571 Markdown => write!(fmt, "markdown"),
572 MarkdownStrict => write!(fmt, "markdown_strict"),
573 MarkdownPhpextra => write!(fmt, "markdown_phpextra"),
574 MarkdownGithub => write!(fmt, "markdown_github"),
575 Commonmark => write!(fmt, "commonmark"),
576 CommonmarkX => write!(fmt, "commonmark_x"),
577 Rst => write!(fmt, "rst"),
578 Html => write!(fmt, "html"),
579 Html5 => write!(fmt, "html5"),
580 Latex => write!(fmt, "latex"),
581 Beamer => write!(fmt, "beamer"),
582 Context => write!(fmt, "context"),
583 Pdf => write!(fmt, "pdf"),
584 Man => write!(fmt, "man"),
585 MediaWiki => write!(fmt, "mediawiki"),
586 Dokuwiki => write!(fmt, "dokuwiki"),
587 Textile => write!(fmt, "textile"),
588 Org => write!(fmt, "org"),
589 Texinfo => write!(fmt, "texinfo"),
590 Opml => write!(fmt, "opml"),
591 Docbook => write!(fmt, "docbook"),
592 OpenDocument => write!(fmt, "open_document"),
593 Odt => write!(fmt, "odt"),
594 Docx => write!(fmt, "docx"),
595 Haddock => write!(fmt, "haddock"),
596 Rtf => write!(fmt, "rtf"),
597 Epub => write!(fmt, "epub"),
598 Epub3 => write!(fmt, "epub3"),
599 Fb2 => write!(fmt, "fb2"),
600 Asciidoc => write!(fmt, "asciidoc"),
601 Icml => write!(fmt, "icml"),
602 Slidy => write!(fmt, "slidy"),
603 Slideous => write!(fmt, "slideous"),
604 Dzslides => write!(fmt, "dzslides"),
605 Revealjs => write!(fmt, "revealjs"),
606 S5 => write!(fmt, "s5"),
607 Lua(_) => unimplemented!(),
608 Other(f) => write!(fmt, "{}", f),
609 }
610 }
611}
612
613#[derive(Debug, Clone)]
615#[non_exhaustive]
616pub enum InputFormat {
617 Native,
619 Json,
621 Markdown,
623 MarkdownStrict,
625 MarkdownPhpextra,
627 MarkdownGithub,
629 Commonmark,
631 CommonmarkX,
633 Textile,
635 Rst,
637 Rtf,
640 Html,
642 DocBook,
644 T2t,
646 Docx,
648 Epub,
650 Opml,
652 Org,
654 MediaWiki,
656 Twiki,
658 Haddock,
660 Latex,
662 Other(String),
664}
665
666impl std::fmt::Display for InputFormat {
667 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
668 use crate::InputFormat::*;
669 match self {
670 Native => write!(fmt, "native"),
671 Json => write!(fmt, "json"),
672 Markdown => write!(fmt, "markdown"),
673 MarkdownStrict => write!(fmt, "markdown_strict"),
674 MarkdownPhpextra => write!(fmt, "markdown_phpextra"),
675 MarkdownGithub => write!(fmt, "markdown_github"),
676 Commonmark => write!(fmt, "commonmark"),
677 CommonmarkX => write!(fmt, "commonmark_x"),
678 Rst => write!(fmt, "rst"),
679 Rtf => write!(fmt, "rtf"),
680 Html => write!(fmt, "html"),
681 Latex => write!(fmt, "latex"),
682 MediaWiki => write!(fmt, "mediawiki"),
683 Textile => write!(fmt, "textile"),
684 Org => write!(fmt, "org"),
685 Opml => write!(fmt, "opml"),
686 Docx => write!(fmt, "docx"),
687 Haddock => write!(fmt, "haddock"),
688 Epub => write!(fmt, "epub"),
689 DocBook => write!(fmt, "docbook"),
690 T2t => write!(fmt, "t2t"),
691 Twiki => write!(fmt, "twiki"),
692 Other(f) => write!(fmt, "{}", f),
693 }
694 }
695}
696
697#[allow(missing_docs)]
698#[derive(Debug, Clone)]
699#[non_exhaustive]
700pub enum MarkdownExtension {
701 Smart,
702 Attributes,
703 EscapedLineBreaks,
704 BlankBeforeHeader,
705 HeaderAttributes,
706 AutoIdentifiers,
707 ImplicitHeaderReferences,
708 BlankBeforeBlockQuote,
709 FencedDivs,
710 FencedCodeBlocks,
711 BacktickCodeBlocks,
712 FencedCodeAttributes,
713 LineBlocks,
714 FancyLists,
715 Startnum,
716 TaskLists,
717 DefinitionLists,
718 ExampleLists,
719 TableCaptions,
720 SimpleTables,
721 MultilineTables,
722 GridTables,
723 PipeTables,
724 PandocTitleBlock,
725 YamlMetadataBlock,
726 AllSymbolsEscapable,
727 IntrawordUnderscores,
728 Strikeout,
729 Superscript,
730 Subscript,
731 InlineCodeAttributes,
732 TexMathDollars,
733 RawAttribute,
734 RawHtml,
735 MarkdownInHtmlBlocks,
736 NativeDivs,
737 NativeSpans,
738 BracketedSpans,
739 RawTex,
740 LatexMacros,
741 ShortcutReferenceLinks,
742 ImplicitFigures,
743 Footnotes,
744 InlineNotes,
745 Citations,
746 ListsWithoutPrecedingBlankline,
747 HardLineBreaks,
748 IgnoreLineBreaks,
749 TexMathSingleBackslash,
750 TexMathDoubleBackslash,
751 MarkdownAttribute,
752 MmdTitleBlock,
753 Abbreviations,
754 AutolinkBareUris,
755 AsciiIdentifiers,
756 LinkAttributes,
757 MmdHeaderIdentifiers,
758 CompactDefinitionLists,
759 RebaseRelativePaths,
760 Other(String),
761}
762
763impl std::fmt::Display for MarkdownExtension {
764 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
765 use crate::MarkdownExtension::*;
766 match self {
767 Smart => write!(fmt, "smart"),
768 Attributes => write!(fmt, "attributes"),
769 EscapedLineBreaks => write!(fmt, "escaped_line_breaks"),
770 BlankBeforeHeader => write!(fmt, "blank_before_header"),
771 HeaderAttributes => write!(fmt, "header_attributes"),
772 AutoIdentifiers => write!(fmt, "auto_identifiers"),
773 ImplicitHeaderReferences => write!(fmt, "implicit_header_references"),
774 BlankBeforeBlockQuote => write!(fmt, "blank_before_block_quote"),
775 FencedDivs => write!(fmt, "fenced_divs"),
776 FencedCodeBlocks => write!(fmt, "fenced_code_blocks"),
777 BacktickCodeBlocks => write!(fmt, "backtick_code_blocks"),
778 FencedCodeAttributes => write!(fmt, "fenced_code_attributes"),
779 LineBlocks => write!(fmt, "line_blocks"),
780 FancyLists => write!(fmt, "fancy_lists"),
781 Startnum => write!(fmt, "startnum"),
782 TaskLists => write!(fmt, "task_lists"),
783 DefinitionLists => write!(fmt, "definition_lists"),
784 ExampleLists => write!(fmt, "example_lists"),
785 TableCaptions => write!(fmt, "table_captions"),
786 SimpleTables => write!(fmt, "simple_tables"),
787 MultilineTables => write!(fmt, "multiline_tables"),
788 GridTables => write!(fmt, "grid_tables"),
789 PipeTables => write!(fmt, "pipe_tables"),
790 PandocTitleBlock => write!(fmt, "pandoc_title_block"),
791 YamlMetadataBlock => write!(fmt, "yaml_metadata_block"),
792 AllSymbolsEscapable => write!(fmt, "all_symbols_escapable"),
793 IntrawordUnderscores => write!(fmt, "intraword_underscores"),
794 Strikeout => write!(fmt, "strikeout"),
795 Superscript => write!(fmt, "superscript"),
796 Subscript => write!(fmt, "subscript"),
797 InlineCodeAttributes => write!(fmt, "inline_code_attributes"),
798 TexMathDollars => write!(fmt, "tex_math_dollars"),
799 RawAttribute => write!(fmt, "raw_attribute"),
800 RawHtml => write!(fmt, "raw_html"),
801 MarkdownInHtmlBlocks => write!(fmt, "markdown_in_html_blocks"),
802 NativeDivs => write!(fmt, "native_divs"),
803 NativeSpans => write!(fmt, "native_spans"),
804 BracketedSpans => write!(fmt, "bracketed_spans"),
805 RawTex => write!(fmt, "raw_tex"),
806 LatexMacros => write!(fmt, "latex_macros"),
807 ShortcutReferenceLinks => write!(fmt, "shortcut_reference_links"),
808 ImplicitFigures => write!(fmt, "implicit_figures"),
809 Footnotes => write!(fmt, "footnotes"),
810 InlineNotes => write!(fmt, "inline_notes"),
811 Citations => write!(fmt, "citations"),
812 ListsWithoutPrecedingBlankline => write!(fmt, "lists_without_preceding_blankline"),
813 HardLineBreaks => write!(fmt, "hard_line_breaks"),
814 IgnoreLineBreaks => write!(fmt, "ignore_line_breaks"),
815 TexMathSingleBackslash => write!(fmt, "tex_math_single_backslash"),
816 TexMathDoubleBackslash => write!(fmt, "tex_math_double_backslash"),
817 MarkdownAttribute => write!(fmt, "markdown_attribute"),
818 MmdTitleBlock => write!(fmt, "Mmd_title_block"),
819 Abbreviations => write!(fmt, "abbreviations"),
820 AutolinkBareUris => write!(fmt, "autolink_bare_uris"),
821 AsciiIdentifiers => write!(fmt, "ascii_identifiers"),
822 LinkAttributes => write!(fmt, "link_attributes"),
823 MmdHeaderIdentifiers => write!(fmt, "mmd_header_identifiers"),
824 CompactDefinitionLists => write!(fmt, "compact_definition_lists"),
825 RebaseRelativePaths => write!(fmt, "rebase_relative_paths"),
826 Other(e) => write!(fmt, "{}", e),
827 }
828 }
829}
830
831#[derive(Clone, Debug)]
832pub enum InputKind {
833 Files(Vec<PathBuf>),
834 Pipe(String),
836}
837
838#[derive(Clone, Debug)]
840pub enum OutputKind {
841 File(PathBuf),
842 Pipe,
843}
844
845#[derive(Default, Clone)]
847pub struct Pandoc {
848 input: Option<InputKind>,
849 input_format: Option<(InputFormat, Vec<MarkdownExtension>)>,
850 output: Option<OutputKind>,
851 output_format: Option<(OutputFormat, Vec<MarkdownExtension>)>,
852 latex_path_hint: Vec<PathBuf>,
853 pandoc_path_hint: Vec<PathBuf>,
854 filters: Vec<Rc<dyn Fn(String) -> String>>,
855 args: Vec<(String, String)>,
856 options: Vec<PandocOption>,
857 print_pandoc_cmdline: bool,
858}
859
860pub fn new() -> Pandoc {
862 Pandoc::new()
863}
864
865impl Pandoc {
866 pub fn new() -> Pandoc {
870 Pandoc {
871 print_pandoc_cmdline: false,
872 ..Default::default()
873 }
874 }
875
876 pub fn add_latex_path_hint<T: AsRef<Path> + ?Sized>(&mut self, path: &T) -> &mut Pandoc {
881 self.latex_path_hint.push(path.as_ref().to_owned());
882 self
883 }
884
885 pub fn add_pandoc_path_hint<T: AsRef<Path> + ?Sized>(&mut self, path: &T) -> &mut Pandoc {
890 self.pandoc_path_hint.push(path.as_ref().to_owned());
891 self
892 }
893
894 pub fn set_doc_class(&mut self, class: DocumentClass) -> &mut Pandoc {
896 self.options.push(PandocOption::Var(
897 "documentclass".to_string(),
898 Some(class.to_string()),
899 ));
900 self
901 }
902
903 pub fn set_show_cmdline(&mut self, flag: bool) -> &mut Pandoc {
908 self.print_pandoc_cmdline = flag;
909 self
910 }
911
912 pub fn set_output_format(
914 &mut self,
915 format: OutputFormat,
916 extensions: Vec<MarkdownExtension>,
917 ) -> &mut Pandoc {
918 self.output_format = Some((format, extensions));
919 self
920 }
921 pub fn set_input_format(
923 &mut self,
924 format: InputFormat,
925 extensions: Vec<MarkdownExtension>,
926 ) -> &mut Pandoc {
927 self.input_format = Some((format, extensions));
928 self
929 }
930
931 pub fn add_input<T: AsRef<Path> + ?Sized>(&mut self, filename: &T) -> &mut Pandoc {
938 let filename = filename.as_ref().to_owned();
939 match self.input {
940 Some(InputKind::Files(ref mut files)) => {
941 files.push(filename);
942 }
943 Some(InputKind::Pipe(_)) => panic!(
944 "Input has been set to stdin already, \
945 adding input file names is impossible"
946 ),
947 None => {
948 self.input = Some(InputKind::Files(vec![filename]));
949 }
950 };
951 self
952 }
953
954 pub fn set_input(&mut self, input: InputKind) -> &mut Pandoc {
967 self.input = Some(input);
968 self
969 }
970
971 pub fn set_output(&mut self, output: OutputKind) -> &mut Pandoc {
973 self.output = Some(output);
974 self
975 }
976
977 pub fn set_bibliography<T: AsRef<Path> + ?Sized>(&mut self, filename: &T) -> &mut Pandoc {
979 self.options
980 .push(PandocOption::Bibliography(filename.as_ref().to_owned()));
981 self
982 }
983
984 pub fn set_csl<T: AsRef<Path> + ?Sized>(&mut self, filename: &T) -> &mut Pandoc {
986 self.options
987 .push(PandocOption::Csl(filename.as_ref().to_owned()));
988 self
989 }
990
991 pub fn set_toc(&mut self) -> &mut Pandoc {
996 self.options.push(PandocOption::TableOfContents);
997 self
998 }
999
1000 pub fn set_chapters(&mut self) -> &mut Pandoc {
1002 self.options
1003 .push(PandocOption::TopLevelDivision(Tld::Chapter));
1004 self
1005 }
1006
1007 pub fn set_number_sections(&mut self) -> &mut Pandoc {
1012 self.options.push(PandocOption::NumberSections);
1013 self
1014 }
1015
1016 pub fn set_latex_template<T: AsRef<Path> + ?Sized>(&mut self, filename: &T) -> &mut Pandoc {
1018 self.options
1019 .push(PandocOption::Template(filename.as_ref().to_owned()));
1020 self
1021 }
1022
1023 pub fn set_slide_level(&mut self, level: u32) -> &mut Pandoc {
1025 self.options.push(PandocOption::SlideLevel(level));
1026 self
1027 }
1028
1029 pub fn set_variable<T: AsRef<str> + ?Sized, U: AsRef<str> + ?Sized>(
1034 &mut self,
1035 key: &T,
1036 value: &U,
1037 ) -> &mut Pandoc {
1038 self.options.push(PandocOption::Var(
1039 key.as_ref().to_owned(),
1040 Some(value.as_ref().to_owned()),
1041 ));
1042 self
1043 }
1044
1045 pub fn add_filter<F>(&mut self, filter: F) -> &mut Pandoc
1055 where
1056 F: 'static + Fn(String) -> String,
1057 {
1058 self.filters.push(Rc::new(filter));
1059 self
1060 }
1061
1062 pub fn add_option(&mut self, option: PandocOption) -> &mut Pandoc {
1064 self.options.push(option);
1065 self
1066 }
1067
1068 pub fn add_options(&mut self, options: &[PandocOption]) -> &mut Pandoc {
1069 self.options.extend_from_slice(options);
1070 self
1071 }
1072
1073 fn run(self) -> Result<Vec<u8>, PandocError> {
1074 let mut cmd = Command::new("pandoc");
1075 if let Some((ref format, ref extensions)) = self.input_format {
1076 use std::fmt::Write;
1077 let mut arg = format.to_string();
1078 for extension in extensions {
1079 write!(arg, "+{}", extension).unwrap();
1080 }
1081 cmd.arg("-f").arg(arg);
1082 }
1083 for (key, val) in self.args {
1084 cmd.arg(format!("--{}={}", key, val));
1085 }
1086 let path: String = Itertools::intersperse(
1087 self.latex_path_hint
1088 .iter()
1089 .chain(self.pandoc_path_hint.iter())
1090 .map(|p| p.to_str().expect("non-utf8 path"))
1091 .chain(PANDOC_PATH.iter().cloned())
1092 .chain(LATEX_PATH.iter().cloned())
1093 .chain(
1094 [env::var("PATH").unwrap()]
1095 .iter()
1096 .map(std::borrow::Borrow::borrow),
1097 ),
1098 PATH_DELIMIT,
1099 )
1100 .collect();
1101 cmd.env("PATH", path);
1102 let output = self.output.ok_or(PandocError::NoOutputSpecified)?;
1103 let input = self.input.ok_or(PandocError::NoInputSpecified)?;
1104 let input = match input {
1105 InputKind::Files(files) => {
1106 for file in files {
1107 cmd.arg(file);
1108 }
1109 String::new()
1110 }
1111 InputKind::Pipe(text) => {
1112 cmd.stdin(std::process::Stdio::piped());
1113 text
1114 }
1115 };
1116 match output {
1117 OutputKind::File(filename) => {
1118 cmd.arg("-o").arg(filename);
1119 }
1120 OutputKind::Pipe => {
1121 match self.output_format {
1122 Some((OutputFormat::Pdf, ..)) => {
1123 cmd.arg("-o").arg("-").stdout(std::process::Stdio::piped())
1124 }
1125 _ => cmd.stdout(std::process::Stdio::piped()),
1126 };
1127 }
1128 }
1129
1130 cmd.stderr(std::process::Stdio::piped());
1132
1133 if let Some((ref format, ref extensions)) = self.output_format {
1134 use std::fmt::Write;
1135 let mut arg = format.to_string();
1136 for extension in extensions {
1137 write!(arg, "+{}", extension).unwrap();
1138 }
1139 cmd.arg("-t").arg(arg);
1140 }
1141
1142 for opt in self.options {
1143 opt.apply(&mut cmd);
1144 }
1145 if self.print_pandoc_cmdline {
1146 println!("{:?}", cmd);
1147 }
1148 let mut child = cmd.spawn()?;
1149 if let Some(ref mut stdin) = child.stdin {
1150 stdin.write_all(input.as_bytes())?;
1151 }
1152 let o = child.wait_with_output()?;
1153 if o.status.success() {
1154 Ok(o.stdout)
1155 } else {
1156 Err(PandocError::Err(o))
1157 }
1158 }
1159
1160 pub fn arg<T: AsRef<str> + ?Sized, U: AsRef<str> + ?Sized>(
1163 &mut self,
1164 key: &T,
1165 value: &U,
1166 ) -> &mut Pandoc {
1167 self.args
1168 .push((key.as_ref().to_owned(), value.as_ref().to_owned()));
1169 self
1170 }
1171
1172 pub fn generate_latex_template<T: AsRef<str> + ?Sized>(mut self, filename: &T) {
1176 let mut format = None;
1177 if let Some((ref f, ref ext)) = self.output_format {
1178 let mut s = f.to_string();
1179 for ext in ext {
1180 use std::fmt::Write;
1181 write!(&mut s, "+{}", ext).unwrap();
1182 }
1183 format = Some(s);
1184 }
1185 let format = format.unwrap();
1186 self.arg("print-default-template", &format);
1187 let output = self.run().unwrap();
1188 let mut file = std::fs::File::create(filename.as_ref()).unwrap();
1189 file.write_all(&output).unwrap();
1190 }
1191
1192 fn preprocess(&mut self) -> Result<(), PandocError> {
1193 let filters = std::mem::take(&mut self.filters);
1194
1195 if filters.is_empty() {
1196 return Ok(());
1197 }
1198
1199 let mut pre = new();
1200 pre.pandoc_path_hint = self.pandoc_path_hint.clone();
1201 pre.latex_path_hint = self.latex_path_hint.clone();
1202 pre.output = Some(OutputKind::Pipe);
1203 pre.set_output_format(OutputFormat::Json, Vec::new());
1204 pre.input = self.input.take();
1205 pre.print_pandoc_cmdline = self.print_pandoc_cmdline;
1206 match self.input_format.take() {
1207 None => self.input_format = Some((InputFormat::Json, Vec::new())),
1208 Some((fmt, ext)) => {
1209 pre.input_format = Some((fmt, ext));
1210 self.input_format = Some((InputFormat::Json, Vec::new()));
1211 }
1212 }
1213 let o = pre.run()?;
1214 let o = String::from_utf8(o).unwrap();
1215 let filtered = filters.into_iter().fold(o, |acc, item| item(acc));
1217 self.input = Some(InputKind::Pipe(filtered));
1218 Ok(())
1219 }
1220
1221 pub fn execute(mut self) -> Result<PandocOutput, PandocError> {
1229 self.preprocess()?;
1230 let output_format = self.output_format.clone();
1231 let output_kind = self.output.clone();
1232 let output = self.run()?;
1233
1234 match output_kind {
1235 Some(OutputKind::File(name)) => Ok(PandocOutput::ToFile(name)),
1236 Some(OutputKind::Pipe) => match output_format {
1237 Some((OutputFormat::Pdf, ..)) => Ok(PandocOutput::ToBufferRaw(output)),
1238
1239 _ => match String::from_utf8(output) {
1240 Ok(string) => Ok(PandocOutput::ToBuffer(string)),
1241 Err(err) => Err(PandocError::from(err.utf8_error())),
1242 },
1243 },
1244 None => Err(PandocError::NoOutputSpecified),
1245 }
1246 }
1247}
1248
1249pub enum PandocOutput {
1251 ToFile(PathBuf),
1253 ToBuffer(String),
1256 ToBufferRaw(Vec<u8>),
1259}
1260
1261pub enum PandocError {
1263 BadUtf8Conversion(usize),
1265 IoErr(std::io::Error),
1267 Err(std::process::Output),
1269 NoOutputSpecified,
1271 NoInputSpecified,
1273 PandocNotFound,
1275}
1276
1277impl std::convert::From<std::io::Error> for PandocError {
1278 fn from(err: std::io::Error) -> Self {
1279 match err.kind() {
1280 std::io::ErrorKind::NotFound => PandocError::PandocNotFound,
1281 _ => PandocError::IoErr(err),
1282 }
1283 }
1284}
1285
1286impl std::convert::From<std::str::Utf8Error> for PandocError {
1287 fn from(error: std::str::Utf8Error) -> Self {
1288 PandocError::BadUtf8Conversion(error.valid_up_to())
1289 }
1290}
1291
1292impl std::fmt::Debug for PandocError {
1293 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
1294 match *self {
1295 PandocError::IoErr(ref e) => write!(fmt, "{:?}", e),
1296 PandocError::Err(ref e) => {
1297 write!(fmt, "exit_code: {:?}", e.status.code())?;
1298 write!(fmt, "stdout: {}", String::from_utf8_lossy(&e.stdout))?;
1299 write!(fmt, "stderr: {}", String::from_utf8_lossy(&e.stderr))
1300 }
1301 PandocError::NoOutputSpecified => write!(fmt, "No output file was specified"),
1302 PandocError::NoInputSpecified => write!(fmt, "No input files were specified"),
1303 PandocError::PandocNotFound => {
1304 write!(fmt, "Pandoc not found, did you forget to install pandoc?")
1305 }
1306 PandocError::BadUtf8Conversion(byte) => write!(
1307 fmt,
1308 "UTF-8 conversion of pandoc output failed after byte {}.",
1309 byte
1310 ),
1311 }
1312 }
1313}
1314
1315impl std::fmt::Display for PandocError {
1316 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
1317 std::fmt::Debug::fmt(self, fmt)
1318 }
1319}
1320
1321impl std::error::Error for PandocError {
1322 fn cause(&self) -> Option<&dyn std::error::Error> {
1323 match *self {
1324 PandocError::IoErr(ref e) => Some(e),
1325 _ => None,
1326 }
1327 }
1328}