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