pandoc/
lib.rs

1//! API that wraps the pandoc command line tool
2
3use itertools::Itertools;
4
5use std::io::Write;
6use std::path::{Path, PathBuf};
7use std::rc::Rc;
8use std::str;
9
10/// path to pandoc executable
11#[cfg(windows)]
12const PANDOC_PATH: &[&str] = &[
13    // this compiles the user's name into the binary, maybe not the greatest idea?
14    concat!(env!("LOCALAPPDATA"), r#"\Pandoc\"#),
15];
16/// path to pandoc executable
17#[cfg(not(windows))]
18const PANDOC_PATH: &[&str] = &[];
19
20/// path where miktex executables can be found
21#[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/// path where miktex executables can be found
27#[cfg(not(windows))]
28const LATEX_PATH: &[&str] = &[r"/usr/local/bin", r"/usr/local/texlive/2015/bin/i386-linux"];
29
30/// character to split path variable on windows
31#[cfg(windows)]
32const PATH_DELIMIT: &str = ";";
33
34/// character to split path variable on 'other platforms'
35#[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    /// --data-dir=DIRECTORY
88    DataDir(PathBuf),
89    /// --defaults=FILE
90    Defaults(PathBuf),
91    /// --strict
92    Strict,
93    /// -R --parse-raw
94    ParseRaw,
95    /// -S --smart
96    Smart,
97    /// --old-dashes
98    OldDashes,
99    /// --base-header-level=NUMBER
100    #[deprecated(note = "replaced by ShiftHeadingLevelBy")]
101    BaseHeaderLevel(u32),
102    /// --shift-heading-level-by=NUMBER,
103    ShiftHeadingLevelBy(i32),
104    /// --indented-code-classes=STRING
105    IndentedCodeClasses(String),
106    /// -F PROGRAM --filter=PROGRAM
107    Filter(PathBuf),
108    /// -L SCRIPT --lua-filter=SCRIPT
109    LuaFilter(PathBuf),
110    /// --normalize
111    Normalize,
112    /// -p --preserve-tabs
113    PreserveTabs,
114    /// --tab-stop=NUMBER
115    TabStop(u32),
116    /// --track-changes=accept|reject|all
117    TrackChanges(TrackChanges),
118    /// --extract-media=PATH
119    ExtractMedia(PathBuf),
120    /// -s --standalone
121    Standalone,
122    /// --template=FILENAME
123    Template(PathBuf),
124    /// -M KEY[:VALUE] --metadata=KEY[:VALUE]
125    Meta(String, Option<String>),
126    /// -V KEY[:VALUE] --variable=KEY[:VALUE]
127    Var(String, Option<String>),
128    /// -D FORMAT --print-default-template=FORMAT
129    PrintDefaultTemplate(String),
130    /// --print-default-data-file=FILE
131    PrintDefaultDataFile(PathBuf),
132    /// --no-wrap
133    NoWrap,
134    /// --columns=NUMBER
135    Columns(u32),
136    /// --toc, --table-of-contents
137    TableOfContents,
138    /// --toc-depth=NUMBER
139    TableOfContentsDepth(u32),
140    /// --no-highlight
141    NoHighlight,
142    /// --highlight-style=STYLE
143    HighlightStyle(String),
144    /// -H FILENAME --include-in-header=FILENAME
145    IncludeInHeader(PathBuf),
146    /// -B FILENAME --include-before-body=FILENAME
147    IncludeBeforeBody(PathBuf),
148    /// -A FILENAME --include-after-body=FILENAME
149    IncludeAfterBody(PathBuf),
150    /// --self-contained
151    SelfContained,
152    /// --offline
153    Offline,
154    /// -5 --html5
155    Html5,
156    /// --html-q-tags
157    HtmlQTags,
158    /// --ascii
159    Ascii,
160    /// --reference-links
161    ReferenceLinks,
162    /// --atx-headers
163    AtxHeaders,
164    /// --top-level-division=
165    TopLevelDivision(Tld),
166    /// -N --number-sections
167    NumberSections,
168    /// --number-offset=NUMBERS
169    NumberOffset(Vec<u32>),
170    /// --no-tex-ligatures
171    NoTexLigatures,
172    /// --listings
173    Listings,
174    /// -i --incremental
175    Incremental,
176    /// --slide-level=NUMBER
177    SlideLevel(u32),
178    /// --section-divs
179    SectionDivs,
180    /// --default-image-extension=extension
181    DefaultImageExtension(String),
182    /// --email-obfuscation=none|javascript|references
183    EmailObfuscation(EmailObfuscation),
184    /// --id-prefix=STRING
185    IdPrefix(String),
186    /// -T STRING --title-prefix=STRING
187    TitlePrefix(String),
188    /// -c URL --css=URL
189    Css(URL),
190    /// --reference-odt=FILENAME
191    ReferenceOdt(PathBuf),
192    /// --reference-docx=FILENAME
193    #[deprecated(note = "replaced by ReferenceDoc")]
194    ReferenceDocx(PathBuf),
195    /// --reference-doc=FILENAME
196    ReferenceDoc(PathBuf),
197    /// --epub-stylesheet=FILENAME
198    EpubStylesheet(PathBuf),
199    /// --epub-cover-image=FILENAME
200    EpubCoverImage(PathBuf),
201    /// --epub-metadata=FILENAME
202    EpubMetadata(PathBuf),
203    /// --epub-embed-font=FILE
204    EpubEmbedFont(PathBuf),
205    /// --epub-chapter-level=NUMBER
206    EpubChapterLevel(u32),
207    /// --pdf-engine=PROGRAM
208    PdfEngine(PathBuf),
209    /// --pdf-engine-opt=STRING
210    PdfEngineOpt(String),
211    /// --citeproc
212    Citeproc,
213    /// --bibliography=FILE
214    Bibliography(PathBuf),
215    /// --csl=FILE
216    Csl(PathBuf),
217    /// --citation-abbreviations=FILE
218    CitationAbbreviations(PathBuf),
219    /// --natbib
220    Natbib,
221    /// --biblatex
222    Biblatex,
223    /// -m[URL] --latexmathml[=URL], --asciimathml[=URL]
224    LatexMathML(Option<URL>),
225    /// --asciimathml[=URL]
226    AsciiMathML(Option<URL>),
227    /// --mathml[=URL]
228    MathML(Option<URL>),
229    /// --mimetex[=URL]
230    MimeTex(Option<URL>),
231    /// --webtex[=URL]
232    WebTex(Option<URL>),
233    /// --jsmath[=URL]
234    JsMath(Option<URL>),
235    /// --mathjax[=URL]
236    MathJax(Option<URL>),
237    /// --katex[=URL]
238    Katex(Option<URL>),
239    /// --katex-stylesheet=URL
240    KatexStylesheet(URL),
241    /// -gladtex
242    GladTex,
243    /// --trace
244    Trace,
245    /// --dump-args
246    DumpArgs,
247    /// --ignore-args
248    IgnoreArgs,
249    /// --verbose
250    Verbose,
251    /// --resource-path=PATH
252    ResourcePath(Vec<PathBuf>),
253    /// +RTS OPTIONS -RTS
254    ///
255    /// In Pandoc's "A note on security" section of the manual ([link](https://pandoc.org/MANUAL.html#a-note-on-security)), there is a recommendation to set a heap size limit to prevent pathological corner cases.
256    ///
257    /// The full list of "RTS" options can be found in the Haskell "Runtime control" section of the manual ([link](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/runtime_control.html)).
258    /// The Runtime System options are way more extensive than the -M option, and cover a lot of use-cases that aren't needed while using pandoc in a production setting.
259    ///
260    /// ## Example Usage
261    ///
262    /// ```
263    /// let mut pandoc = pandoc::new();
264    /// pandoc.add_option(pandoc::PandocOption::RuntimeSystem(vec![
265    ///   // Limit the heap size to 512 MB while processing an arbitrary input file.
266    ///   pandoc::PandocRuntimeSystemOption::MaximumHeapMemory("512M".to_string()),
267    /// ]));
268    /// ```
269    RuntimeSystem(Vec<PandocRuntimeSystemOption>),
270    /// --sandbox
271    ///
272    /// [This option is strongly recommended for any use-case
273    /// involving untrusted user
274    /// input.](https://pandoc.org/MANUAL.html#option--sandbox) Note
275    /// that it does require a pandoc binary compiled with the
276    /// `embed_data_files` option, in order to process some formats
277    /// such as docx without external file access.
278    Sandbox,
279    /// Manually specify line endings: crlf (Windows), lf (macOS/Linux/UNIX), or native
280    /// (line endings appropriate to the OS on which pandoc is being run). The default is native.
281    EOL(String),
282}
283
284#[derive(PartialEq, Clone, Debug)]
285#[non_exhaustive]
286pub enum PandocRuntimeSystemOption {
287    /// -M<size>
288    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/// equivalent to the latex document class
454#[derive(Debug, Clone)]
455pub enum DocumentClass {
456    /// compact form of report
457    Article,
458    /// abstract + chapters + custom page for title, abstract and toc
459    Report,
460    /// no abstract
461    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/// typesafe access to -t FORMAT, -w FORMAT, --to=FORMAT, --write=FORMAT
477#[derive(Debug, Clone)]
478#[non_exhaustive]
479pub enum OutputFormat {
480    /// native Haskell
481    Native,
482    /// JSON version of native AST
483    Json,
484    /// Plain text
485    Plain,
486    /// pandoc’s extended markdown
487    Markdown,
488    /// original unextended markdown
489    MarkdownStrict,
490    /// PHP Markdown extra extended markdown
491    MarkdownPhpextra,
492    /// github extended markdown
493    MarkdownGithub,
494    /// CommonMark markdown
495    Commonmark,
496    /// CommonMark markdown with extensions
497    CommonmarkX,
498    /// reStructuredText
499    Rst,
500    /// XHTML 1
501    Html,
502    /// HTML 5
503    Html5,
504    /// LaTeX
505    Latex,
506    /// LaTeX beamer slide show
507    Beamer,
508    /// ConTeXt
509    Context,
510    /// PDF (via LaTeX)
511    Pdf,
512    /// Groff man
513    Man,
514    /// MediaWiki markup
515    MediaWiki,
516    /// DokuWiki markup
517    Dokuwiki,
518    /// Textile
519    Textile,
520    /// Emacs Org-Mode
521    Org,
522    /// GNU Texinfo
523    Texinfo,
524    /// OPML
525    Opml,
526    /// DocBook
527    Docbook,
528    /// Open Document
529    OpenDocument,
530    /// OpenOffice text document
531    Odt,
532    /// Word docx
533    Docx,
534    /// Haddock markup
535    Haddock,
536    /// Rich text format
537    Rtf,
538    /// EPUB v2 book
539    Epub,
540    /// EPUB v3
541    Epub3,
542    /// FictionBook2 e-book
543    Fb2,
544    /// AsciiDoc
545    Asciidoc,
546    /// InDesign ICML
547    Icml,
548    /// Slidy HTML and javascript slide show
549    Slidy,
550    /// Slideous HTML and javascript slide show
551    Slideous,
552    /// DZSlides HTML5 + javascript slide show
553    Dzslides,
554    /// reveal.js HTML5 + javascript slide show
555    Revealjs,
556    /// S5 HTML and javascript slide show
557    S5,
558    /// the path of a custom lua writer (see Custom writers)
559    Lua(String),
560    /// Other
561    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/// typesafe access to -f FORMAT, -r FORMAT, --from=FORMAT, --read=FORMAT
614#[derive(Debug, Clone)]
615#[non_exhaustive]
616pub enum InputFormat {
617    /// native Haskell
618    Native,
619    /// JSON version of native AST
620    Json,
621    /// pandoc’s extended markdown
622    Markdown,
623    /// original unextended markdown
624    MarkdownStrict,
625    /// PHP Markdown extra extended markdown
626    MarkdownPhpextra,
627    /// github extended markdown
628    MarkdownGithub,
629    /// CommonMark markdown
630    Commonmark,
631    /// CommonMark markdown with extensions
632    CommonmarkX,
633    /// Textile
634    Textile,
635    /// reStructuredText
636    Rst,
637    /// Rich text format \
638    /// *Only available as of `pandoc 2.14.2 (2021-08-21)`*
639    Rtf,
640    /// HTML
641    Html,
642    /// DocBook
643    DocBook,
644    /// txt2tags
645    T2t,
646    /// Word docx
647    Docx,
648    /// EPUB
649    Epub,
650    /// OPML
651    Opml,
652    /// Emacs Org-Mode
653    Org,
654    /// MediaWiki markup
655    MediaWiki,
656    /// TWiki markup
657    Twiki,
658    /// Haddock markup
659    Haddock,
660    /// LaTeX
661    Latex,
662    /// Other
663    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    /// passed to the pandoc executable through stdin
835    Pipe(String),
836}
837
838/// Specify whether to generate a file or pipe the output to stdout.
839#[derive(Clone, Debug)]
840pub enum OutputKind {
841    File(PathBuf),
842    Pipe,
843}
844
845/// the argument builder
846#[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
860/// Convenience function to call Pandoc::new()
861pub fn new() -> Pandoc {
862    Pandoc::new()
863}
864
865impl Pandoc {
866    /// Get a new Pandoc object
867    /// This function returns a builder object to configure the Pandoc
868    /// execution.
869    pub fn new() -> Pandoc {
870        Pandoc {
871            print_pandoc_cmdline: false,
872            ..Default::default()
873        }
874    }
875
876    /// Add a path hint to search for the LaTeX executable.
877    ///
878    /// The supplied path is searched first for the latex executable, then the environment variable
879    /// `PATH`, then some hard-coded location hints.
880    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    /// Add a path hint to search for the Pandoc executable.
886    ///
887    /// The supplied path is searched first for the Pandoc executable, then the environment variable `PATH`, then
888    /// some hard-coded location hints.
889    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    /// Set or overwrite the document-class.
895    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    /// Set whether Pandoc should print the used command-line
904    ///
905    /// If set to true, the command-line to execute pandoc (as a subprocess)
906    /// will be displayed on stdout.
907    pub fn set_show_cmdline(&mut self, flag: bool) -> &mut Pandoc {
908        self.print_pandoc_cmdline = flag;
909        self
910    }
911
912    /// Set or overwrite the output format.
913    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    /// Set or overwrite the input format
922    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    /// Add additional input files
932    ///
933    /// The order of adding the files is the order in which they are processed, hence the order is
934    /// important.
935    /// This function does not work, if input has been already set to standard input using
936    /// [`set_input`](#method.set_input_format).
937    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    /// Set input for Pandoc.
955    ///
956    /// The input is given with `pandoc::InputKind` and overrides any inputs already
957    /// supplied.
958    ///
959    /// # Example
960    ///
961    /// ```
962    /// // pass in a string using standard input:
963    /// let markdown = "**very** _important".into();
964    /// let mut p = pandoc::new(); // assign to variable to increase life time
965    /// p.set_input(pandoc::InputKind::Pipe(markdown));
966    pub fn set_input(&mut self, input: InputKind) -> &mut Pandoc {
967        self.input = Some(input);
968        self
969    }
970
971    /// Set or overwrite the output filename.
972    pub fn set_output(&mut self, output: OutputKind) -> &mut Pandoc {
973        self.output = Some(output);
974        self
975    }
976
977    /// Set the file name of the bibliography database.
978    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    /// Set the filename of the citation style file.
985    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    /// Enable the generation of a table of contents
992    ///
993    /// By default, documents are transformed as they are. If this option is set, a table of
994    /// contents is added right in front of the actual document.
995    pub fn set_toc(&mut self) -> &mut Pandoc {
996        self.options.push(PandocOption::TableOfContents);
997        self
998    }
999
1000    /// Treat top-level headers as chapters in LaTeX, ConTeXt, and DocBook output.
1001    pub fn set_chapters(&mut self) -> &mut Pandoc {
1002        self.options
1003            .push(PandocOption::TopLevelDivision(Tld::Chapter));
1004        self
1005    }
1006
1007    /// Set custom prefix for sections.
1008    ///
1009    /// If this function is called, all sections will be numbered. Normally, sections in LaTeX,
1010    /// ConTeXt, HTML, or EPUB output are unnumbered.
1011    pub fn set_number_sections(&mut self) -> &mut Pandoc {
1012        self.options.push(PandocOption::NumberSections);
1013        self
1014    }
1015
1016    /// Set a custom latex template.
1017    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    /// Set the header level that causes a new slide to be generated.
1024    pub fn set_slide_level(&mut self, level: u32) -> &mut Pandoc {
1025        self.options.push(PandocOption::SlideLevel(level));
1026        self
1027    }
1028
1029    /// Set a custom variable.
1030    ///
1031    /// This method sets a custom Pandoc variable. It is adviced not to use this function, because
1032    /// there are convenience functions for most of the available variables.
1033    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    /// Add a Pandoc filter.
1046    ///
1047    /// Pandoc parses any of the supported input formats to an abstract syntax tree (AST). If a
1048    /// filter is specified, it will receive a JSON representation of this AST and can transform it
1049    /// to its liking and add/modify/remove elements. The output is then passed back to Pandoc.
1050    ///
1051    /// The provided filter function must live at least as long as the Pandoc instance,
1052    /// which will typically be achieved by making it a function, or else a closure which
1053    /// does not attempt to hold references to anything which isn't `'static`.
1054    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    /// Add a [PandocOption](PandocOption.t.html).
1063    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        // always capture stderr
1131        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    /// Add a raw command-line argument. You should generally use one of the
1161    /// convenience functions instead.
1162    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    /// generate a latex template from the given settings
1173    ///
1174    /// Warning: this function can panic in a lot of places.
1175    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        // apply all filters
1216        let filtered = filters.into_iter().fold(o, |acc, item| item(acc));
1217        self.input = Some(InputKind::Pipe(filtered));
1218        Ok(())
1219    }
1220
1221    /// Execute the Pandoc configured command.
1222    ///
1223    /// A successful Pandoc run can return either the path to a file written by
1224    /// the operation, or the result of the operation from `stdio`.
1225    ///
1226    /// The `PandocOutput` variant returned depends on the `OutputKind`
1227    /// configured:
1228    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
1249/// The output from Pandoc: the file written to, or a buffer with its output.
1250pub enum PandocOutput {
1251    /// The results of the pandoc operation are stored in `Path`
1252    ToFile(PathBuf),
1253    /// The results of the pandoc operation are returned as a `String` (constructed from the UTF-8
1254    /// stream returned by pandoc). This will be the case for text-based formats.
1255    ToBuffer(String),
1256    /// The results of the pandoc operation are returned as a `Vec<u8>`. This will be the case for
1257    /// binary formats such as PDF.
1258    ToBufferRaw(Vec<u8>),
1259}
1260
1261/// Possible errors that can occur before or during pandoc execution
1262pub enum PandocError {
1263    /// conversion from UTF-8 failed; includes valid-up-to byte count.
1264    BadUtf8Conversion(usize),
1265    /// some kind of IO-Error
1266    IoErr(std::io::Error),
1267    /// pandoc execution failed, provide output from stderr
1268    Err(std::process::Output),
1269    /// forgot to specify an output file
1270    NoOutputSpecified,
1271    /// forgot to specify any input files
1272    NoInputSpecified,
1273    /// pandoc executable not found
1274    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}