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