// Copyright 2020 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. use std::cell::RefCell;use std::error;use std::fmt;use std::io;use std::io::Write;use std::iter;use std::rc::Rc; use bstr::BStr;use bstr::BString;use jj_lib::backend::Signature;use jj_lib::backend::Timestamp;use jj_lib::config::ConfigValue;use jj_lib::op_store::TimestampRange; use crate::formatter::FormatRecorder;use crate::formatter::Formatter;use crate::formatter::LabeledWriter;use crate::formatter::PlainTextFormatter;use crate::text_util;use crate::time_util; /// Represents printable type or compiled template containing placeholder value.pub trait Template { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()>;} /// Template that supports list-like behavior.pub trait ListTemplate: Template { /// Concatenates items with the given separator. fn join<'a>(self: Box<Self>, separator: Box<dyn Template + 'a>) -> Box<dyn Template + 'a> where Self: 'a; /// Upcasts to the template type. fn into_template<'a>(self: Box<Self>) -> Box<dyn Template + 'a> where Self: 'a;} impl<T: Template + ?Sized> Template for &T { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { <T as Template>::format(self, formatter) }} impl<T: Template + ?Sized> Template for Box<T> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { <T as Template>::format(self, formatter) }} // All optional printable types should be printable, and it's unlikely to// implement different formatting per type.impl<T: Template> Template for Option<T> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { self.as_ref().map_or(Ok(()), |t| t.format(formatter)) }} impl Template for BString { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { formatter.as_mut().write_all(self) }} impl Template for &BStr { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { formatter.as_mut().write_all(self) }} impl Template for ConfigValue { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { write!(formatter, "{self}") }} impl Template for Signature { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { write!(formatter.labeled("name"), "{}", self.name)?; if !self.name.is_empty() && !self.email.is_empty() { write!(formatter, " ")?; } if !self.email.is_empty() { write!(formatter, "<")?; let email = Email(self.email.clone()); email.format(formatter)?; write!(formatter, ">")?; } Ok(()) }} #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]#[serde(transparent)]pub struct Email(pub String); impl Template for Email { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { let (local, domain) = text_util::split_email(&self.0); write!(formatter.labeled("local"), "{local}")?; if let Some(domain) = domain { write!(formatter, "@")?; write!(formatter.labeled("domain"), "{domain}")?; } Ok(()) }} // In template language, an integer value is represented as i64. However, we use// usize here because it's more convenient to guarantee that the lower value is// bounded to 0.pub type SizeHint = (usize, Option<usize>); impl Template for String { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { write!(formatter, "{self}") }} impl Template for &str { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { write!(formatter, "{self}") }} impl Template for Timestamp { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { match time_util::format_absolute_timestamp(self) { Ok(formatted) => write!(formatter, "{formatted}"), Err(err) => formatter.handle_error(err.into()), } }} impl Template for TimestampRange { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { self.start.format(formatter)?; write!(formatter, " - ")?; self.end.format(formatter)?; Ok(()) }} impl Template for Vec<String> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { format_joined(formatter, self, " ") }} impl Template for bool { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { let repr = if *self { "true" } else { "false" }; write!(formatter, "{repr}") }} impl Template for i64 { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { write!(formatter, "{self}") }} pub struct LabelTemplate<T, L> { content: T, labels: L,} impl<T, L> LabelTemplate<T, L> { pub fn new(content: T, labels: L) -> Self where T: Template, L: TemplateProperty<Output = Vec<String>>, { LabelTemplate { content, labels } }} impl<T, L> Template for LabelTemplate<T, L>where T: Template, L: TemplateProperty<Output = Vec<String>>,{ fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { match self.labels.extract() { Ok(labels) => format_labeled(formatter, &self.content, &labels), Err(err) => formatter.handle_error(err), } }} pub struct RawEscapeSequenceTemplate<T>(pub T); impl<T: Template> Template for RawEscapeSequenceTemplate<T> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { let rewrap = formatter.rewrap_fn(); let mut raw_formatter = PlainTextFormatter::new(formatter.raw()?); self.0.format(&mut rewrap(&mut raw_formatter)) }} /// Renders contents in order, and returns the first non-empty output.pub struct CoalesceTemplate<T>(pub Vec<T>); impl<T: Template> Template for CoalesceTemplate<T> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { let Some((last, contents)) = self.0.split_last() else { return Ok(()); }; let record_non_empty = record_non_empty_fn(formatter); if let Some(recorder) = contents.iter().find_map(record_non_empty) { recorder?.replay(formatter.as_mut()) } else { last.format(formatter) // no need to capture the last content } }} pub struct ConcatTemplate<T>(pub Vec<T>); impl<T: Template> Template for ConcatTemplate<T> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { for template in &self.0 { template.format(formatter)?; } Ok(()) }} /// Renders the content to buffer, and transforms it without losing labels.pub struct ReformatTemplate<T, F> { content: T, reformat: F,} impl<T, F> ReformatTemplate<T, F> { pub fn new(content: T, reformat: F) -> Self where T: Template, F: Fn(&mut TemplateFormatter, &FormatRecorder) -> io::Result<()>, { ReformatTemplate { content, reformat } }} impl<T, F> Template for ReformatTemplate<T, F>where T: Template, F: Fn(&mut TemplateFormatter, &FormatRecorder) -> io::Result<()>,{ fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { let rewrap = formatter.rewrap_fn(); let mut recorder = FormatRecorder::new(); self.content.format(&mut rewrap(&mut recorder))?; (self.reformat)(formatter, &recorder) }} /// Like `ConcatTemplate`, but inserts a separator between non-empty templates.pub struct SeparateTemplate<S, T> { separator: S, contents: Vec<T>,} impl<S, T> SeparateTemplate<S, T> { pub fn new(separator: S, contents: Vec<T>) -> Self where S: Template, T: Template, { SeparateTemplate { separator, contents, } }} impl<S, T> Template for SeparateTemplate<S, T>where S: Template, T: Template,{ fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { let record_non_empty = record_non_empty_fn(formatter); let mut content_recorders = self.contents.iter().filter_map(record_non_empty).fuse(); if let Some(recorder) = content_recorders.next() { recorder?.replay(formatter.as_mut())?; } for recorder in content_recorders { self.separator.format(formatter)?; recorder?.replay(formatter.as_mut())?; } Ok(()) }} /// Wrapper around an error occurred during template evaluation.#[derive(Debug)]pub struct TemplatePropertyError(pub Box<dyn error::Error + Send + Sync>); // Implements conversion from any error type to support `expr?` in function// binding. This type doesn't implement `std::error::Error` instead.// https://github.com/dtolnay/anyhow/issues/25#issuecomment-544140480impl<E> From<E> for TemplatePropertyErrorwhere E: error::Error + Send + Sync + 'static,{ fn from(err: E) -> Self { TemplatePropertyError(err.into()) }} pub trait TemplateProperty { type Output; fn extract(&self) -> Result<Self::Output, TemplatePropertyError>;} impl<P: TemplateProperty + ?Sized> TemplateProperty for Box<P> { type Output = <P as TemplateProperty>::Output; fn extract(&self) -> Result<Self::Output, TemplatePropertyError> { <P as TemplateProperty>::extract(self) }} impl<P: TemplateProperty> TemplateProperty for Option<P> { type Output = Option<P::Output>; fn extract(&self) -> Result<Self::Output, TemplatePropertyError> { self.as_ref().map(|property| property.extract()).transpose() }} // Implement TemplateProperty for tuplesmacro_rules! tuple_impls { ($( ( $($n:tt $T:ident),+ ) )+) => { $( impl<$($T: TemplateProperty,)+> TemplateProperty for ($($T,)+) { type Output = ($($T::Output,)+); fn extract(&self) -> Result<Self::Output, TemplatePropertyError> { Ok(($(self.$n.extract()?,)+)) } } )+ }} tuple_impls! { (0 T0) (0 T0, 1 T1) (0 T0, 1 T1, 2 T2) (0 T0, 1 T1, 2 T2, 3 T3)} pub type BoxedTemplateProperty<'a, O> = Box<dyn TemplateProperty<Output = O> + 'a>;pub type BoxedSerializeProperty<'a> = BoxedTemplateProperty<'a, Box<dyn erased_serde::Serialize + 'a>>; /// `TemplateProperty` adapters that are useful when implementing methods.pub trait TemplatePropertyExt: TemplateProperty { /// Translates to a property that will apply fallible `function` to an /// extracted value. fn and_then<O, F>(self, function: F) -> TemplateFunction<Self, F> where Self: Sized, F: Fn(Self::Output) -> Result<O, TemplatePropertyError>, { TemplateFunction::new(self, function) } /// Translates to a property that will apply `function` to an extracted /// value, leaving `Err` untouched. fn map<O, F>(self, function: F) -> impl TemplateProperty<Output = O> where Self: Sized, F: Fn(Self::Output) -> O, { TemplateFunction::new(self, move |value| Ok(function(value))) } /// Translates to a property that will unwrap an extracted `Option` value /// of the specified `type_name`, mapping `None` to `Err`. fn try_unwrap<O>(self, type_name: &str) -> impl TemplateProperty<Output = O> where Self: TemplateProperty<Output = Option<O>> + Sized, { self.and_then(move |opt| { opt.ok_or_else(|| TemplatePropertyError(format!("No {type_name} available").into())) }) } /// Converts this property into boxed serialize property. fn into_serialize<'a>(self) -> BoxedSerializeProperty<'a> where Self: Sized + 'a, Self::Output: serde::Serialize, { Box::new(self.map(|value| Box::new(value) as Box<dyn erased_serde::Serialize>)) } /// Converts this property into `Template`. fn into_template<'a>(self) -> Box<dyn Template + 'a> where Self: Sized + 'a, Self::Output: Template, { Box::new(FormattablePropertyTemplate::new(self)) } /// Converts this property into boxed trait object. fn into_dyn<'a>(self) -> BoxedTemplateProperty<'a, Self::Output> where Self: Sized + 'a, { Box::new(self) } /// Converts this property into wrapped (or tagged) type. /// /// Use `W::wrap_property()` if the self type is known to be boxed. fn into_dyn_wrapped<'a, W>(self) -> W where Self: Sized + 'a, W: WrapTemplateProperty<'a, Self::Output>, { W::wrap_property(self.into_dyn()) }} impl<P: TemplateProperty + ?Sized> TemplatePropertyExt for P {} /// Wraps template property of type `O` in tagged type.////// This is basically `From<BoxedTemplateProperty<'a, O>>`, but is restricted to/// property types.#[diagnostic::on_unimplemented( message = "the template property of type `{O}` cannot be wrapped in `{Self}`")]pub trait WrapTemplateProperty<'a, O>: Sized { fn wrap_property(property: BoxedTemplateProperty<'a, O>) -> Self;} /// Adapter that wraps literal value in `TemplateProperty`.pub struct Literal<O>(pub O); impl<O: Template> Template for Literal<O> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { self.0.format(formatter) }} impl<O: Clone> TemplateProperty for Literal<O> { type Output = O; fn extract(&self) -> Result<Self::Output, TemplatePropertyError> { Ok(self.0.clone()) }} /// Adapter to extract template value from property for displaying.pub struct FormattablePropertyTemplate<P> { property: P,} impl<P> FormattablePropertyTemplate<P> { pub fn new(property: P) -> Self where P: TemplateProperty, P::Output: Template, { FormattablePropertyTemplate { property } }} impl<P> Template for FormattablePropertyTemplate<P>where P: TemplateProperty, P::Output: Template,{ fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { match self.property.extract() { Ok(template) => template.format(formatter), Err(err) => formatter.handle_error(err),Lines 1–500 of 925. Load more
// Copyright 2020 The Jujutsu Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::cell::RefCell; use std::error; use std::fmt; use std::io; use std::io::Write; use std::iter; use std::rc::Rc; use bstr::BStr; use bstr::BString; use jj_lib::backend::Signature; use jj_lib::backend::Timestamp; use jj_lib::config::ConfigValue; use jj_lib::op_store::TimestampRange; use crate::formatter::FormatRecorder; use crate::formatter::Formatter; use crate::formatter::LabeledWriter; use crate::formatter::PlainTextFormatter; use crate::text_util; use crate::time_util; /// Represents printable type or compiled template containing placeholder value. pub trait Template { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()>; } /// Template that supports list-like behavior. pub trait ListTemplate: Template { /// Concatenates items with the given separator. fn join<'a>(self: Box<Self>, separator: Box<dyn Template + 'a>) -> Box<dyn Template + 'a> where Self: 'a; /// Upcasts to the template type. fn into_template<'a>(self: Box<Self>) -> Box<dyn Template + 'a> where Self: 'a; } impl<T: Template + ?Sized> Template for &T { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { <T as Template>::format(self, formatter) } } impl<T: Template + ?Sized> Template for Box<T> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { <T as Template>::format(self, formatter) } } // All optional printable types should be printable, and it's unlikely to // implement different formatting per type. impl<T: Template> Template for Option<T> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { self.as_ref().map_or(Ok(()), |t| t.format(formatter)) } } impl Template for BString { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { formatter.as_mut().write_all(self) } } impl Template for &BStr { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { formatter.as_mut().write_all(self) } } impl Template for ConfigValue { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { write!(formatter, "{self}") } } impl Template for Signature { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { write!(formatter.labeled("name"), "{}", self.name)?; if !self.name.is_empty() && !self.email.is_empty() { write!(formatter, " ")?; } if !self.email.is_empty() { write!(formatter, "<")?; let email = Email(self.email.clone()); email.format(formatter)?; write!(formatter, ">")?; } Ok(()) } } #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] #[serde(transparent)] pub struct Email(pub String); impl Template for Email { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { let (local, domain) = text_util::split_email(&self.0); write!(formatter.labeled("local"), "{local}")?; if let Some(domain) = domain { write!(formatter, "@")?; write!(formatter.labeled("domain"), "{domain}")?; } Ok(()) } } // In template language, an integer value is represented as i64. However, we use // usize here because it's more convenient to guarantee that the lower value is // bounded to 0. pub type SizeHint = (usize, Option<usize>); impl Template for String { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { write!(formatter, "{self}") } } impl Template for &str { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { write!(formatter, "{self}") } } impl Template for Timestamp { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { match time_util::format_absolute_timestamp(self) { Ok(formatted) => write!(formatter, "{formatted}"), Err(err) => formatter.handle_error(err.into()), } } } impl Template for TimestampRange { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { self.start.format(formatter)?; write!(formatter, " - ")?; self.end.format(formatter)?; Ok(()) } } impl Template for Vec<String> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { format_joined(formatter, self, " ") } } impl Template for bool { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { let repr = if *self { "true" } else { "false" }; write!(formatter, "{repr}") } } impl Template for i64 { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { write!(formatter, "{self}") } } pub struct LabelTemplate<T, L> { content: T, labels: L, } impl<T, L> LabelTemplate<T, L> { pub fn new(content: T, labels: L) -> Self where T: Template, L: TemplateProperty<Output = Vec<String>>, { LabelTemplate { content, labels } } } impl<T, L> Template for LabelTemplate<T, L> where T: Template, L: TemplateProperty<Output = Vec<String>>, { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { match self.labels.extract() { Ok(labels) => format_labeled(formatter, &self.content, &labels), Err(err) => formatter.handle_error(err), } } } pub struct RawEscapeSequenceTemplate<T>(pub T); impl<T: Template> Template for RawEscapeSequenceTemplate<T> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { let rewrap = formatter.rewrap_fn(); let mut raw_formatter = PlainTextFormatter::new(formatter.raw()?); self.0.format(&mut rewrap(&mut raw_formatter)) } } /// Renders contents in order, and returns the first non-empty output. pub struct CoalesceTemplate<T>(pub Vec<T>); impl<T: Template> Template for CoalesceTemplate<T> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { let Some((last, contents)) = self.0.split_last() else { return Ok(()); }; let record_non_empty = record_non_empty_fn(formatter); if let Some(recorder) = contents.iter().find_map(record_non_empty) { recorder?.replay(formatter.as_mut()) } else { last.format(formatter) // no need to capture the last content } } } pub struct ConcatTemplate<T>(pub Vec<T>); impl<T: Template> Template for ConcatTemplate<T> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { for template in &self.0 { template.format(formatter)?; } Ok(()) } } /// Renders the content to buffer, and transforms it without losing labels. pub struct ReformatTemplate<T, F> { content: T, reformat: F, } impl<T, F> ReformatTemplate<T, F> { pub fn new(content: T, reformat: F) -> Self where T: Template, F: Fn(&mut TemplateFormatter, &FormatRecorder) -> io::Result<()>, { ReformatTemplate { content, reformat } } } impl<T, F> Template for ReformatTemplate<T, F> where T: Template, F: Fn(&mut TemplateFormatter, &FormatRecorder) -> io::Result<()>, { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { let rewrap = formatter.rewrap_fn(); let mut recorder = FormatRecorder::new(); self.content.format(&mut rewrap(&mut recorder))?; (self.reformat)(formatter, &recorder) } } /// Like `ConcatTemplate`, but inserts a separator between non-empty templates. pub struct SeparateTemplate<S, T> { separator: S, contents: Vec<T>, } impl<S, T> SeparateTemplate<S, T> { pub fn new(separator: S, contents: Vec<T>) -> Self where S: Template, T: Template, { SeparateTemplate { separator, contents, } } } impl<S, T> Template for SeparateTemplate<S, T> where S: Template, T: Template, { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { let record_non_empty = record_non_empty_fn(formatter); let mut content_recorders = self.contents.iter().filter_map(record_non_empty).fuse(); if let Some(recorder) = content_recorders.next() { recorder?.replay(formatter.as_mut())?; } for recorder in content_recorders { self.separator.format(formatter)?; recorder?.replay(formatter.as_mut())?; } Ok(()) } } /// Wrapper around an error occurred during template evaluation. #[derive(Debug)] pub struct TemplatePropertyError(pub Box<dyn error::Error + Send + Sync>); // Implements conversion from any error type to support `expr?` in function // binding. This type doesn't implement `std::error::Error` instead. // https://github.com/dtolnay/anyhow/issues/25#issuecomment-544140480 impl<E> From<E> for TemplatePropertyError where E: error::Error + Send + Sync + 'static, { fn from(err: E) -> Self { TemplatePropertyError(err.into()) } } pub trait TemplateProperty { type Output; fn extract(&self) -> Result<Self::Output, TemplatePropertyError>; } impl<P: TemplateProperty + ?Sized> TemplateProperty for Box<P> { type Output = <P as TemplateProperty>::Output; fn extract(&self) -> Result<Self::Output, TemplatePropertyError> { <P as TemplateProperty>::extract(self) } } impl<P: TemplateProperty> TemplateProperty for Option<P> { type Output = Option<P::Output>; fn extract(&self) -> Result<Self::Output, TemplatePropertyError> { self.as_ref().map(|property| property.extract()).transpose() } } // Implement TemplateProperty for tuples macro_rules! tuple_impls { ($( ( $($n:tt $T:ident),+ ) )+) => { $( impl<$($T: TemplateProperty,)+> TemplateProperty for ($($T,)+) { type Output = ($($T::Output,)+); fn extract(&self) -> Result<Self::Output, TemplatePropertyError> { Ok(($(self.$n.extract()?,)+)) } } )+ } } tuple_impls! { (0 T0) (0 T0, 1 T1) (0 T0, 1 T1, 2 T2) (0 T0, 1 T1, 2 T2, 3 T3) } pub type BoxedTemplateProperty<'a, O> = Box<dyn TemplateProperty<Output = O> + 'a>; pub type BoxedSerializeProperty<'a> = BoxedTemplateProperty<'a, Box<dyn erased_serde::Serialize + 'a>>; /// `TemplateProperty` adapters that are useful when implementing methods. pub trait TemplatePropertyExt: TemplateProperty { /// Translates to a property that will apply fallible `function` to an /// extracted value. fn and_then<O, F>(self, function: F) -> TemplateFunction<Self, F> where Self: Sized, F: Fn(Self::Output) -> Result<O, TemplatePropertyError>, { TemplateFunction::new(self, function) } /// Translates to a property that will apply `function` to an extracted /// value, leaving `Err` untouched. fn map<O, F>(self, function: F) -> impl TemplateProperty<Output = O> where Self: Sized, F: Fn(Self::Output) -> O, { TemplateFunction::new(self, move |value| Ok(function(value))) } /// Translates to a property that will unwrap an extracted `Option` value /// of the specified `type_name`, mapping `None` to `Err`. fn try_unwrap<O>(self, type_name: &str) -> impl TemplateProperty<Output = O> where Self: TemplateProperty<Output = Option<O>> + Sized, { self.and_then(move |opt| { opt.ok_or_else(|| TemplatePropertyError(format!("No {type_name} available").into())) }) } /// Converts this property into boxed serialize property. fn into_serialize<'a>(self) -> BoxedSerializeProperty<'a> where Self: Sized + 'a, Self::Output: serde::Serialize, { Box::new(self.map(|value| Box::new(value) as Box<dyn erased_serde::Serialize>)) } /// Converts this property into `Template`. fn into_template<'a>(self) -> Box<dyn Template + 'a> where Self: Sized + 'a, Self::Output: Template, { Box::new(FormattablePropertyTemplate::new(self)) } /// Converts this property into boxed trait object. fn into_dyn<'a>(self) -> BoxedTemplateProperty<'a, Self::Output> where Self: Sized + 'a, { Box::new(self) } /// Converts this property into wrapped (or tagged) type. /// /// Use `W::wrap_property()` if the self type is known to be boxed. fn into_dyn_wrapped<'a, W>(self) -> W where Self: Sized + 'a, W: WrapTemplateProperty<'a, Self::Output>, { W::wrap_property(self.into_dyn()) } } impl<P: TemplateProperty + ?Sized> TemplatePropertyExt for P {} /// Wraps template property of type `O` in tagged type. /// /// This is basically `From<BoxedTemplateProperty<'a, O>>`, but is restricted to /// property types. #[diagnostic::on_unimplemented( message = "the template property of type `{O}` cannot be wrapped in `{Self}`" )] pub trait WrapTemplateProperty<'a, O>: Sized { fn wrap_property(property: BoxedTemplateProperty<'a, O>) -> Self; } /// Adapter that wraps literal value in `TemplateProperty`. pub struct Literal<O>(pub O); impl<O: Template> Template for Literal<O> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { self.0.format(formatter) } } impl<O: Clone> TemplateProperty for Literal<O> { type Output = O; fn extract(&self) -> Result<Self::Output, TemplatePropertyError> { Ok(self.0.clone()) } } /// Adapter to extract template value from property for displaying. pub struct FormattablePropertyTemplate<P> { property: P, } impl<P> FormattablePropertyTemplate<P> { pub fn new(property: P) -> Self where P: TemplateProperty, P::Output: Template, { FormattablePropertyTemplate { property } } } impl<P> Template for FormattablePropertyTemplate<P> where P: TemplateProperty, P::Output: Template, { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { match self.property.extract() { Ok(template) => template.format(formatter), Err(err) => formatter.handle_error(err),