revset: propagate evaluation errors from other Revset methods
Some checks are pending
binaries / Build binary artifacts (linux-aarch64-gnu, ubuntu-24.04, aarch64-unknown-linux-gnu) (push) Waiting to run
binaries / Build binary artifacts (linux-aarch64-musl, ubuntu-24.04, aarch64-unknown-linux-musl) (push) Waiting to run
binaries / Build binary artifacts (linux-x86_64-gnu, ubuntu-24.04, x86_64-unknown-linux-gnu) (push) Waiting to run
binaries / Build binary artifacts (linux-x86_64-musl, ubuntu-24.04, x86_64-unknown-linux-musl) (push) Waiting to run
binaries / Build binary artifacts (macos-aarch64, macos-14, aarch64-apple-darwin) (push) Waiting to run
binaries / Build binary artifacts (macos-x86_64, macos-13, x86_64-apple-darwin) (push) Waiting to run
binaries / Build binary artifacts (win-x86_64, windows-2022, x86_64-pc-windows-msvc) (push) Waiting to run
nix / flake check (macos-14) (push) Waiting to run
nix / flake check (ubuntu-latest) (push) Waiting to run
build / build (, macos-13) (push) Waiting to run
build / build (, macos-14) (push) Waiting to run
build / build (, ubuntu-latest) (push) Waiting to run
build / build (, windows-latest) (push) Waiting to run
build / build (--all-features, ubuntu-latest) (push) Waiting to run
build / Build jj-lib without Git support (push) Waiting to run
build / Check protos (push) Waiting to run
build / Check formatting (push) Waiting to run
build / Check that MkDocs can build the docs (push) Waiting to run
build / Check that MkDocs can build the docs with Poetry 1.8 (push) Waiting to run
build / cargo-deny (advisories) (push) Waiting to run
build / cargo-deny (bans licenses sources) (push) Waiting to run
build / Clippy check (push) Waiting to run
Codespell / Codespell (push) Waiting to run
website / prerelease-docs-build-deploy (ubuntu-latest) (push) Waiting to run
Scorecards supply-chain security / Scorecards analysis (push) Waiting to run

is_empty() could also return Result<bool, _>, but I think the current definition
is also good. If an error occurred, revset.iter() would return at least one
item, so it's not empty.
This commit is contained in:
Yuya Nishihara 2024-10-19 18:48:11 +09:00
parent 9d98ca491e
commit a493913000
6 changed files with 42 additions and 26 deletions

View file

@ -13,7 +13,6 @@
// limitations under the License.
use itertools::Itertools as _;
use jj_lib::backend::CommitId;
use jj_lib::object_id::ObjectId as _;
use jj_lib::op_store::RefTarget;
use jj_lib::str_util::StringPattern;
@ -77,26 +76,41 @@ pub fn cmd_bookmark_move(
let target_commit = workspace_command.resolve_single_rev(ui, &args.to)?;
let matched_bookmarks = {
let is_source_commit = if !args.from.is_empty() {
workspace_command
let is_source_ref: Box<dyn Fn(&RefTarget) -> _> = if !args.from.is_empty() {
let is_source_commit = workspace_command
.parse_union_revsets(ui, &args.from)?
.evaluate()?
.containing_fn()
.containing_fn();
Box::new(move |target: &RefTarget| {
for id in target.added_ids() {
if is_source_commit(id)? {
return Ok(true);
}
}
Ok(false)
})
} else {
Box::new(|_: &CommitId| true)
Box::new(|_| Ok(true))
};
let mut bookmarks = if !args.names.is_empty() {
find_bookmarks_with(&args.names, |pattern| {
repo.view()
.local_bookmarks_matching(pattern)
.filter(|(_, target)| target.added_ids().any(&is_source_commit))
.map(Ok)
.filter_map(|(name, target)| {
is_source_ref(target)
.map(|matched| matched.then_some((name, target)))
.transpose()
})
})?
} else {
repo.view()
.local_bookmarks()
.filter(|(_, target)| target.added_ids().any(&is_source_commit))
.collect()
.filter_map(|(name, target)| {
is_source_ref(target)
.map(|matched| matched.then_some((name, target)))
.transpose()
})
.try_collect()?
};
// Noop matches aren't error, but should be excluded from stats.
bookmarks.retain(|(_, old_target)| old_target.as_normal() != Some(target_commit.id()));

View file

@ -336,7 +336,7 @@ fn validate_commits_ready_to_push(
.evaluate()?
.containing_fn()
} else {
Box::new(|_: &CommitId| false)
Box::new(|_: &CommitId| Ok(false))
};
for commit in workspace_helper
@ -362,7 +362,7 @@ fn validate_commits_ready_to_push(
if commit.has_conflict()? {
reasons.push("it has conflicts");
}
if !args.allow_private && is_private(commit.id()) {
if !args.allow_private && is_private(commit.id())? {
reasons.push("it is private");
}
if !reasons.is_empty() {

View file

@ -743,7 +743,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
.keyword_cache
.is_immutable_fn(language, function.name_span)?
.clone();
let out_property = self_property.map(move |commit| is_immutable(commit.id()));
let out_property = self_property.and_then(move |commit| Ok(is_immutable(commit.id())?));
Ok(L::wrap_boolean(out_property))
},
);
@ -757,7 +757,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
Ok(evaluate_user_revset(language, diagnostics, span, revset)?.containing_fn())
})?;
let out_property = self_property.map(move |commit| is_contained(commit.id()));
let out_property = self_property.and_then(move |commit| Ok(is_contained(commit.id())?));
Ok(L::wrap_boolean(out_property))
},
);
@ -1020,7 +1020,7 @@ impl RefName {
.get_or_try_init(|| {
let self_ids = self.target.added_ids().cloned().collect_vec();
let other_ids = tracking.target.added_ids().cloned().collect_vec();
Ok(revset::walk_revs(repo, &self_ids, &other_ids)?.count_estimate())
Ok(revset::walk_revs(repo, &self_ids, &other_ids)?.count_estimate()?)
})
.copied()
}
@ -1035,7 +1035,7 @@ impl RefName {
.get_or_try_init(|| {
let self_ids = self.target.added_ids().cloned().collect_vec();
let other_ids = tracking.target.added_ids().cloned().collect_vec();
Ok(revset::walk_revs(repo, &other_ids, &self_ids)?.count_estimate())
Ok(revset::walk_revs(repo, &other_ids, &self_ids)?.count_estimate()?)
})
.copied()
}

