Skip to main content

jj_cli/
templater.rs

1// Copyright 2020 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Tools for lazily evaluating templates that produce text in a fallible
16//! manner.
17
18use std::cell::RefCell;
19use std::error;
20use std::fmt;
21use std::io;
22use std::io::Write;
23use std::iter;
24use std::rc::Rc;
25
26use bstr::BStr;
27use bstr::BString;
28use jj_lib::backend::Signature;
29use jj_lib::backend::Timestamp;
30use jj_lib::config::ConfigValue;
31use jj_lib::op_store::TimestampRange;
32
33use crate::formatter::FormatRecorder;
34use crate::formatter::Formatter;
35use crate::formatter::FormatterExt as _;
36use crate::formatter::LabeledScope;
37use crate::formatter::PlainTextFormatter;
38use crate::text_util;
39use crate::time_util;
40
41/// Represents a printable type or a compiled template containing a placeholder
42/// value.
43///
44/// This is analogous to [`std::fmt::Display`], but with customized error
45/// handling.
46pub trait Template {
47    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()>;
48}
49
50/// Template that supports list-like behavior.
51pub trait ListTemplate: Template {
52    /// Concatenates items with the given separator.
53    fn join<'a>(self: Box<Self>, separator: Box<dyn Template + 'a>) -> Box<dyn Template + 'a>
54    where
55        Self: 'a;
56}
57
58impl<T: Template + ?Sized> Template for &T {
59    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
60        <T as Template>::format(self, formatter)
61    }
62}
63
64impl<T: Template + ?Sized> Template for Box<T> {
65    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
66        <T as Template>::format(self, formatter)
67    }
68}
69
70// All optional printable types should be printable, and it's unlikely to
71// implement different formatting per type.
72impl<T: Template> Template for Option<T> {
73    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
74        self.as_ref().map_or(Ok(()), |t| t.format(formatter))
75    }
76}
77
78impl Template for BString {
79    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
80        formatter.as_mut().write_all(self)
81    }
82}
83
84impl Template for &BStr {
85    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
86        formatter.as_mut().write_all(self)
87    }
88}
89
90impl Template for ConfigValue {
91    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
92        write!(formatter, "{self}")
93    }
94}
95
96impl Template for Signature {
97    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
98        write!(formatter.labeled("name"), "{}", self.name)?;
99        if !self.name.is_empty() && !self.email.is_empty() {
100            write!(formatter, " ")?;
101        }
102        if !self.email.is_empty() {
103            write!(formatter, "<")?;
104            let email = Email(self.email.clone());
105            email.format(formatter)?;
106            write!(formatter, ">")?;
107        }
108        Ok(())
109    }
110}
111
112#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
113#[serde(transparent)]
114pub struct Email(pub String);
115
116impl Template for Email {
117    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
118        let (local, domain) = text_util::split_email(&self.0);
119        write!(formatter.labeled("local"), "{local}")?;
120        if let Some(domain) = domain {
121            write!(formatter, "@")?;
122            write!(formatter.labeled("domain"), "{domain}")?;
123        }
124        Ok(())
125    }
126}
127
128// In template language, an integer value is represented as i64. However, we use
129// usize here because it's more convenient to guarantee that the lower value is
130// bounded to 0.
131pub type SizeHint = (usize, Option<usize>);
132
133impl Template for String {
134    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
135        write!(formatter, "{self}")
136    }
137}
138
139impl Template for &str {
140    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
141        write!(formatter, "{self}")
142    }
143}
144
145impl Template for Timestamp {
146    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
147        match time_util::format_absolute_timestamp(self) {
148            Ok(formatted) => write!(formatter, "{formatted}"),
149            Err(err) => formatter.handle_error(err.into()),
150        }
151    }
152}
153
154impl Template for TimestampRange {
155    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
156        self.start.format(formatter)?;
157        write!(formatter, " - ")?;
158        self.end.format(formatter)?;
159        Ok(())
160    }
161}
162
163impl Template for Vec<String> {
164    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
165        format_joined(formatter, self, " ")
166    }
167}
168
169impl Template for bool {
170    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
171        let repr = if *self { "true" } else { "false" };
172        write!(formatter, "{repr}")
173    }
174}
175
176impl Template for i64 {
177    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
178        write!(formatter, "{self}")
179    }
180}
181
182pub struct LabelTemplate<T, L> {
183    content: T,
184    labels: L,
185}
186
187impl<T, L> LabelTemplate<T, L> {
188    pub fn new(content: T, labels: L) -> Self
189    where
190        T: Template,
191        L: TemplateProperty<Output = Vec<String>>,
192    {
193        Self { content, labels }
194    }
195}
196
197impl<T, L> Template for LabelTemplate<T, L>
198where
199    T: Template,
200    L: TemplateProperty<Output = Vec<String>>,
201{
202    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
203        match self.labels.extract() {
204            Ok(labels) => format_labeled(formatter, &self.content, &labels),
205            Err(err) => formatter.handle_error(err),
206        }
207    }
208}
209
210pub struct RawEscapeSequenceTemplate<T>(pub T);
211
212impl<T: Template> Template for RawEscapeSequenceTemplate<T> {
213    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
214        let rewrap = formatter.rewrap_fn();
215        let mut raw_formatter = PlainTextFormatter::new(formatter.raw()?);
216        self.0.format(&mut rewrap(&mut raw_formatter))
217    }
218}
219
220/// Renders a hyperlink using OSC 8 escape sequences when supported, or a
221/// fallback otherwise.
222pub struct HyperlinkTemplate<U, T, F> {
223    url: U,
224    text: T,
225    fallback: Option<F>,
226}
227
228impl<U, T, F> HyperlinkTemplate<U, T, F> {
229    pub fn new(url: U, text: T, fallback: Option<F>) -> Self {
230        Self {
231            url,
232            text,
233            fallback,
234        }
235    }
236}
237
238impl<U, T, F> Template for HyperlinkTemplate<U, T, F>
239where
240    U: TemplateProperty<Output = String>,
241    T: Template,
242    F: Template,
243{
244    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
245        // Extract URL string
246        let url_str = match self.url.extract() {
247            Ok(url) => url,
248            Err(err) => return formatter.handle_error(err),
249        };
250
251        if !formatter.maybe_color() {
252            if let Some(fallback) = &self.fallback {
253                return fallback.format(formatter);
254            }
255            return self.text.format(formatter);
256        }
257
258        // Write OSC 8 hyperlink via raw()
259        write!(formatter.raw()?, "\x1b]8;;{url_str}\x1b\\")?;
260        self.text.format(formatter)?;
261        write!(formatter.raw()?, "\x1b]8;;\x1b\\")
262    }
263}
264
265/// Renders contents in order, and returns the first non-empty output.
266pub struct CoalesceTemplate<T>(pub Vec<T>);
267
268impl<T: Template> Template for CoalesceTemplate<T> {
269    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
270        let Some((last, contents)) = self.0.split_last() else {
271            return Ok(());
272        };
273        let record_non_empty = record_non_empty_fn(formatter);
274        if let Some(recorder) = contents.iter().find_map(record_non_empty) {
275            recorder?.replay(formatter.as_mut())
276        } else {
277            last.format(formatter) // no need to capture the last content
278        }
279    }
280}
281
282pub struct ConcatTemplate<T>(pub Vec<T>);
283
284impl<T: Template> Template for ConcatTemplate<T> {
285    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
286        for template in &self.0 {
287            template.format(formatter)?;
288        }
289        Ok(())
290    }
291}
292
293/// Renders the content to buffer, and transforms it without losing labels.
294pub struct ReformatTemplate<T, F> {
295    content: T,
296    reformat: F,
297}
298
299impl<T, F> ReformatTemplate<T, F> {
300    pub fn new(content: T, reformat: F) -> Self
301    where
302        T: Template,
303        F: Fn(&mut TemplateFormatter, &FormatRecorder) -> io::Result<()>,
304    {
305        Self { content, reformat }
306    }
307}
308
309impl<T, F> Template for ReformatTemplate<T, F>
310where
311    T: Template,
312    F: Fn(&mut TemplateFormatter, &FormatRecorder) -> io::Result<()>,
313{
314    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
315        let rewrap = formatter.rewrap_fn();
316        let mut recorder = FormatRecorder::new(formatter.maybe_color());
317        self.content.format(&mut rewrap(&mut recorder))?;
318        (self.reformat)(formatter, &recorder)
319    }
320}
321
322/// Like `ConcatTemplate`, but inserts a separator between contents.
323pub struct JoinTemplate<S, T> {
324    separator: S,
325    contents: Vec<T>,
326}
327
328impl<S, T> JoinTemplate<S, T> {
329    pub fn new(separator: S, contents: Vec<T>) -> Self
330    where
331        S: Template,
332        T: Template,
333    {
334        Self {
335            separator,
336            contents,
337        }
338    }
339}
340
341impl<S, T> Template for JoinTemplate<S, T>
342where
343    S: Template,
344    T: Template,
345{
346    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
347        format_joined(formatter, &self.contents, &self.separator)
348    }
349}
350
351/// Like `JoinTemplate`, but ignores empty contents.
352pub struct SeparateTemplate<S, T> {
353    separator: S,
354    contents: Vec<T>,
355}
356
357impl<S, T> SeparateTemplate<S, T> {
358    pub fn new(separator: S, contents: Vec<T>) -> Self
359    where
360        S: Template,
361        T: Template,
362    {
363        Self {
364            separator,
365            contents,
366        }
367    }
368}
369
370impl<S, T> Template for SeparateTemplate<S, T>
371where
372    S: Template,
373    T: Template,
374{
375    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
376        let record_non_empty = record_non_empty_fn(formatter);
377        let content_recorders = self.contents.iter().filter_map(record_non_empty);
378        format_joined_with(
379            formatter,
380            content_recorders,
381            &self.separator,
382            |formatter, recorder| recorder?.replay(formatter.as_mut()),
383        )
384    }
385}
386
387/// Wrapper around an error occurred during template evaluation.
388#[derive(Debug)]
389pub struct TemplatePropertyError(pub Box<dyn error::Error + Send + Sync>);
390
391// Implements conversion from any error type to support `expr?` in function
392// binding. This type doesn't implement `std::error::Error` instead.
393// <https://github.com/dtolnay/anyhow/issues/25#issuecomment-544140480>
394impl<E> From<E> for TemplatePropertyError
395where
396    E: error::Error + Send + Sync + 'static,
397{
398    fn from(err: E) -> Self {
399        Self(err.into())
400    }
401}
402
403/// Lazily evaluated value which can fail to evaluate.
404pub trait TemplateProperty {
405    type Output;
406
407    fn extract(&self) -> Result<Self::Output, TemplatePropertyError>;
408}
409
410impl<P: TemplateProperty + ?Sized> TemplateProperty for Box<P> {
411    type Output = <P as TemplateProperty>::Output;
412
413    fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
414        <P as TemplateProperty>::extract(self)
415    }
416}
417
418impl<P: TemplateProperty> TemplateProperty for Option<P> {
419    type Output = Option<P::Output>;
420
421    fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
422        self.as_ref().map(|property| property.extract()).transpose()
423    }
424}
425
426// Implement TemplateProperty for tuples
427macro_rules! tuple_impls {
428    ($( ( $($n:tt $T:ident),+ ) )+) => {
429        $(
430            impl<$($T: TemplateProperty,)+> TemplateProperty for ($($T,)+) {
431                type Output = ($($T::Output,)+);
432
433                fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
434                    Ok(($(self.$n.extract()?,)+))
435                }
436            }
437        )+
438    }
439}
440
441tuple_impls! {
442    (0 T0)
443    (0 T0, 1 T1)
444    (0 T0, 1 T1, 2 T2)
445    (0 T0, 1 T1, 2 T2, 3 T3)
446}
447
448/// Type-erased [`TemplateProperty`].
449pub type BoxedTemplateProperty<'a, O> = Box<dyn TemplateProperty<Output = O> + 'a>;
450pub type BoxedSerializeProperty<'a> =
451    BoxedTemplateProperty<'a, Box<dyn erased_serde::Serialize + 'a>>;
452
453/// [`TemplateProperty`] adapters that are useful when implementing methods.
454pub trait TemplatePropertyExt: TemplateProperty {
455    /// Translates to a property that will apply fallible `function` to an
456    /// extracted value.
457    fn and_then<O, F>(self, function: F) -> TemplateFunction<Self, F>
458    where
459        Self: Sized,
460        F: Fn(Self::Output) -> Result<O, TemplatePropertyError>,
461    {
462        TemplateFunction::new(self, function)
463    }
464
465    /// Translates to a property that will apply `function` to an extracted
466    /// value, leaving `Err` untouched.
467    fn map<O, F>(self, function: F) -> impl TemplateProperty<Output = O>
468    where
469        Self: Sized,
470        F: Fn(Self::Output) -> O,
471    {
472        TemplateFunction::new(self, move |value| Ok(function(value)))
473    }
474
475    /// Translates to a property that will unwrap an extracted `Option` value
476    /// of the specified `type_name`, mapping `None` to `Err`.
477    fn try_unwrap<O>(self, type_name: &str) -> impl TemplateProperty<Output = O>
478    where
479        Self: TemplateProperty<Output = Option<O>> + Sized,
480    {
481        self.and_then(move |opt| {
482            opt.ok_or_else(|| TemplatePropertyError(format!("No {type_name} available").into()))
483        })
484    }
485
486    /// Converts this property into boxed serialize property.
487    fn into_serialize<'a>(self) -> BoxedSerializeProperty<'a>
488    where
489        Self: Sized + 'a,
490        Self::Output: serde::Serialize,
491    {
492        Box::new(self.map(|value| Box::new(value) as Box<dyn erased_serde::Serialize>))
493    }
494
495    /// Converts this property into `Template`.
496    fn into_template<'a>(self) -> Box<dyn Template + 'a>
497    where
498        Self: Sized + 'a,
499        Self::Output: Template,
500    {
501        Box::new(FormattablePropertyTemplate::new(self))
502    }
503
504    /// Converts this property into boxed trait object.
505    fn into_dyn<'a>(self) -> BoxedTemplateProperty<'a, Self::Output>
506    where
507        Self: Sized + 'a,
508    {
509        Box::new(self)
510    }
511
512    /// Converts this property into wrapped (or tagged) type.
513    ///
514    /// Use `W::wrap_property()` if the self type is known to be boxed.
515    fn into_dyn_wrapped<'a, W>(self) -> W
516    where
517        Self: Sized + 'a,
518        W: WrapTemplateProperty<'a, Self::Output>,
519    {
520        W::wrap_property(self.into_dyn())
521    }
522}
523
524impl<P: TemplateProperty + ?Sized> TemplatePropertyExt for P {}
525
526/// Wraps template property of type `O` in tagged type.
527///
528/// This is basically [`From<BoxedTemplateProperty<'a, O>>`], but is restricted
529/// to property types.
530#[diagnostic::on_unimplemented(
531    message = "the template property of type `{O}` cannot be wrapped in `{Self}`"
532)]
533pub trait WrapTemplateProperty<'a, O>: Sized {
534    fn wrap_property(property: BoxedTemplateProperty<'a, O>) -> Self;
535}
536
537/// Adapter that wraps literal value in `TemplateProperty`.
538pub struct Literal<O>(pub O);
539
540impl<O: Template> Template for Literal<O> {
541    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
542        self.0.format(formatter)
543    }
544}
545
546impl<O: Clone> TemplateProperty for Literal<O> {
547    type Output = O;
548
549    fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
550        Ok(self.0.clone())
551    }
552}
553
554/// Adapter to extract template value from property for displaying.
555pub struct FormattablePropertyTemplate<P> {
556    property: P,
557}
558
559impl<P> FormattablePropertyTemplate<P> {
560    pub fn new(property: P) -> Self
561    where
562        P: TemplateProperty,
563        P::Output: Template,
564    {
565        Self { property }
566    }
567}
568
569impl<P> Template for FormattablePropertyTemplate<P>
570where
571    P: TemplateProperty,
572    P::Output: Template,
573{
574    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
575        match self.property.extract() {
576            Ok(template) => template.format(formatter),
577            Err(err) => formatter.handle_error(err),
578        }
579    }
580}
581
582/// Adapter to turn template back to string property.
583pub struct PlainTextFormattedProperty<T> {
584    template: T,
585}
586
587impl<T> PlainTextFormattedProperty<T> {
588    pub fn new(template: T) -> Self {
589        Self { template }
590    }
591}
592
593impl<T: Template> TemplateProperty for PlainTextFormattedProperty<T> {
594    type Output = String;
595
596    fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
597        let mut output = vec![];
598        let mut formatter = PlainTextFormatter::new(&mut output);
599        let mut wrapper = TemplateFormatter::new(&mut formatter, propagate_property_error);
600        self.template.format(&mut wrapper)?;
601        Ok(String::from_utf8(output).map_err(|err| err.utf8_error())?)
602    }
603}
604
605/// Renders template property of list type with the given separator.
606///
607/// Each list item will be formatted by the given `format_item()` function.
608pub struct ListPropertyTemplate<P, S, F> {
609    property: P,
610    separator: S,
611    format_item: F,
612}
613
614impl<P, S, F> ListPropertyTemplate<P, S, F> {
615    pub fn new<O>(property: P, separator: S, format_item: F) -> Self
616    where
617        P: TemplateProperty,
618        P::Output: IntoIterator<Item = O>,
619        S: Template,
620        F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
621    {
622        Self {
623            property,
624            separator,
625            format_item,
626        }
627    }
628}
629
630impl<O, P, S, F> Template for ListPropertyTemplate<P, S, F>
631where
632    P: TemplateProperty,
633    P::Output: IntoIterator<Item = O>,
634    S: Template,
635    F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
636{
637    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
638        let contents = match self.property.extract() {
639            Ok(contents) => contents,
640            Err(err) => return formatter.handle_error(err),
641        };
642        format_joined_with(formatter, contents, &self.separator, &self.format_item)
643    }
644}
645
646impl<O, P, S, F> ListTemplate for ListPropertyTemplate<P, S, F>
647where
648    P: TemplateProperty,
649    P::Output: IntoIterator<Item = O>,
650    S: Template,
651    F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
652{
653    fn join<'a>(self: Box<Self>, separator: Box<dyn Template + 'a>) -> Box<dyn Template + 'a>
654    where
655        Self: 'a,
656    {
657        // Once join()-ed, list-like API should be dropped. This is guaranteed by
658        // the return type.
659        Box::new(ListPropertyTemplate::new(
660            self.property,
661            separator,
662            self.format_item,
663        ))
664    }
665}
666
667/// Template which selects an output based on a boolean condition.
668///
669/// When `None` is specified for the false template and the condition is false,
670/// this writes nothing.
671pub struct ConditionalTemplate<P, T, U> {
672    pub condition: P,
673    pub true_template: T,
674    pub false_template: Option<U>,
675}
676
677impl<P, T, U> ConditionalTemplate<P, T, U> {
678    pub fn new(condition: P, true_template: T, false_template: Option<U>) -> Self
679    where
680        P: TemplateProperty<Output = bool>,
681        T: Template,
682        U: Template,
683    {
684        Self {
685            condition,
686            true_template,
687            false_template,
688        }
689    }
690}
691
692impl<P, T, U> Template for ConditionalTemplate<P, T, U>
693where
694    P: TemplateProperty<Output = bool>,
695    T: Template,
696    U: Template,
697{
698    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
699        let condition = match self.condition.extract() {
700            Ok(condition) => condition,
701            Err(err) => return formatter.handle_error(err),
702        };
703        if condition {
704            self.true_template.format(formatter)?;
705        } else if let Some(false_template) = &self.false_template {
706            false_template.format(formatter)?;
707        }
708        Ok(())
709    }
710}
711
712/// Adapter to apply fallible `function` to the `property`.
713///
714/// This is usually created by `TemplatePropertyExt::and_then()`/`map()`.
715pub struct TemplateFunction<P, F> {
716    pub property: P,
717    pub function: F,
718}
719
720impl<P, F> TemplateFunction<P, F> {
721    pub fn new<O>(property: P, function: F) -> Self
722    where
723        P: TemplateProperty,
724        F: Fn(P::Output) -> Result<O, TemplatePropertyError>,
725    {
726        Self { property, function }
727    }
728}
729
730impl<O, P, F> TemplateProperty for TemplateFunction<P, F>
731where
732    P: TemplateProperty,
733    F: Fn(P::Output) -> Result<O, TemplatePropertyError>,
734{
735    type Output = O;
736
737    fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
738        (self.function)(self.property.extract()?)
739    }
740}
741
742/// Property which will be compiled into template once, and substituted later.
743#[derive(Clone, Debug)]
744pub struct PropertyPlaceholder<O> {
745    value: Rc<RefCell<Option<O>>>,
746}
747
748impl<O> PropertyPlaceholder<O> {
749    pub fn new() -> Self {
750        Self {
751            value: Rc::new(RefCell::new(None)),
752        }
753    }
754
755    pub fn set(&self, value: O) {
756        *self.value.borrow_mut() = Some(value);
757    }
758
759    pub fn take(&self) -> Option<O> {
760        self.value.borrow_mut().take()
761    }
762
763    pub fn with_value<R>(&self, value: O, f: impl FnOnce() -> R) -> R {
764        self.set(value);
765        let result = f();
766        self.take();
767        result
768    }
769}
770
771impl<O> Default for PropertyPlaceholder<O> {
772    fn default() -> Self {
773        Self::new()
774    }
775}
776
777impl<O: Clone> TemplateProperty for PropertyPlaceholder<O> {
778    type Output = O;
779
780    fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
781        if let Some(value) = self.value.borrow().as_ref() {
782            Ok(value.clone())
783        } else {
784            Err(TemplatePropertyError("Placeholder value is not set".into()))
785        }
786    }
787}
788
789/// Adapter that renders compiled `template` with the `placeholder` value set.
790pub struct TemplateRenderer<'a, C> {
791    template: Box<dyn Template + 'a>,
792    placeholder: PropertyPlaceholder<C>,
793    labels: Vec<String>,
794}
795
796impl<'a, C: Clone> TemplateRenderer<'a, C> {
797    pub fn new(template: Box<dyn Template + 'a>, placeholder: PropertyPlaceholder<C>) -> Self {
798        Self {
799            template,
800            placeholder,
801            labels: Vec::new(),
802        }
803    }
804
805    /// Returns renderer that will format template with the given `labels`.
806    ///
807    /// This is equivalent to wrapping the content template with `label()`
808    /// function. For example,
809    /// `content.labeled(["foo", "bar"]).labeled(["baz"])` can be expressed as
810    /// `label("baz", label("foo bar", content))` in template.
811    pub fn labeled<S: Into<String>>(mut self, labels: impl IntoIterator<Item = S>) -> Self {
812        self.labels.splice(0..0, labels.into_iter().map(Into::into));
813        self
814    }
815
816    pub fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
817        let mut wrapper = TemplateFormatter::new(formatter, format_property_error_inline);
818        self.placeholder.with_value(context.clone(), || {
819            format_labeled(&mut wrapper, &self.template, &self.labels)
820        })
821    }
822
823    /// Renders template into buffer ignoring any color labels.
824    ///
825    /// The output is usually UTF-8, but it can contain arbitrary bytes such as
826    /// file content.
827    pub fn format_plain_text(&self, context: &C) -> Vec<u8> {
828        let mut output = Vec::new();
829        self.format(context, &mut PlainTextFormatter::new(&mut output))
830            .expect("write() to vec backed formatter should never fail");
831        output
832    }
833}
834
835/// Wrapper to pass around `Formatter` and error handler.
836pub struct TemplateFormatter<'a> {
837    formatter: &'a mut dyn Formatter,
838    error_handler: PropertyErrorHandler,
839}
840
841impl<'a> TemplateFormatter<'a> {
842    fn new(formatter: &'a mut dyn Formatter, error_handler: PropertyErrorHandler) -> Self {
843        Self {
844            formatter,
845            error_handler,
846        }
847    }
848
849    /// Returns function that wraps another `Formatter` with the current error
850    /// handling strategy.
851    ///
852    /// This does not borrow `self` so the underlying formatter can be mutably
853    /// borrowed.
854    pub fn rewrap_fn(&self) -> impl Fn(&mut dyn Formatter) -> TemplateFormatter<'_> + use<> {
855        let error_handler = self.error_handler;
856        move |formatter| TemplateFormatter::new(formatter, error_handler)
857    }
858
859    pub fn raw(&mut self) -> io::Result<Box<dyn Write + '_>> {
860        self.formatter.raw()
861    }
862
863    pub fn labeled(&mut self, label: &str) -> LabeledScope<&mut (dyn Formatter + 'a)> {
864        self.formatter.labeled(label)
865    }
866
867    pub fn push_label(&mut self, label: &str) {
868        self.formatter.push_label(label);
869    }
870
871    pub fn pop_label(&mut self) {
872        self.formatter.pop_label();
873    }
874
875    pub fn maybe_color(&self) -> bool {
876        self.formatter.maybe_color()
877    }
878
879    pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
880        self.formatter.write_fmt(args)
881    }
882
883    /// Handles the given template property evaluation error.
884    ///
885    /// This usually prints the given error inline, and returns `Ok`. It's up to
886    /// caller to decide whether or not to continue template processing on `Ok`.
887    /// For example, `if(cond, ..)` expression will terminate if the `cond`
888    /// failed to evaluate, whereas `concat(x, y, ..)` will continue processing.
889    ///
890    /// If `Err` is returned, the error should be propagated.
891    pub fn handle_error(&mut self, err: TemplatePropertyError) -> io::Result<()> {
892        (self.error_handler)(self.formatter, err)
893    }
894}
895
896impl<'a> AsMut<dyn Formatter + 'a> for TemplateFormatter<'a> {
897    fn as_mut(&mut self) -> &mut (dyn Formatter + 'a) {
898        self.formatter
899    }
900}
901
902pub fn format_joined<I, S>(
903    formatter: &mut TemplateFormatter,
904    contents: I,
905    separator: S,
906) -> io::Result<()>
907where
908    I: IntoIterator,
909    I::Item: Template,
910    S: Template,
911{
912    format_joined_with(formatter, contents, separator, |formatter, item| {
913        item.format(formatter)
914    })
915}
916
917fn format_joined_with<I, S, F>(
918    formatter: &mut TemplateFormatter,
919    contents: I,
920    separator: S,
921    mut format_item: F,
922) -> io::Result<()>
923where
924    I: IntoIterator,
925    S: Template,
926    F: FnMut(&mut TemplateFormatter, I::Item) -> io::Result<()>,
927{
928    let mut contents_iter = contents.into_iter().fuse();
929    if let Some(item) = contents_iter.next() {
930        format_item(formatter, item)?;
931    }
932    for item in contents_iter {
933        separator.format(formatter)?;
934        format_item(formatter, item)?;
935    }
936    Ok(())
937}
938
939fn format_labeled<T: Template + ?Sized>(
940    formatter: &mut TemplateFormatter,
941    content: &T,
942    labels: &[String],
943) -> io::Result<()> {
944    for label in labels {
945        formatter.push_label(label);
946    }
947    content.format(formatter)?;
948    for _label in labels {
949        formatter.pop_label();
950    }
951    Ok(())
952}
953
954type PropertyErrorHandler = fn(&mut dyn Formatter, TemplatePropertyError) -> io::Result<()>;
955
956/// Prints property evaluation error as inline template output.
957fn format_property_error_inline(
958    formatter: &mut dyn Formatter,
959    err: TemplatePropertyError,
960) -> io::Result<()> {
961    let TemplatePropertyError(err) = &err;
962    let mut formatter = formatter.labeled("error");
963    write!(formatter, "<")?;
964    write!(formatter.labeled("heading"), "Error: ")?;
965    write!(formatter, "{err}")?;
966    for err in iter::successors(err.source(), |err| err.source()) {
967        write!(formatter, ": {err}")?;
968    }
969    write!(formatter, ">")?;
970    Ok(())
971}
972
973fn propagate_property_error(
974    _formatter: &mut dyn Formatter,
975    err: TemplatePropertyError,
976) -> io::Result<()> {
977    Err(io::Error::other(err.0))
978}
979
980/// Creates function that renders a template to buffer and returns the buffer
981/// only if it isn't empty.
982///
983/// This inherits the error handling strategy from the given `formatter`.
984fn record_non_empty_fn<T: Template + ?Sized>(
985    formatter: &TemplateFormatter,
986    // TODO: T doesn't have to be captured, but "currently, all type parameters
987    // are required to be mentioned in the precise captures list" as of rustc
988    // 1.85.0.
989) -> impl Fn(&T) -> Option<io::Result<FormatRecorder>> + use<T> {
990    let rewrap = formatter.rewrap_fn();
991    let maybe_color = formatter.maybe_color();
992    move |template| {
993        let mut recorder = FormatRecorder::new(maybe_color);
994        match template.format(&mut rewrap(&mut recorder)) {
995            Ok(()) if recorder.data().is_empty() => None, // omit empty content
996            Ok(()) => Some(Ok(recorder)),
997            Err(e) => Some(Err(e)),
998        }
999    }
1000}