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