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.
|
|
|
|
|
2023-01-02 23:34:54 +00:00
|
|
|
use jujutsu_lib::backend::{Signature, Timestamp};
|
2021-05-15 16:16:31 +00:00
|
|
|
use jujutsu_lib::commit::Commit;
|
2022-02-02 18:14:03 +00:00
|
|
|
use jujutsu_lib::op_store::WorkspaceId;
|
2021-05-15 16:16:31 +00:00
|
|
|
use jujutsu_lib::repo::RepoRef;
|
2021-03-14 17:46:35 +00:00
|
|
|
use pest::iterators::{Pair, Pairs};
|
2020-12-12 08:00:42 +00:00
|
|
|
use pest::Parser;
|
2022-09-22 04:52:04 +00:00
|
|
|
use pest_derive::Parser;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2021-06-02 22:50:08 +00:00
|
|
|
use crate::formatter::PlainTextFormatter;
|
2020-12-12 08:00:42 +00:00
|
|
|
use crate::templater::{
|
2023-01-19 03:17:17 +00:00
|
|
|
AuthorProperty, BranchProperty, ChangeIdProperty, CommitIdProperty, CommitOrChangeId,
|
2023-01-19 04:19:11 +00:00
|
|
|
CommitOrChangeIdShort, CommitOrChangeIdShortPrefixAndBrackets, CommitterProperty,
|
2023-01-23 03:03:13 +00:00
|
|
|
ConditionalTemplate, ConflictProperty, DescriptionProperty, DivergentProperty,
|
2023-01-22 23:02:46 +00:00
|
|
|
DynamicLabelTemplate, EmptyProperty, FormattablePropertyTemplate, GitRefsProperty,
|
|
|
|
IsGitHeadProperty, IsWorkingCopyProperty, LabelTemplate, ListTemplate, Literal,
|
|
|
|
SignatureTimestamp, TagProperty, Template, TemplateFunction, TemplateProperty,
|
|
|
|
WorkingCopiesProperty,
|
2020-12-12 08:00:42 +00:00
|
|
|
};
|
2023-01-14 13:40:04 +00:00
|
|
|
use crate::time_util;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
|
|
|
#[derive(Parser)]
|
|
|
|
#[grammar = "template.pest"]
|
|
|
|
pub struct TemplateParser;
|
|
|
|
|
|
|
|
fn parse_string_literal(pair: Pair<Rule>) -> String {
|
|
|
|
assert_eq!(pair.as_rule(), Rule::literal);
|
|
|
|
let mut result = String::new();
|
|
|
|
for part in pair.into_inner() {
|
|
|
|
match part.as_rule() {
|
|
|
|
Rule::raw_literal => {
|
|
|
|
result.push_str(part.as_str());
|
|
|
|
}
|
|
|
|
Rule::escape => match part.as_str().as_bytes()[1] as char {
|
|
|
|
'"' => result.push('"'),
|
|
|
|
'\\' => result.push('\\'),
|
|
|
|
'n' => result.push('\n'),
|
2022-12-15 02:30:06 +00:00
|
|
|
char => panic!("invalid escape: \\{char:?}"),
|
2020-12-12 08:00:42 +00:00
|
|
|
},
|
2022-12-15 02:30:06 +00:00
|
|
|
_ => panic!("unexpected part of string: {part:?}"),
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
result
|
|
|
|
}
|
|
|
|
|
|
|
|
struct StringFirstLine;
|
|
|
|
|
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 TemplateProperty<String> for StringFirstLine {
|
|
|
|
type Output = String;
|
|
|
|
|
|
|
|
fn extract(&self, context: &String) -> Self::Output {
|
2020-12-12 08:00:42 +00:00
|
|
|
context.lines().next().unwrap().to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct SignatureName;
|
|
|
|
|
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 TemplateProperty<Signature> for SignatureName {
|
|
|
|
type Output = String;
|
|
|
|
|
|
|
|
fn extract(&self, context: &Signature) -> Self::Output {
|
2020-12-12 08:00:42 +00:00
|
|
|
context.name.clone()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct SignatureEmail;
|
|
|
|
|
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 TemplateProperty<Signature> for SignatureEmail {
|
|
|
|
type Output = String;
|
|
|
|
|
|
|
|
fn extract(&self, context: &Signature) -> Self::Output {
|
2020-12-12 08:00:42 +00:00
|
|
|
context.email.clone()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-26 01:33:24 +00:00
|
|
|
struct RelativeTimestampString;
|
|
|
|
|
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 TemplateProperty<Timestamp> for RelativeTimestampString {
|
|
|
|
type Output = String;
|
|
|
|
|
|
|
|
fn extract(&self, context: &Timestamp) -> Self::Output {
|
2023-01-14 13:40:04 +00:00
|
|
|
time_util::format_timestamp_relative_to_now(context)
|
2021-04-26 21:54:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-07 02:04:08 +00:00
|
|
|
enum Property<'a, I> {
|
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
|
|
|
String(Box<dyn TemplateProperty<I, Output = String> + 'a>),
|
|
|
|
Boolean(Box<dyn TemplateProperty<I, Output = bool> + 'a>),
|
2023-01-23 06:51:01 +00:00
|
|
|
CommitOrChangeId(Box<dyn TemplateProperty<I, Output = CommitOrChangeId<'a>> + 'a>),
|
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
|
|
|
Signature(Box<dyn TemplateProperty<I, Output = Signature> + 'a>),
|
|
|
|
Timestamp(Box<dyn TemplateProperty<I, Output = Timestamp> + 'a>),
|
2023-01-07 02:04:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, I: 'a> Property<'a, I> {
|
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
|
|
|
fn after<C: 'a>(self, first: Box<dyn TemplateProperty<C, Output = I> + 'a>) -> Property<'a, C> {
|
2023-01-23 03:22:32 +00:00
|
|
|
fn chain<'a, C: 'a, I: 'a, O: 'a>(
|
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
|
|
|
first: Box<dyn TemplateProperty<C, Output = I> + 'a>,
|
|
|
|
second: Box<dyn TemplateProperty<I, Output = O> + 'a>,
|
|
|
|
) -> Box<dyn TemplateProperty<C, Output = O> + 'a> {
|
2023-01-23 10:42:57 +00:00
|
|
|
Box::new(TemplateFunction::new(first, move |value| {
|
|
|
|
second.extract(&value)
|
|
|
|
}))
|
2023-01-23 03:22:32 +00:00
|
|
|
}
|
|
|
|
match self {
|
|
|
|
Property::String(property) => Property::String(chain(first, property)),
|
|
|
|
Property::Boolean(property) => Property::Boolean(chain(first, property)),
|
2023-01-23 06:51:01 +00:00
|
|
|
Property::CommitOrChangeId(property) => {
|
|
|
|
Property::CommitOrChangeId(chain(first, property))
|
2023-01-23 03:22:32 +00:00
|
|
|
}
|
|
|
|
Property::Signature(property) => Property::Signature(chain(first, property)),
|
|
|
|
Property::Timestamp(property) => Property::Timestamp(chain(first, property)),
|
2023-01-07 02:04:08 +00:00
|
|
|
}
|
|
|
|
}
|
2023-01-22 23:02:46 +00:00
|
|
|
|
|
|
|
fn into_template(self) -> Box<dyn Template<I> + 'a> {
|
|
|
|
fn wrap<'a, I: 'a, O: Template<()> + 'a>(
|
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
|
|
|
property: Box<dyn TemplateProperty<I, Output = O> + 'a>,
|
2023-01-22 23:02:46 +00:00
|
|
|
) -> Box<dyn Template<I> + 'a> {
|
|
|
|
Box::new(FormattablePropertyTemplate::new(property))
|
|
|
|
}
|
|
|
|
match self {
|
|
|
|
Property::String(property) => wrap(property),
|
|
|
|
Property::Boolean(property) => wrap(property),
|
2023-01-23 06:51:01 +00:00
|
|
|
Property::CommitOrChangeId(property) => wrap(property),
|
2023-01-22 23:02:46 +00:00
|
|
|
Property::Signature(property) => wrap(property),
|
|
|
|
Property::Timestamp(property) => wrap(property),
|
|
|
|
}
|
|
|
|
}
|
2023-01-07 02:04:08 +00:00
|
|
|
}
|
|
|
|
|
2020-12-12 08:00:42 +00:00
|
|
|
fn parse_method_chain<'a, I: 'a>(
|
|
|
|
pair: Pair<Rule>,
|
|
|
|
input_property: Property<'a, I>,
|
2023-01-07 20:00:51 +00:00
|
|
|
) -> PropertyAndLabels<'a, I> {
|
2020-12-12 08:00:42 +00:00
|
|
|
assert_eq!(pair.as_rule(), Rule::maybe_method);
|
|
|
|
if pair.as_str().is_empty() {
|
2023-01-07 20:00:51 +00:00
|
|
|
PropertyAndLabels(input_property, vec![])
|
2020-12-12 08:00:42 +00:00
|
|
|
} else {
|
2023-01-23 03:22:32 +00:00
|
|
|
fn chain<'a, I: 'a, O: 'a>(
|
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
|
|
|
property: Box<dyn TemplateProperty<I, Output = O> + 'a>,
|
2023-01-23 03:22:32 +00:00
|
|
|
parse: impl FnOnce() -> PropertyAndLabels<'a, O>,
|
|
|
|
) -> (Property<'a, I>, Vec<String>) {
|
|
|
|
let PropertyAndLabels(next_method, labels) = parse();
|
|
|
|
(next_method.after(property), labels)
|
|
|
|
}
|
2020-12-12 08:00:42 +00:00
|
|
|
let method = pair.into_inner().next().unwrap();
|
2023-01-07 20:00:51 +00:00
|
|
|
let label = method
|
|
|
|
.clone()
|
|
|
|
.into_inner()
|
|
|
|
.next()
|
|
|
|
.unwrap()
|
2023-01-27 09:41:54 +00:00
|
|
|
.into_inner()
|
|
|
|
.next()
|
|
|
|
.unwrap()
|
2023-01-07 20:00:51 +00:00
|
|
|
.as_str()
|
|
|
|
.to_string();
|
|
|
|
let (property, mut labels) = match input_property {
|
2023-01-23 03:22:32 +00:00
|
|
|
Property::String(property) => chain(property, || parse_string_method(method)),
|
|
|
|
Property::Boolean(property) => chain(property, || parse_boolean_method(method)),
|
2023-01-23 06:51:01 +00:00
|
|
|
Property::CommitOrChangeId(property) => {
|
|
|
|
chain(property, || parse_commit_or_change_id_method(method))
|
2022-11-26 01:33:24 +00:00
|
|
|
}
|
2023-01-23 03:22:32 +00:00
|
|
|
Property::Signature(property) => chain(property, || parse_signature_method(method)),
|
|
|
|
Property::Timestamp(property) => chain(property, || parse_timestamp_method(method)),
|
2023-01-07 20:00:51 +00:00
|
|
|
};
|
|
|
|
labels.insert(0, label);
|
|
|
|
PropertyAndLabels(property, labels)
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-07 20:00:51 +00:00
|
|
|
fn parse_string_method<'a>(method: Pair<Rule>) -> PropertyAndLabels<'a, String> {
|
2020-12-12 08:00:42 +00:00
|
|
|
assert_eq!(method.as_rule(), Rule::method);
|
|
|
|
let mut inner = method.into_inner();
|
2023-01-27 09:41:54 +00:00
|
|
|
let func = inner.next().unwrap();
|
|
|
|
assert_eq!(func.as_rule(), Rule::function);
|
|
|
|
let name = func.into_inner().next().unwrap();
|
2020-12-12 08:00:42 +00:00
|
|
|
// TODO: validate arguments
|
|
|
|
|
|
|
|
let this_function = match name.as_str() {
|
|
|
|
"first_line" => Property::String(Box::new(StringFirstLine)),
|
2022-12-15 02:30:06 +00:00
|
|
|
name => panic!("no such string method: {name}"),
|
2020-12-12 08:00:42 +00:00
|
|
|
};
|
|
|
|
let chain_method = inner.last().unwrap();
|
|
|
|
parse_method_chain(chain_method, this_function)
|
|
|
|
}
|
|
|
|
|
2023-01-07 20:00:51 +00:00
|
|
|
fn parse_boolean_method<'a>(method: Pair<Rule>) -> PropertyAndLabels<'a, bool> {
|
2023-01-27 09:41:54 +00:00
|
|
|
assert_eq!(method.as_rule(), Rule::method);
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut inner = method.into_inner();
|
2023-01-27 09:41:54 +00:00
|
|
|
let func = inner.next().unwrap();
|
|
|
|
assert_eq!(func.as_rule(), Rule::function);
|
|
|
|
let name = func.into_inner().next().unwrap();
|
2020-12-12 08:00:42 +00:00
|
|
|
// TODO: validate arguments
|
|
|
|
|
|
|
|
panic!("no such boolean method: {}", name.as_str());
|
|
|
|
}
|
|
|
|
|
2023-01-21 05:38:42 +00:00
|
|
|
fn parse_commit_or_change_id_method<'a>(
|
2023-01-02 23:34:54 +00:00
|
|
|
method: Pair<Rule>,
|
2023-01-23 06:51:01 +00:00
|
|
|
) -> PropertyAndLabels<'a, CommitOrChangeId<'a>> {
|
2020-12-12 08:00:42 +00:00
|
|
|
assert_eq!(method.as_rule(), Rule::method);
|
|
|
|
let mut inner = method.into_inner();
|
2023-01-27 09:41:54 +00:00
|
|
|
let func = inner.next().unwrap();
|
|
|
|
assert_eq!(func.as_rule(), Rule::function);
|
|
|
|
let name = func.into_inner().next().unwrap();
|
2020-12-12 08:00:42 +00:00
|
|
|
// TODO: validate arguments
|
|
|
|
|
|
|
|
let this_function = match name.as_str() {
|
2023-01-23 07:43:56 +00:00
|
|
|
"short" => Property::String(Box::new(CommitOrChangeIdShort)),
|
2023-01-03 00:24:00 +00:00
|
|
|
"short_prefix_and_brackets" => {
|
2023-01-23 06:51:01 +00:00
|
|
|
Property::String(Box::new(CommitOrChangeIdShortPrefixAndBrackets))
|
2023-01-03 00:24:00 +00:00
|
|
|
}
|
2022-12-15 02:30:06 +00:00
|
|
|
name => panic!("no such commit ID method: {name}"),
|
2020-12-12 08:00:42 +00:00
|
|
|
};
|
|
|
|
let chain_method = inner.last().unwrap();
|
|
|
|
parse_method_chain(chain_method, this_function)
|
|
|
|
}
|
|
|
|
|
2023-01-07 20:00:51 +00:00
|
|
|
fn parse_signature_method<'a>(method: Pair<Rule>) -> PropertyAndLabels<'a, Signature> {
|
2020-12-12 08:00:42 +00:00
|
|
|
assert_eq!(method.as_rule(), Rule::method);
|
|
|
|
let mut inner = method.into_inner();
|
2023-01-27 09:41:54 +00:00
|
|
|
let func = inner.next().unwrap();
|
|
|
|
assert_eq!(func.as_rule(), Rule::function);
|
|
|
|
let name = func.into_inner().next().unwrap();
|
2020-12-12 08:00:42 +00:00
|
|
|
// TODO: validate arguments
|
|
|
|
|
|
|
|
let this_function: Property<'a, Signature> = match name.as_str() {
|
|
|
|
"name" => Property::String(Box::new(SignatureName)),
|
|
|
|
"email" => Property::String(Box::new(SignatureEmail)),
|
2022-11-26 01:33:24 +00:00
|
|
|
"timestamp" => Property::Timestamp(Box::new(SignatureTimestamp)),
|
2022-12-15 02:30:06 +00:00
|
|
|
name => panic!("no such commit ID method: {name}"),
|
2020-12-12 08:00:42 +00:00
|
|
|
};
|
|
|
|
let chain_method = inner.last().unwrap();
|
|
|
|
parse_method_chain(chain_method, this_function)
|
|
|
|
}
|
|
|
|
|
2023-01-07 20:00:51 +00:00
|
|
|
fn parse_timestamp_method<'a>(method: Pair<Rule>) -> PropertyAndLabels<'a, Timestamp> {
|
2022-11-26 01:33:24 +00:00
|
|
|
assert_eq!(method.as_rule(), Rule::method);
|
|
|
|
let mut inner = method.into_inner();
|
2023-01-27 09:41:54 +00:00
|
|
|
let func = inner.next().unwrap();
|
|
|
|
assert_eq!(func.as_rule(), Rule::function);
|
|
|
|
let name = func.into_inner().next().unwrap();
|
2022-11-26 01:33:24 +00:00
|
|
|
// TODO: validate arguments
|
|
|
|
|
|
|
|
let this_function = match name.as_str() {
|
|
|
|
"ago" => Property::String(Box::new(RelativeTimestampString)),
|
2022-12-15 02:30:06 +00:00
|
|
|
name => panic!("no such timestamp method: {name}"),
|
2022-11-26 01:33:24 +00:00
|
|
|
};
|
|
|
|
let chain_method = inner.last().unwrap();
|
|
|
|
parse_method_chain(chain_method, this_function)
|
|
|
|
}
|
|
|
|
|
2023-01-07 19:41:30 +00:00
|
|
|
struct PropertyAndLabels<'a, C>(Property<'a, C>, Vec<String>);
|
|
|
|
|
2022-02-02 18:14:03 +00:00
|
|
|
fn parse_commit_keyword<'a>(
|
|
|
|
repo: RepoRef<'a>,
|
|
|
|
workspace_id: &WorkspaceId,
|
|
|
|
pair: Pair<Rule>,
|
2023-01-07 19:41:30 +00:00
|
|
|
) -> PropertyAndLabels<'a, Commit> {
|
2020-12-12 08:00:42 +00:00
|
|
|
assert_eq!(pair.as_rule(), Rule::identifier);
|
|
|
|
let property = match pair.as_str() {
|
|
|
|
"description" => Property::String(Box::new(DescriptionProperty)),
|
2023-01-23 06:51:01 +00:00
|
|
|
"change_id" => Property::CommitOrChangeId(Box::new(ChangeIdProperty { repo })),
|
|
|
|
"commit_id" => Property::CommitOrChangeId(Box::new(CommitIdProperty { repo })),
|
2020-12-12 08:00:42 +00:00
|
|
|
"author" => Property::Signature(Box::new(AuthorProperty)),
|
|
|
|
"committer" => Property::Signature(Box::new(CommitterProperty)),
|
2022-09-18 21:46:12 +00:00
|
|
|
"working_copies" => Property::String(Box::new(WorkingCopiesProperty { repo })),
|
|
|
|
"current_working_copy" => Property::Boolean(Box::new(IsWorkingCopyProperty {
|
2022-02-02 18:14:03 +00:00
|
|
|
repo,
|
|
|
|
workspace_id: workspace_id.clone(),
|
|
|
|
})),
|
2021-07-15 08:31:48 +00:00
|
|
|
"branches" => Property::String(Box::new(BranchProperty { repo })),
|
|
|
|
"tags" => Property::String(Box::new(TagProperty { repo })),
|
2021-01-03 08:26:57 +00:00
|
|
|
"git_refs" => Property::String(Box::new(GitRefsProperty { repo })),
|
2021-11-29 05:33:37 +00:00
|
|
|
"is_git_head" => Property::Boolean(Box::new(IsGitHeadProperty::new(repo))),
|
2021-10-07 05:50:11 +00:00
|
|
|
"divergent" => Property::Boolean(Box::new(DivergentProperty::new(repo))),
|
2020-12-12 08:00:42 +00:00
|
|
|
"conflict" => Property::Boolean(Box::new(ConflictProperty)),
|
2023-01-14 10:43:23 +00:00
|
|
|
"empty" => Property::Boolean(Box::new(EmptyProperty { repo })),
|
2022-12-15 02:30:06 +00:00
|
|
|
name => panic!("unexpected identifier: {name}"),
|
2020-12-12 08:00:42 +00:00
|
|
|
};
|
2023-01-07 19:41:30 +00:00
|
|
|
PropertyAndLabels(property, vec![pair.as_str().to_string()])
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2021-04-11 16:13:00 +00:00
|
|
|
fn parse_boolean_commit_property<'a>(
|
|
|
|
repo: RepoRef<'a>,
|
2022-02-02 18:14:03 +00:00
|
|
|
workspace_id: &WorkspaceId,
|
2020-12-12 08:00:42 +00:00
|
|
|
pair: Pair<Rule>,
|
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
|
|
|
) -> Box<dyn TemplateProperty<Commit, Output = bool> + 'a> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut inner = pair.into_inner();
|
|
|
|
let pair = inner.next().unwrap();
|
|
|
|
let _method = inner.next().unwrap();
|
|
|
|
assert!(inner.next().is_none());
|
|
|
|
match pair.as_rule() {
|
2022-02-02 18:14:03 +00:00
|
|
|
Rule::identifier => match parse_commit_keyword(repo, workspace_id, pair.clone()).0 {
|
2020-12-12 08:00:42 +00:00
|
|
|
Property::Boolean(property) => property,
|
2023-01-23 10:42:57 +00:00
|
|
|
Property::String(property) => {
|
|
|
|
Box::new(TemplateFunction::new(property, |string| !string.is_empty()))
|
|
|
|
}
|
2022-12-15 02:30:06 +00:00
|
|
|
_ => panic!("cannot yet use this as boolean: {pair:?}"),
|
2020-12-12 08:00:42 +00:00
|
|
|
},
|
2022-12-15 02:30:06 +00:00
|
|
|
_ => panic!("cannot yet use this as boolean: {pair:?}"),
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-02 18:14:03 +00:00
|
|
|
fn parse_commit_term<'a>(
|
|
|
|
repo: RepoRef<'a>,
|
|
|
|
workspace_id: &WorkspaceId,
|
|
|
|
pair: Pair<Rule>,
|
|
|
|
) -> Box<dyn Template<Commit> + 'a> {
|
2020-12-12 08:00:42 +00:00
|
|
|
assert_eq!(pair.as_rule(), Rule::term);
|
|
|
|
if pair.as_str().is_empty() {
|
2023-01-23 03:03:13 +00:00
|
|
|
Box::new(Literal(String::new()))
|
2020-12-12 08:00:42 +00:00
|
|
|
} else {
|
|
|
|
let mut inner = pair.into_inner();
|
|
|
|
let expr = inner.next().unwrap();
|
|
|
|
let maybe_method = inner.next().unwrap();
|
|
|
|
assert!(inner.next().is_none());
|
|
|
|
match expr.as_rule() {
|
|
|
|
Rule::literal => {
|
|
|
|
let text = parse_string_literal(expr);
|
|
|
|
if maybe_method.as_str().is_empty() {
|
2023-01-23 03:03:13 +00:00
|
|
|
Box::new(Literal(text))
|
2020-12-12 08:00:42 +00:00
|
|
|
} else {
|
2023-01-23 03:03:13 +00:00
|
|
|
let input_property = Property::String(Box::new(Literal(text)));
|
2023-01-07 20:00:51 +00:00
|
|
|
let PropertyAndLabels(property, method_labels) =
|
|
|
|
parse_method_chain(maybe_method, input_property);
|
2023-01-22 23:02:46 +00:00
|
|
|
Box::new(LabelTemplate::new(property.into_template(), method_labels))
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Rule::identifier => {
|
2023-01-07 20:00:51 +00:00
|
|
|
let PropertyAndLabels(term_property, keyword_labels) =
|
2023-01-07 19:41:30 +00:00
|
|
|
parse_commit_keyword(repo, workspace_id, expr);
|
2023-01-07 20:00:51 +00:00
|
|
|
let PropertyAndLabels(property, method_labels) =
|
|
|
|
parse_method_chain(maybe_method, term_property);
|
|
|
|
let mut labels = keyword_labels;
|
|
|
|
labels.extend(method_labels);
|
2023-01-22 23:02:46 +00:00
|
|
|
Box::new(LabelTemplate::new(property.into_template(), labels))
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
Rule::function => {
|
|
|
|
let mut inner = expr.into_inner();
|
|
|
|
let name = inner.next().unwrap().as_str();
|
|
|
|
match name {
|
|
|
|
"label" => {
|
|
|
|
let label_pair = inner.next().unwrap();
|
|
|
|
let label_template = parse_commit_template_rule(
|
|
|
|
repo,
|
2022-02-02 18:14:03 +00:00
|
|
|
workspace_id,
|
2020-12-12 08:00:42 +00:00
|
|
|
label_pair.into_inner().next().unwrap(),
|
|
|
|
);
|
|
|
|
let arg_template = match inner.next() {
|
|
|
|
None => panic!("label() requires two arguments"),
|
|
|
|
Some(pair) => pair,
|
|
|
|
};
|
|
|
|
if inner.next().is_some() {
|
|
|
|
panic!("label() accepts only two arguments")
|
|
|
|
}
|
|
|
|
let content: Box<dyn Template<Commit> + 'a> =
|
2022-02-02 18:14:03 +00:00
|
|
|
parse_commit_template_rule(repo, workspace_id, arg_template);
|
2023-01-07 19:14:01 +00:00
|
|
|
let get_labels = move |commit: &Commit| -> Vec<String> {
|
2022-11-06 05:00:21 +00:00
|
|
|
let mut buf = vec![];
|
|
|
|
let mut formatter = PlainTextFormatter::new(&mut buf);
|
|
|
|
label_template.format(commit, &mut formatter).unwrap();
|
2023-01-07 19:14:01 +00:00
|
|
|
String::from_utf8(buf)
|
|
|
|
.unwrap()
|
|
|
|
.split_whitespace()
|
|
|
|
.map(ToString::to_string)
|
|
|
|
.collect()
|
2020-12-12 08:00:42 +00:00
|
|
|
};
|
2023-01-23 10:42:57 +00:00
|
|
|
Box::new(DynamicLabelTemplate::new(content, get_labels))
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
"if" => {
|
|
|
|
let condition_pair = inner.next().unwrap();
|
|
|
|
let condition_template = condition_pair.into_inner().next().unwrap();
|
2022-02-02 18:14:03 +00:00
|
|
|
let condition =
|
|
|
|
parse_boolean_commit_property(repo, workspace_id, condition_template);
|
2020-12-12 08:00:42 +00:00
|
|
|
|
|
|
|
let true_template = match inner.next() {
|
|
|
|
None => panic!("if() requires at least two arguments"),
|
2022-02-02 18:14:03 +00:00
|
|
|
Some(pair) => parse_commit_template_rule(repo, workspace_id, pair),
|
2020-12-12 08:00:42 +00:00
|
|
|
};
|
2021-02-27 06:45:28 +00:00
|
|
|
let false_template = inner
|
|
|
|
.next()
|
2022-02-02 18:14:03 +00:00
|
|
|
.map(|pair| parse_commit_template_rule(repo, workspace_id, pair));
|
2020-12-12 08:00:42 +00:00
|
|
|
if inner.next().is_some() {
|
|
|
|
panic!("if() accepts at most three arguments")
|
|
|
|
}
|
|
|
|
Box::new(ConditionalTemplate::new(
|
|
|
|
condition,
|
|
|
|
true_template,
|
|
|
|
false_template,
|
|
|
|
))
|
|
|
|
}
|
2022-12-15 02:30:06 +00:00
|
|
|
name => panic!("function {name} not implemented"),
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
2022-12-15 02:30:06 +00:00
|
|
|
other => panic!("unexpected term: {other:?}"),
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-11 16:13:00 +00:00
|
|
|
fn parse_commit_template_rule<'a>(
|
|
|
|
repo: RepoRef<'a>,
|
2022-02-02 18:14:03 +00:00
|
|
|
workspace_id: &WorkspaceId,
|
2020-12-12 08:00:42 +00:00
|
|
|
pair: Pair<Rule>,
|
|
|
|
) -> Box<dyn Template<Commit> + 'a> {
|
|
|
|
match pair.as_rule() {
|
|
|
|
Rule::template => {
|
|
|
|
let mut inner = pair.into_inner();
|
2022-02-02 18:14:03 +00:00
|
|
|
let formatter = parse_commit_template_rule(repo, workspace_id, inner.next().unwrap());
|
2020-12-12 08:00:42 +00:00
|
|
|
assert!(inner.next().is_none());
|
|
|
|
formatter
|
|
|
|
}
|
2022-02-02 18:14:03 +00:00
|
|
|
Rule::term => parse_commit_term(repo, workspace_id, pair),
|
2020-12-12 08:00:42 +00:00
|
|
|
Rule::list => {
|
|
|
|
let mut formatters: Vec<Box<dyn Template<Commit>>> = vec![];
|
|
|
|
for inner_pair in pair.into_inner() {
|
2022-02-02 18:14:03 +00:00
|
|
|
formatters.push(parse_commit_template_rule(repo, workspace_id, inner_pair));
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
Box::new(ListTemplate(formatters))
|
|
|
|
}
|
2023-01-23 03:03:13 +00:00
|
|
|
_ => Box::new(Literal(String::new())),
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-11 16:13:00 +00:00
|
|
|
pub fn parse_commit_template<'a>(
|
|
|
|
repo: RepoRef<'a>,
|
2022-02-02 18:14:03 +00:00
|
|
|
workspace_id: &WorkspaceId,
|
2020-12-12 08:00:42 +00:00
|
|
|
template_text: &str,
|
|
|
|
) -> Box<dyn Template<Commit> + 'a> {
|
|
|
|
let mut pairs: Pairs<Rule> = TemplateParser::parse(Rule::template, template_text).unwrap();
|
|
|
|
|
|
|
|
let first_pair = pairs.next().unwrap();
|
|
|
|
assert!(pairs.next().is_none());
|
|
|
|
|
2022-11-13 23:05:07 +00:00
|
|
|
assert_eq!(
|
|
|
|
first_pair.as_span().end(),
|
|
|
|
template_text.len(),
|
2021-10-11 19:52:57 +00:00
|
|
|
"failed to parse template past position {}",
|
|
|
|
first_pair.as_span().end()
|
|
|
|
);
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-02-02 18:14:03 +00:00
|
|
|
parse_commit_template_rule(repo, workspace_id, first_pair)
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|