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 contents in order, and returns the first non-empty output.
221pub struct CoalesceTemplate<T>(pub Vec<T>);
222
223impl<T: Template> Template for CoalesceTemplate<T> {
224    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
225        let Some((last, contents)) = self.0.split_last() else {
226            return Ok(());
227        };
228        let record_non_empty = record_non_empty_fn(formatter);
229        if let Some(recorder) = contents.iter().find_map(record_non_empty) {
230            recorder?.replay(formatter.as_mut())
231        } else {
232            last.format(formatter) // no need to capture the last content
233        }
234    }
235}
236
237pub struct ConcatTemplate<T>(pub Vec<T>);
238
239impl<T: Template> Template for ConcatTemplate<T> {
240    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
241        for template in &self.0 {
242            template.format(formatter)?;
243        }
244        Ok(())
245    }
246}
247
248/// Renders the content to buffer, and transforms it without losing labels.
249pub struct ReformatTemplate<T, F> {
250    content: T,
251    reformat: F,
252}
253
254impl<T, F> ReformatTemplate<T, F> {
255    pub fn new(content: T, reformat: F) -> Self
256    where
257        T: Template,
258        F: Fn(&mut TemplateFormatter, &FormatRecorder) -> io::Result<()>,
259    {
260        Self { content, reformat }
261    }
262}
263
264impl<T, F> Template for ReformatTemplate<T, F>
265where
266    T: Template,
267    F: Fn(&mut TemplateFormatter, &FormatRecorder) -> io::Result<()>,
268{
269    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
270        let rewrap = formatter.rewrap_fn();
271        let mut recorder = FormatRecorder::new();
272        self.content.format(&mut rewrap(&mut recorder))?;
273        (self.reformat)(formatter, &recorder)
274    }
275}
276
277/// Like `ConcatTemplate`, but inserts a separator between contents.
278pub struct JoinTemplate<S, T> {
279    separator: S,
280    contents: Vec<T>,
281}
282
283impl<S, T> JoinTemplate<S, T> {
284    pub fn new(separator: S, contents: Vec<T>) -> Self
285    where
286        S: Template,
287        T: Template,
288    {
289        Self {
290            separator,
291            contents,
292        }
293    }
294}
295
296impl<S, T> Template for JoinTemplate<S, T>
297where
298    S: Template,
299    T: Template,
300{
301    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
302        format_joined(formatter, &self.contents, &self.separator)
303    }
304}
305
306/// Like `JoinTemplate`, but ignores empty contents.
307pub struct SeparateTemplate<S, T> {
308    separator: S,
309    contents: Vec<T>,
310}
311
312impl<S, T> SeparateTemplate<S, T> {
313    pub fn new(separator: S, contents: Vec<T>) -> Self
314    where
315        S: Template,
316        T: Template,
317    {
318        Self {
319            separator,
320            contents,
321        }
322    }
323}
324
325impl<S, T> Template for SeparateTemplate<S, T>
326where
327    S: Template,
328    T: Template,
329{
330    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
331        let record_non_empty = record_non_empty_fn(formatter);
332        let content_recorders = self.contents.iter().filter_map(record_non_empty);
333        format_joined_with(
334            formatter,
335            content_recorders,
336            &self.separator,
337            |formatter, recorder| recorder?.replay(formatter.as_mut()),
338        )
339    }
340}
341
342/// Wrapper around an error occurred during template evaluation.
343#[derive(Debug)]
344pub struct TemplatePropertyError(pub Box<dyn error::Error + Send + Sync>);
345
346// Implements conversion from any error type to support `expr?` in function
347// binding. This type doesn't implement `std::error::Error` instead.
348// <https://github.com/dtolnay/anyhow/issues/25#issuecomment-544140480>
349impl<E> From<E> for TemplatePropertyError
350where
351    E: error::Error + Send + Sync + 'static,
352{
353    fn from(err: E) -> Self {
354        Self(err.into())
355    }
356}
357
358/// Lazily evaluated value which can fail to evaluate.
359pub trait TemplateProperty {
360    type Output;
361
362    fn extract(&self) -> Result<Self::Output, TemplatePropertyError>;
363}
364
365impl<P: TemplateProperty + ?Sized> TemplateProperty for Box<P> {
366    type Output = <P as TemplateProperty>::Output;
367
368    fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
369        <P as TemplateProperty>::extract(self)
370    }
371}
372
373impl<P: TemplateProperty> TemplateProperty for Option<P> {
374    type Output = Option<P::Output>;
375
376    fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
377        self.as_ref().map(|property| property.extract()).transpose()
378    }
379}
380
381// Implement TemplateProperty for tuples
382macro_rules! tuple_impls {
383    ($( ( $($n:tt $T:ident),+ ) )+) => {
384        $(
385            impl<$($T: TemplateProperty,)+> TemplateProperty for ($($T,)+) {
386                type Output = ($($T::Output,)+);
387
388                fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
389                    Ok(($(self.$n.extract()?,)+))
390                }
391            }
392        )+
393    }
394}
395
396tuple_impls! {
397    (0 T0)
398    (0 T0, 1 T1)
399    (0 T0, 1 T1, 2 T2)
400    (0 T0, 1 T1, 2 T2, 3 T3)
401}
402
403/// Type-erased [`TemplateProperty`].
404pub type BoxedTemplateProperty<'a, O> = Box<dyn TemplateProperty<Output = O> + 'a>;
405pub type BoxedSerializeProperty<'a> =
406    BoxedTemplateProperty<'a, Box<dyn erased_serde::Serialize + 'a>>;
407
408/// [`TemplateProperty`] adapters that are useful when implementing methods.
409pub trait TemplatePropertyExt: TemplateProperty {
410    /// Translates to a property that will apply fallible `function` to an
411    /// extracted value.
412    fn and_then<O, F>(self, function: F) -> TemplateFunction<Self, F>
413    where
414        Self: Sized,
415        F: Fn(Self::Output) -> Result<O, TemplatePropertyError>,
416    {
417        TemplateFunction::new(self, function)
418    }
419
420    /// Translates to a property that will apply `function` to an extracted
421    /// value, leaving `Err` untouched.
422    fn map<O, F>(self, function: F) -> impl TemplateProperty<Output = O>
423    where
424        Self: Sized,
425        F: Fn(Self::Output) -> O,
426    {
427        TemplateFunction::new(self, move |value| Ok(function(value)))
428    }
429
430    /// Translates to a property that will unwrap an extracted `Option` value
431    /// of the specified `type_name`, mapping `None` to `Err`.
432    fn try_unwrap<O>(self, type_name: &str) -> impl TemplateProperty<Output = O>
433    where
434        Self: TemplateProperty<Output = Option<O>> + Sized,
435    {
436        self.and_then(move |opt| {
437            opt.ok_or_else(|| TemplatePropertyError(format!("No {type_name} available").into()))
438        })
439    }
440
441    /// Converts this property into boxed serialize property.
442    fn into_serialize<'a>(self) -> BoxedSerializeProperty<'a>
443    where
444        Self: Sized + 'a,
445        Self::Output: serde::Serialize,
446    {
447        Box::new(self.map(|value| Box::new(value) as Box<dyn erased_serde::Serialize>))
448    }
449
450    /// Converts this property into `Template`.
451    fn into_template<'a>(self) -> Box<dyn Template + 'a>
452    where
453        Self: Sized + 'a,
454        Self::Output: Template,
455    {
456        Box::new(FormattablePropertyTemplate::new(self))
457    }
458
459    /// Converts this property into boxed trait object.
460    fn into_dyn<'a>(self) -> BoxedTemplateProperty<'a, Self::Output>
461    where
462        Self: Sized + 'a,
463    {
464        Box::new(self)
465    }
466
467    /// Converts this property into wrapped (or tagged) type.
468    ///
469    /// Use `W::wrap_property()` if the self type is known to be boxed.
470    fn into_dyn_wrapped<'a, W>(self) -> W
471    where
472        Self: Sized + 'a,
473        W: WrapTemplateProperty<'a, Self::Output>,
474    {
475        W::wrap_property(self.into_dyn())
476    }
477}
478
479impl<P: TemplateProperty + ?Sized> TemplatePropertyExt for P {}
480
481/// Wraps template property of type `O` in tagged type.
482///
483/// This is basically [`From<BoxedTemplateProperty<'a, O>>`], but is restricted
484/// to property types.
485#[diagnostic::on_unimplemented(
486    message = "the template property of type `{O}` cannot be wrapped in `{Self}`"
487)]
488pub trait WrapTemplateProperty<'a, O>: Sized {
489    fn wrap_property(property: BoxedTemplateProperty<'a, O>) -> Self;
490}
491
492/// Adapter that wraps literal value in `TemplateProperty`.
493pub struct Literal<O>(pub O);
494
495impl<O: Template> Template for Literal<O> {
496    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
497        self.0.format(formatter)
498    }
499}
500
501impl<O: Clone> TemplateProperty for Literal<O> {
502    type Output = O;
503
504    fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
505        Ok(self.0.clone())
506    }
507}
508
509/// Adapter to extract template value from property for displaying.
510pub struct FormattablePropertyTemplate<P> {
511    property: P,
512}
513
514impl<P> FormattablePropertyTemplate<P> {
515    pub fn new(property: P) -> Self
516    where
517        P: TemplateProperty,
518        P::Output: Template,
519    {
520        Self { property }
521    }
522}
523
524impl<P> Template for FormattablePropertyTemplate<P>
525where
526    P: TemplateProperty,
527    P::Output: Template,
528{
529    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
530        match self.property.extract() {
531            Ok(template) => template.format(formatter),
532            Err(err) => formatter.handle_error(err),
533        }
534    }
535}
536
537/// Adapter to turn template back to string property.
538pub struct PlainTextFormattedProperty<T> {
539    template: T,
540}
541
542impl<T> PlainTextFormattedProperty<T> {
543    pub fn new(template: T) -> Self {
544        Self { template }
545    }
546}
547
548impl<T: Template> TemplateProperty for PlainTextFormattedProperty<T> {
549    type Output = String;
550
551    fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
552        let mut output = vec![];
553        let mut formatter = PlainTextFormatter::new(&mut output);
554        let mut wrapper = TemplateFormatter::new(&mut formatter, propagate_property_error);
555        self.template.format(&mut wrapper)?;
556        Ok(String::from_utf8(output).map_err(|err| err.utf8_error())?)
557    }
558}
559
560/// Renders template property of list type with the given separator.
561///
562/// Each list item will be formatted by the given `format_item()` function.
563pub struct ListPropertyTemplate<P, S, F> {
564    property: P,
565    separator: S,
566    format_item: F,
567}
568
569impl<P, S, F> ListPropertyTemplate<P, S, F> {
570    pub fn new<O>(property: P, separator: S, format_item: F) -> Self
571    where
572        P: TemplateProperty,
573        P::Output: IntoIterator<Item = O>,
574        S: Template,
575        F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
576    {
577        Self {
578            property,
579            separator,
580            format_item,
581        }
582    }
583}
584
585impl<O, P, S, F> Template for ListPropertyTemplate<P, S, F>
586where
587    P: TemplateProperty,
588    P::Output: IntoIterator<Item = O>,
589    S: Template,
590    F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
591{
592    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
593        let contents = match self.property.extract() {
594            Ok(contents) => contents,
595            Err(err) => return formatter.handle_error(err),
596        };
597        format_joined_with(formatter, contents, &self.separator, &self.format_item)
598    }
599}
600
601impl<O, P, S, F> ListTemplate for ListPropertyTemplate<P, S, F>
602where
603    P: TemplateProperty,
604    P::Output: IntoIterator<Item = O>,
605    S: Template,
606    F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
607{
608    fn join<'a>(self: Box<Self>, separator: Box<dyn Template + 'a>) -> Box<dyn Template + 'a>
609    where
610        Self: 'a,
611    {
612        // Once join()-ed, list-like API should be dropped. This is guaranteed by
613        // the return type.
614        Box::new(ListPropertyTemplate::new(
615            self.property,
616            separator,
617            self.format_item,
618        ))
619    }
620}
621
622/// Template which selects an output based on a boolean condition.
623///
624/// When `None` is specified for the false template and the condition is false,
625/// this writes nothing.
626pub struct ConditionalTemplate<P, T, U> {
627    pub condition: P,
628    pub true_template: T,
629    pub false_template: Option<U>,
630}
631
632impl<P, T, U> ConditionalTemplate<P, T, U> {
633    pub fn new(condition: P, true_template: T, false_template: Option<U>) -> Self
634    where
635        P: TemplateProperty<Output = bool>,
636        T: Template,
637        U: Template,
638    {
639        Self {
640            condition,
641            true_template,
642            false_template,
643        }
644    }
645}
646
647impl<P, T, U> Template for ConditionalTemplate<P, T, U>
648where
649    P: TemplateProperty<Output = bool>,
650    T: Template,
651    U: Template,
652{
653    fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
654        let condition = match self.condition.extract() {
655            Ok(condition) => condition,
656            Err(err) => return formatter.handle_error(err),
657        };
658        if condition {
659            self.true_template.format(formatter)?;
660        } else if let Some(false_template) = &self.false_template {
661            false_template.format(formatter)?;
662        }
663        Ok(())
664    }
665}
666
667/// Adapter to apply fallible `function` to the `property`.
668///
669/// This is usually created by `TemplatePropertyExt::and_then()`/`map()`.
670pub struct TemplateFunction<P, F> {
671    pub property: P,
672    pub function: F,
673}
674
675impl<P, F> TemplateFunction<P, F> {
676    pub fn new<O>(property: P, function: F) -> Self
677    where
678        P: TemplateProperty,
679        F: Fn(P::Output) -> Result<O, TemplatePropertyError>,
680    {
681        Self { property, function }
682    }
683}
684
685impl<O, P, F> TemplateProperty for TemplateFunction<P, F>
686where
687    P: TemplateProperty,
688    F: Fn(P::Output) -> Result<O, TemplatePropertyError>,
689{
690    type Output = O;
691
692    fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
693        (self.function)(self.property.extract()?)
694    }
695}
696
697/// Property which will be compiled into template once, and substituted later.
698#[derive(Clone, Debug)]
699pub struct PropertyPlaceholder<O> {
700    value: Rc<RefCell<Option<O>>>,
701}
702
703impl<O> PropertyPlaceholder<O> {
704    pub fn new() -> Self {
705        Self {
706            value: Rc::new(RefCell::new(None)),
707        }
708    }
709
710    pub fn set(&self, value: O) {
711        *self.value.borrow_mut() = Some(value);
712    }
713
714    pub fn take(&self) -> Option<O> {
715        self.value.borrow_mut().take()
716    }
717
718    pub fn with_value<R>(&self, value: O, f: impl FnOnce() -> R) -> R {
719        self.set(value);
720        let result = f();
721        self.take();
722        result
723    }
724}
725
726impl<O> Default for PropertyPlaceholder<O> {
727    fn default() -> Self {
728        Self::new()
729    }
730}
731
732impl<O: Clone> TemplateProperty for PropertyPlaceholder<O> {
733    type Output = O;
734
735    fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
736        if let Some(value) = self.value.borrow().as_ref() {
737            Ok(value.clone())
738        } else {
739            Err(TemplatePropertyError("Placeholder value is not set".into()))
740        }
741    }
742}
743
744/// Adapter that renders compiled `template` with the `placeholder` value set.
745pub struct TemplateRenderer<'a, C> {
746    template: Box<dyn Template + 'a>,
747    placeholder: PropertyPlaceholder<C>,
748    labels: Vec<String>,
749}
750
751impl<'a, C: Clone> TemplateRenderer<'a, C> {
752    pub fn new(template: Box<dyn Template + 'a>, placeholder: PropertyPlaceholder<C>) -> Self {
753        Self {
754            template,
755            placeholder,
756            labels: Vec::new(),
757        }
758    }
759
760    /// Returns renderer that will format template with the given `labels`.
761    ///
762    /// This is equivalent to wrapping the content template with `label()`
763    /// function. For example,
764    /// `content.labeled(["foo", "bar"]).labeled(["baz"])` can be expressed as
765    /// `label("baz", label("foo bar", content))` in template.
766    pub fn labeled<S: Into<String>>(mut self, labels: impl IntoIterator<Item = S>) -> Self {
767        self.labels.splice(0..0, labels.into_iter().map(Into::into));
768        self
769    }
770
771    pub fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
772        let mut wrapper = TemplateFormatter::new(formatter, format_property_error_inline);
773        self.placeholder.with_value(context.clone(), || {
774            format_labeled(&mut wrapper, &self.template, &self.labels)
775        })
776    }
777
778    /// Renders template into buffer ignoring any color labels.
779    ///
780    /// The output is usually UTF-8, but it can contain arbitrary bytes such as
781    /// file content.
782    pub fn format_plain_text(&self, context: &C) -> Vec<u8> {
783        let mut output = Vec::new();
784        self.format(context, &mut PlainTextFormatter::new(&mut output))
785            .expect("write() to vec backed formatter should never fail");
786        output
787    }
788}
789
790/// Wrapper to pass around `Formatter` and error handler.
791pub struct TemplateFormatter<'a> {
792    formatter: &'a mut dyn Formatter,
793    error_handler: PropertyErrorHandler,
794}
795
796impl<'a> TemplateFormatter<'a> {
797    fn new(formatter: &'a mut dyn Formatter, error_handler: PropertyErrorHandler) -> Self {
798        Self {
799            formatter,
800            error_handler,
801        }
802    }
803
804    /// Returns function that wraps another `Formatter` with the current error
805    /// handling strategy.
806    ///
807    /// This does not borrow `self` so the underlying formatter can be mutably
808    /// borrowed.
809    pub fn rewrap_fn(&self) -> impl Fn(&mut dyn Formatter) -> TemplateFormatter<'_> + use<> {
810        let error_handler = self.error_handler;
811        move |formatter| TemplateFormatter::new(formatter, error_handler)
812    }
813
814    pub fn raw(&mut self) -> io::Result<Box<dyn Write + '_>> {
815        self.formatter.raw()
816    }
817
818    pub fn labeled(&mut self, label: &str) -> LabeledScope<&mut (dyn Formatter + 'a)> {
819        self.formatter.labeled(label)
820    }
821
822    pub fn push_label(&mut self, label: &str) {
823        self.formatter.push_label(label);
824    }
825
826    pub fn pop_label(&mut self) {
827        self.formatter.pop_label();
828    }
829
830    pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
831        self.formatter.write_fmt(args)
832    }
833
834    /// Handles the given template property evaluation error.
835    ///
836    /// This usually prints the given error inline, and returns `Ok`. It's up to
837    /// caller to decide whether or not to continue template processing on `Ok`.
838    /// For example, `if(cond, ..)` expression will terminate if the `cond`
839    /// failed to evaluate, whereas `concat(x, y, ..)` will continue processing.
840    ///
841    /// If `Err` is returned, the error should be propagated.
842    pub fn handle_error(&mut self, err: TemplatePropertyError) -> io::Result<()> {
843        (self.error_handler)(self.formatter, err)
844    }
845}
846
847impl<'a> AsMut<dyn Formatter + 'a> for TemplateFormatter<'a> {
848    fn as_mut(&mut self) -> &mut (dyn Formatter + 'a) {
849        self.formatter
850    }
851}
852
853pub fn format_joined<I, S>(
854    formatter: &mut TemplateFormatter,
855    contents: I,
856    separator: S,
857) -> io::Result<()>
858where
859    I: IntoIterator,
860    I::Item: Template,
861    S: Template,
862{
863    format_joined_with(formatter, contents, separator, |formatter, item| {
864        item.format(formatter)
865    })
866}
867
868fn format_joined_with<I, S, F>(
869    formatter: &mut TemplateFormatter,
870    contents: I,
871    separator: S,
872    mut format_item: F,
873) -> io::Result<()>
874where
875    I: IntoIterator,
876    S: Template,
877    F: FnMut(&mut TemplateFormatter, I::Item) -> io::Result<()>,
878{
879    let mut contents_iter = contents.into_iter().fuse();
880    if let Some(item) = contents_iter.next() {
881        format_item(formatter, item)?;
882    }
883    for item in contents_iter {
884        separator.format(formatter)?;
885        format_item(formatter, item)?;
886    }
887    Ok(())
888}
889
890fn format_labeled<T: Template + ?Sized>(
891    formatter: &mut TemplateFormatter,
892    content: &T,
893    labels: &[String],
894) -> io::Result<()> {
895    for label in labels {
896        formatter.push_label(label);
897    }
898    content.format(formatter)?;
899    for _label in labels {
900        formatter.pop_label();
901    }
902    Ok(())
903}
904
905type PropertyErrorHandler = fn(&mut dyn Formatter, TemplatePropertyError) -> io::Result<()>;
906
907/// Prints property evaluation error as inline template output.
908fn format_property_error_inline(
909    formatter: &mut dyn Formatter,
910    err: TemplatePropertyError,
911) -> io::Result<()> {
912    let TemplatePropertyError(err) = &err;
913    let mut formatter = formatter.labeled("error");
914    write!(formatter, "<")?;
915    write!(formatter.labeled("heading"), "Error: ")?;
916    write!(formatter, "{err}")?;
917    for err in iter::successors(err.source(), |err| err.source()) {
918        write!(formatter, ": {err}")?;
919    }
920    write!(formatter, ">")?;
921    Ok(())
922}
923
924fn propagate_property_error(
925    _formatter: &mut dyn Formatter,
926    err: TemplatePropertyError,
927) -> io::Result<()> {
928    Err(io::Error::other(err.0))
929}
930
931/// Creates function that renders a template to buffer and returns the buffer
932/// only if it isn't empty.
933///
934/// This inherits the error handling strategy from the given `formatter`.
935fn record_non_empty_fn<T: Template + ?Sized>(
936    formatter: &TemplateFormatter,
937    // TODO: T doesn't have to be captured, but "currently, all type parameters
938    // are required to be mentioned in the precise captures list" as of rustc
939    // 1.85.0.
940) -> impl Fn(&T) -> Option<io::Result<FormatRecorder>> + use<T> {
941    let rewrap = formatter.rewrap_fn();
942    move |template| {
943        let mut recorder = FormatRecorder::new();
944        match template.format(&mut rewrap(&mut recorder)) {
945            Ok(()) if recorder.data().is_empty() => None, // omit empty content
946            Ok(()) => Some(Ok(recorder)),
947            Err(e) => Some(Err(e)),
948        }
949    }
950}