1use 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
41pub trait Template {
47 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()>;
48}
49
50pub trait ListTemplate: Template {
52 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
70impl<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
128pub 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
220pub 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) }
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
248pub 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
277pub struct JoinTemplate<S, T> {
279 separator: S,
280 contents: Vec<T>,
281}
282
283impl<S, T> JoinTemplate<S, T> {
284 pub fn new(separator: S, contents: Vec<T>) -> Self
285 where
286 S: Template,
287 T: Template,
288 {
289 Self {
290 separator,
291 contents,
292 }
293 }
294}
295
296impl<S, T> Template for JoinTemplate<S, T>
297where
298 S: Template,
299 T: Template,
300{
301 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
302 format_joined(formatter, &self.contents, &self.separator)
303 }
304}
305
306pub struct SeparateTemplate<S, T> {
308 separator: S,
309 contents: Vec<T>,
310}
311
312impl<S, T> SeparateTemplate<S, T> {
313 pub fn new(separator: S, contents: Vec<T>) -> Self
314 where
315 S: Template,
316 T: Template,
317 {
318 Self {
319 separator,
320 contents,
321 }
322 }
323}
324
325impl<S, T> Template for SeparateTemplate<S, T>
326where
327 S: Template,
328 T: Template,
329{
330 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
331 let record_non_empty = record_non_empty_fn(formatter);
332 let content_recorders = self.contents.iter().filter_map(record_non_empty);
333 format_joined_with(
334 formatter,
335 content_recorders,
336 &self.separator,
337 |formatter, recorder| recorder?.replay(formatter.as_mut()),
338 )
339 }
340}
341
342#[derive(Debug)]
344pub struct TemplatePropertyError(pub Box<dyn error::Error + Send + Sync>);
345
346impl<E> From<E> for TemplatePropertyError
350where
351 E: error::Error + Send + Sync + 'static,
352{
353 fn from(err: E) -> Self {
354 Self(err.into())
355 }
356}
357
358pub trait TemplateProperty {
360 type Output;
361
362 fn extract(&self) -> Result<Self::Output, TemplatePropertyError>;
363}
364
365impl<P: TemplateProperty + ?Sized> TemplateProperty for Box<P> {
366 type Output = <P as TemplateProperty>::Output;
367
368 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
369 <P as TemplateProperty>::extract(self)
370 }
371}
372
373impl<P: TemplateProperty> TemplateProperty for Option<P> {
374 type Output = Option<P::Output>;
375
376 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
377 self.as_ref().map(|property| property.extract()).transpose()
378 }
379}
380
381macro_rules! tuple_impls {
383 ($( ( $($n:tt $T:ident),+ ) )+) => {
384 $(
385 impl<$($T: TemplateProperty,)+> TemplateProperty for ($($T,)+) {
386 type Output = ($($T::Output,)+);
387
388 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
389 Ok(($(self.$n.extract()?,)+))
390 }
391 }
392 )+
393 }
394}
395
396tuple_impls! {
397 (0 T0)
398 (0 T0, 1 T1)
399 (0 T0, 1 T1, 2 T2)
400 (0 T0, 1 T1, 2 T2, 3 T3)
401}
402
403pub type BoxedTemplateProperty<'a, O> = Box<dyn TemplateProperty<Output = O> + 'a>;
405pub type BoxedSerializeProperty<'a> =
406 BoxedTemplateProperty<'a, Box<dyn erased_serde::Serialize + 'a>>;
407
408pub trait TemplatePropertyExt: TemplateProperty {
410 fn and_then<O, F>(self, function: F) -> TemplateFunction<Self, F>
413 where
414 Self: Sized,
415 F: Fn(Self::Output) -> Result<O, TemplatePropertyError>,
416 {
417 TemplateFunction::new(self, function)
418 }
419
420 fn map<O, F>(self, function: F) -> impl TemplateProperty<Output = O>
423 where
424 Self: Sized,
425 F: Fn(Self::Output) -> O,
426 {
427 TemplateFunction::new(self, move |value| Ok(function(value)))
428 }
429
430 fn try_unwrap<O>(self, type_name: &str) -> impl TemplateProperty<Output = O>
433 where
434 Self: TemplateProperty<Output = Option<O>> + Sized,
435 {
436 self.and_then(move |opt| {
437 opt.ok_or_else(|| TemplatePropertyError(format!("No {type_name} available").into()))
438 })
439 }
440
441 fn into_serialize<'a>(self) -> BoxedSerializeProperty<'a>
443 where
444 Self: Sized + 'a,
445 Self::Output: serde::Serialize,
446 {
447 Box::new(self.map(|value| Box::new(value) as Box<dyn erased_serde::Serialize>))
448 }
449
450 fn into_template<'a>(self) -> Box<dyn Template + 'a>
452 where
453 Self: Sized + 'a,
454 Self::Output: Template,
455 {
456 Box::new(FormattablePropertyTemplate::new(self))
457 }
458
459 fn into_dyn<'a>(self) -> BoxedTemplateProperty<'a, Self::Output>
461 where
462 Self: Sized + 'a,
463 {
464 Box::new(self)
465 }
466
467 fn into_dyn_wrapped<'a, W>(self) -> W
471 where
472 Self: Sized + 'a,
473 W: WrapTemplateProperty<'a, Self::Output>,
474 {
475 W::wrap_property(self.into_dyn())
476 }
477}
478
479impl<P: TemplateProperty + ?Sized> TemplatePropertyExt for P {}
480
481#[diagnostic::on_unimplemented(
486 message = "the template property of type `{O}` cannot be wrapped in `{Self}`"
487)]
488pub trait WrapTemplateProperty<'a, O>: Sized {
489 fn wrap_property(property: BoxedTemplateProperty<'a, O>) -> Self;
490}
491
492pub struct Literal<O>(pub O);
494
495impl<O: Template> Template for Literal<O> {
496 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
497 self.0.format(formatter)
498 }
499}
500
501impl<O: Clone> TemplateProperty for Literal<O> {
502 type Output = O;
503
504 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
505 Ok(self.0.clone())
506 }
507}
508
509pub struct FormattablePropertyTemplate<P> {
511 property: P,
512}
513
514impl<P> FormattablePropertyTemplate<P> {
515 pub fn new(property: P) -> Self
516 where
517 P: TemplateProperty,
518 P::Output: Template,
519 {
520 Self { property }
521 }
522}
523
524impl<P> Template for FormattablePropertyTemplate<P>
525where
526 P: TemplateProperty,
527 P::Output: Template,
528{
529 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
530 match self.property.extract() {
531 Ok(template) => template.format(formatter),
532 Err(err) => formatter.handle_error(err),
533 }
534 }
535}
536
537pub struct PlainTextFormattedProperty<T> {
539 template: T,
540}
541
542impl<T> PlainTextFormattedProperty<T> {
543 pub fn new(template: T) -> Self {
544 Self { template }
545 }
546}
547
548impl<T: Template> TemplateProperty for PlainTextFormattedProperty<T> {
549 type Output = String;
550
551 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
552 let mut output = vec![];
553 let mut formatter = PlainTextFormatter::new(&mut output);
554 let mut wrapper = TemplateFormatter::new(&mut formatter, propagate_property_error);
555 self.template.format(&mut wrapper)?;
556 Ok(String::from_utf8(output).map_err(|err| err.utf8_error())?)
557 }
558}
559
560pub struct ListPropertyTemplate<P, S, F> {
564 property: P,
565 separator: S,
566 format_item: F,
567}
568
569impl<P, S, F> ListPropertyTemplate<P, S, F> {
570 pub fn new<O>(property: P, separator: S, format_item: F) -> Self
571 where
572 P: TemplateProperty,
573 P::Output: IntoIterator<Item = O>,
574 S: Template,
575 F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
576 {
577 Self {
578 property,
579 separator,
580 format_item,
581 }
582 }
583}
584
585impl<O, P, S, F> Template for ListPropertyTemplate<P, S, F>
586where
587 P: TemplateProperty,
588 P::Output: IntoIterator<Item = O>,
589 S: Template,
590 F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
591{
592 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
593 let contents = match self.property.extract() {
594 Ok(contents) => contents,
595 Err(err) => return formatter.handle_error(err),
596 };
597 format_joined_with(formatter, contents, &self.separator, &self.format_item)
598 }
599}
600
601impl<O, P, S, F> ListTemplate for ListPropertyTemplate<P, S, F>
602where
603 P: TemplateProperty,
604 P::Output: IntoIterator<Item = O>,
605 S: Template,
606 F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
607{
608 fn join<'a>(self: Box<Self>, separator: Box<dyn Template + 'a>) -> Box<dyn Template + 'a>
609 where
610 Self: 'a,
611 {
612 Box::new(ListPropertyTemplate::new(
615 self.property,
616 separator,
617 self.format_item,
618 ))
619 }
620}
621
622pub struct ConditionalTemplate<P, T, U> {
627 pub condition: P,
628 pub true_template: T,
629 pub false_template: Option<U>,
630}
631
632impl<P, T, U> ConditionalTemplate<P, T, U> {
633 pub fn new(condition: P, true_template: T, false_template: Option<U>) -> Self
634 where
635 P: TemplateProperty<Output = bool>,
636 T: Template,
637 U: Template,
638 {
639 Self {
640 condition,
641 true_template,
642 false_template,
643 }
644 }
645}
646
647impl<P, T, U> Template for ConditionalTemplate<P, T, U>
648where
649 P: TemplateProperty<Output = bool>,
650 T: Template,
651 U: Template,
652{
653 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
654 let condition = match self.condition.extract() {
655 Ok(condition) => condition,
656 Err(err) => return formatter.handle_error(err),
657 };
658 if condition {
659 self.true_template.format(formatter)?;
660 } else if let Some(false_template) = &self.false_template {
661 false_template.format(formatter)?;
662 }
663 Ok(())
664 }
665}
666
667pub struct TemplateFunction<P, F> {
671 pub property: P,
672 pub function: F,
673}
674
675impl<P, F> TemplateFunction<P, F> {
676 pub fn new<O>(property: P, function: F) -> Self
677 where
678 P: TemplateProperty,
679 F: Fn(P::Output) -> Result<O, TemplatePropertyError>,
680 {
681 Self { property, function }
682 }
683}
684
685impl<O, P, F> TemplateProperty for TemplateFunction<P, F>
686where
687 P: TemplateProperty,
688 F: Fn(P::Output) -> Result<O, TemplatePropertyError>,
689{
690 type Output = O;
691
692 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
693 (self.function)(self.property.extract()?)
694 }
695}
696
697#[derive(Clone, Debug)]
699pub struct PropertyPlaceholder<O> {
700 value: Rc<RefCell<Option<O>>>,
701}
702
703impl<O> PropertyPlaceholder<O> {
704 pub fn new() -> Self {
705 Self {
706 value: Rc::new(RefCell::new(None)),
707 }
708 }
709
710 pub fn set(&self, value: O) {
711 *self.value.borrow_mut() = Some(value);
712 }
713
714 pub fn take(&self) -> Option<O> {
715 self.value.borrow_mut().take()
716 }
717
718 pub fn with_value<R>(&self, value: O, f: impl FnOnce() -> R) -> R {
719 self.set(value);
720 let result = f();
721 self.take();
722 result
723 }
724}
725
726impl<O> Default for PropertyPlaceholder<O> {
727 fn default() -> Self {
728 Self::new()
729 }
730}
731
732impl<O: Clone> TemplateProperty for PropertyPlaceholder<O> {
733 type Output = O;
734
735 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
736 if let Some(value) = self.value.borrow().as_ref() {
737 Ok(value.clone())
738 } else {
739 Err(TemplatePropertyError("Placeholder value is not set".into()))
740 }
741 }
742}
743
744pub struct TemplateRenderer<'a, C> {
746 template: Box<dyn Template + 'a>,
747 placeholder: PropertyPlaceholder<C>,
748 labels: Vec<String>,
749}
750
751impl<'a, C: Clone> TemplateRenderer<'a, C> {
752 pub fn new(template: Box<dyn Template + 'a>, placeholder: PropertyPlaceholder<C>) -> Self {
753 Self {
754 template,
755 placeholder,
756 labels: Vec::new(),
757 }
758 }
759
760 pub fn labeled<S: Into<String>>(mut self, labels: impl IntoIterator<Item = S>) -> Self {
767 self.labels.splice(0..0, labels.into_iter().map(Into::into));
768 self
769 }
770
771 pub fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
772 let mut wrapper = TemplateFormatter::new(formatter, format_property_error_inline);
773 self.placeholder.with_value(context.clone(), || {
774 format_labeled(&mut wrapper, &self.template, &self.labels)
775 })
776 }
777
778 pub fn format_plain_text(&self, context: &C) -> Vec<u8> {
783 let mut output = Vec::new();
784 self.format(context, &mut PlainTextFormatter::new(&mut output))
785 .expect("write() to vec backed formatter should never fail");
786 output
787 }
788}
789
790pub struct TemplateFormatter<'a> {
792 formatter: &'a mut dyn Formatter,
793 error_handler: PropertyErrorHandler,
794}
795
796impl<'a> TemplateFormatter<'a> {
797 fn new(formatter: &'a mut dyn Formatter, error_handler: PropertyErrorHandler) -> Self {
798 Self {
799 formatter,
800 error_handler,
801 }
802 }
803
804 pub fn rewrap_fn(&self) -> impl Fn(&mut dyn Formatter) -> TemplateFormatter<'_> + use<> {
810 let error_handler = self.error_handler;
811 move |formatter| TemplateFormatter::new(formatter, error_handler)
812 }
813
814 pub fn raw(&mut self) -> io::Result<Box<dyn Write + '_>> {
815 self.formatter.raw()
816 }
817
818 pub fn labeled(&mut self, label: &str) -> LabeledScope<&mut (dyn Formatter + 'a)> {
819 self.formatter.labeled(label)
820 }
821
822 pub fn push_label(&mut self, label: &str) {
823 self.formatter.push_label(label);
824 }
825
826 pub fn pop_label(&mut self) {
827 self.formatter.pop_label();
828 }
829
830 pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
831 self.formatter.write_fmt(args)
832 }
833
834 pub fn handle_error(&mut self, err: TemplatePropertyError) -> io::Result<()> {
843 (self.error_handler)(self.formatter, err)
844 }
845}
846
847impl<'a> AsMut<dyn Formatter + 'a> for TemplateFormatter<'a> {
848 fn as_mut(&mut self) -> &mut (dyn Formatter + 'a) {
849 self.formatter
850 }
851}
852
853pub fn format_joined<I, S>(
854 formatter: &mut TemplateFormatter,
855 contents: I,
856 separator: S,
857) -> io::Result<()>
858where
859 I: IntoIterator,
860 I::Item: Template,
861 S: Template,
862{
863 format_joined_with(formatter, contents, separator, |formatter, item| {
864 item.format(formatter)
865 })
866}
867
868fn format_joined_with<I, S, F>(
869 formatter: &mut TemplateFormatter,
870 contents: I,
871 separator: S,
872 mut format_item: F,
873) -> io::Result<()>
874where
875 I: IntoIterator,
876 S: Template,
877 F: FnMut(&mut TemplateFormatter, I::Item) -> io::Result<()>,
878{
879 let mut contents_iter = contents.into_iter().fuse();
880 if let Some(item) = contents_iter.next() {
881 format_item(formatter, item)?;
882 }
883 for item in contents_iter {
884 separator.format(formatter)?;
885 format_item(formatter, item)?;
886 }
887 Ok(())
888}
889
890fn format_labeled<T: Template + ?Sized>(
891 formatter: &mut TemplateFormatter,
892 content: &T,
893 labels: &[String],
894) -> io::Result<()> {
895 for label in labels {
896 formatter.push_label(label);
897 }
898 content.format(formatter)?;
899 for _label in labels {
900 formatter.pop_label();
901 }
902 Ok(())
903}
904
905type PropertyErrorHandler = fn(&mut dyn Formatter, TemplatePropertyError) -> io::Result<()>;
906
907fn format_property_error_inline(
909 formatter: &mut dyn Formatter,
910 err: TemplatePropertyError,
911) -> io::Result<()> {
912 let TemplatePropertyError(err) = &err;
913 let mut formatter = formatter.labeled("error");
914 write!(formatter, "<")?;
915 write!(formatter.labeled("heading"), "Error: ")?;
916 write!(formatter, "{err}")?;
917 for err in iter::successors(err.source(), |err| err.source()) {
918 write!(formatter, ": {err}")?;
919 }
920 write!(formatter, ">")?;
921 Ok(())
922}
923
924fn propagate_property_error(
925 _formatter: &mut dyn Formatter,
926 err: TemplatePropertyError,
927) -> io::Result<()> {
928 Err(io::Error::other(err.0))
929}
930
931fn record_non_empty_fn<T: Template + ?Sized>(
936 formatter: &TemplateFormatter,
937 ) -> impl Fn(&T) -> Option<io::Result<FormatRecorder>> + use<T> {
941 let rewrap = formatter.rewrap_fn();
942 move |template| {
943 let mut recorder = FormatRecorder::new();
944 match template.format(&mut rewrap(&mut recorder)) {
945 Ok(()) if recorder.data().is_empty() => None, Ok(()) => Some(Ok(recorder)),
947 Err(e) => Some(Err(e)),
948 }
949 }
950}