View file

@ -191,20 +191,21 @@ impl<I: AsCompositeIndex + Clone> Revset for RevsetImpl<I> {
self.positions().next().is_none()
}
fn count_estimate(&self) -> (usize, Option<usize>) {
fn count_estimate(&self) -> Result<(usize, Option<usize>), RevsetEvaluationError> {
if cfg!(feature = "testing") {
// Exercise the estimation feature in tests. (If we ever have a Revset
// implementation in production code that returns estimates, we can probably
// remove this and rewrite the associated tests.)
let count = self.positions().take(10).count();
if count < 10 {
(count, Some(count))
Ok((count, Some(count)))
} else {
(10, None)
Ok((10, None))
}
} else {
// TODO: propagate error
let count = self.positions().count();
(count, Some(count))
Ok((count, Some(count)))
}
}
@ -213,7 +214,7 @@ impl<I: AsCompositeIndex + Clone> Revset for RevsetImpl<I> {
Self: 'a,
{
let positions = PositionsAccumulator::new(self.index.clone(), self.inner.positions());
Box::new(move |commit_id| positions.contains(commit_id))
Box::new(move |commit_id| Ok(positions.contains(commit_id)))
}
}

View file

@ -2207,13 +2207,14 @@ pub trait Revset: fmt::Debug {
where
Self: 'a;
/// Returns true if iterator will emit at least one commit or error.
fn is_empty(&self) -> bool;
/// Inclusive lower bound and, optionally, inclusive upper bound of how many
/// commits are in the revset. The implementation can use its discretion as
/// to how much effort should be put into the estimation, and how accurate
/// the resulting estimate should be.
fn count_estimate(&self) -> (usize, Option<usize>);
fn count_estimate(&self) -> Result<(usize, Option<usize>), RevsetEvaluationError>;
/// Returns a closure that checks if a commit is contained within the
/// revset.
@ -2226,7 +2227,7 @@ pub trait Revset: fmt::Debug {
}
/// Function that checks if a commit is contained within the revset.
pub type RevsetContainingFn<'a> = dyn Fn(&CommitId) -> bool + 'a;
pub type RevsetContainingFn<'a> = dyn Fn(&CommitId) -> Result<bool, RevsetEvaluationError> + 'a;
pub trait RevsetIteratorExt<'index, I> {
fn commits(self, store: &Arc<Store>) -> RevsetCommitIterator<I>;

View file

@ -3765,8 +3765,8 @@ fn test_revset_containing_fn() {
let revset = revset_for_commits(repo.as_ref(), &[&commit_b, &commit_d]);
let revset_has_commit = revset.containing_fn();
assert!(!revset_has_commit(commit_a.id()));
assert!(revset_has_commit(commit_b.id()));
assert!(!revset_has_commit(commit_c.id()));
assert!(revset_has_commit(commit_d.id()));
assert!(!revset_has_commit(commit_a.id()).unwrap());
assert!(revset_has_commit(commit_b.id()).unwrap());
assert!(!revset_has_commit(commit_c.id()).unwrap());
assert!(revset_has_commit(commit_d.id()).unwrap());
}