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 HyperlinkTemplate<U, T, F> {
223 url: U,
224 text: T,
225 fallback: Option<F>,
226}
227
228impl<U, T, F> HyperlinkTemplate<U, T, F> {
229 pub fn new(url: U, text: T, fallback: Option<F>) -> Self {
230 Self {
231 url,
232 text,
233 fallback,
234 }
235 }
236}
237
238impl<U, T, F> Template for HyperlinkTemplate<U, T, F>
239where
240 U: TemplateProperty<Output = String>,
241 T: Template,
242 F: Template,
243{
244 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
245 let url_str = match self.url.extract() {
247 Ok(url) => url,
248 Err(err) => return formatter.handle_error(err),
249 };
250
251 if !formatter.maybe_color() {
252 if let Some(fallback) = &self.fallback {
253 return fallback.format(formatter);
254 }
255 return self.text.format(formatter);
256 }
257
258 write!(formatter.raw()?, "\x1b]8;;{url_str}\x1b\\")?;
260 self.text.format(formatter)?;
261 write!(formatter.raw()?, "\x1b]8;;\x1b\\")
262 }
263}
264
265pub struct CoalesceTemplate<T>(pub Vec<T>);
267
268impl<T: Template> Template for CoalesceTemplate<T> {
269 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
270 let Some((last, contents)) = self.0.split_last() else {
271 return Ok(());
272 };
273 let record_non_empty = record_non_empty_fn(formatter);
274 if let Some(recorder) = contents.iter().find_map(record_non_empty) {
275 recorder?.replay(formatter.as_mut())
276 } else {
277 last.format(formatter) }
279 }
280}
281
282pub struct ConcatTemplate<T>(pub Vec<T>);
283
284impl<T: Template> Template for ConcatTemplate<T> {
285 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
286 for template in &self.0 {
287 template.format(formatter)?;
288 }
289 Ok(())
290 }
291}
292
293pub struct ReformatTemplate<T, F> {
295 content: T,
296 reformat: F,
297}
298
299impl<T, F> ReformatTemplate<T, F> {
300 pub fn new(content: T, reformat: F) -> Self
301 where
302 T: Template,
303 F: Fn(&mut TemplateFormatter, &FormatRecorder) -> io::Result<()>,
304 {
305 Self { content, reformat }
306 }
307}
308
309impl<T, F> Template for ReformatTemplate<T, F>
310where
311 T: Template,
312 F: Fn(&mut TemplateFormatter, &FormatRecorder) -> io::Result<()>,
313{
314 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
315 let rewrap = formatter.rewrap_fn();
316 let mut recorder = FormatRecorder::new(formatter.maybe_color());
317 self.content.format(&mut rewrap(&mut recorder))?;
318 (self.reformat)(formatter, &recorder)
319 }
320}
321
322pub struct JoinTemplate<S, T> {
324 separator: S,
325 contents: Vec<T>,
326}
327
328impl<S, T> JoinTemplate<S, T> {
329 pub fn new(separator: S, contents: Vec<T>) -> Self
330 where
331 S: Template,
332 T: Template,
333 {
334 Self {
335 separator,
336 contents,
337 }
338 }
339}
340
341impl<S, T> Template for JoinTemplate<S, T>
342where
343 S: Template,
344 T: Template,
345{
346 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
347 format_joined(formatter, &self.contents, &self.separator)
348 }
349}
350
351pub struct SeparateTemplate<S, T> {
353 separator: S,
354 contents: Vec<T>,
355}
356
357impl<S, T> SeparateTemplate<S, T> {
358 pub fn new(separator: S, contents: Vec<T>) -> Self
359 where
360 S: Template,
361 T: Template,
362 {
363 Self {
364 separator,
365 contents,
366 }
367 }
368}
369
370impl<S, T> Template for SeparateTemplate<S, T>
371where
372 S: Template,
373 T: Template,
374{
375 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
376 let record_non_empty = record_non_empty_fn(formatter);
377 let content_recorders = self.contents.iter().filter_map(record_non_empty);
378 format_joined_with(
379 formatter,
380 content_recorders,
381 &self.separator,
382 |formatter, recorder| recorder?.replay(formatter.as_mut()),
383 )
384 }
385}
386
387#[derive(Debug)]
389pub struct TemplatePropertyError(pub Box<dyn error::Error + Send + Sync>);
390
391impl<E> From<E> for TemplatePropertyError
395where
396 E: error::Error + Send + Sync + 'static,
397{
398 fn from(err: E) -> Self {
399 Self(err.into())
400 }
401}
402
403pub trait TemplateProperty {
405 type Output;
406
407 fn extract(&self) -> Result<Self::Output, TemplatePropertyError>;
408}
409
410impl<P: TemplateProperty + ?Sized> TemplateProperty for Box<P> {
411 type Output = <P as TemplateProperty>::Output;
412
413 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
414 <P as TemplateProperty>::extract(self)
415 }
416}
417
418impl<P: TemplateProperty> TemplateProperty for Option<P> {
419 type Output = Option<P::Output>;
420
421 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
422 self.as_ref().map(|property| property.extract()).transpose()
423 }
424}
425
426macro_rules! tuple_impls {
428 ($( ( $($n:tt $T:ident),+ ) )+) => {
429 $(
430 impl<$($T: TemplateProperty,)+> TemplateProperty for ($($T,)+) {
431 type Output = ($($T::Output,)+);
432
433 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
434 Ok(($(self.$n.extract()?,)+))
435 }
436 }
437 )+
438 }
439}
440
441tuple_impls! {
442 (0 T0)
443 (0 T0, 1 T1)
444 (0 T0, 1 T1, 2 T2)
445 (0 T0, 1 T1, 2 T2, 3 T3)
446}
447
448pub type BoxedTemplateProperty<'a, O> = Box<dyn TemplateProperty<Output = O> + 'a>;
450pub type BoxedSerializeProperty<'a> =
451 BoxedTemplateProperty<'a, Box<dyn erased_serde::Serialize + 'a>>;
452
453pub trait TemplatePropertyExt: TemplateProperty {
455 fn and_then<O, F>(self, function: F) -> TemplateFunction<Self, F>
458 where
459 Self: Sized,
460 F: Fn(Self::Output) -> Result<O, TemplatePropertyError>,
461 {
462 TemplateFunction::new(self, function)
463 }
464
465 fn map<O, F>(self, function: F) -> impl TemplateProperty<Output = O>
468 where
469 Self: Sized,
470 F: Fn(Self::Output) -> O,
471 {
472 TemplateFunction::new(self, move |value| Ok(function(value)))
473 }
474
475 fn try_unwrap<O>(self, type_name: &str) -> impl TemplateProperty<Output = O>
478 where
479 Self: TemplateProperty<Output = Option<O>> + Sized,
480 {
481 self.and_then(move |opt| {
482 opt.ok_or_else(|| TemplatePropertyError(format!("No {type_name} available").into()))
483 })
484 }
485
486 fn into_serialize<'a>(self) -> BoxedSerializeProperty<'a>
488 where
489 Self: Sized + 'a,
490 Self::Output: serde::Serialize,
491 {
492 Box::new(self.map(|value| Box::new(value) as Box<dyn erased_serde::Serialize>))
493 }
494
495 fn into_template<'a>(self) -> Box<dyn Template + 'a>
497 where
498 Self: Sized + 'a,
499 Self::Output: Template,
500 {
501 Box::new(FormattablePropertyTemplate::new(self))
502 }
503
504 fn into_dyn<'a>(self) -> BoxedTemplateProperty<'a, Self::Output>
506 where
507 Self: Sized + 'a,
508 {
509 Box::new(self)
510 }
511
512 fn into_dyn_wrapped<'a, W>(self) -> W
516 where
517 Self: Sized + 'a,
518 W: WrapTemplateProperty<'a, Self::Output>,
519 {
520 W::wrap_property(self.into_dyn())
521 }
522}
523
524impl<P: TemplateProperty + ?Sized> TemplatePropertyExt for P {}
525
526#[diagnostic::on_unimplemented(
531 message = "the template property of type `{O}` cannot be wrapped in `{Self}`"
532)]
533pub trait WrapTemplateProperty<'a, O>: Sized {
534 fn wrap_property(property: BoxedTemplateProperty<'a, O>) -> Self;
535}
536
537pub struct Literal<O>(pub O);
539
540impl<O: Template> Template for Literal<O> {
541 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
542 self.0.format(formatter)
543 }
544}
545
546impl<O: Clone> TemplateProperty for Literal<O> {
547 type Output = O;
548
549 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
550 Ok(self.0.clone())
551 }
552}
553
554pub struct FormattablePropertyTemplate<P> {
556 property: P,
557}
558
559impl<P> FormattablePropertyTemplate<P> {
560 pub fn new(property: P) -> Self
561 where
562 P: TemplateProperty,
563 P::Output: Template,
564 {
565 Self { property }
566 }
567}
568
569impl<P> Template for FormattablePropertyTemplate<P>
570where
571 P: TemplateProperty,
572 P::Output: Template,
573{
574 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
575 match self.property.extract() {
576 Ok(template) => template.format(formatter),
577 Err(err) => formatter.handle_error(err),
578 }
579 }
580}
581
582pub struct PlainTextFormattedProperty<T> {
584 template: T,
585}
586
587impl<T> PlainTextFormattedProperty<T> {
588 pub fn new(template: T) -> Self {
589 Self { template }
590 }
591}
592
593impl<T: Template> TemplateProperty for PlainTextFormattedProperty<T> {
594 type Output = String;
595
596 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
597 let mut output = vec![];
598 let mut formatter = PlainTextFormatter::new(&mut output);
599 let mut wrapper = TemplateFormatter::new(&mut formatter, propagate_property_error);
600 self.template.format(&mut wrapper)?;
601 Ok(String::from_utf8(output).map_err(|err| err.utf8_error())?)
602 }
603}
604
605pub struct ListPropertyTemplate<P, S, F> {
609 property: P,
610 separator: S,
611 format_item: F,
612}
613
614impl<P, S, F> ListPropertyTemplate<P, S, F> {
615 pub fn new<O>(property: P, separator: S, format_item: F) -> Self
616 where
617 P: TemplateProperty,
618 P::Output: IntoIterator<Item = O>,
619 S: Template,
620 F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
621 {
622 Self {
623 property,
624 separator,
625 format_item,
626 }
627 }
628}
629
630impl<O, P, S, F> Template for ListPropertyTemplate<P, S, F>
631where
632 P: TemplateProperty,
633 P::Output: IntoIterator<Item = O>,
634 S: Template,
635 F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
636{
637 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
638 let contents = match self.property.extract() {
639 Ok(contents) => contents,
640 Err(err) => return formatter.handle_error(err),
641 };
642 format_joined_with(formatter, contents, &self.separator, &self.format_item)
643 }
644}
645
646impl<O, P, S, F> ListTemplate for ListPropertyTemplate<P, S, F>
647where
648 P: TemplateProperty,
649 P::Output: IntoIterator<Item = O>,
650 S: Template,
651 F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
652{
653 fn join<'a>(self: Box<Self>, separator: Box<dyn Template + 'a>) -> Box<dyn Template + 'a>
654 where
655 Self: 'a,
656 {
657 Box::new(ListPropertyTemplate::new(
660 self.property,
661 separator,
662 self.format_item,
663 ))
664 }
665}
666
667pub struct ConditionalTemplate<P, T, U> {
672 pub condition: P,
673 pub true_template: T,
674 pub false_template: Option<U>,
675}
676
677impl<P, T, U> ConditionalTemplate<P, T, U> {
678 pub fn new(condition: P, true_template: T, false_template: Option<U>) -> Self
679 where
680 P: TemplateProperty<Output = bool>,
681 T: Template,
682 U: Template,
683 {
684 Self {
685 condition,
686 true_template,
687 false_template,
688 }
689 }
690}
691
692impl<P, T, U> Template for ConditionalTemplate<P, T, U>
693where
694 P: TemplateProperty<Output = bool>,
695 T: Template,
696 U: Template,
697{
698 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
699 let condition = match self.condition.extract() {
700 Ok(condition) => condition,
701 Err(err) => return formatter.handle_error(err),
702 };
703 if condition {
704 self.true_template.format(formatter)?;
705 } else if let Some(false_template) = &self.false_template {
706 false_template.format(formatter)?;
707 }
708 Ok(())
709 }
710}
711
712pub struct TemplateFunction<P, F> {
716 pub property: P,
717 pub function: F,
718}
719
720impl<P, F> TemplateFunction<P, F> {
721 pub fn new<O>(property: P, function: F) -> Self
722 where
723 P: TemplateProperty,
724 F: Fn(P::Output) -> Result<O, TemplatePropertyError>,
725 {
726 Self { property, function }
727 }
728}
729
730impl<O, P, F> TemplateProperty for TemplateFunction<P, F>
731where
732 P: TemplateProperty,
733 F: Fn(P::Output) -> Result<O, TemplatePropertyError>,
734{
735 type Output = O;
736
737 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
738 (self.function)(self.property.extract()?)
739 }
740}
741
742#[derive(Clone, Debug)]
744pub struct PropertyPlaceholder<O> {
745 value: Rc<RefCell<Option<O>>>,
746}
747
748impl<O> PropertyPlaceholder<O> {
749 pub fn new() -> Self {
750 Self {
751 value: Rc::new(RefCell::new(None)),
752 }
753 }
754
755 pub fn set(&self, value: O) {
756 *self.value.borrow_mut() = Some(value);
757 }
758
759 pub fn take(&self) -> Option<O> {
760 self.value.borrow_mut().take()
761 }
762
763 pub fn with_value<R>(&self, value: O, f: impl FnOnce() -> R) -> R {
764 self.set(value);
765 let result = f();
766 self.take();
767 result
768 }
769}
770
771impl<O> Default for PropertyPlaceholder<O> {
772 fn default() -> Self {
773 Self::new()
774 }
775}
776
777impl<O: Clone> TemplateProperty for PropertyPlaceholder<O> {
778 type Output = O;
779
780 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
781 if let Some(value) = self.value.borrow().as_ref() {
782 Ok(value.clone())
783 } else {
784 Err(TemplatePropertyError("Placeholder value is not set".into()))
785 }
786 }
787}
788
789pub struct TemplateRenderer<'a, C> {
791 template: Box<dyn Template + 'a>,
792 placeholder: PropertyPlaceholder<C>,
793 labels: Vec<String>,
794}
795
796impl<'a, C: Clone> TemplateRenderer<'a, C> {
797 pub fn new(template: Box<dyn Template + 'a>, placeholder: PropertyPlaceholder<C>) -> Self {
798 Self {
799 template,
800 placeholder,
801 labels: Vec::new(),
802 }
803 }
804
805 pub fn labeled<S: Into<String>>(mut self, labels: impl IntoIterator<Item = S>) -> Self {
812 self.labels.splice(0..0, labels.into_iter().map(Into::into));
813 self
814 }
815
816 pub fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
817 let mut wrapper = TemplateFormatter::new(formatter, format_property_error_inline);
818 self.placeholder.with_value(context.clone(), || {
819 format_labeled(&mut wrapper, &self.template, &self.labels)
820 })
821 }
822
823 pub fn format_plain_text(&self, context: &C) -> Vec<u8> {
828 let mut output = Vec::new();
829 self.format(context, &mut PlainTextFormatter::new(&mut output))
830 .expect("write() to vec backed formatter should never fail");
831 output
832 }
833}
834
835pub struct TemplateFormatter<'a> {
837 formatter: &'a mut dyn Formatter,
838 error_handler: PropertyErrorHandler,
839}
840
841impl<'a> TemplateFormatter<'a> {
842 fn new(formatter: &'a mut dyn Formatter, error_handler: PropertyErrorHandler) -> Self {
843 Self {
844 formatter,
845 error_handler,
846 }
847 }
848
849 pub fn rewrap_fn(&self) -> impl Fn(&mut dyn Formatter) -> TemplateFormatter<'_> + use<> {
855 let error_handler = self.error_handler;
856 move |formatter| TemplateFormatter::new(formatter, error_handler)
857 }
858
859 pub fn raw(&mut self) -> io::Result<Box<dyn Write + '_>> {
860 self.formatter.raw()
861 }
862
863 pub fn labeled(&mut self, label: &str) -> LabeledScope<&mut (dyn Formatter + 'a)> {
864 self.formatter.labeled(label)
865 }
866
867 pub fn push_label(&mut self, label: &str) {
868 self.formatter.push_label(label);
869 }
870
871 pub fn pop_label(&mut self) {
872 self.formatter.pop_label();
873 }
874
875 pub fn maybe_color(&self) -> bool {
876 self.formatter.maybe_color()
877 }
878
879 pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
880 self.formatter.write_fmt(args)
881 }
882
883 pub fn handle_error(&mut self, err: TemplatePropertyError) -> io::Result<()> {
892 (self.error_handler)(self.formatter, err)
893 }
894}
895
896impl<'a> AsMut<dyn Formatter + 'a> for TemplateFormatter<'a> {
897 fn as_mut(&mut self) -> &mut (dyn Formatter + 'a) {
898 self.formatter
899 }
900}
901
902pub fn format_joined<I, S>(
903 formatter: &mut TemplateFormatter,
904 contents: I,
905 separator: S,
906) -> io::Result<()>
907where
908 I: IntoIterator,
909 I::Item: Template,
910 S: Template,
911{
912 format_joined_with(formatter, contents, separator, |formatter, item| {
913 item.format(formatter)
914 })
915}
916
917fn format_joined_with<I, S, F>(
918 formatter: &mut TemplateFormatter,
919 contents: I,
920 separator: S,
921 mut format_item: F,
922) -> io::Result<()>
923where
924 I: IntoIterator,
925 S: Template,
926 F: FnMut(&mut TemplateFormatter, I::Item) -> io::Result<()>,
927{
928 let mut contents_iter = contents.into_iter().fuse();
929 if let Some(item) = contents_iter.next() {
930 format_item(formatter, item)?;
931 }
932 for item in contents_iter {
933 separator.format(formatter)?;
934 format_item(formatter, item)?;
935 }
936 Ok(())
937}
938
939fn format_labeled<T: Template + ?Sized>(
940 formatter: &mut TemplateFormatter,
941 content: &T,
942 labels: &[String],
943) -> io::Result<()> {
944 for label in labels {
945 formatter.push_label(label);
946 }
947 content.format(formatter)?;
948 for _label in labels {
949 formatter.pop_label();
950 }
951 Ok(())
952}
953
954type PropertyErrorHandler = fn(&mut dyn Formatter, TemplatePropertyError) -> io::Result<()>;
955
956fn format_property_error_inline(
958 formatter: &mut dyn Formatter,
959 err: TemplatePropertyError,
960) -> io::Result<()> {
961 let TemplatePropertyError(err) = &err;
962 let mut formatter = formatter.labeled("error");
963 write!(formatter, "<")?;
964 write!(formatter.labeled("heading"), "Error: ")?;
965 write!(formatter, "{err}")?;
966 for err in iter::successors(err.source(), |err| err.source()) {
967 write!(formatter, ": {err}")?;
968 }
969 write!(formatter, ">")?;
970 Ok(())
971}
972
973fn propagate_property_error(
974 _formatter: &mut dyn Formatter,
975 err: TemplatePropertyError,
976) -> io::Result<()> {
977 Err(io::Error::other(err.0))
978}
979
980fn record_non_empty_fn<T: Template + ?Sized>(
985 formatter: &TemplateFormatter,
986 ) -> impl Fn(&T) -> Option<io::Result<FormatRecorder>> + use<T> {
990 let rewrap = formatter.rewrap_fn();
991 let maybe_color = formatter.maybe_color();
992 move |template| {
993 let mut recorder = FormatRecorder::new(maybe_color);
994 match template.format(&mut rewrap(&mut recorder)) {
995 Ok(()) if recorder.data().is_empty() => None, Ok(()) => Some(Ok(recorder)),
997 Err(e) => Some(Err(e)),
998 }
999 }
1000}