Martin von Zweigbergkexamples: use Revset::stream()9be2a837 months agoHistory
208 lines · 6.5 KB · Rust
RawDownload
// Copyright 2024 The Jujutsu Authors//// 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. use std::sync::Arc; use futures::TryStreamExt as _;use jj_cli::cli_util::CliRunner;use jj_cli::commit_templater::CommitTemplateBuildFnTable;use jj_cli::commit_templater::CommitTemplateLanguageExtension;use jj_cli::template_parser;use jj_cli::template_parser::TemplateParseError;use jj_cli::templater::TemplatePropertyExt as _;use jj_lib::backend::CommitId;use jj_lib::commit::Commit;use jj_lib::extensions_map::ExtensionsMap;use jj_lib::object_id::ObjectId as _;use jj_lib::repo::Repo;use jj_lib::revset::FunctionCallNode;use jj_lib::revset::LoweringContext;use jj_lib::revset::PartialSymbolResolver;use jj_lib::revset::RevsetDiagnostics;use jj_lib::revset::RevsetExpression;use jj_lib::revset::RevsetFilterExtension;use jj_lib::revset::RevsetFilterPredicate;use jj_lib::revset::RevsetParseError;use jj_lib::revset::RevsetResolutionError;use jj_lib::revset::SymbolResolverExtension;use jj_lib::revset::UserRevsetExpression;use once_cell::sync::OnceCell;use pollster::FutureExt as _; struct HexCounter; fn num_digits_in_id(id: &CommitId) -> i64 {    let mut count = 0;    for ch in id.hex().chars() {        if ch.is_ascii_digit() {            count += 1;        }    }    count} fn num_char_in_id(commit: Commit, ch_match: char) -> i64 {    let mut count = 0;    for ch in commit.id().hex().chars() {        if ch == ch_match {            count += 1;        }    }    count} #[derive(Default)]struct MostDigitsInId {    count: OnceCell<i64>,} impl MostDigitsInId {    fn count(&self, repo: &dyn Repo) -> i64 {        *self.count.get_or_init(|| {            RevsetExpression::all()                .evaluate(repo)                .unwrap()                .stream()                .try_collect::<Vec<_>>()                .block_on()                .unwrap()                .iter()                .map(num_digits_in_id)                .max()                .unwrap_or(0)        })    }} #[derive(Default)]struct TheDigitestResolver {    cache: MostDigitsInId,} impl PartialSymbolResolver for TheDigitestResolver {    fn resolve_symbol(        &self,        repo: &dyn Repo,        symbol: &str,    ) -> Result<Option<CommitId>, RevsetResolutionError> {        if symbol != "thedigitest" {            return Ok(None);        }         Ok(RevsetExpression::all()            .evaluate(repo)            .map_err(|err| RevsetResolutionError::Other(err.into()))?            .stream()            .try_collect::<Vec<_>>()            .block_on()            .unwrap()            .iter()            .find(|id| num_digits_in_id(id) == self.cache.count(repo))            .cloned())    }} struct TheDigitest; impl SymbolResolverExtension for TheDigitest {    fn new_resolvers<'a>(&self, _repo: &'a dyn Repo) -> Vec<Box<dyn PartialSymbolResolver + 'a>> {        vec![Box::<TheDigitestResolver>::default()]    }} impl CommitTemplateLanguageExtension for HexCounter {    fn build_fn_table<'repo>(&self) -> CommitTemplateBuildFnTable<'repo> {        let mut table = CommitTemplateBuildFnTable::empty();        table.commit_methods.insert(            "has_most_digits",            |language, _diagnostics, _build_context, property, call| {                call.expect_no_arguments()?;                let most_digits = language                    .cache_extension::<MostDigitsInId>()                    .unwrap()                    .count(language.repo());                let out_property =                    property.map(move |commit| num_digits_in_id(commit.id()) == most_digits);                Ok(out_property.into_dyn_wrapped())            },        );        table.commit_methods.insert(            "num_digits_in_id",            |_language, _diagnostics, _build_context, property, call| {                call.expect_no_arguments()?;                let out_property = property.map(|commit| num_digits_in_id(commit.id()));                Ok(out_property.into_dyn_wrapped())            },        );        table.commit_methods.insert(            "num_char_in_id",            |_language, diagnostics, _build_context, property, call| {                let [string_arg] = call.expect_exact_arguments()?;                let char_arg = template_parser::catch_aliases(                    diagnostics,                    string_arg,                    |_diagnostics, arg| {                        let string = template_parser::expect_string_literal(arg)?;                        let chars: Vec<_> = string.chars().collect();                        match chars[..] {                            [ch] => Ok(ch),                            _ => Err(TemplateParseError::expression(                                "Expected singular character argument",                                arg.span,                            )),                        }                    },                )?;                 let out_property = property.map(move |commit| num_char_in_id(commit, char_arg));                Ok(out_property.into_dyn_wrapped())            },        );         table    }     fn build_cache_extensions(&self, extensions: &mut ExtensionsMap) {        extensions.insert(MostDigitsInId::default());    }} #[derive(Debug)]struct EvenDigitsFilter; impl RevsetFilterExtension for EvenDigitsFilter {    fn matches_commit(&self, commit: &Commit) -> bool {        num_digits_in_id(commit.id()) % 2 == 0    }} fn even_digits(    _diagnostics: &mut RevsetDiagnostics,    function: &FunctionCallNode,    _context: &LoweringContext,) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {    function.expect_no_arguments()?;    Ok(RevsetExpression::filter(RevsetFilterPredicate::Extension(        Arc::new(EvenDigitsFilter),    )))} fn main() -> std::process::ExitCode {    CliRunner::init()        .add_symbol_resolver_extension(Box::new(TheDigitest))        .add_revset_function_extension("even_digits", even_digits)        .add_commit_template_extension(Box::new(HexCounter))        .run()        .into()}