1use std::cell::RefCell;
19use std::collections::HashMap;
20use std::error;
21use std::fmt;
22use std::io;
23use std::io::Write;
24use std::iter;
25use std::ops::Range;
26use std::rc::Rc;
27
28use bstr::BStr;
29use bstr::BString;
30use jj_lib::backend::Signature;
31use jj_lib::backend::Timestamp;
32use jj_lib::config::ConfigValue;
33use jj_lib::op_store::TimestampRange;
34
35use crate::formatter::FormatRecorder;
36use crate::formatter::Formatter;
37use crate::formatter::FormatterExt as _;
38use crate::formatter::LabeledScope;
39use crate::formatter::PlainTextFormatter;
40use crate::text_util;
41use crate::time_util;
42
43pub trait Template {
49 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()>;
50}
51
52impl<T: Template + ?Sized> Template for &T {
53 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
54 <T as Template>::format(self, formatter)
55 }
56}
57
58impl<T: Template + ?Sized> Template for Box<T> {
59 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
60 <T as Template>::format(self, formatter)
61 }
62}
63
64impl<T: Template> Template for Option<T> {
67 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
68 self.as_ref().map_or(Ok(()), |t| t.format(formatter))
69 }
70}
71
72impl Template for BString {
73 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
74 formatter.as_mut().write_all(self)
75 }
76}
77
78impl Template for &BStr {
79 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
80 formatter.as_mut().write_all(self)
81 }
82}
83
84impl Template for ConfigValue {
85 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
86 write!(formatter, "{self}")
87 }
88}
89
90impl Template for Signature {
91 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
92 write!(formatter.labeled("name"), "{}", self.name)?;
93 if !self.name.is_empty() && !self.email.is_empty() {
94 write!(formatter, " ")?;
95 }
96 if !self.email.is_empty() {
97 write!(formatter, "<")?;
98 let email = Email(self.email.clone());
99 email.format(formatter)?;
100 write!(formatter, ">")?;
101 }
102 Ok(())
103 }
104}
105
106#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
107#[serde(transparent)]
108pub struct Email(pub String);
109
110impl Template for Email {
111 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
112 let (local, domain) = text_util::split_email(&self.0);
113 write!(formatter.labeled("local"), "{local}")?;
114 if let Some(domain) = domain {
115 write!(formatter, "@")?;
116 write!(formatter.labeled("domain"), "{domain}")?;
117 }
118 Ok(())
119 }
120}
121
122pub type SizeHint = (usize, Option<usize>);
126
127#[derive(Clone, Debug)]
129pub struct RegexCaptures {
130 haystack: Vec<u8>,
132 capture_ranges: Vec<Range<usize>>,
135 names: HashMap<String, usize>,
137}
138
139impl RegexCaptures {
140 pub fn new(
141 haystack: Vec<u8>,
142 capture_ranges: Vec<Range<usize>>,
143 names: HashMap<String, usize>,
144 ) -> Self {
145 Self {
146 haystack,
147 capture_ranges,
148 names,
149 }
150 }
151
152 #[expect(clippy::len_without_is_empty)]
153 pub fn len(&self) -> usize {
154 self.capture_ranges.len()
155 }
156
157 pub fn get(&self, index: usize) -> Option<BString> {
158 self.capture_ranges
159 .get(index)
160 .map(|range| self.haystack[range.start..range.end].into())
161 }
162
163 pub fn name(&self, name: &str) -> Option<BString> {
164 self.names.get(name).and_then(|&i| self.get(i))
165 }
166}
167
168impl Template for String {
169 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
170 write!(formatter, "{self}")
171 }
172}
173
174impl Template for &str {
175 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
176 write!(formatter, "{self}")
177 }
178}
179
180impl Template for Timestamp {
181 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
182 match time_util::format_absolute_timestamp(self) {
183 Ok(formatted) => write!(formatter, "{formatted}"),
184 Err(err) => formatter.handle_error(err.into()),
185 }
186 }
187}
188
189impl Template for TimestampRange {
190 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
191 self.start.format(formatter)?;
192 write!(formatter, " - ")?;
193 self.end.format(formatter)?;
194 Ok(())
195 }
196}
197
198impl Template for Vec<BString> {
199 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
200 format_joined(formatter, self, " ")
201 }
202}
203
204impl Template for Vec<String> {
205 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
206 format_joined(formatter, self, " ")
207 }
208}
209
210impl Template for bool {
211 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
212 let repr = if *self { "true" } else { "false" };
213 write!(formatter, "{repr}")
214 }
215}
216
217impl Template for i64 {
218 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
219 write!(formatter, "{self}")
220 }
221}
222
223pub struct LabelTemplate<T, L> {
224 content: T,
225 labels: L,
226}
227
228impl<T, L> LabelTemplate<T, L> {
229 pub fn new(content: T, labels: L) -> Self
230 where
231 T: Template,
232 L: TemplateProperty<Output = Vec<String>>,
233 {
234 Self { content, labels }
235 }
236}
237
238impl<T, L> Template for LabelTemplate<T, L>
239where
240 T: Template,
241 L: TemplateProperty<Output = Vec<String>>,
242{
243 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
244 match self.labels.extract() {
245 Ok(labels) => format_labeled(formatter, &self.content, &labels),
246 Err(err) => formatter.handle_error(err),
247 }
248 }
249}
250
251pub struct RawEscapeSequenceTemplate<T>(pub T);
252
253impl<T: Template> Template for RawEscapeSequenceTemplate<T> {
254 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
255 let rewrap = formatter.rewrap_fn();
256 let mut raw_formatter = PlainTextFormatter::new(formatter.raw()?);
257 self.0.format(&mut rewrap(&mut raw_formatter))
258 }
259}
260
261pub struct HyperlinkTemplate<U, T, F> {
264 url: U,
265 text: T,
266 fallback: Option<F>,
267}
268
269impl<U, T, F> HyperlinkTemplate<U, T, F> {
270 pub fn new(url: U, text: T, fallback: Option<F>) -> Self {
271 Self {
272 url,
273 text,
274 fallback,
275 }
276 }
277}
278
279impl<U, T, F> Template for HyperlinkTemplate<U, T, F>
280where
281 U: TemplateProperty<Output = String>,
282 T: Template,
283 F: Template,
284{
285 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
286 let url_str = match self.url.extract() {
288 Ok(url) => url,
289 Err(err) => return formatter.handle_error(err),
290 };
291
292 if !formatter.maybe_color() {
293 if let Some(fallback) = &self.fallback {
294 return fallback.format(formatter);
295 }
296 return self.text.format(formatter);
297 }
298
299 write!(formatter.raw()?, "\x1b]8;;{url_str}\x1b\\")?;
301 self.text.format(formatter)?;
302 write!(formatter.raw()?, "\x1b]8;;\x1b\\")
303 }
304}
305
306pub struct CoalesceTemplate<T>(pub Vec<T>);
308
309impl<T: Template> Template for CoalesceTemplate<T> {
310 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
311 let Some((last, contents)) = self.0.split_last() else {
312 return Ok(());
313 };
314 let record_non_empty = record_non_empty_fn(formatter);
315 if let Some(recorder) = contents.iter().find_map(record_non_empty) {
316 recorder?.replay(formatter.as_mut())
317 } else {
318 last.format(formatter) }
320 }
321}
322
323pub struct ConcatTemplate<T>(pub Vec<T>);
324
325impl<T: Template> Template for ConcatTemplate<T> {
326 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
327 for template in &self.0 {
328 template.format(formatter)?;
329 }
330 Ok(())
331 }
332}
333
334pub struct ReformatTemplate<T, F> {
336 content: T,
337 reformat: F,
338}
339
340impl<T, F> ReformatTemplate<T, F> {
341 pub fn new(content: T, reformat: F) -> Self
342 where
343 T: Template,
344 F: Fn(&mut TemplateFormatter, &FormatRecorder) -> io::Result<()>,
345 {
346 Self { content, reformat }
347 }
348}
349
350impl<T, F> Template for ReformatTemplate<T, F>
351where
352 T: Template,
353 F: Fn(&mut TemplateFormatter, &FormatRecorder) -> io::Result<()>,
354{
355 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
356 let rewrap = formatter.rewrap_fn();
357 let mut recorder = FormatRecorder::new(formatter.maybe_color());
358 self.content.format(&mut rewrap(&mut recorder))?;
359 (self.reformat)(formatter, &recorder)
360 }
361}
362
363pub struct JoinTemplate<S, T> {
365 separator: S,
366 contents: Vec<T>,
367}
368
369impl<S, T> JoinTemplate<S, T> {
370 pub fn new(separator: S, contents: Vec<T>) -> Self
371 where
372 S: Template,
373 T: Template,
374 {
375 Self {
376 separator,
377 contents,
378 }
379 }
380}
381
382impl<S, T> Template for JoinTemplate<S, T>
383where
384 S: Template,
385 T: Template,
386{
387 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
388 format_joined(formatter, &self.contents, &self.separator)
389 }
390}
391
392pub struct SeparateTemplate<S, T> {
394 separator: S,
395 contents: Vec<T>,
396}
397
398impl<S, T> SeparateTemplate<S, T> {
399 pub fn new(separator: S, contents: Vec<T>) -> Self
400 where
401 S: Template,
402 T: Template,
403 {
404 Self {
405 separator,
406 contents,
407 }
408 }
409}
410
411impl<S, T> Template for SeparateTemplate<S, T>
412where
413 S: Template,
414 T: Template,
415{
416 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
417 let record_non_empty = record_non_empty_fn(formatter);
418 let content_recorders = self.contents.iter().filter_map(record_non_empty);
419 format_joined_with(
420 formatter,
421 content_recorders,
422 &self.separator,
423 |formatter, recorder| recorder?.replay(formatter.as_mut()),
424 )
425 }
426}
427
428#[derive(Debug)]
430pub struct TemplatePropertyError(pub Box<dyn error::Error + Send + Sync>);
431
432impl<E> From<E> for TemplatePropertyError
436where
437 E: error::Error + Send + Sync + 'static,
438{
439 fn from(err: E) -> Self {
440 Self(err.into())
441 }
442}
443
444pub trait TemplateProperty {
446 type Output;
447
448 fn extract(&self) -> Result<Self::Output, TemplatePropertyError>;
449}
450
451impl<P: TemplateProperty + ?Sized> TemplateProperty for Box<P> {
452 type Output = <P as TemplateProperty>::Output;
453
454 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
455 <P as TemplateProperty>::extract(self)
456 }
457}
458
459impl<P: TemplateProperty> TemplateProperty for Option<P> {
460 type Output = Option<P::Output>;
461
462 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
463 self.as_ref().map(|property| property.extract()).transpose()
464 }
465}
466
467macro_rules! tuple_impls {
469 ($( ( $($n:tt $T:ident),+ ) )+) => {
470 $(
471 impl<$($T: TemplateProperty,)+> TemplateProperty for ($($T,)+) {
472 type Output = ($($T::Output,)+);
473
474 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
475 Ok(($(self.$n.extract()?,)+))
476 }
477 }
478 )+
479 }
480}
481
482tuple_impls! {
483 (0 T0)
484 (0 T0, 1 T1)
485 (0 T0, 1 T1, 2 T2)
486 (0 T0, 1 T1, 2 T2, 3 T3)
487}
488
489pub type BoxedTemplateProperty<'a, O> = Box<dyn TemplateProperty<Output = O> + 'a>;
491pub type BoxedSerializeProperty<'a> =
492 BoxedTemplateProperty<'a, Box<dyn erased_serde::Serialize + 'a>>;
493
494pub trait TemplatePropertyExt: TemplateProperty {
496 fn and_then<O, F>(self, function: F) -> TemplateFunction<Self, F>
499 where
500 Self: Sized,
501 F: Fn(Self::Output) -> Result<O, TemplatePropertyError>,
502 {
503 TemplateFunction::new(self, function)
504 }
505
506 fn map<O, F>(self, function: F) -> impl TemplateProperty<Output = O>
509 where
510 Self: Sized,
511 F: Fn(Self::Output) -> O,
512 {
513 TemplateFunction::new(self, move |value| Ok(function(value)))
514 }
515
516 fn try_unwrap<O>(self, type_name: &str) -> impl TemplateProperty<Output = O>
519 where
520 Self: TemplateProperty<Output = Option<O>> + Sized,
521 {
522 self.and_then(move |opt| {
523 opt.ok_or_else(|| TemplatePropertyError(format!("No {type_name} available").into()))
524 })
525 }
526
527 fn into_serialize<'a>(self) -> BoxedSerializeProperty<'a>
529 where
530 Self: Sized + 'a,
531 Self::Output: serde::Serialize,
532 {
533 Box::new(self.map(|value| Box::new(value) as Box<dyn erased_serde::Serialize>))
534 }
535
536 fn into_template<'a>(self) -> Box<dyn Template + 'a>
538 where
539 Self: Sized + 'a,
540 Self::Output: Template,
541 {
542 Box::new(FormattablePropertyTemplate::new(self))
543 }
544
545 fn into_dyn<'a>(self) -> BoxedTemplateProperty<'a, Self::Output>
547 where
548 Self: Sized + 'a,
549 {
550 Box::new(self)
551 }
552
553 fn into_dyn_wrapped<'a, W>(self) -> W
557 where
558 Self: Sized + 'a,
559 W: WrapTemplateProperty<'a, Self::Output>,
560 {
561 W::wrap_property(self.into_dyn())
562 }
563}
564
565impl<P: TemplateProperty + ?Sized> TemplatePropertyExt for P {}
566
567#[diagnostic::on_unimplemented(
572 message = "the template property of type `{O}` cannot be wrapped in `{Self}`"
573)]
574pub trait WrapTemplateProperty<'a, O>: Sized {
575 fn wrap_property(property: BoxedTemplateProperty<'a, O>) -> Self;
576}
577
578pub trait AnyTemplateProperty<'a> {
580 fn try_into_serialize(self: Box<Self>) -> Option<BoxedSerializeProperty<'a>>;
581
582 fn try_into_template(self: Box<Self>) -> Option<Box<dyn Template + 'a>>;
583
584 fn try_join(
587 self: Box<Self>,
588 separator: Box<dyn Template + 'a>,
589 ) -> Option<Box<dyn Template + 'a>>;
590}
591pub type BoxedAnyProperty<'a> = Box<dyn AnyTemplateProperty<'a> + 'a>;
592
593pub struct Literal<O>(pub O);
595
596impl<O: Template> Template for Literal<O> {
597 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
598 self.0.format(formatter)
599 }
600}
601
602impl<O: Clone> TemplateProperty for Literal<O> {
603 type Output = O;
604
605 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
606 Ok(self.0.clone())
607 }
608}
609
610pub struct FormattablePropertyTemplate<P> {
612 property: P,
613}
614
615impl<P> FormattablePropertyTemplate<P> {
616 pub fn new(property: P) -> Self
617 where
618 P: TemplateProperty,
619 P::Output: Template,
620 {
621 Self { property }
622 }
623}
624
625impl<P> Template for FormattablePropertyTemplate<P>
626where
627 P: TemplateProperty,
628 P::Output: Template,
629{
630 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
631 match self.property.extract() {
632 Ok(template) => template.format(formatter),
633 Err(err) => formatter.handle_error(err),
634 }
635 }
636}
637
638pub struct PlainTextFormattedProperty<T> {
640 template: T,
641}
642
643impl<T> PlainTextFormattedProperty<T> {
644 pub fn new(template: T) -> Self {
645 Self { template }
646 }
647}
648
649impl<T: Template> TemplateProperty for PlainTextFormattedProperty<T> {
650 type Output = BString;
651
652 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
653 let mut output = vec![];
654 let mut formatter = PlainTextFormatter::new(&mut output);
655 let mut wrapper = TemplateFormatter::new(&mut formatter, propagate_property_error);
656 self.template.format(&mut wrapper)?;
657 Ok(BString::new(output))
658 }
659}
660
661pub struct ListPropertyTemplate<P, S, F> {
665 property: P,
666 separator: S,
667 format_item: F,
668}
669
670impl<P, S, F> ListPropertyTemplate<P, S, F> {
671 pub fn new<O>(property: P, separator: S, format_item: F) -> Self
672 where
673 P: TemplateProperty,
674 P::Output: IntoIterator<Item = O>,
675 S: Template,
676 F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
677 {
678 Self {
679 property,
680 separator,
681 format_item,
682 }
683 }
684}
685
686impl<O, P, S, F> Template for ListPropertyTemplate<P, S, F>
687where
688 P: TemplateProperty,
689 P::Output: IntoIterator<Item = O>,
690 S: Template,
691 F: Fn(&mut TemplateFormatter, O) -> io::Result<()>,
692{
693 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
694 let contents = match self.property.extract() {
695 Ok(contents) => contents,
696 Err(err) => return formatter.handle_error(err),
697 };
698 format_joined_with(formatter, contents, &self.separator, &self.format_item)
699 }
700}
701
702pub struct ListMapProperty<'a, P, O> {
707 property: P,
708 placeholder: PropertyPlaceholder<O>,
709 mapped: BoxedAnyProperty<'a>,
710}
711
712impl<'a, P, O> ListMapProperty<'a, P, O> {
713 pub fn new(
714 property: P,
715 placeholder: PropertyPlaceholder<O>,
716 mapped: BoxedAnyProperty<'a>,
717 ) -> Self {
718 Self {
719 property,
720 placeholder,
721 mapped,
722 }
723 }
724}
725
726impl<'a, P, O> AnyTemplateProperty<'a> for ListMapProperty<'a, P, O>
727where
728 P: TemplateProperty + 'a,
729 P::Output: IntoIterator<Item = O>,
730 O: Clone + 'a,
731{
732 fn try_into_serialize(self: Box<Self>) -> Option<BoxedSerializeProperty<'a>> {
733 let placeholder = self.placeholder;
734 let mapped = self.mapped.try_into_serialize()?;
735 Some(
736 self.property
737 .and_then(move |property| {
738 property
739 .into_iter()
740 .map(|i| placeholder.with_value(i, || mapped.extract()))
741 .collect::<Result<Vec<_>, _>>()
742 })
743 .into_serialize(),
744 )
745 }
746
747 fn try_into_template(self: Box<Self>) -> Option<Box<dyn Template + 'a>> {
748 self.try_join(Box::new(Literal(" ")))
749 }
750
751 fn try_join(
752 self: Box<Self>,
753 separator: Box<dyn Template + 'a>,
754 ) -> Option<Box<dyn Template + 'a>> {
755 let placeholder = self.placeholder;
756 let mapped = self.mapped.try_into_template()?;
757 Some(Box::new(ListPropertyTemplate::new(
758 self.property,
759 separator,
760 move |formatter, value| placeholder.with_value(value, || mapped.format(formatter)),
761 )))
762 }
763}
764
765pub struct ConditionalProperty<'a, P> {
767 pub condition: P,
768 pub on_true: BoxedAnyProperty<'a>,
769 pub on_false: Option<BoxedAnyProperty<'a>>,
770}
771
772impl<'a, P> ConditionalProperty<'a, P> {
773 pub fn new(
774 condition: P,
775 on_true: BoxedAnyProperty<'a>,
776 on_false: Option<BoxedAnyProperty<'a>>,
777 ) -> Self
778 where
779 P: TemplateProperty<Output = bool> + 'a,
780 {
781 Self {
782 condition,
783 on_true,
784 on_false,
785 }
786 }
787}
788
789impl<'a, P> AnyTemplateProperty<'a> for ConditionalProperty<'a, P>
790where
791 P: TemplateProperty<Output = bool> + 'a,
792{
793 fn try_into_serialize(self: Box<Self>) -> Option<BoxedSerializeProperty<'a>> {
794 Some(
795 (
796 self.condition,
797 self.on_true.try_into_serialize()?,
798 self.on_false?.try_into_serialize()?,
799 )
800 .map(
801 move |(condition, on_true, on_false)| {
802 if condition { on_true } else { on_false }
803 },
804 )
805 .into_serialize(),
806 )
807 }
808
809 fn try_into_template(self: Box<Self>) -> Option<Box<dyn Template + 'a>> {
810 Some(Box::new(ConditionalTemplate::new(
811 self.condition,
812 self.on_true.try_into_template()?,
813 match self.on_false {
816 Some(on_false) => on_false.try_into_template()?,
817 None => Box::new(Literal("")),
818 },
819 )))
820 }
821
822 fn try_join(
823 self: Box<Self>,
824 _separator: Box<dyn Template + 'a>,
825 ) -> Option<Box<dyn Template + 'a>> {
826 None
828 }
829}
830
831pub struct ConditionalTemplate<P, T, U> {
833 pub condition: P,
834 pub true_template: T,
835 pub false_template: U,
836}
837
838impl<P, T, U> ConditionalTemplate<P, T, U> {
839 pub fn new(condition: P, true_template: T, false_template: U) -> Self
840 where
841 P: TemplateProperty<Output = bool>,
842 T: Template,
843 U: Template,
844 {
845 Self {
846 condition,
847 true_template,
848 false_template,
849 }
850 }
851}
852
853impl<P, T, U> Template for ConditionalTemplate<P, T, U>
854where
855 P: TemplateProperty<Output = bool>,
856 T: Template,
857 U: Template,
858{
859 fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
860 let condition = match self.condition.extract() {
861 Ok(condition) => condition,
862 Err(err) => return formatter.handle_error(err),
863 };
864 match condition {
865 true => self.true_template.format(formatter),
866 false => self.false_template.format(formatter),
867 }
868 }
869}
870
871pub struct TemplateFunction<P, F> {
875 pub property: P,
876 pub function: F,
877}
878
879impl<P, F> TemplateFunction<P, F> {
880 pub fn new<O>(property: P, function: F) -> Self
881 where
882 P: TemplateProperty,
883 F: Fn(P::Output) -> Result<O, TemplatePropertyError>,
884 {
885 Self { property, function }
886 }
887}
888
889impl<O, P, F> TemplateProperty for TemplateFunction<P, F>
890where
891 P: TemplateProperty,
892 F: Fn(P::Output) -> Result<O, TemplatePropertyError>,
893{
894 type Output = O;
895
896 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
897 (self.function)(self.property.extract()?)
898 }
899}
900
901#[derive(Clone, Debug)]
903pub struct PropertyPlaceholder<O> {
904 value: Rc<RefCell<Option<O>>>,
905}
906
907impl<O> PropertyPlaceholder<O> {
908 pub fn new() -> Self {
909 Self {
910 value: Rc::new(RefCell::new(None)),
911 }
912 }
913
914 pub fn set(&self, value: O) {
915 *self.value.borrow_mut() = Some(value);
916 }
917
918 pub fn take(&self) -> Option<O> {
919 self.value.borrow_mut().take()
920 }
921
922 pub fn with_value<R>(&self, value: O, f: impl FnOnce() -> R) -> R {
923 self.set(value);
924 let result = f();
925 self.take();
926 result
927 }
928}
929
930impl<O> Default for PropertyPlaceholder<O> {
931 fn default() -> Self {
932 Self::new()
933 }
934}
935
936impl<O: Clone> TemplateProperty for PropertyPlaceholder<O> {
937 type Output = O;
938
939 fn extract(&self) -> Result<Self::Output, TemplatePropertyError> {
940 if let Some(value) = self.value.borrow().as_ref() {
941 Ok(value.clone())
942 } else {
943 Err(TemplatePropertyError("Placeholder value is not set".into()))
944 }
945 }
946}
947
948pub struct TemplateRenderer<'a, C> {
950 template: Box<dyn Template + 'a>,
951 placeholder: PropertyPlaceholder<C>,
952 labels: Vec<String>,
953}
954
955impl<'a, C: Clone> TemplateRenderer<'a, C> {
956 pub fn new(template: Box<dyn Template + 'a>, placeholder: PropertyPlaceholder<C>) -> Self {
957 Self {
958 template,
959 placeholder,
960 labels: Vec::new(),
961 }
962 }
963
964 pub fn labeled<S: Into<String>>(mut self, labels: impl IntoIterator<Item = S>) -> Self {
971 self.labels.splice(0..0, labels.into_iter().map(Into::into));
972 self
973 }
974
975 pub fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
976 let mut wrapper = TemplateFormatter::new(formatter, format_property_error_inline);
977 self.placeholder.with_value(context.clone(), || {
978 format_labeled(&mut wrapper, &self.template, &self.labels)
979 })
980 }
981
982 pub fn format_plain_text(&self, context: &C) -> Vec<u8> {
987 let mut output = Vec::new();
988 self.format(context, &mut PlainTextFormatter::new(&mut output))
989 .expect("write() to vec backed formatter should never fail");
990 output
991 }
992}
993
994pub struct TemplateFormatter<'a> {
996 formatter: &'a mut dyn Formatter,
997 error_handler: PropertyErrorHandler,
998}
999
1000impl<'a> TemplateFormatter<'a> {
1001 fn new(formatter: &'a mut dyn Formatter, error_handler: PropertyErrorHandler) -> Self {
1002 Self {
1003 formatter,
1004 error_handler,
1005 }
1006 }
1007
1008 pub fn rewrap_fn(&self) -> impl Fn(&mut dyn Formatter) -> TemplateFormatter<'_> + use<> {
1014 let error_handler = self.error_handler;
1015 move |formatter| TemplateFormatter::new(formatter, error_handler)
1016 }
1017
1018 pub fn raw(&mut self) -> io::Result<Box<dyn Write + '_>> {
1019 self.formatter.raw()
1020 }
1021
1022 pub fn labeled(&mut self, label: &str) -> LabeledScope<&mut (dyn Formatter + 'a)> {
1023 self.formatter.labeled(label)
1024 }
1025
1026 pub fn push_label(&mut self, label: &str) {
1027 self.formatter.push_label(label);
1028 }
1029
1030 pub fn pop_label(&mut self) {
1031 self.formatter.pop_label();
1032 }
1033
1034 pub fn maybe_color(&self) -> bool {
1035 self.formatter.maybe_color()
1036 }
1037
1038 pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
1039 self.formatter.write_fmt(args)
1040 }
1041
1042 pub fn handle_error(&mut self, err: TemplatePropertyError) -> io::Result<()> {
1051 (self.error_handler)(self.formatter, err)
1052 }
1053}
1054
1055impl<'a> AsMut<dyn Formatter + 'a> for TemplateFormatter<'a> {
1056 fn as_mut(&mut self) -> &mut (dyn Formatter + 'a) {
1057 self.formatter
1058 }
1059}
1060
1061pub fn format_joined<I, S>(
1062 formatter: &mut TemplateFormatter,
1063 contents: I,
1064 separator: S,
1065) -> io::Result<()>
1066where
1067 I: IntoIterator,
1068 I::Item: Template,
1069 S: Template,
1070{
1071 format_joined_with(formatter, contents, separator, |formatter, item| {
1072 item.format(formatter)
1073 })
1074}
1075
1076fn format_joined_with<I, S, F>(
1077 formatter: &mut TemplateFormatter,
1078 contents: I,
1079 separator: S,
1080 mut format_item: F,
1081) -> io::Result<()>
1082where
1083 I: IntoIterator,
1084 S: Template,
1085 F: FnMut(&mut TemplateFormatter, I::Item) -> io::Result<()>,
1086{
1087 let mut contents_iter = contents.into_iter().fuse();
1088 if let Some(item) = contents_iter.next() {
1089 format_item(formatter, item)?;
1090 }
1091 for item in contents_iter {
1092 separator.format(formatter)?;
1093 format_item(formatter, item)?;
1094 }
1095 Ok(())
1096}
1097
1098fn format_labeled<T: Template + ?Sized>(
1099 formatter: &mut TemplateFormatter,
1100 content: &T,
1101 labels: &[String],
1102) -> io::Result<()> {
1103 for label in labels {
1104 formatter.push_label(label);
1105 }
1106 content.format(formatter)?;
1107 for _label in labels {
1108 formatter.pop_label();
1109 }
1110 Ok(())
1111}
1112
1113type PropertyErrorHandler = fn(&mut dyn Formatter, TemplatePropertyError) -> io::Result<()>;
1114
1115fn format_property_error_inline(
1117 formatter: &mut dyn Formatter,
1118 err: TemplatePropertyError,
1119) -> io::Result<()> {
1120 let TemplatePropertyError(err) = &err;
1121 let mut formatter = formatter.labeled("error");
1122 write!(formatter, "<")?;
1123 write!(formatter.labeled("heading"), "Error: ")?;
1124 write!(formatter, "{err}")?;
1125 for err in iter::successors(err.source(), |err| err.source()) {
1126 write!(formatter, ": {err}")?;
1127 }
1128 write!(formatter, ">")?;
1129 Ok(())
1130}
1131
1132fn propagate_property_error(
1133 _formatter: &mut dyn Formatter,
1134 err: TemplatePropertyError,
1135) -> io::Result<()> {
1136 Err(io::Error::other(err.0))
1137}
1138
1139fn record_non_empty_fn<T: Template + ?Sized>(
1144 formatter: &TemplateFormatter,
1145 ) -> impl Fn(&T) -> Option<io::Result<FormatRecorder>> + use<T> {
1149 let rewrap = formatter.rewrap_fn();
1150 let maybe_color = formatter.maybe_color();
1151 move |template| {
1152 let mut recorder = FormatRecorder::new(maybe_color);
1153 match template.format(&mut rewrap(&mut recorder)) {
1154 Ok(()) if recorder.data().is_empty() => None, Ok(()) => Some(Ok(recorder)),
1156 Err(e) => Some(Err(e)),
1157 }
1158 }
1159}