2022-11-26 23:57:50 +00:00
|
|
|
// Copyright 2020 The Jujutsu Authors
|
2020-12-12 08:00:42 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2021-04-07 06:05:16 +00:00
|
|
|
use std::io;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2023-02-18 08:42:43 +00:00
|
|
|
use jujutsu_lib::backend::{Signature, Timestamp};
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2023-03-02 07:56:59 +00:00
|
|
|
use crate::formatter::{FormatRecorder, Formatter, PlainTextFormatter};
|
2023-03-05 03:13:18 +00:00
|
|
|
use crate::time_util;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
|
|
|
pub trait Template<C> {
|
2021-06-02 22:50:08 +00:00
|
|
|
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()>;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2023-02-19 11:19:53 +00:00
|
|
|
pub trait IntoTemplate<'a, C> {
|
|
|
|
fn into_template(self) -> Box<dyn Template<C> + 'a>;
|
|
|
|
}
|
|
|
|
|
2023-03-06 12:14:07 +00:00
|
|
|
impl<C, T: Template<C> + ?Sized> Template<C> for &T {
|
|
|
|
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
|
|
|
|
<T as Template<C>>::format(self, context, formatter)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-23 10:42:57 +00:00
|
|
|
impl<C, T: Template<C> + ?Sized> Template<C> for Box<T> {
|
|
|
|
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
|
|
|
|
<T as Template<C>>::format(self, context, formatter)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-23 02:38:16 +00:00
|
|
|
impl Template<()> for Signature {
|
|
|
|
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
2023-01-23 04:11:29 +00:00
|
|
|
write!(formatter.labeled("name"), "{}", self.name)?;
|
2023-01-22 12:16:52 +00:00
|
|
|
write!(formatter, " <")?;
|
|
|
|
write!(formatter.labeled("email"), "{}", self.email)?;
|
|
|
|
write!(formatter, ">")?;
|
|
|
|
Ok(())
|
2023-01-23 02:38:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Template<()> for String {
|
|
|
|
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
|
|
|
formatter.write_str(self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-06 12:35:26 +00:00
|
|
|
impl Template<()> for &str {
|
|
|
|
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
|
|
|
formatter.write_str(self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-23 02:38:16 +00:00
|
|
|
impl Template<()> for Timestamp {
|
|
|
|
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
|
|
|
formatter.write_str(&time_util::format_absolute_timestamp(self))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-19 07:20:41 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct TimestampRange {
|
|
|
|
// Could be aliased to Range<Timestamp> if needed.
|
|
|
|
pub start: Timestamp,
|
|
|
|
pub end: Timestamp,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TimestampRange {
|
|
|
|
// TODO: Introduce duration type, and move formatting to it.
|
|
|
|
pub fn duration(&self) -> String {
|
|
|
|
let mut f = timeago::Formatter::new();
|
|
|
|
f.min_unit(timeago::TimeUnit::Microseconds).ago("");
|
|
|
|
let duration = time_util::format_duration(&self.start, &self.end, &f);
|
|
|
|
if duration == "now" {
|
|
|
|
"less than a microsecond".to_owned()
|
|
|
|
} else {
|
|
|
|
duration
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Template<()> for TimestampRange {
|
|
|
|
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
|
|
|
self.start.format(&(), formatter)?;
|
|
|
|
write!(formatter, " - ")?;
|
|
|
|
self.end.format(&(), formatter)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-07 09:19:50 +00:00
|
|
|
impl Template<()> for Vec<String> {
|
|
|
|
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
|
|
|
format_joined(&(), formatter, self, " ")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-23 02:38:16 +00:00
|
|
|
impl Template<()> for bool {
|
|
|
|
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
|
|
|
formatter.write_str(if *self { "true" } else { "false" })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-04 12:36:11 +00:00
|
|
|
impl Template<()> for i64 {
|
|
|
|
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
|
|
|
write!(formatter, "{self}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-26 11:00:52 +00:00
|
|
|
pub struct LabelTemplate<T, L> {
|
2023-01-23 10:42:57 +00:00
|
|
|
content: T,
|
2023-01-26 11:00:52 +00:00
|
|
|
labels: L,
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2023-01-26 11:00:52 +00:00
|
|
|
impl<T, L> LabelTemplate<T, L> {
|
|
|
|
pub fn new<C>(content: T, labels: L) -> Self
|
2023-01-23 10:42:57 +00:00
|
|
|
where
|
|
|
|
T: Template<C>,
|
2023-01-26 11:00:52 +00:00
|
|
|
L: TemplateProperty<C, Output = Vec<String>>,
|
2023-01-23 10:42:57 +00:00
|
|
|
{
|
2020-12-12 08:00:42 +00:00
|
|
|
LabelTemplate { content, labels }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-26 11:00:52 +00:00
|
|
|
impl<C, T, L> Template<C> for LabelTemplate<T, L>
|
2023-01-23 10:42:57 +00:00
|
|
|
where
|
|
|
|
T: Template<C>,
|
2023-01-26 11:00:52 +00:00
|
|
|
L: TemplateProperty<C, Output = Vec<String>>,
|
2023-01-23 10:42:57 +00:00
|
|
|
{
|
2021-06-02 22:50:08 +00:00
|
|
|
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
|
2023-01-26 11:00:52 +00:00
|
|
|
let labels = self.labels.extract(context);
|
2020-12-12 08:00:42 +00:00
|
|
|
for label in &labels {
|
2023-01-12 08:00:12 +00:00
|
|
|
formatter.push_label(label)?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2021-06-02 22:50:08 +00:00
|
|
|
self.content.format(context, formatter)?;
|
2020-12-12 08:00:42 +00:00
|
|
|
for _label in &labels {
|
2023-01-12 08:00:12 +00:00
|
|
|
formatter.pop_label()?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2021-04-07 06:05:16 +00:00
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-07 09:11:54 +00:00
|
|
|
pub struct ConcatTemplate<T>(pub Vec<T>);
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2023-03-07 09:11:54 +00:00
|
|
|
impl<C, T: Template<C>> Template<C> for ConcatTemplate<T> {
|
2021-06-02 22:50:08 +00:00
|
|
|
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
for template in &self.0 {
|
2021-06-02 22:50:08 +00:00
|
|
|
template.format(context, formatter)?
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2021-04-07 06:05:16 +00:00
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-05 03:13:18 +00:00
|
|
|
/// 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<C>(content: T, reformat: F) -> Self
|
|
|
|
where
|
|
|
|
T: Template<C>,
|
|
|
|
F: Fn(&C, &mut dyn Formatter, &FormatRecorder) -> io::Result<()>,
|
|
|
|
{
|
|
|
|
ReformatTemplate { content, reformat }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<C, T, F> Template<C> for ReformatTemplate<T, F>
|
|
|
|
where
|
|
|
|
T: Template<C>,
|
|
|
|
F: Fn(&C, &mut dyn Formatter, &FormatRecorder) -> io::Result<()>,
|
|
|
|
{
|
|
|
|
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
|
|
|
|
let mut recorder = FormatRecorder::new();
|
|
|
|
self.content.format(context, &mut recorder)?;
|
|
|
|
(self.reformat)(context, formatter, &recorder)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-07 09:11:54 +00:00
|
|
|
/// Like `ConcatTemplate`, but inserts a separator between non-empty templates.
|
2023-02-03 05:36:01 +00:00
|
|
|
pub struct SeparateTemplate<S, T> {
|
|
|
|
separator: S,
|
|
|
|
contents: Vec<T>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<S, T> SeparateTemplate<S, T> {
|
|
|
|
pub fn new<C>(separator: S, contents: Vec<T>) -> Self
|
|
|
|
where
|
|
|
|
S: Template<C>,
|
|
|
|
T: Template<C>,
|
|
|
|
{
|
|
|
|
SeparateTemplate {
|
|
|
|
separator,
|
|
|
|
contents,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<C, S, T> Template<C> for SeparateTemplate<S, T>
|
|
|
|
where
|
|
|
|
S: Template<C>,
|
|
|
|
T: Template<C>,
|
|
|
|
{
|
|
|
|
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
|
2023-03-02 07:56:59 +00:00
|
|
|
let mut content_recorders = self
|
2023-02-03 05:36:01 +00:00
|
|
|
.contents
|
|
|
|
.iter()
|
2023-03-02 07:56:59 +00:00
|
|
|
.filter_map(|template| {
|
|
|
|
let mut recorder = FormatRecorder::new();
|
|
|
|
match template.format(context, &mut recorder) {
|
|
|
|
Ok(()) if recorder.data().is_empty() => None, // omit empty content
|
|
|
|
Ok(()) => Some(Ok(recorder)),
|
|
|
|
Err(e) => Some(Err(e)),
|
|
|
|
}
|
|
|
|
})
|
2023-02-03 05:36:01 +00:00
|
|
|
.fuse();
|
2023-03-02 07:56:59 +00:00
|
|
|
if let Some(recorder) = content_recorders.next() {
|
|
|
|
recorder?.replay(formatter)?;
|
2023-02-03 05:36:01 +00:00
|
|
|
}
|
2023-03-02 07:56:59 +00:00
|
|
|
for recorder in content_recorders {
|
2023-02-03 05:36:01 +00:00
|
|
|
self.separator.format(context, formatter)?;
|
2023-03-02 07:56:59 +00:00
|
|
|
recorder?.replay(formatter)?;
|
2023-02-03 05:36:01 +00:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
templater: turn output parameter of TemplateProperty into associated type
When implementing FormattablePropertyTemplate, I tried a generic 'property: P'
first, and I couldn't figure out how to constrain the output type.
impl<C, O, P> Template<C> for FormattablePropertyTemplate<P>
where
P: TemplateProperty<C, O>, // 'O' isn't constrained by type
O: Template<()>,
According to the book, the problem is that we can add multiple implementations
of 'TemplateProperty<C, *>'. Since TemplateProperty is basically a function
to extract data from 'C', I think the output parameter shouldn't be freely
chosen.
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
With this change, I can express the type constraint as follows:
impl<C, P> Template<C> for FormattablePropertyTemplate<P>
where
P: TemplateProperty<C>,
P::Output: Template<()>,
2023-01-23 06:26:27 +00:00
|
|
|
pub trait TemplateProperty<C> {
|
|
|
|
type Output;
|
|
|
|
|
|
|
|
fn extract(&self, context: &C) -> Self::Output;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2023-01-23 10:42:57 +00:00
|
|
|
impl<C, P: TemplateProperty<C> + ?Sized> TemplateProperty<C> for Box<P> {
|
|
|
|
type Output = <P as TemplateProperty<C>>::Output;
|
|
|
|
|
|
|
|
fn extract(&self, context: &C) -> Self::Output {
|
|
|
|
<P as TemplateProperty<C>>::extract(self, context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-05 10:35:34 +00:00
|
|
|
impl<C, P: TemplateProperty<C>> TemplateProperty<C> for Option<P> {
|
|
|
|
type Output = Option<P::Output>;
|
|
|
|
|
|
|
|
fn extract(&self, context: &C) -> Self::Output {
|
|
|
|
self.as_ref().map(|property| property.extract(context))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-03 09:53:20 +00:00
|
|
|
// Implement TemplateProperty for tuples
|
|
|
|
macro_rules! tuple_impls {
|
|
|
|
($( ( $($n:tt $T:ident),+ ) )+) => {
|
|
|
|
$(
|
|
|
|
impl<C, $($T: TemplateProperty<C>,)+> TemplateProperty<C> for ($($T,)+) {
|
|
|
|
type Output = ($($T::Output,)+);
|
|
|
|
|
|
|
|
fn extract(&self, context: &C) -> Self::Output {
|
|
|
|
($(self.$n.extract(context),)+)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)+
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tuple_impls! {
|
|
|
|
(0 T0)
|
|
|
|
(0 T0, 1 T1)
|
|
|
|
(0 T0, 1 T1, 2 T2)
|
|
|
|
(0 T0, 1 T1, 2 T2, 3 T3)
|
|
|
|
}
|
|
|
|
|
2023-01-23 03:03:13 +00:00
|
|
|
/// Adapter to drop template context.
|
|
|
|
pub struct Literal<O>(pub O);
|
|
|
|
|
|
|
|
impl<C, O: Template<()>> Template<C> for Literal<O> {
|
|
|
|
fn format(&self, _context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
|
|
|
|
self.0.format(&(), formatter)
|
|
|
|
}
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
templater: turn output parameter of TemplateProperty into associated type
When implementing FormattablePropertyTemplate, I tried a generic 'property: P'
first, and I couldn't figure out how to constrain the output type.
impl<C, O, P> Template<C> for FormattablePropertyTemplate<P>
where
P: TemplateProperty<C, O>, // 'O' isn't constrained by type
O: Template<()>,
According to the book, the problem is that we can add multiple implementations
of 'TemplateProperty<C, *>'. Since TemplateProperty is basically a function
to extract data from 'C', I think the output parameter shouldn't be freely
chosen.
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
With this change, I can express the type constraint as follows:
impl<C, P> Template<C> for FormattablePropertyTemplate<P>
where
P: TemplateProperty<C>,
P::Output: Template<()>,
2023-01-23 06:26:27 +00:00
|
|
|
impl<C, O: Clone> TemplateProperty<C> for Literal<O> {
|
|
|
|
type Output = O;
|
|
|
|
|
2020-12-12 08:00:42 +00:00
|
|
|
fn extract(&self, _context: &C) -> O {
|
2023-01-23 03:03:13 +00:00
|
|
|
self.0.clone()
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-29 08:46:43 +00:00
|
|
|
/// Adapter to turn closure into property.
|
|
|
|
pub struct TemplatePropertyFn<F>(pub F);
|
|
|
|
|
|
|
|
impl<C, O, F: Fn(&C) -> O> TemplateProperty<C> for TemplatePropertyFn<F> {
|
|
|
|
type Output = O;
|
|
|
|
|
|
|
|
fn extract(&self, context: &C) -> Self::Output {
|
|
|
|
(self.0)(context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-22 23:02:46 +00:00
|
|
|
/// Adapter to extract context-less template value from property for displaying.
|
2023-01-23 10:42:57 +00:00
|
|
|
pub struct FormattablePropertyTemplate<P> {
|
|
|
|
property: P,
|
2023-01-22 23:02:46 +00:00
|
|
|
}
|
|
|
|
|
2023-01-23 10:42:57 +00:00
|
|
|
impl<P> FormattablePropertyTemplate<P> {
|
|
|
|
pub fn new<C>(property: P) -> Self
|
|
|
|
where
|
|
|
|
P: TemplateProperty<C>,
|
|
|
|
P::Output: Template<()>,
|
|
|
|
{
|
2023-01-22 23:02:46 +00:00
|
|
|
FormattablePropertyTemplate { property }
|
|
|
|
}
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2023-01-23 10:42:57 +00:00
|
|
|
impl<C, P> Template<C> for FormattablePropertyTemplate<P>
|
2023-01-22 23:02:46 +00:00
|
|
|
where
|
2023-01-23 10:42:57 +00:00
|
|
|
P: TemplateProperty<C>,
|
|
|
|
P::Output: Template<()>,
|
2023-01-22 23:02:46 +00:00
|
|
|
{
|
2021-06-02 22:50:08 +00:00
|
|
|
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
|
2023-01-22 23:02:46 +00:00
|
|
|
let template = self.property.extract(context);
|
|
|
|
template.format(&(), formatter)
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-19 10:56:06 +00:00
|
|
|
impl<'a, C: 'a, O> IntoTemplate<'a, C> for Box<dyn TemplateProperty<C, Output = O> + 'a>
|
|
|
|
where
|
|
|
|
O: Template<()> + 'a,
|
|
|
|
{
|
|
|
|
fn into_template(self) -> Box<dyn Template<C> + 'a> {
|
|
|
|
Box::new(FormattablePropertyTemplate::new(self))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-26 11:20:43 +00:00
|
|
|
/// Adapter to turn template back to string property.
|
|
|
|
pub struct PlainTextFormattedProperty<T> {
|
|
|
|
template: T,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> PlainTextFormattedProperty<T> {
|
|
|
|
pub fn new(template: T) -> Self {
|
|
|
|
PlainTextFormattedProperty { template }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<C, T: Template<C>> TemplateProperty<C> for PlainTextFormattedProperty<T> {
|
|
|
|
type Output = String;
|
|
|
|
|
|
|
|
fn extract(&self, context: &C) -> Self::Output {
|
|
|
|
let mut output = vec![];
|
|
|
|
self.template
|
|
|
|
.format(context, &mut PlainTextFormatter::new(&mut output))
|
|
|
|
.expect("write() to PlainTextFormatter should never fail");
|
|
|
|
// TODO: Use from_utf8_lossy() if we added template that embeds file content
|
|
|
|
String::from_utf8(output).expect("template output should be utf-8 bytes")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-06 13:02:39 +00:00
|
|
|
/// Renders a list of template properties with the given separator.
|
|
|
|
///
|
|
|
|
/// Each template property can be extracted as a context-less value, but
|
|
|
|
/// the separator takes a context of type `C`.
|
|
|
|
pub struct FormattablePropertyListTemplate<P, S> {
|
|
|
|
property: P,
|
|
|
|
separator: S,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<P, S> FormattablePropertyListTemplate<P, S> {
|
|
|
|
pub fn new<C>(property: P, separator: S) -> Self
|
|
|
|
where
|
|
|
|
P: TemplateProperty<C>,
|
|
|
|
P::Output: IntoIterator,
|
|
|
|
<P::Output as IntoIterator>::Item: Template<()>,
|
|
|
|
S: Template<C>,
|
|
|
|
{
|
|
|
|
FormattablePropertyListTemplate {
|
|
|
|
property,
|
|
|
|
separator,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<C, P, S> Template<C> for FormattablePropertyListTemplate<P, S>
|
|
|
|
where
|
|
|
|
P: TemplateProperty<C>,
|
|
|
|
P::Output: IntoIterator,
|
|
|
|
<P::Output as IntoIterator>::Item: Template<()>,
|
|
|
|
S: Template<C>,
|
|
|
|
{
|
|
|
|
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
|
|
|
|
let contents = self.property.extract(context);
|
2023-03-14 10:02:42 +00:00
|
|
|
format_joined_with(
|
|
|
|
context,
|
|
|
|
formatter,
|
|
|
|
contents,
|
|
|
|
&self.separator,
|
|
|
|
|_, formatter, item| item.format(&(), formatter),
|
|
|
|
)
|
2023-03-06 13:02:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-23 10:42:57 +00:00
|
|
|
pub struct ConditionalTemplate<P, T, U> {
|
|
|
|
pub condition: P,
|
|
|
|
pub true_template: T,
|
|
|
|
pub false_template: Option<U>,
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2023-01-23 10:42:57 +00:00
|
|
|
impl<P, T, U> ConditionalTemplate<P, T, U> {
|
|
|
|
pub fn new<C>(condition: P, true_template: T, false_template: Option<U>) -> Self
|
|
|
|
where
|
|
|
|
P: TemplateProperty<C, Output = bool>,
|
|
|
|
T: Template<C>,
|
|
|
|
U: Template<C>,
|
|
|
|
{
|
2020-12-12 08:00:42 +00:00
|
|
|
ConditionalTemplate {
|
|
|
|
condition,
|
|
|
|
true_template,
|
|
|
|
false_template,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-23 10:42:57 +00:00
|
|
|
impl<C, P, T, U> Template<C> for ConditionalTemplate<P, T, U>
|
|
|
|
where
|
|
|
|
P: TemplateProperty<C, Output = bool>,
|
|
|
|
T: Template<C>,
|
|
|
|
U: Template<C>,
|
|
|
|
{
|
2021-06-02 22:50:08 +00:00
|
|
|
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
if self.condition.extract(context) {
|
2021-06-02 22:50:08 +00:00
|
|
|
self.true_template.format(context, formatter)?;
|
2020-12-12 08:00:42 +00:00
|
|
|
} else if let Some(false_template) = &self.false_template {
|
2021-06-02 22:50:08 +00:00
|
|
|
false_template.format(context, formatter)?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2021-04-07 06:05:16 +00:00
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: If needed, add a ContextualTemplateFunction where the function also
|
|
|
|
// gets the context
|
2023-01-23 10:42:57 +00:00
|
|
|
pub struct TemplateFunction<P, F> {
|
|
|
|
pub property: P,
|
|
|
|
pub function: F,
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2023-01-23 10:42:57 +00:00
|
|
|
impl<P, F> TemplateFunction<P, F> {
|
2023-01-26 07:32:44 +00:00
|
|
|
pub fn new<C, O>(property: P, function: F) -> Self
|
2023-01-23 10:42:57 +00:00
|
|
|
where
|
|
|
|
P: TemplateProperty<C>,
|
|
|
|
F: Fn(P::Output) -> O,
|
|
|
|
{
|
2023-01-26 07:32:44 +00:00
|
|
|
TemplateFunction { property, function }
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-23 10:42:57 +00:00
|
|
|
impl<C, O, P, F> TemplateProperty<C> for TemplateFunction<P, F>
|
|
|
|
where
|
|
|
|
P: TemplateProperty<C>,
|
|
|
|
F: Fn(P::Output) -> O,
|
|
|
|
{
|
templater: turn output parameter of TemplateProperty into associated type
When implementing FormattablePropertyTemplate, I tried a generic 'property: P'
first, and I couldn't figure out how to constrain the output type.
impl<C, O, P> Template<C> for FormattablePropertyTemplate<P>
where
P: TemplateProperty<C, O>, // 'O' isn't constrained by type
O: Template<()>,
According to the book, the problem is that we can add multiple implementations
of 'TemplateProperty<C, *>'. Since TemplateProperty is basically a function
to extract data from 'C', I think the output parameter shouldn't be freely
chosen.
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
With this change, I can express the type constraint as follows:
impl<C, P> Template<C> for FormattablePropertyTemplate<P>
where
P: TemplateProperty<C>,
P::Output: Template<()>,
2023-01-23 06:26:27 +00:00
|
|
|
type Output = O;
|
|
|
|
|
|
|
|
fn extract(&self, context: &C) -> Self::Output {
|
2020-12-12 08:00:42 +00:00
|
|
|
(self.function)(self.property.extract(context))
|
|
|
|
}
|
|
|
|
}
|
2023-03-06 12:14:07 +00:00
|
|
|
|
|
|
|
pub fn format_joined<C, I, S>(
|
|
|
|
context: &C,
|
|
|
|
formatter: &mut dyn Formatter,
|
|
|
|
contents: I,
|
|
|
|
separator: S,
|
|
|
|
) -> io::Result<()>
|
|
|
|
where
|
|
|
|
I: IntoIterator,
|
|
|
|
I::Item: Template<C>,
|
|
|
|
S: Template<C>,
|
2023-03-14 10:02:42 +00:00
|
|
|
{
|
|
|
|
format_joined_with(
|
|
|
|
context,
|
|
|
|
formatter,
|
|
|
|
contents,
|
|
|
|
separator,
|
|
|
|
|context, formatter, item| item.format(context, formatter),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn format_joined_with<C, I, S, F>(
|
|
|
|
context: &C,
|
|
|
|
formatter: &mut dyn Formatter,
|
|
|
|
contents: I,
|
|
|
|
separator: S,
|
|
|
|
mut format_item: F,
|
|
|
|
) -> io::Result<()>
|
|
|
|
where
|
|
|
|
I: IntoIterator,
|
|
|
|
S: Template<C>,
|
|
|
|
F: FnMut(&C, &mut dyn Formatter, I::Item) -> io::Result<()>,
|
2023-03-06 12:14:07 +00:00
|
|
|
{
|
|
|
|
let mut contents_iter = contents.into_iter().fuse();
|
2023-03-14 10:02:42 +00:00
|
|
|
if let Some(item) = contents_iter.next() {
|
|
|
|
format_item(context, formatter, item)?;
|
2023-03-06 12:14:07 +00:00
|
|
|
}
|
2023-03-14 10:02:42 +00:00
|
|
|
for item in contents_iter {
|
2023-03-06 12:14:07 +00:00
|
|
|
separator.format(context, formatter)?;
|
2023-03-14 10:02:42 +00:00
|
|
|
format_item(context, formatter, item)?;
|
2023-03-06 12:14:07 +00:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|