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 fn render(&self, app: &App, output: &Path, context: RenderContext) -> Result<()>;
62
63 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}