mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-16 09:11:55 +00:00
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::formatter::PlainTextFormatter;
|
||||||
use crate::templater::{
|
use crate::templater::{
|
||||||
AuthorProperty, BranchProperty, ChangeIdProperty, CommitIdProperty, CommitOrChangeId,
|
AuthorProperty, BranchProperty, ChangeIdProperty, CommitIdProperty, CommitOrChangeId,
|
||||||
CommitOrChangeIdShort, CommitOrChangeIdShortestPrefixAndBrackets, CommitterProperty,
|
CommitterProperty, ConditionalTemplate, ConflictProperty, DescriptionProperty,
|
||||||
ConditionalTemplate, ConflictProperty, DescriptionProperty, DivergentProperty,
|
DivergentProperty, DynamicLabelTemplate, EmptyProperty, FormattablePropertyTemplate,
|
||||||
DynamicLabelTemplate, EmptyProperty, FormattablePropertyTemplate, GitHeadProperty,
|
GitHeadProperty, GitRefsProperty, IdWithHighlightedPrefix, IsWorkingCopyProperty,
|
||||||
GitRefsProperty, HighlightPrefix, IdWithHighlightedPrefix, IsWorkingCopyProperty,
|
LabelTemplate, ListTemplate, Literal, TagProperty, Template, TemplateFunction,
|
||||||
LabelTemplate, ListTemplate, Literal, SignatureTimestamp, TagProperty, Template,
|
TemplateProperty, TemplatePropertyFn, WorkingCopiesProperty,
|
||||||
TemplateFunction, TemplateProperty, WorkingCopiesProperty,
|
|
||||||
};
|
};
|
||||||
use crate::time_util;
|
use crate::time_util;
|
||||||
|
|
||||||
|
@ -57,46 +56,6 @@ fn parse_string_literal(pair: Pair<Rule>) -> String {
|
||||||
result
|
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> {
|
enum Property<'a, I> {
|
||||||
String(Box<dyn TemplateProperty<I, Output = String> + 'a>),
|
String(Box<dyn TemplateProperty<I, Output = String> + 'a>),
|
||||||
Boolean(Box<dyn TemplateProperty<I, Output = bool> + '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 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
|
// TODO: validate arguments
|
||||||
match name.as_str() {
|
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}"),
|
name => panic!("no such string method: {name}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,31 +161,48 @@ fn parse_commit_or_change_id_method<'a>(
|
||||||
name: Pair<Rule>,
|
name: Pair<Rule>,
|
||||||
_args: Pairs<Rule>,
|
_args: Pairs<Rule>,
|
||||||
) -> Property<'a, CommitOrChangeId<'a>> {
|
) -> 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
|
// TODO: validate arguments
|
||||||
match name.as_str() {
|
match name.as_str() {
|
||||||
"short" => Property::String(Box::new(CommitOrChangeIdShort)),
|
"short" => Property::String(wrap_fn(|id| id.short())),
|
||||||
"shortest_prefix_and_brackets" => {
|
"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}"),
|
name => panic!("no such commit ID method: {name}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_signature_method<'a>(name: Pair<Rule>, _args: Pairs<Rule>) -> Property<'a, Signature> {
|
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
|
// TODO: validate arguments
|
||||||
match name.as_str() {
|
match name.as_str() {
|
||||||
"name" => Property::String(Box::new(SignatureName)),
|
"name" => Property::String(wrap_fn(|signature| signature.name.clone())),
|
||||||
"email" => Property::String(Box::new(SignatureEmail)),
|
"email" => Property::String(wrap_fn(|signature| signature.email.clone())),
|
||||||
"timestamp" => Property::Timestamp(Box::new(SignatureTimestamp)),
|
"timestamp" => Property::Timestamp(wrap_fn(|signature| signature.timestamp.clone())),
|
||||||
name => panic!("no such commit ID method: {name}"),
|
name => panic!("no such commit ID method: {name}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_timestamp_method<'a>(name: Pair<Rule>, _args: Pairs<Rule>) -> Property<'a, Timestamp> {
|
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
|
// TODO: validate arguments
|
||||||
match name.as_str() {
|
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}"),
|
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.
|
/// Adapter to extract context-less template value from property for displaying.
|
||||||
pub struct FormattablePropertyTemplate<P> {
|
pub struct FormattablePropertyTemplate<P> {
|
||||||
property: P,
|
property: P,
|
||||||
|
@ -494,7 +505,7 @@ impl CommitOrChangeId<'_> {
|
||||||
hex
|
hex
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shortest_prefix_and_brackets(&self) -> String {
|
pub fn shortest_prefix_and_brackets(&self) -> String {
|
||||||
let hex = self.hex();
|
let hex = self.hex();
|
||||||
let (prefix, rest) = extract_entire_prefix_and_trimmed_tail(
|
let (prefix, rest) = extract_entire_prefix_and_trimmed_tail(
|
||||||
&hex,
|
&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 struct CommitIdProperty<'a> {
|
||||||
pub repo: RepoRef<'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 struct EmptyProperty<'a> {
|
||||||
pub repo: RepoRef<'a>,
|
pub repo: RepoRef<'a>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue