just playing with tangled
1// Copyright 2024 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use itertools::Itertools;
16use jj_cli::cli_util::CliRunner;
17use jj_cli::commit_templater::{
18 CommitTemplateBuildFnTable, CommitTemplateLanguage, CommitTemplateLanguageExtension,
19};
20use jj_cli::template_builder::TemplateLanguage;
21use jj_cli::template_parser::{self, TemplateParseError};
22use jj_cli::templater::TemplatePropertyExt as _;
23use jj_lib::backend::CommitId;
24use jj_lib::commit::Commit;
25use jj_lib::extensions_map::ExtensionsMap;
26use jj_lib::object_id::ObjectId;
27use jj_lib::repo::Repo;
28use jj_lib::revset::{
29 PartialSymbolResolver, RevsetExpression, RevsetResolutionError, SymbolResolverExtension,
30};
31use once_cell::sync::OnceCell;
32
33struct HexCounter;
34
35fn num_digits_in_id(id: &CommitId) -> i64 {
36 let mut count = 0;
37 for ch in id.hex().chars() {
38 if ch.is_ascii_digit() {
39 count += 1;
40 }
41 }
42 count
43}
44
45fn num_char_in_id(commit: Commit, ch_match: char) -> i64 {
46 let mut count = 0;
47 for ch in commit.id().hex().chars() {
48 if ch == ch_match {
49 count += 1;
50 }
51 }
52 count
53}
54
55#[derive(Default)]
56struct MostDigitsInId {
57 count: OnceCell<i64>,
58}
59
60impl MostDigitsInId {
61 fn count(&self, repo: &dyn Repo) -> i64 {
62 *self.count.get_or_init(|| {
63 RevsetExpression::all()
64 .evaluate_programmatic(repo)
65 .unwrap()
66 .iter()
67 .map(|id| num_digits_in_id(&id))
68 .max()
69 .unwrap_or(0)
70 })
71 }
72}
73
74#[derive(Default)]
75struct TheDigitestResolver {
76 cache: MostDigitsInId,
77}
78
79impl PartialSymbolResolver for TheDigitestResolver {
80 fn resolve_symbol(
81 &self,
82 repo: &dyn Repo,
83 symbol: &str,
84 ) -> Result<Option<Vec<CommitId>>, RevsetResolutionError> {
85 if symbol != "thedigitest" {
86 return Ok(None);
87 }
88
89 Ok(Some(
90 RevsetExpression::all()
91 .evaluate_programmatic(repo)
92 .map_err(|err| RevsetResolutionError::Other(err.into()))?
93 .iter()
94 .filter(|id| num_digits_in_id(id) == self.cache.count(repo))
95 .collect_vec(),
96 ))
97 }
98}
99
100struct TheDigitest;
101
102impl SymbolResolverExtension for TheDigitest {
103 fn new_resolvers<'a>(&self, _repo: &'a dyn Repo) -> Vec<Box<dyn PartialSymbolResolver + 'a>> {
104 vec![Box::<TheDigitestResolver>::default()]
105 }
106}
107
108impl CommitTemplateLanguageExtension for HexCounter {
109 fn build_fn_table<'repo>(&self) -> CommitTemplateBuildFnTable<'repo> {
110 type L<'repo> = CommitTemplateLanguage<'repo>;
111 let mut table = CommitTemplateBuildFnTable::empty();
112 table.commit_methods.insert(
113 "has_most_digits",
114 |language, _build_context, property, call| {
115 template_parser::expect_no_arguments(call)?;
116 let most_digits = language
117 .cache_extension::<MostDigitsInId>()
118 .unwrap()
119 .count(language.repo());
120 Ok(L::wrap_boolean(property.map(move |commit| {
121 num_digits_in_id(commit.id()) == most_digits
122 })))
123 },
124 );
125 table.commit_methods.insert(
126 "num_digits_in_id",
127 |_language, _build_context, property, call| {
128 template_parser::expect_no_arguments(call)?;
129 Ok(L::wrap_integer(
130 property.map(|commit| num_digits_in_id(commit.id())),
131 ))
132 },
133 );
134 table.commit_methods.insert(
135 "num_char_in_id",
136 |_language, _build_context, property, call| {
137 let [string_arg] = template_parser::expect_exact_arguments(call)?;
138 let char_arg =
139 template_parser::expect_string_literal_with(string_arg, |string, span| {
140 let chars: Vec<_> = string.chars().collect();
141 match chars[..] {
142 [ch] => Ok(ch),
143 _ => Err(TemplateParseError::expression(
144 "Expected singular character argument",
145 span,
146 )),
147 }
148 })?;
149
150 Ok(L::wrap_integer(
151 property.map(move |commit| num_char_in_id(commit, char_arg)),
152 ))
153 },
154 );
155
156 table
157 }
158
159 fn build_cache_extensions(&self, extensions: &mut ExtensionsMap) {
160 extensions.insert(MostDigitsInId::default());
161 }
162}
163
164fn main() -> std::process::ExitCode {
165 CliRunner::init()
166 .add_symbol_resolver_extension(Box::new(TheDigitest))
167 .add_commit_template_extension(Box::new(HexCounter))
168 .run()
169}