Skip to main content

sofie_engine/rendering/
mod.rs

1#[cfg(feature = "gates")]
2mod gates;
3#[cfg(all(feature = "gates", feature = "parse"))]
4mod parse;
5#[cfg(feature = "gates")]
6mod withheld;
7
8use sofie_core::Show;
9
10#[cfg(feature = "gates")]
11pub use self::gates::Gates;
12#[cfg(feature = "gates")]
13pub(crate) use self::gates::{Asks, Gate, GatesRef};
14#[cfg(all(feature = "gates", feature = "parse"))]
15pub(crate) use self::parse::parse_gate;
16#[cfg(feature = "gates")]
17pub(crate) use self::withheld::GatedPaths;
18#[cfg(feature = "gates")]
19pub use self::withheld::Withheld;
20#[cfg(all(feature = "gates", feature = "analysis"))]
21pub use self::withheld::{Withholdable, withholdable};
22use crate::language::Language;
23use crate::template::{mark_out_of_band, render_part, render_whole};
24use crate::{RenderError, Submitted, Template, TemplateError};
25
26/// [`Template`]'s own renders are for no request: plain language, nothing gated shown.
27#[derive(Clone, Copy)]
28pub struct Rendering<'t> {
29    template: &'t Template,
30    language: Language<'t>,
31    keyed_forms: bool,
32}
33
34impl<'t> Rendering<'t> {
35    #[must_use]
36    pub fn new(template: &'t Template) -> Self {
37        Self {
38            template,
39            language: Language::plain(),
40            keyed_forms: false,
41        }
42    }
43
44    /// Each form carries a fresh idempotency key, so one posted without JavaScript reaches an
45    /// `.idempotent()` route as engine.js's header would.
46    #[must_use]
47    pub fn keyed_forms(mut self) -> Self {
48        self.keyed_forms = true;
49        self
50    }
51
52    #[cfg(feature = "i18n")]
53    #[must_use]
54    pub fn language(mut self, language: crate::Language<'t>) -> Self {
55        #[cfg(feature = "gates")]
56        let language = language.with_gates(self.language.gates());
57        self.language = language;
58        self
59    }
60
61    #[cfg(feature = "gates")]
62    #[must_use]
63    pub fn gates(mut self, gates: &'t dyn Gates) -> Self {
64        self.language = self.language.with_gates(Some(gates));
65        self
66    }
67
68    /// The data paths bound only inside parts this viewer may not see, which JSON and a client
69    /// area's envelope leave out as the page does.
70    #[cfg(feature = "gates")]
71    #[must_use]
72    pub fn withheld(&self) -> Withheld<'t> {
73        self.template
74            .precomputed
75            .gated_paths
76            .withheld(self.language.gates())
77    }
78
79    /// What a client area's screen is given beside its data, in this rendering's language and
80    /// for its viewer.
81    #[cfg(feature = "clients")]
82    #[must_use]
83    pub fn client_view(&self) -> crate::ClientView {
84        crate::client_view::client_parts(self.template).view(self.language)
85    }
86
87    /// A listing's refused filters in a client area's data, written in this rendering's language.
88    #[cfg(all(feature = "clients", feature = "listing"))]
89    pub fn translate_listing_errors(&self, data: &mut serde_json::Value) {
90        crate::client_view::client_parts(self.template)
91            .translate_listing_errors(data, self.language);
92    }
93
94    pub fn render(&self, data: &(impl Show + ?Sized)) -> Result<String, TemplateError> {
95        render_whole(
96            self.template,
97            data.show(),
98            None,
99            self.language,
100            self.keyed_forms,
101        )
102    }
103
104    pub fn render_submitted(
105        &self,
106        data: &(impl Show + ?Sized),
107        submitted: &Submitted<'_>,
108    ) -> Result<String, TemplateError> {
109        render_whole(
110            self.template,
111            data.show(),
112            Some(submitted),
113            self.language,
114            self.keyed_forms,
115        )
116    }
117
118    pub fn render_fragment(
119        &self,
120        data: &(impl Show + ?Sized),
121        id: &str,
122    ) -> Result<String, RenderError> {
123        render_part(
124            self.template,
125            data.show(),
126            id,
127            self.language,
128            self.keyed_forms,
129        )
130    }
131
132    pub fn render_out_of_band(
133        &self,
134        data: &(impl Show + ?Sized),
135        id: &str,
136    ) -> Result<String, RenderError> {
137        self.render_fragment(data, id).map(mark_out_of_band)
138    }
139}