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
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
62impl<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
120pub 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
212pub 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 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!(formatter.raw()?, "\x1b]8;;{url_str}\x1b\\")?;
252 self.text.format(formatter)?;
253 write!(formatter.raw()?, "\x1b]8;;\x1b\\")
254 }
255}
256
257pub 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) }
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
285pub 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
314pub 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
343pub 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#[derive(Debug)]
381pub struct TemplatePropertyError(pub Box<dyn error::Error + Send + Sync>);
382
383impl<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
395pub 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
418macro_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
440pub type BoxedTemplateProperty<'a, O> = Box<dyn TemplateProperty<Output = O> + 'a>;
442pub type BoxedSerializeProperty<'a> =
443 BoxedTemplateProperty<'a, Box<dyn erased_serde::Serialize + 'a>>;
444
445pub trait TemplatePropertyExt: TemplateProperty {
447 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 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 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 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 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 fn into_dyn<'a>(self) -> BoxedTemplateProperty<'a, Self::Output>
498 where
499 Self: Sized + 'a,
500 {
501 Box::new(self)
502 }
503
504 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#[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
529pub 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 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
544pub 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
561pub 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
589pub 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
612pub 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
653pub 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
716pub 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 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 None
779 }
780}
781
782pub 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
822pub 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#[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
899pub 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 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 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
945pub 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 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 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
1066fn 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
1090fn record_non_empty_fn<T: Template + ?Sized>(
1095 formatter: &TemplateFormatter,
1096 ) -> 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, Ok(()) => Some(Ok(recorder)),
1107 Err(e) => Some(Err(e)),
1108 }
1109 }
1110}