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