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