672 lines · 18.4 KB · Rust
RawDownload
// 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::rc::Rc;use std::{error, io, iter}; use jj_lib::backend::{Signature, Timestamp}; use crate::formatter::{FormatRecorder, Formatter, PlainTextFormatter};use crate::time_util; pub trait Template<C> {    fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()>;} /// Template that supports list-like behavior.pub trait ListTemplate<C>: Template<C> {    /// Concatenates items with the given separator.    fn join<'a>(self: Box<Self>, separator: Box<dyn Template<C> + 'a>) -> Box<dyn Template<C> + 'a>    where        Self: 'a,        C: 'a;     /// Upcasts to the template type.    fn into_template<'a>(self: Box<Self>) -> Box<dyn Template<C> + 'a>    where        Self: 'a;} pub trait IntoTemplate<'a, C> {    fn into_template(self) -> Box<dyn Template<C> + 'a>;} 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)    }} 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)    }} impl Template<()> for Signature {    fn format(&self, _: &(), formatter: &mut dyn Formatter) -> 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, "<")?;            write!(formatter.labeled("email"), "{}", self.email)?;            write!(formatter, ">")?;        }        Ok(())    }} impl Template<()> for String {    fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {        formatter.write_str(self)    }} impl Template<()> for &str {    fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {        formatter.write_str(self)    }} impl Template<()> for Timestamp {    fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {        match time_util::format_absolute_timestamp(self) {            Ok(formatted) => formatter.write_str(&formatted),            Err(err) => format_error_inline(formatter, &err),        }    }} #[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) -> Result<String, time_util::TimestampOutOfRange> {        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" {            Ok("less than a microsecond".to_owned())        } else {            Ok(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(())    }} impl Template<()> for Vec<String> {    fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {        format_joined(&(), formatter, self, " ")    }} impl Template<()> for bool {    fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {        formatter.write_str(if *self { "true" } else { "false" })    }} impl Template<()> for i64 {    fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {        write!(formatter, "{self}")    }} pub struct LabelTemplate<T, L> {    content: T,    labels: L,} impl<T, L> LabelTemplate<T, L> {    pub fn new<C>(content: T, labels: L) -> Self    where        T: Template<C>,        L: TemplateProperty<C, Output = Vec<String>>,    {        LabelTemplate { content, labels }    }} impl<C, T, L> Template<C> for LabelTemplate<T, L>where    T: Template<C>,    L: TemplateProperty<C, Output = Vec<String>>,{    fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {        let labels = match self.labels.extract(context) {            Ok(labels) => labels,            Err(err) => return err.format(&(), formatter),        };        for label in &labels {            formatter.push_label(label)?;        }        self.content.format(context, formatter)?;        for _label in &labels {            formatter.pop_label()?;        }        Ok(())    }} pub struct ConcatTemplate<T>(pub Vec<T>); impl<C, T: Template<C>> Template<C> for ConcatTemplate<T> {    fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {        for template in &self.0 {            template.format(context, 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<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)    }} /// 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<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<()> {        let mut content_recorders = self            .contents            .iter()            .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)),                }            })            .fuse();        if let Some(recorder) = content_recorders.next() {            recorder?.replay(formatter)?;        }        for recorder in content_recorders {            self.separator.format(context, formatter)?;            recorder?.replay(formatter)?;        }        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())    }} /// Prints the evaluation error as inline template output.impl Template<()> for TemplatePropertyError {    fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {        format_error_inline(formatter, &*self.0)    }} pub trait TemplateProperty<C> {    type Output;     fn extract(&self, context: &C) -> Result<Self::Output, TemplatePropertyError>;} impl<C, P: TemplateProperty<C> + ?Sized> TemplateProperty<C> for Box<P> {    type Output = <P as TemplateProperty<C>>::Output;     fn extract(&self, context: &C) -> Result<Self::Output, TemplatePropertyError> {        <P as TemplateProperty<C>>::extract(self, context)    }} impl<C, P: TemplateProperty<C>> TemplateProperty<C> for Option<P> {    type Output = Option<P::Output>;     fn extract(&self, context: &C) -> Result<Self::Output, TemplatePropertyError> {        self.as_ref()            .map(|property| property.extract(context))            .transpose()    }} // Implement TemplateProperty for tuplesmacro_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) -> Result<Self::Output, TemplatePropertyError> {                    Ok(($(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)} /// 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)    }} impl<C, O: Clone> TemplateProperty<C> for Literal<O> {    type Output = O;     fn extract(&self, _context: &C) -> Result<Self::Output, TemplatePropertyError> {        Ok(self.0.clone())    }} /// Adapter to turn closure into property.pub struct TemplatePropertyFn<F>(pub F); impl<C, O, F> TemplateProperty<C> for TemplatePropertyFn<F>where    F: Fn(&C) -> Result<O, TemplatePropertyError>,{    type Output = O;     fn extract(&self, context: &C) -> Result<Self::Output, TemplatePropertyError> {        (self.0)(context)    }} /// Adapter to extract context-less template value from property for displaying.pub struct FormattablePropertyTemplate<P> {    property: P,} impl<P> FormattablePropertyTemplate<P> {    pub fn new<C>(property: P) -> Self    where        P: TemplateProperty<C>,        P::Output: Template<()>,    {        FormattablePropertyTemplate { property }    }} impl<C, P> Template<C> for FormattablePropertyTemplate<P>where    P: TemplateProperty<C>,    P::Output: Template<()>,{    fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {        match self.property.extract(context) {            Ok(template) => template.format(&(), formatter),            Err(err) => err.format(&(), formatter),        }    }} 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))    }} /// 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) -> Result<Self::Output, TemplatePropertyError> {        let mut output = vec![];        self.template            .format(context, &mut PlainTextFormatter::new(&mut output))            .expect("write() to PlainTextFormatter should never fail");        Ok(String::from_utf8(output).map_err(|err| err.utf8_error())?)    }} /// Renders template property of list type with the given separator.////// Each list item will be formatted by the given `format_item()` function./// The separator takes a context of type `C`.pub struct ListPropertyTemplate<P, S, F> {    property: P,    separator: S,    format_item: F,} impl<P, S, F> ListPropertyTemplate<P, S, F> {    pub fn new<C, O>(property: P, separator: S, format_item: F) -> Self    where        P: TemplateProperty<C>,        P::Output: IntoIterator<Item = O>,        S: Template<C>,        F: Fn(&C, &mut dyn Formatter, O) -> io::Result<()>,    {        ListPropertyTemplate {            property,            separator,            format_item,        }    }} impl<C, O, P, S, F> Template<C> for ListPropertyTemplate<P, S, F>where    P: TemplateProperty<C>,    P::Output: IntoIterator<Item = O>,    S: Template<C>,    F: Fn(&C, &mut dyn Formatter, O) -> io::Result<()>,{    fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {        let contents = match self.property.extract(context) {            Ok(contents) => contents,            Err(err) => return err.format(&(), formatter),        };        format_joined_with(            context,            formatter,            contents,            &self.separator,            &self.format_item,        )    }} impl<C, O, P, S, F> ListTemplate<C> for ListPropertyTemplate<P, S, F>where    P: TemplateProperty<C>,    P::Output: IntoIterator<Item = O>,    S: Template<C>,    F: Fn(&C, &mut dyn Formatter, O) -> io::Result<()>,{    fn join<'a>(self: Box<Self>, separator: Box<dyn Template<C> + 'a>) -> Box<dyn Template<C> + 'a>    where        Self: 'a,        C: 'a,    {        // Once join()-ed, list-like API should be dropped. This is guaranteed by        // the return type.        Box::new(ListPropertyTemplate::new(            self.property,            separator,            self.format_item,        ))    }     fn into_template<'a>(self: Box<Self>) -> Box<dyn Template<C> + 'a>    where        Self: 'a,    {        self    }}

Lines 1–500 of 672.