bard/
render.rs

1use std::borrow::Cow;
2
3use semver::Version;
4use serde::Serialize;
5
6use crate::app::App;
7use crate::book::{Song, SongRef};
8use crate::music::Notation;
9use crate::prelude::*;
10use crate::project::{Format, Metadata, Output, Project};
11use crate::util::ImgCache;
12use crate::{ProgramMeta, PROGRAM_META};
13
14#[macro_use]
15pub mod template;
16pub mod hovorka;
17pub mod html;
18pub mod json;
19pub mod pdf;
20pub mod tex_tools;
21pub mod xml;
22
23pub use self::hovorka::RHovorka;
24pub use self::html::RHtml;
25pub use self::json::RJson;
26pub use self::pdf::RPdf;
27use self::template::DefaultTemaplate;
28pub use self::xml::RXml;
29
30pub static DEFAULT_TEMPLATES: &[&DefaultTemaplate] = &[
31    &pdf::DEFAULT_TEMPLATE,
32    &html::DEFAULT_TEMPLATE,
33    &hovorka::DEFAULT_TEMPLATE,
34];
35
36#[derive(Serialize, Debug)]
37pub struct RenderContext<'a> {
38    book: Cow<'a, Metadata>,
39    songs: &'a [Song],
40    songs_sorted: &'a [SongRef],
41    notation: Notation,
42    output: &'a Output,
43    program: &'static ProgramMeta,
44}
45
46impl<'a> RenderContext<'a> {
47    fn new(project: &'a Project, output: &'a Output) -> Self {
48        RenderContext {
49            book: output.override_book_section(project.book_section()),
50            songs: project.songs(),
51            songs_sorted: project.songs_sorted(),
52            notation: project.settings.notation,
53            output,
54            program: &PROGRAM_META,
55        }
56    }
57}
58
59trait Render {
60    /// Render the output file based on `project` and `output`.
61    fn render(&self, app: &App, output: &Path, context: RenderContext) -> Result<()>;
62
63    /// Returns the AST version specified in the template, if any.
64    fn version(&self) -> Option<Version> {
65        None
66    }
67}
68
69pub struct Renderer<'a> {
70    project: &'a Project,
71    output: &'a Output,
72    render: Box<dyn Render>,
73}
74
75impl<'a> Renderer<'a> {
76    pub fn new(project: &'a Project, output: &'a Output, img_cache: &ImgCache) -> Result<Self> {
77        let render: Box<dyn Render> = match output.format() {
78            Format::Pdf => Box::new(RPdf::new(project, output, img_cache)?),
79            Format::Html => Box::new(RHtml::new(project, output, img_cache)?),
80            Format::Hovorka => Box::new(RHovorka::new(project, output, img_cache)?),
81            Format::Json => Box::new(RJson::new()),
82            Format::Xml => Box::new(RXml::new()),
83        };
84
85        Ok(Self {
86            project,
87            output,
88            render,
89        })
90    }
91
92    pub fn version(&self) -> Option<Version> {
93        self.render.version()
94    }
95
96    pub fn render(&self, app: &App) -> Result<()> {
97        let context = RenderContext::new(self.project, self.output);
98        self.render.render(app, &self.output.file, context)
99    }
100}