forked from mirrors/jj
templater: add newtype to implement property over closure
Many template keywords and methods are one liners, and I think that's actually good because writing tests for templater would be more involved than for pure functions. This patch introduces a wrapper for such one-line functions, and migrates method parser to that. Some of the commit keyword structs can also be ported to this wrapper.
This commit is contained in:
parent
67eac2a455
commit
f1e6146d6d
2 changed files with 47 additions and 94 deletions
|
@ -24,12 +24,11 @@ use pest_derive::Parser;
|
|||
use crate::formatter::PlainTextFormatter;
|
||||
use crate::templater::{
|
||||
AuthorProperty, BranchProperty, ChangeIdProperty, CommitIdProperty, CommitOrChangeId,
|
||||
CommitOrChangeIdShort, CommitOrChangeIdShortestPrefixAndBrackets, CommitterProperty,
|
||||
ConditionalTemplate, ConflictProperty, DescriptionProperty, DivergentProperty,
|
||||
DynamicLabelTemplate, EmptyProperty, FormattablePropertyTemplate, GitHeadProperty,
|
||||
GitRefsProperty, HighlightPrefix, IdWithHighlightedPrefix, IsWorkingCopyProperty,
|
||||
LabelTemplate, ListTemplate, Literal, SignatureTimestamp, TagProperty, Template,
|
||||
TemplateFunction, TemplateProperty, WorkingCopiesProperty,
|
||||
CommitterProperty, ConditionalTemplate, ConflictProperty, DescriptionProperty,
|
||||
DivergentProperty, DynamicLabelTemplate, EmptyProperty, FormattablePropertyTemplate,
|
||||
GitHeadProperty, GitRefsProperty, IdWithHighlightedPrefix, IsWorkingCopyProperty,
|
||||
LabelTemplate, ListTemplate, Literal, TagProperty, Template, TemplateFunction,
|
||||
TemplateProperty, TemplatePropertyFn, WorkingCopiesProperty,
|
||||
};
|
||||
use crate::time_util;
|
||||
|
||||
|
@ -57,46 +56,6 @@ fn parse_string_literal(pair: Pair<Rule>) -> String {
|
|||
result
|
||||
}
|
||||
|
||||
struct StringFirstLine;
|
||||
|
||||
impl TemplateProperty<String> for StringFirstLine {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &String) -> Self::Output {
|
||||
context.lines().next().unwrap().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
struct SignatureName;
|
||||
|
||||
impl TemplateProperty<Signature> for SignatureName {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &Signature) -> Self::Output {
|
||||
context.name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
struct SignatureEmail;
|
||||
|
||||
impl TemplateProperty<Signature> for SignatureEmail {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &Signature) -> Self::Output {
|
||||
context.email.clone()
|
||||
}
|
||||
}
|
||||
|
||||
struct RelativeTimestampString;
|
||||
|
||||
impl TemplateProperty<Timestamp> for RelativeTimestampString {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &Timestamp) -> Self::Output {
|
||||
time_util::format_timestamp_relative_to_now(context)
|
||||
}
|
||||
}
|
||||
|
||||
enum Property<'a, I> {
|
||||
String(Box<dyn TemplateProperty<I, Output = String> + 'a>),
|
||||
Boolean(Box<dyn TemplateProperty<I, Output = bool> + 'a>),
|
||||
|
@ -181,9 +140,14 @@ fn parse_method_chain<'a, I: 'a>(
|
|||
}
|
||||
|
||||
fn parse_string_method<'a>(name: Pair<Rule>, _args: Pairs<Rule>) -> Property<'a, String> {
|
||||
fn wrap_fn<'a, O>(
|
||||
f: impl Fn(&String) -> O + 'a,
|
||||
) -> Box<dyn TemplateProperty<String, Output = O> + 'a> {
|
||||
Box::new(TemplatePropertyFn(f))
|
||||
}
|
||||
// TODO: validate arguments
|
||||
match name.as_str() {
|
||||
"first_line" => Property::String(Box::new(StringFirstLine)),
|
||||
"first_line" => Property::String(wrap_fn(|s| s.lines().next().unwrap().to_string())),
|
||||
name => panic!("no such string method: {name}"),
|
||||
}
|
||||
}
|
||||
|
@ -197,31 +161,48 @@ fn parse_commit_or_change_id_method<'a>(
|
|||
name: Pair<Rule>,
|
||||
_args: Pairs<Rule>,
|
||||
) -> Property<'a, CommitOrChangeId<'a>> {
|
||||
fn wrap_fn<'a, O>(
|
||||
f: impl Fn(&CommitOrChangeId<'a>) -> O + 'a,
|
||||
) -> Box<dyn TemplateProperty<CommitOrChangeId<'a>, Output = O> + 'a> {
|
||||
Box::new(TemplatePropertyFn(f))
|
||||
}
|
||||
// TODO: validate arguments
|
||||
match name.as_str() {
|
||||
"short" => Property::String(Box::new(CommitOrChangeIdShort)),
|
||||
"short" => Property::String(wrap_fn(|id| id.short())),
|
||||
"shortest_prefix_and_brackets" => {
|
||||
Property::String(Box::new(CommitOrChangeIdShortestPrefixAndBrackets))
|
||||
Property::String(wrap_fn(|id| id.shortest_prefix_and_brackets()))
|
||||
}
|
||||
"shortest_styled_prefix" => {
|
||||
Property::IdWithHighlightedPrefix(wrap_fn(|id| id.shortest_styled_prefix()))
|
||||
}
|
||||
"shortest_styled_prefix" => Property::IdWithHighlightedPrefix(Box::new(HighlightPrefix)),
|
||||
name => panic!("no such commit ID method: {name}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_signature_method<'a>(name: Pair<Rule>, _args: Pairs<Rule>) -> Property<'a, Signature> {
|
||||
fn wrap_fn<'a, O>(
|
||||
f: impl Fn(&Signature) -> O + 'a,
|
||||
) -> Box<dyn TemplateProperty<Signature, Output = O> + 'a> {
|
||||
Box::new(TemplatePropertyFn(f))
|
||||
}
|
||||
// TODO: validate arguments
|
||||
match name.as_str() {
|
||||
"name" => Property::String(Box::new(SignatureName)),
|
||||
"email" => Property::String(Box::new(SignatureEmail)),
|
||||
"timestamp" => Property::Timestamp(Box::new(SignatureTimestamp)),
|
||||
"name" => Property::String(wrap_fn(|signature| signature.name.clone())),
|
||||
"email" => Property::String(wrap_fn(|signature| signature.email.clone())),
|
||||
"timestamp" => Property::Timestamp(wrap_fn(|signature| signature.timestamp.clone())),
|
||||
name => panic!("no such commit ID method: {name}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_timestamp_method<'a>(name: Pair<Rule>, _args: Pairs<Rule>) -> Property<'a, Timestamp> {
|
||||
fn wrap_fn<'a, O>(
|
||||
f: impl Fn(&Timestamp) -> O + 'a,
|
||||
) -> Box<dyn TemplateProperty<Timestamp, Output = O> + 'a> {
|
||||
Box::new(TemplatePropertyFn(f))
|
||||
}
|
||||
// TODO: validate arguments
|
||||
match name.as_str() {
|
||||
"ago" => Property::String(Box::new(RelativeTimestampString)),
|
||||
"ago" => Property::String(wrap_fn(time_util::format_timestamp_relative_to_now)),
|
||||
name => panic!("no such timestamp method: {name}"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,6 +168,17 @@ impl<C, O: Clone> TemplateProperty<C> for Literal<O> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adapter to extract context-less template value from property for displaying.
|
||||
pub struct FormattablePropertyTemplate<P> {
|
||||
property: P,
|
||||
|
@ -494,7 +505,7 @@ impl CommitOrChangeId<'_> {
|
|||
hex
|
||||
}
|
||||
|
||||
fn shortest_prefix_and_brackets(&self) -> String {
|
||||
pub fn shortest_prefix_and_brackets(&self) -> String {
|
||||
let hex = self.hex();
|
||||
let (prefix, rest) = extract_entire_prefix_and_trimmed_tail(
|
||||
&hex,
|
||||
|
@ -586,35 +597,6 @@ impl Template<()> for IdWithHighlightedPrefix {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct HighlightPrefix;
|
||||
impl TemplateProperty<CommitOrChangeId<'_>> for HighlightPrefix {
|
||||
type Output = IdWithHighlightedPrefix;
|
||||
|
||||
fn extract(&self, context: &CommitOrChangeId) -> Self::Output {
|
||||
context.shortest_styled_prefix()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommitOrChangeIdShort;
|
||||
|
||||
impl TemplateProperty<CommitOrChangeId<'_>> for CommitOrChangeIdShort {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &CommitOrChangeId) -> Self::Output {
|
||||
context.short()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommitOrChangeIdShortestPrefixAndBrackets;
|
||||
|
||||
impl TemplateProperty<CommitOrChangeId<'_>> for CommitOrChangeIdShortestPrefixAndBrackets {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &CommitOrChangeId) -> Self::Output {
|
||||
context.shortest_prefix_and_brackets()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommitIdProperty<'a> {
|
||||
pub repo: RepoRef<'a>,
|
||||
}
|
||||
|
@ -645,16 +627,6 @@ impl<'a> TemplateProperty<Commit> for ChangeIdProperty<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct SignatureTimestamp;
|
||||
|
||||
impl TemplateProperty<Signature> for SignatureTimestamp {
|
||||
type Output = Timestamp;
|
||||
|
||||
fn extract(&self, context: &Signature) -> Self::Output {
|
||||
context.timestamp.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EmptyProperty<'a> {
|
||||
pub repo: RepoRef<'a>,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue