just playing with tangled
at diffedit3 169 lines 5.4 kB view raw
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}