1use 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
36pub trait Template {
38 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()>;
39}
40
41pub trait ListTemplate: Template {
43 fn join<'a>(self: Box<Self>, separator: Box<dyn Template + 'a>) -> Box<dyn Template + 'a>
45 where
46 Self: 'a;
47
48 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
66impl<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
123pub 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 pub start: Timestamp,
153 pub end: Timestamp,
154}
155
156impl TimestampRange {
157 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
236pub 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) }
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
264pub 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
293pub 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#[derive(Debug)]
333pub struct TemplatePropertyError(pub Box<dyn error::Error + Send + Sync>);
334
335impl<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
369macro_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
393pub trait TemplatePropertyExt: TemplateProperty {
395 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 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 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 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 fn into_dyn<'a>(self) -> BoxedTemplateProperty<'a, Self::Output>
437 where
438 Self: Sized + 'a,
439 {
440 Box::new(self)
441 }
442
443 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#[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
468pub 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
485pub 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
513pub 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
536pub 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 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
646pub 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#[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
723pub 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 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
758pub 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 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 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
878fn 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
903fn record_non_empty_fn<T: Template + ?Sized>(
908 formatter: &TemplateFormatter,
909 ) -> 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, Ok(()) => Some(Ok(recorder)),
919 Err(e) => Some(Err(e)),
920 }
921 }
922}