1#![expect(missing_docs)]
16
17use std::any::Any;
18use std::collections::HashMap;
19use std::collections::hash_map;
20use std::convert::Infallible;
21use std::fmt;
22use std::ops::ControlFlow;
23use std::ops::Range;
24use std::sync::Arc;
25use std::sync::LazyLock;
26
27use itertools::Itertools as _;
28use thiserror::Error;
29
30use crate::backend::BackendError;
31use crate::backend::ChangeId;
32use crate::backend::CommitId;
33use crate::commit::Commit;
34use crate::dsl_util;
35use crate::dsl_util::collect_similar;
36use crate::fileset;
37use crate::fileset::FilesetDiagnostics;
38use crate::fileset::FilesetExpression;
39use crate::graph::GraphNode;
40use crate::id_prefix::IdPrefixContext;
41use crate::id_prefix::IdPrefixIndex;
42use crate::object_id::HexPrefix;
43use crate::object_id::PrefixResolution;
44use crate::op_store::RefTarget;
45use crate::op_store::RemoteRefState;
46use crate::op_walk;
47use crate::ref_name::RemoteName;
48use crate::ref_name::RemoteRefSymbol;
49use crate::ref_name::RemoteRefSymbolBuf;
50use crate::ref_name::WorkspaceName;
51use crate::ref_name::WorkspaceNameBuf;
52use crate::repo::ReadonlyRepo;
53use crate::repo::Repo;
54use crate::repo::RepoLoaderError;
55use crate::repo_path::RepoPathUiConverter;
56use crate::revset_parser;
57pub use crate::revset_parser::BinaryOp;
58pub use crate::revset_parser::ExpressionKind;
59pub use crate::revset_parser::ExpressionNode;
60pub use crate::revset_parser::FunctionCallNode;
61pub use crate::revset_parser::RevsetAliasesMap;
62pub use crate::revset_parser::RevsetDiagnostics;
63pub use crate::revset_parser::RevsetParseError;
64pub use crate::revset_parser::RevsetParseErrorKind;
65pub use crate::revset_parser::UnaryOp;
66pub use crate::revset_parser::expect_literal;
67pub use crate::revset_parser::parse_program;
68pub use crate::revset_parser::parse_symbol;
69use crate::store::Store;
70use crate::str_util::StringExpression;
71use crate::str_util::StringPattern;
72use crate::time_util::DatePattern;
73use crate::time_util::DatePatternContext;
74
75#[derive(Debug, Error)]
77pub enum RevsetResolutionError {
78 #[error("Revision `{name}` doesn't exist")]
79 NoSuchRevision {
80 name: String,
81 candidates: Vec<String>,
82 },
83 #[error("Workspace `{}` doesn't have a working-copy commit", name.as_symbol())]
84 WorkspaceMissingWorkingCopy { name: WorkspaceNameBuf },
85 #[error("An empty string is not a valid revision")]
86 EmptyString,
87 #[error("Commit ID prefix `{0}` is ambiguous")]
88 AmbiguousCommitIdPrefix(String),
89 #[error("Change ID prefix `{0}` is ambiguous")]
90 AmbiguousChangeIdPrefix(String),
91 #[error("Change ID `{symbol}` is divergent")]
92 DivergentChangeId {
93 symbol: String,
94 targets: Vec<CommitId>,
95 },
96 #[error("Name `{symbol}` is conflicted")]
97 ConflictedRef {
98 kind: &'static str,
99 symbol: String,
100 targets: Vec<CommitId>,
101 },
102 #[error("Unexpected error from commit backend")]
103 Backend(#[source] BackendError),
104 #[error(transparent)]
105 Other(#[from] Box<dyn std::error::Error + Send + Sync>),
106}
107
108#[derive(Debug, Error)]
110pub enum RevsetEvaluationError {
111 #[error("Unexpected error from commit backend")]
112 Backend(#[from] BackendError),
113 #[error(transparent)]
114 Other(Box<dyn std::error::Error + Send + Sync>),
115}
116
117impl RevsetEvaluationError {
118 pub fn into_backend_error(self) -> BackendError {
121 match self {
122 Self::Backend(err) => err,
123 Self::Other(err) => BackendError::Other(err),
124 }
125 }
126}
127
128pub const GENERATION_RANGE_FULL: Range<u64> = 0..u64::MAX;
130pub const GENERATION_RANGE_EMPTY: Range<u64> = 0..0;
131
132pub const PARENTS_RANGE_FULL: Range<u32> = 0..u32::MAX;
133
134#[derive(Clone, Copy, Debug, Eq, PartialEq)]
139pub enum RevsetModifier {
140 All,
143}
144
145#[derive(Clone, Debug)]
147pub enum RevsetCommitRef {
148 WorkingCopy(WorkspaceNameBuf),
149 WorkingCopies,
150 Symbol(String),
151 RemoteSymbol(RemoteRefSymbolBuf),
152 ChangeId(HexPrefix),
153 CommitId(HexPrefix),
154 Bookmarks(StringExpression),
155 RemoteBookmarks {
156 bookmark: StringExpression,
157 remote: StringExpression,
158 remote_ref_state: Option<RemoteRefState>,
159 },
160 Tags(StringExpression),
161 GitRefs,
162 GitHead,
163}
164
165pub trait RevsetFilterExtension: std::fmt::Debug + Any + Send + Sync {
167 fn matches_commit(&self, commit: &Commit) -> bool;
169}
170
171impl dyn RevsetFilterExtension {
172 pub fn downcast_ref<T: RevsetFilterExtension>(&self) -> Option<&T> {
174 (self as &dyn Any).downcast_ref()
175 }
176}
177
178#[derive(Clone, Debug)]
179pub enum RevsetFilterPredicate {
180 ParentCount(Range<u32>),
182 Description(StringExpression),
184 Subject(StringExpression),
186 AuthorName(StringExpression),
188 AuthorEmail(StringExpression),
190 AuthorDate(DatePattern),
192 CommitterName(StringExpression),
194 CommitterEmail(StringExpression),
196 CommitterDate(DatePattern),
198 File(FilesetExpression),
200 DiffContains {
202 text: StringExpression,
203 files: FilesetExpression,
204 },
205 HasConflict,
207 Signed,
209 Extension(Arc<dyn RevsetFilterExtension>),
211}
212
213mod private {
214 pub trait ExpressionState {
216 type CommitRef: Clone;
217 type Operation: Clone;
218 }
219
220 #[derive(Debug)]
222 pub enum UserExpressionState {}
223 #[derive(Debug)]
224 pub enum ResolvedExpressionState {}
225}
226
227use private::ExpressionState;
228use private::ResolvedExpressionState;
229use private::UserExpressionState;
230
231impl ExpressionState for UserExpressionState {
232 type CommitRef = RevsetCommitRef;
233 type Operation = String;
234}
235
236impl ExpressionState for ResolvedExpressionState {
237 type CommitRef = Infallible;
238 type Operation = Infallible;
239}
240
241pub type UserRevsetExpression = RevsetExpression<UserExpressionState>;
243pub type ResolvedRevsetExpression = RevsetExpression<ResolvedExpressionState>;
245
246#[derive(Clone, Debug)]
251pub enum RevsetExpression<St: ExpressionState> {
252 None,
253 All,
254 VisibleHeads,
255 VisibleHeadsOrReferenced,
258 Root,
259 Commits(Vec<CommitId>),
260 CommitRef(St::CommitRef),
261 Ancestors {
262 heads: Arc<Self>,
263 generation: Range<u64>,
264 parents_range: Range<u32>,
265 },
266 Descendants {
267 roots: Arc<Self>,
268 generation: Range<u64>,
269 },
270 Range {
272 roots: Arc<Self>,
273 heads: Arc<Self>,
274 generation: Range<u64>,
275 parents_range: Range<u32>,
277 },
278 DagRange {
280 roots: Arc<Self>,
281 heads: Arc<Self>,
282 },
284 Reachable {
286 sources: Arc<Self>,
287 domain: Arc<Self>,
288 },
289 Heads(Arc<Self>),
290 HeadsRange {
293 roots: Arc<Self>,
294 heads: Arc<Self>,
295 parents_range: Range<u32>,
296 filter: Arc<Self>,
297 },
298 Roots(Arc<Self>),
299 ForkPoint(Arc<Self>),
300 Bisect(Arc<Self>),
301 HasSize {
302 candidates: Arc<Self>,
303 count: usize,
304 },
305 Latest {
306 candidates: Arc<Self>,
307 count: usize,
308 },
309 Filter(RevsetFilterPredicate),
310 AsFilter(Arc<Self>),
312 AtOperation {
314 operation: St::Operation,
315 candidates: Arc<Self>,
316 },
317 WithinReference {
320 candidates: Arc<Self>,
321 commits: Vec<CommitId>,
323 },
324 WithinVisibility {
326 candidates: Arc<Self>,
327 visible_heads: Vec<CommitId>,
329 },
330 Coalesce(Arc<Self>, Arc<Self>),
331 Present(Arc<Self>),
332 NotIn(Arc<Self>),
333 Union(Arc<Self>, Arc<Self>),
334 Intersection(Arc<Self>, Arc<Self>),
335 Difference(Arc<Self>, Arc<Self>),
336}
337
338impl<St: ExpressionState> RevsetExpression<St> {
341 pub fn none() -> Arc<Self> {
342 Arc::new(Self::None)
343 }
344
345 pub fn all() -> Arc<Self> {
348 Arc::new(Self::All)
349 }
350
351 pub fn visible_heads() -> Arc<Self> {
352 Arc::new(Self::VisibleHeads)
353 }
354
355 fn visible_heads_or_referenced() -> Arc<Self> {
356 Arc::new(Self::VisibleHeadsOrReferenced)
357 }
358
359 pub fn root() -> Arc<Self> {
360 Arc::new(Self::Root)
361 }
362
363 pub fn commit(commit_id: CommitId) -> Arc<Self> {
364 Self::commits(vec![commit_id])
365 }
366
367 pub fn commits(commit_ids: Vec<CommitId>) -> Arc<Self> {
368 Arc::new(Self::Commits(commit_ids))
369 }
370
371 pub fn filter(predicate: RevsetFilterPredicate) -> Arc<Self> {
372 Arc::new(Self::Filter(predicate))
373 }
374
375 pub fn is_empty() -> Arc<Self> {
377 Self::filter(RevsetFilterPredicate::File(FilesetExpression::all())).negated()
378 }
379}
380
381impl<St: ExpressionState<CommitRef = RevsetCommitRef>> RevsetExpression<St> {
383 pub fn working_copy(name: WorkspaceNameBuf) -> Arc<Self> {
384 Arc::new(Self::CommitRef(RevsetCommitRef::WorkingCopy(name)))
385 }
386
387 pub fn working_copies() -> Arc<Self> {
388 Arc::new(Self::CommitRef(RevsetCommitRef::WorkingCopies))
389 }
390
391 pub fn symbol(value: String) -> Arc<Self> {
392 Arc::new(Self::CommitRef(RevsetCommitRef::Symbol(value)))
393 }
394
395 pub fn remote_symbol(value: RemoteRefSymbolBuf) -> Arc<Self> {
396 let commit_ref = RevsetCommitRef::RemoteSymbol(value);
397 Arc::new(Self::CommitRef(commit_ref))
398 }
399
400 pub fn change_id_prefix(prefix: HexPrefix) -> Arc<Self> {
401 let commit_ref = RevsetCommitRef::ChangeId(prefix);
402 Arc::new(Self::CommitRef(commit_ref))
403 }
404
405 pub fn commit_id_prefix(prefix: HexPrefix) -> Arc<Self> {
406 let commit_ref = RevsetCommitRef::CommitId(prefix);
407 Arc::new(Self::CommitRef(commit_ref))
408 }
409
410 pub fn bookmarks(expression: StringExpression) -> Arc<Self> {
411 Arc::new(Self::CommitRef(RevsetCommitRef::Bookmarks(expression)))
412 }
413
414 pub fn remote_bookmarks(
415 bookmark: StringExpression,
416 remote: StringExpression,
417 remote_ref_state: Option<RemoteRefState>,
418 ) -> Arc<Self> {
419 Arc::new(Self::CommitRef(RevsetCommitRef::RemoteBookmarks {
420 bookmark,
421 remote,
422 remote_ref_state,
423 }))
424 }
425
426 pub fn tags(expression: StringExpression) -> Arc<Self> {
427 Arc::new(Self::CommitRef(RevsetCommitRef::Tags(expression)))
428 }
429
430 pub fn git_refs() -> Arc<Self> {
431 Arc::new(Self::CommitRef(RevsetCommitRef::GitRefs))
432 }
433
434 pub fn git_head() -> Arc<Self> {
435 Arc::new(Self::CommitRef(RevsetCommitRef::GitHead))
436 }
437}
438
439impl<St: ExpressionState> RevsetExpression<St> {
441 pub fn latest(self: &Arc<Self>, count: usize) -> Arc<Self> {
442 Arc::new(Self::Latest {
443 candidates: self.clone(),
444 count,
445 })
446 }
447
448 pub fn heads(self: &Arc<Self>) -> Arc<Self> {
450 Arc::new(Self::Heads(self.clone()))
451 }
452
453 pub fn roots(self: &Arc<Self>) -> Arc<Self> {
455 Arc::new(Self::Roots(self.clone()))
456 }
457
458 pub fn parents(self: &Arc<Self>) -> Arc<Self> {
460 self.ancestors_at(1)
461 }
462
463 pub fn ancestors(self: &Arc<Self>) -> Arc<Self> {
465 self.ancestors_range(GENERATION_RANGE_FULL)
466 }
467
468 pub fn ancestors_at(self: &Arc<Self>, generation: u64) -> Arc<Self> {
471 self.ancestors_range(generation..generation.saturating_add(1))
472 }
473
474 pub fn ancestors_range(self: &Arc<Self>, generation_range: Range<u64>) -> Arc<Self> {
476 Arc::new(Self::Ancestors {
477 heads: self.clone(),
478 generation: generation_range,
479 parents_range: PARENTS_RANGE_FULL,
480 })
481 }
482
483 pub fn first_ancestors(self: &Arc<Self>) -> Arc<Self> {
485 self.first_ancestors_range(GENERATION_RANGE_FULL)
486 }
487
488 pub fn first_ancestors_at(self: &Arc<Self>, generation: u64) -> Arc<Self> {
491 self.first_ancestors_range(generation..generation.saturating_add(1))
492 }
493
494 pub fn first_ancestors_range(self: &Arc<Self>, generation_range: Range<u64>) -> Arc<Self> {
496 Arc::new(Self::Ancestors {
497 heads: self.clone(),
498 generation: generation_range,
499 parents_range: 0..1,
500 })
501 }
502
503 pub fn children(self: &Arc<Self>) -> Arc<Self> {
505 self.descendants_at(1)
506 }
507
508 pub fn descendants(self: &Arc<Self>) -> Arc<Self> {
510 self.descendants_range(GENERATION_RANGE_FULL)
511 }
512
513 pub fn descendants_at(self: &Arc<Self>, generation: u64) -> Arc<Self> {
516 self.descendants_range(generation..generation.saturating_add(1))
517 }
518
519 pub fn descendants_range(self: &Arc<Self>, generation_range: Range<u64>) -> Arc<Self> {
521 Arc::new(Self::Descendants {
522 roots: self.clone(),
523 generation: generation_range,
524 })
525 }
526
527 pub fn fork_point(self: &Arc<Self>) -> Arc<Self> {
529 Arc::new(Self::ForkPoint(self.clone()))
530 }
531
532 pub fn bisect(self: &Arc<Self>) -> Arc<Self> {
534 Arc::new(Self::Bisect(self.clone()))
535 }
536
537 pub fn has_size(self: &Arc<Self>, count: usize) -> Arc<Self> {
539 Arc::new(Self::HasSize {
540 candidates: self.clone(),
541 count,
542 })
543 }
544
545 pub fn filtered(self: &Arc<Self>, predicate: RevsetFilterPredicate) -> Arc<Self> {
547 self.intersection(&Self::filter(predicate))
548 }
549
550 pub fn dag_range_to(self: &Arc<Self>, heads: &Arc<Self>) -> Arc<Self> {
553 Arc::new(Self::DagRange {
554 roots: self.clone(),
555 heads: heads.clone(),
556 })
557 }
558
559 pub fn connected(self: &Arc<Self>) -> Arc<Self> {
562 self.dag_range_to(self)
563 }
564
565 pub fn reachable(self: &Arc<Self>, domain: &Arc<Self>) -> Arc<Self> {
568 Arc::new(Self::Reachable {
569 sources: self.clone(),
570 domain: domain.clone(),
571 })
572 }
573
574 pub fn range(self: &Arc<Self>, heads: &Arc<Self>) -> Arc<Self> {
576 Arc::new(Self::Range {
577 roots: self.clone(),
578 heads: heads.clone(),
579 generation: GENERATION_RANGE_FULL,
580 parents_range: PARENTS_RANGE_FULL,
581 })
582 }
583
584 pub fn present(self: &Arc<Self>) -> Arc<Self> {
586 Arc::new(Self::Present(self.clone()))
587 }
588
589 pub fn negated(self: &Arc<Self>) -> Arc<Self> {
591 Arc::new(Self::NotIn(self.clone()))
592 }
593
594 pub fn union(self: &Arc<Self>, other: &Arc<Self>) -> Arc<Self> {
596 Arc::new(Self::Union(self.clone(), other.clone()))
597 }
598
599 pub fn union_all(expressions: &[Arc<Self>]) -> Arc<Self> {
601 to_binary_expression(expressions, &Self::none, &Self::union)
602 }
603
604 pub fn intersection(self: &Arc<Self>, other: &Arc<Self>) -> Arc<Self> {
606 Arc::new(Self::Intersection(self.clone(), other.clone()))
607 }
608
609 pub fn minus(self: &Arc<Self>, other: &Arc<Self>) -> Arc<Self> {
611 Arc::new(Self::Difference(self.clone(), other.clone()))
612 }
613
614 pub fn coalesce(expressions: &[Arc<Self>]) -> Arc<Self> {
617 to_binary_expression(expressions, &Self::none, &Self::coalesce2)
618 }
619
620 fn coalesce2(self: &Arc<Self>, other: &Arc<Self>) -> Arc<Self> {
621 Arc::new(Self::Coalesce(self.clone(), other.clone()))
622 }
623}
624
625impl<St: ExpressionState<CommitRef = RevsetCommitRef>> RevsetExpression<St> {
626 pub fn as_symbol(&self) -> Option<&str> {
628 match self {
629 Self::CommitRef(RevsetCommitRef::Symbol(name)) => Some(name),
630 _ => None,
631 }
632 }
633}
634
635impl UserRevsetExpression {
636 pub fn resolve_user_expression(
639 &self,
640 repo: &dyn Repo,
641 symbol_resolver: &SymbolResolver,
642 ) -> Result<Arc<ResolvedRevsetExpression>, RevsetResolutionError> {
643 resolve_symbols(repo, self, symbol_resolver)
644 }
645}
646
647impl ResolvedRevsetExpression {
648 pub fn evaluate<'index>(
650 self: Arc<Self>,
651 repo: &'index dyn Repo,
652 ) -> Result<Box<dyn Revset + 'index>, RevsetEvaluationError> {
653 let expr = optimize(self).to_backend_expression(repo);
654 repo.index().evaluate_revset(&expr, repo.store())
655 }
656
657 pub fn evaluate_unoptimized<'index>(
662 self: &Arc<Self>,
663 repo: &'index dyn Repo,
664 ) -> Result<Box<dyn Revset + 'index>, RevsetEvaluationError> {
665 let expr = resolve_referenced_commits(self)
668 .as_ref()
669 .unwrap_or(self)
670 .to_backend_expression(repo);
671 repo.index().evaluate_revset(&expr, repo.store())
672 }
673
674 pub fn to_backend_expression(&self, repo: &dyn Repo) -> ResolvedExpression {
677 resolve_visibility(repo, self)
678 }
679}
680
681#[derive(Clone, Debug)]
682pub enum ResolvedPredicateExpression {
683 Filter(RevsetFilterPredicate),
685 Set(Box<ResolvedExpression>),
688 NotIn(Box<Self>),
689 Union(Box<Self>, Box<Self>),
690 Intersection(Box<Self>, Box<Self>),
691}
692
693#[derive(Clone, Debug)]
701pub enum ResolvedExpression {
702 Commits(Vec<CommitId>),
703 Ancestors {
704 heads: Box<Self>,
705 generation: Range<u64>,
706 parents_range: Range<u32>,
707 },
708 Range {
710 roots: Box<Self>,
711 heads: Box<Self>,
712 generation: Range<u64>,
713 parents_range: Range<u32>,
715 },
716 DagRange {
718 roots: Box<Self>,
719 heads: Box<Self>,
720 generation_from_roots: Range<u64>,
721 },
722 Reachable {
724 sources: Box<Self>,
725 domain: Box<Self>,
726 },
727 Heads(Box<Self>),
728 HeadsRange {
731 roots: Box<Self>,
732 heads: Box<Self>,
733 parents_range: Range<u32>,
734 filter: Option<ResolvedPredicateExpression>,
735 },
736 Roots(Box<Self>),
737 ForkPoint(Box<Self>),
738 Bisect(Box<Self>),
739 HasSize {
740 candidates: Box<Self>,
741 count: usize,
742 },
743 Latest {
744 candidates: Box<Self>,
745 count: usize,
746 },
747 Coalesce(Box<Self>, Box<Self>),
748 Union(Box<Self>, Box<Self>),
749 FilterWithin {
751 candidates: Box<Self>,
752 predicate: ResolvedPredicateExpression,
753 },
754 Intersection(Box<Self>, Box<Self>),
756 Difference(Box<Self>, Box<Self>),
757}
758
759pub type RevsetFunction = fn(
760 &mut RevsetDiagnostics,
761 &FunctionCallNode,
762 &LoweringContext,
763) -> Result<Arc<UserRevsetExpression>, RevsetParseError>;
764
765static BUILTIN_FUNCTION_MAP: LazyLock<HashMap<&str, RevsetFunction>> = LazyLock::new(|| {
766 let mut map: HashMap<&str, RevsetFunction> = HashMap::new();
769 map.insert("parents", |diagnostics, function, context| {
770 let ([arg], [depth_opt_arg]) = function.expect_arguments()?;
771 let expression = lower_expression(diagnostics, arg, context)?;
772 if let Some(depth_arg) = depth_opt_arg {
773 let depth = expect_literal("integer", depth_arg)?;
774 Ok(expression.ancestors_at(depth))
775 } else {
776 Ok(expression.parents())
777 }
778 });
779 map.insert("children", |diagnostics, function, context| {
780 let ([arg], [depth_opt_arg]) = function.expect_arguments()?;
781 let expression = lower_expression(diagnostics, arg, context)?;
782 if let Some(depth_arg) = depth_opt_arg {
783 let depth = expect_literal("integer", depth_arg)?;
784 Ok(expression.descendants_at(depth))
785 } else {
786 Ok(expression.children())
787 }
788 });
789 map.insert("ancestors", |diagnostics, function, context| {
790 let ([heads_arg], [depth_opt_arg]) = function.expect_arguments()?;
791 let heads = lower_expression(diagnostics, heads_arg, context)?;
792 let generation = if let Some(depth_arg) = depth_opt_arg {
793 let depth = expect_literal("integer", depth_arg)?;
794 0..depth
795 } else {
796 GENERATION_RANGE_FULL
797 };
798 Ok(heads.ancestors_range(generation))
799 });
800 map.insert("descendants", |diagnostics, function, context| {
801 let ([roots_arg], [depth_opt_arg]) = function.expect_arguments()?;
802 let roots = lower_expression(diagnostics, roots_arg, context)?;
803 let generation = if let Some(depth_arg) = depth_opt_arg {
804 let depth = expect_literal("integer", depth_arg)?;
805 0..depth
806 } else {
807 GENERATION_RANGE_FULL
808 };
809 Ok(roots.descendants_range(generation))
810 });
811 map.insert("first_parent", |diagnostics, function, context| {
812 let ([arg], [depth_opt_arg]) = function.expect_arguments()?;
813 let expression = lower_expression(diagnostics, arg, context)?;
814 let depth = if let Some(depth_arg) = depth_opt_arg {
815 expect_literal("integer", depth_arg)?
816 } else {
817 1
818 };
819 Ok(expression.first_ancestors_at(depth))
820 });
821 map.insert("first_ancestors", |diagnostics, function, context| {
822 let ([heads_arg], [depth_opt_arg]) = function.expect_arguments()?;
823 let heads = lower_expression(diagnostics, heads_arg, context)?;
824 let generation = if let Some(depth_arg) = depth_opt_arg {
825 let depth = expect_literal("integer", depth_arg)?;
826 0..depth
827 } else {
828 GENERATION_RANGE_FULL
829 };
830 Ok(heads.first_ancestors_range(generation))
831 });
832 map.insert("connected", |diagnostics, function, context| {
833 let [arg] = function.expect_exact_arguments()?;
834 let candidates = lower_expression(diagnostics, arg, context)?;
835 Ok(candidates.connected())
836 });
837 map.insert("reachable", |diagnostics, function, context| {
838 let [source_arg, domain_arg] = function.expect_exact_arguments()?;
839 let sources = lower_expression(diagnostics, source_arg, context)?;
840 let domain = lower_expression(diagnostics, domain_arg, context)?;
841 Ok(sources.reachable(&domain))
842 });
843 map.insert("none", |_diagnostics, function, _context| {
844 function.expect_no_arguments()?;
845 Ok(RevsetExpression::none())
846 });
847 map.insert("all", |_diagnostics, function, _context| {
848 function.expect_no_arguments()?;
849 Ok(RevsetExpression::all())
850 });
851 map.insert("working_copies", |_diagnostics, function, _context| {
852 function.expect_no_arguments()?;
853 Ok(RevsetExpression::working_copies())
854 });
855 map.insert("heads", |diagnostics, function, context| {
856 let [arg] = function.expect_exact_arguments()?;
857 let candidates = lower_expression(diagnostics, arg, context)?;
858 Ok(candidates.heads())
859 });
860 map.insert("roots", |diagnostics, function, context| {
861 let [arg] = function.expect_exact_arguments()?;
862 let candidates = lower_expression(diagnostics, arg, context)?;
863 Ok(candidates.roots())
864 });
865 map.insert("visible_heads", |_diagnostics, function, _context| {
866 function.expect_no_arguments()?;
867 Ok(RevsetExpression::visible_heads())
868 });
869 map.insert("root", |_diagnostics, function, _context| {
870 function.expect_no_arguments()?;
871 Ok(RevsetExpression::root())
872 });
873 map.insert("change_id", |diagnostics, function, _context| {
874 let [arg] = function.expect_exact_arguments()?;
875 let prefix = revset_parser::catch_aliases(diagnostics, arg, |_diagnostics, arg| {
876 let value = revset_parser::expect_string_literal("change ID prefix", arg)?;
877 HexPrefix::try_from_reverse_hex(value)
878 .ok_or_else(|| RevsetParseError::expression("Invalid change ID prefix", arg.span))
879 })?;
880 Ok(RevsetExpression::change_id_prefix(prefix))
881 });
882 map.insert("commit_id", |diagnostics, function, _context| {
883 let [arg] = function.expect_exact_arguments()?;
884 let prefix = revset_parser::catch_aliases(diagnostics, arg, |_diagnostics, arg| {
885 let value = revset_parser::expect_string_literal("commit ID prefix", arg)?;
886 HexPrefix::try_from_hex(value)
887 .ok_or_else(|| RevsetParseError::expression("Invalid commit ID prefix", arg.span))
888 })?;
889 Ok(RevsetExpression::commit_id_prefix(prefix))
890 });
891 map.insert("bookmarks", |diagnostics, function, _context| {
892 let ([], [opt_arg]) = function.expect_arguments()?;
893 let expr = if let Some(arg) = opt_arg {
894 expect_string_expression(diagnostics, arg)?
895 } else {
896 StringExpression::all()
897 };
898 Ok(RevsetExpression::bookmarks(expr))
899 });
900 map.insert("remote_bookmarks", |diagnostics, function, context| {
901 parse_remote_bookmarks_arguments(
902 diagnostics,
903 function,
904 None,
905 context.default_ignored_remote,
906 )
907 });
908 map.insert(
909 "tracked_remote_bookmarks",
910 |diagnostics, function, context| {
911 parse_remote_bookmarks_arguments(
912 diagnostics,
913 function,
914 Some(RemoteRefState::Tracked),
915 context.default_ignored_remote,
916 )
917 },
918 );
919 map.insert(
920 "untracked_remote_bookmarks",
921 |diagnostics, function, context| {
922 parse_remote_bookmarks_arguments(
923 diagnostics,
924 function,
925 Some(RemoteRefState::New),
926 context.default_ignored_remote,
927 )
928 },
929 );
930 map.insert("tags", |diagnostics, function, _context| {
931 let ([], [opt_arg]) = function.expect_arguments()?;
932 let expr = if let Some(arg) = opt_arg {
933 expect_string_expression(diagnostics, arg)?
934 } else {
935 StringExpression::all()
936 };
937 Ok(RevsetExpression::tags(expr))
938 });
939 map.insert("git_refs", |_diagnostics, function, _context| {
940 function.expect_no_arguments()?;
941 Ok(RevsetExpression::git_refs())
942 });
943 map.insert("git_head", |_diagnostics, function, _context| {
944 function.expect_no_arguments()?;
945 Ok(RevsetExpression::git_head())
946 });
947 map.insert("latest", |diagnostics, function, context| {
948 let ([candidates_arg], [count_opt_arg]) = function.expect_arguments()?;
949 let candidates = lower_expression(diagnostics, candidates_arg, context)?;
950 let count = if let Some(count_arg) = count_opt_arg {
951 expect_literal("integer", count_arg)?
952 } else {
953 1
954 };
955 Ok(candidates.latest(count))
956 });
957 map.insert("fork_point", |diagnostics, function, context| {
958 let [expression_arg] = function.expect_exact_arguments()?;
959 let expression = lower_expression(diagnostics, expression_arg, context)?;
960 Ok(RevsetExpression::fork_point(&expression))
961 });
962 map.insert("bisect", |diagnostics, function, context| {
963 let [expression_arg] = function.expect_exact_arguments()?;
964 let expression = lower_expression(diagnostics, expression_arg, context)?;
965 Ok(RevsetExpression::bisect(&expression))
966 });
967 map.insert("exactly", |diagnostics, function, context| {
968 let ([candidates_arg, count_arg], []) = function.expect_arguments()?;
969 let candidates = lower_expression(diagnostics, candidates_arg, context)?;
970 let count = expect_literal("integer", count_arg)?;
971 Ok(candidates.has_size(count))
972 });
973 map.insert("merges", |_diagnostics, function, _context| {
974 function.expect_no_arguments()?;
975 Ok(RevsetExpression::filter(
976 RevsetFilterPredicate::ParentCount(2..u32::MAX),
977 ))
978 });
979 map.insert("description", |diagnostics, function, _context| {
980 let [arg] = function.expect_exact_arguments()?;
981 let expr = expect_string_expression(diagnostics, arg)?;
982 let predicate = RevsetFilterPredicate::Description(expr);
983 Ok(RevsetExpression::filter(predicate))
984 });
985 map.insert("subject", |diagnostics, function, _context| {
986 let [arg] = function.expect_exact_arguments()?;
987 let expr = expect_string_expression(diagnostics, arg)?;
988 let predicate = RevsetFilterPredicate::Subject(expr);
989 Ok(RevsetExpression::filter(predicate))
990 });
991 map.insert("author", |diagnostics, function, _context| {
992 let [arg] = function.expect_exact_arguments()?;
993 let expr = expect_string_expression(diagnostics, arg)?;
994 let name_predicate = RevsetFilterPredicate::AuthorName(expr.clone());
995 let email_predicate = RevsetFilterPredicate::AuthorEmail(expr);
996 Ok(RevsetExpression::filter(name_predicate)
997 .union(&RevsetExpression::filter(email_predicate)))
998 });
999 map.insert("author_name", |diagnostics, function, _context| {
1000 let [arg] = function.expect_exact_arguments()?;
1001 let expr = expect_string_expression(diagnostics, arg)?;
1002 let predicate = RevsetFilterPredicate::AuthorName(expr);
1003 Ok(RevsetExpression::filter(predicate))
1004 });
1005 map.insert("author_email", |diagnostics, function, _context| {
1006 let [arg] = function.expect_exact_arguments()?;
1007 let expr = expect_string_expression(diagnostics, arg)?;
1008 let predicate = RevsetFilterPredicate::AuthorEmail(expr);
1009 Ok(RevsetExpression::filter(predicate))
1010 });
1011 map.insert("author_date", |diagnostics, function, context| {
1012 let [arg] = function.expect_exact_arguments()?;
1013 let pattern = expect_date_pattern(diagnostics, arg, context.date_pattern_context())?;
1014 Ok(RevsetExpression::filter(RevsetFilterPredicate::AuthorDate(
1015 pattern,
1016 )))
1017 });
1018 map.insert("signed", |_diagnostics, function, _context| {
1019 function.expect_no_arguments()?;
1020 let predicate = RevsetFilterPredicate::Signed;
1021 Ok(RevsetExpression::filter(predicate))
1022 });
1023 map.insert("mine", |_diagnostics, function, context| {
1024 function.expect_no_arguments()?;
1025 let pattern = StringPattern::exact_i(context.user_email);
1029 let predicate = RevsetFilterPredicate::AuthorEmail(StringExpression::pattern(pattern));
1030 Ok(RevsetExpression::filter(predicate))
1031 });
1032 map.insert("committer", |diagnostics, function, _context| {
1033 let [arg] = function.expect_exact_arguments()?;
1034 let expr = expect_string_expression(diagnostics, arg)?;
1035 let name_predicate = RevsetFilterPredicate::CommitterName(expr.clone());
1036 let email_predicate = RevsetFilterPredicate::CommitterEmail(expr);
1037 Ok(RevsetExpression::filter(name_predicate)
1038 .union(&RevsetExpression::filter(email_predicate)))
1039 });
1040 map.insert("committer_name", |diagnostics, function, _context| {
1041 let [arg] = function.expect_exact_arguments()?;
1042 let expr = expect_string_expression(diagnostics, arg)?;
1043 let predicate = RevsetFilterPredicate::CommitterName(expr);
1044 Ok(RevsetExpression::filter(predicate))
1045 });
1046 map.insert("committer_email", |diagnostics, function, _context| {
1047 let [arg] = function.expect_exact_arguments()?;
1048 let expr = expect_string_expression(diagnostics, arg)?;
1049 let predicate = RevsetFilterPredicate::CommitterEmail(expr);
1050 Ok(RevsetExpression::filter(predicate))
1051 });
1052 map.insert("committer_date", |diagnostics, function, context| {
1053 let [arg] = function.expect_exact_arguments()?;
1054 let pattern = expect_date_pattern(diagnostics, arg, context.date_pattern_context())?;
1055 Ok(RevsetExpression::filter(
1056 RevsetFilterPredicate::CommitterDate(pattern),
1057 ))
1058 });
1059 map.insert("empty", |_diagnostics, function, _context| {
1060 function.expect_no_arguments()?;
1061 Ok(RevsetExpression::is_empty())
1062 });
1063 map.insert("files", |diagnostics, function, context| {
1064 let ctx = context.workspace.as_ref().ok_or_else(|| {
1065 RevsetParseError::with_span(
1066 RevsetParseErrorKind::FsPathWithoutWorkspace,
1067 function.args_span, )
1069 })?;
1070 let [arg] = function.expect_exact_arguments()?;
1071 let expr = expect_fileset_expression(diagnostics, arg, ctx.path_converter)?;
1072 Ok(RevsetExpression::filter(RevsetFilterPredicate::File(expr)))
1073 });
1074 map.insert("diff_contains", |diagnostics, function, context| {
1075 let ([text_arg], [files_opt_arg]) = function.expect_arguments()?;
1076 let text = expect_string_expression(diagnostics, text_arg)?;
1077 let files = if let Some(files_arg) = files_opt_arg {
1078 let ctx = context.workspace.as_ref().ok_or_else(|| {
1079 RevsetParseError::with_span(
1080 RevsetParseErrorKind::FsPathWithoutWorkspace,
1081 files_arg.span,
1082 )
1083 })?;
1084 expect_fileset_expression(diagnostics, files_arg, ctx.path_converter)?
1085 } else {
1086 FilesetExpression::all()
1089 };
1090 Ok(RevsetExpression::filter(
1091 RevsetFilterPredicate::DiffContains { text, files },
1092 ))
1093 });
1094 map.insert("conflicts", |_diagnostics, function, _context| {
1095 function.expect_no_arguments()?;
1096 Ok(RevsetExpression::filter(RevsetFilterPredicate::HasConflict))
1097 });
1098 map.insert("present", |diagnostics, function, context| {
1099 let [arg] = function.expect_exact_arguments()?;
1100 let expression = lower_expression(diagnostics, arg, context)?;
1101 Ok(expression.present())
1102 });
1103 map.insert("at_operation", |diagnostics, function, context| {
1104 let [op_arg, cand_arg] = function.expect_exact_arguments()?;
1105 let operation = revset_parser::catch_aliases(diagnostics, op_arg, |_diagnostics, node| {
1107 Ok(node.span.as_str().to_owned())
1108 })?;
1109 let candidates = lower_expression(diagnostics, cand_arg, context)?;
1110 Ok(Arc::new(RevsetExpression::AtOperation {
1111 operation,
1112 candidates,
1113 }))
1114 });
1115 map.insert("coalesce", |diagnostics, function, context| {
1116 let ([], args) = function.expect_some_arguments()?;
1117 let expressions: Vec<_> = args
1118 .iter()
1119 .map(|arg| lower_expression(diagnostics, arg, context))
1120 .try_collect()?;
1121 Ok(RevsetExpression::coalesce(&expressions))
1122 });
1123 map
1124});
1125
1126pub fn expect_fileset_expression(
1128 diagnostics: &mut RevsetDiagnostics,
1129 node: &ExpressionNode,
1130 path_converter: &RepoPathUiConverter,
1131) -> Result<FilesetExpression, RevsetParseError> {
1132 revset_parser::catch_aliases(diagnostics, node, |diagnostics, node| {
1136 let mut inner_diagnostics = FilesetDiagnostics::new();
1137 let expression = fileset::parse(&mut inner_diagnostics, node.span.as_str(), path_converter)
1138 .map_err(|err| {
1139 RevsetParseError::expression("In fileset expression", node.span).with_source(err)
1140 })?;
1141 diagnostics.extend_with(inner_diagnostics, |diag| {
1142 RevsetParseError::expression("In fileset expression", node.span).with_source(diag)
1143 });
1144 Ok(expression)
1145 })
1146}
1147
1148pub fn expect_string_expression(
1150 diagnostics: &mut RevsetDiagnostics,
1151 node: &ExpressionNode,
1152) -> Result<StringExpression, RevsetParseError> {
1153 revset_parser::catch_aliases(diagnostics, node, |diagnostics, node| {
1154 let expr_error = || RevsetParseError::expression("Invalid string expression", node.span);
1155 let pattern_error = || RevsetParseError::expression("Invalid string pattern", node.span);
1156 match &node.kind {
1157 ExpressionKind::Identifier(value) => Ok(StringExpression::substring(*value)),
1158 ExpressionKind::String(value) => Ok(StringExpression::substring(value)),
1159 ExpressionKind::StringPattern { kind, value } => {
1160 let pattern = StringPattern::from_str_kind(value, kind)
1161 .map_err(|err| pattern_error().with_source(err))?;
1162 Ok(StringExpression::pattern(pattern))
1163 }
1164 ExpressionKind::RemoteSymbol(_)
1165 | ExpressionKind::AtWorkspace(_)
1166 | ExpressionKind::AtCurrentWorkspace
1167 | ExpressionKind::DagRangeAll
1168 | ExpressionKind::RangeAll => Err(expr_error()),
1169 ExpressionKind::Unary(op, arg_node) => {
1170 let arg = expect_string_expression(diagnostics, arg_node)?;
1171 match op {
1172 UnaryOp::Negate => Ok(arg.negated()),
1173 UnaryOp::DagRangePre
1174 | UnaryOp::DagRangePost
1175 | UnaryOp::RangePre
1176 | UnaryOp::RangePost
1177 | UnaryOp::Parents
1178 | UnaryOp::Children => Err(expr_error()),
1179 }
1180 }
1181 ExpressionKind::Binary(op, lhs_node, rhs_node) => {
1182 let lhs = expect_string_expression(diagnostics, lhs_node)?;
1183 let rhs = expect_string_expression(diagnostics, rhs_node)?;
1184 match op {
1185 BinaryOp::Intersection => Ok(lhs.intersection(rhs)),
1186 BinaryOp::Difference => Ok(lhs.intersection(rhs.negated())),
1187 BinaryOp::DagRange | BinaryOp::Range => Err(expr_error()),
1188 }
1189 }
1190 ExpressionKind::UnionAll(nodes) => {
1191 let expressions = nodes
1192 .iter()
1193 .map(|node| expect_string_expression(diagnostics, node))
1194 .try_collect()?;
1195 Ok(StringExpression::union_all(expressions))
1196 }
1197 ExpressionKind::FunctionCall(_) | ExpressionKind::Modifier(_) => Err(expr_error()),
1198 ExpressionKind::AliasExpanded(..) => unreachable!(),
1199 }
1200 })
1201}
1202
1203pub fn expect_date_pattern(
1204 diagnostics: &mut RevsetDiagnostics,
1205 node: &ExpressionNode,
1206 context: &DatePatternContext,
1207) -> Result<DatePattern, RevsetParseError> {
1208 revset_parser::catch_aliases(diagnostics, node, |_diagnostics, node| {
1209 let (value, kind) = revset_parser::expect_string_pattern("date pattern", node)?;
1210 let kind = kind.ok_or_else(|| {
1211 RevsetParseError::expression("Date pattern must specify 'after' or 'before'", node.span)
1212 })?;
1213 context.parse_relative(value, kind).map_err(|err| {
1214 RevsetParseError::expression("Invalid date pattern", node.span).with_source(err)
1215 })
1216 })
1217}
1218
1219fn parse_remote_bookmarks_arguments(
1220 diagnostics: &mut RevsetDiagnostics,
1221 function: &FunctionCallNode,
1222 remote_ref_state: Option<RemoteRefState>,
1223 default_ignored_remote: Option<&RemoteName>,
1224) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
1225 let ([], [bookmark_opt_arg, remote_opt_arg]) =
1226 function.expect_named_arguments(&["", "remote"])?;
1227 let bookmark_expr = if let Some(bookmark_arg) = bookmark_opt_arg {
1228 expect_string_expression(diagnostics, bookmark_arg)?
1229 } else {
1230 StringExpression::all()
1231 };
1232 let remote_expr = if let Some(remote_arg) = remote_opt_arg {
1233 expect_string_expression(diagnostics, remote_arg)?
1234 } else if let Some(remote) = default_ignored_remote {
1235 StringExpression::exact(remote).negated()
1236 } else {
1237 StringExpression::all()
1238 };
1239 Ok(RevsetExpression::remote_bookmarks(
1240 bookmark_expr,
1241 remote_expr,
1242 remote_ref_state,
1243 ))
1244}
1245
1246fn lower_function_call(
1248 diagnostics: &mut RevsetDiagnostics,
1249 function: &FunctionCallNode,
1250 context: &LoweringContext,
1251) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
1252 let function_map = &context.extensions.function_map;
1253 if let Some(func) = function_map.get(function.name) {
1254 func(diagnostics, function, context)
1255 } else {
1256 Err(RevsetParseError::with_span(
1257 RevsetParseErrorKind::NoSuchFunction {
1258 name: function.name.to_owned(),
1259 candidates: collect_similar(function.name, function_map.keys()),
1260 },
1261 function.name_span,
1262 ))
1263 }
1264}
1265
1266pub fn lower_expression(
1269 diagnostics: &mut RevsetDiagnostics,
1270 node: &ExpressionNode,
1271 context: &LoweringContext,
1272) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
1273 revset_parser::catch_aliases(diagnostics, node, |diagnostics, node| match &node.kind {
1274 ExpressionKind::Identifier(name) => Ok(RevsetExpression::symbol((*name).to_owned())),
1275 ExpressionKind::String(name) => Ok(RevsetExpression::symbol(name.to_owned())),
1276 ExpressionKind::StringPattern { .. } => Err(RevsetParseError::with_span(
1277 RevsetParseErrorKind::NotInfixOperator {
1278 op: ":".to_owned(),
1279 similar_op: "::".to_owned(),
1280 description: "DAG range".to_owned(),
1281 },
1282 node.span,
1283 )),
1284 ExpressionKind::RemoteSymbol(symbol) => Ok(RevsetExpression::remote_symbol(symbol.clone())),
1285 ExpressionKind::AtWorkspace(name) => Ok(RevsetExpression::working_copy(name.into())),
1286 ExpressionKind::AtCurrentWorkspace => {
1287 let ctx = context.workspace.as_ref().ok_or_else(|| {
1288 RevsetParseError::with_span(
1289 RevsetParseErrorKind::WorkingCopyWithoutWorkspace,
1290 node.span,
1291 )
1292 })?;
1293 Ok(RevsetExpression::working_copy(
1294 ctx.workspace_name.to_owned(),
1295 ))
1296 }
1297 ExpressionKind::DagRangeAll => Ok(RevsetExpression::all()),
1298 ExpressionKind::RangeAll => Ok(RevsetExpression::root().negated()),
1299 ExpressionKind::Unary(op, arg_node) => {
1300 let arg = lower_expression(diagnostics, arg_node, context)?;
1301 match op {
1302 UnaryOp::Negate => Ok(arg.negated()),
1303 UnaryOp::DagRangePre => Ok(arg.ancestors()),
1304 UnaryOp::DagRangePost => Ok(arg.descendants()),
1305 UnaryOp::RangePre => Ok(RevsetExpression::root().range(&arg)),
1306 UnaryOp::RangePost => Ok(arg.ancestors().negated()),
1307 UnaryOp::Parents => Ok(arg.parents()),
1308 UnaryOp::Children => Ok(arg.children()),
1309 }
1310 }
1311 ExpressionKind::Binary(op, lhs_node, rhs_node) => {
1312 let lhs = lower_expression(diagnostics, lhs_node, context)?;
1313 let rhs = lower_expression(diagnostics, rhs_node, context)?;
1314 match op {
1315 BinaryOp::Intersection => Ok(lhs.intersection(&rhs)),
1316 BinaryOp::Difference => Ok(lhs.minus(&rhs)),
1317 BinaryOp::DagRange => Ok(lhs.dag_range_to(&rhs)),
1318 BinaryOp::Range => Ok(lhs.range(&rhs)),
1319 }
1320 }
1321 ExpressionKind::UnionAll(nodes) => {
1322 let expressions: Vec<_> = nodes
1323 .iter()
1324 .map(|node| lower_expression(diagnostics, node, context))
1325 .try_collect()?;
1326 Ok(RevsetExpression::union_all(&expressions))
1327 }
1328 ExpressionKind::FunctionCall(function) => {
1329 lower_function_call(diagnostics, function, context)
1330 }
1331 ExpressionKind::Modifier(modifier) => {
1332 let name = modifier.name;
1333 Err(RevsetParseError::expression(
1334 format!("Modifier `{name}:` is not allowed in sub expression"),
1335 modifier.name_span,
1336 ))
1337 }
1338 ExpressionKind::AliasExpanded(..) => unreachable!(),
1339 })
1340}
1341
1342pub fn parse(
1343 diagnostics: &mut RevsetDiagnostics,
1344 revset_str: &str,
1345 context: &RevsetParseContext,
1346) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
1347 let node = parse_program(revset_str)?;
1348 let node =
1349 dsl_util::expand_aliases_with_locals(node, context.aliases_map, &context.local_variables)?;
1350 lower_expression(diagnostics, &node, &context.to_lowering_context())
1351 .map_err(|err| err.extend_function_candidates(context.aliases_map.function_names()))
1352}
1353
1354pub fn parse_with_modifier(
1355 diagnostics: &mut RevsetDiagnostics,
1356 revset_str: &str,
1357 context: &RevsetParseContext,
1358) -> Result<(Arc<UserRevsetExpression>, Option<RevsetModifier>), RevsetParseError> {
1359 let node = parse_program(revset_str)?;
1360 let node =
1361 dsl_util::expand_aliases_with_locals(node, context.aliases_map, &context.local_variables)?;
1362 revset_parser::catch_aliases(diagnostics, &node, |diagnostics, node| match &node.kind {
1363 ExpressionKind::Modifier(modifier) => {
1364 let parsed_modifier = match modifier.name {
1365 "all" => {
1366 diagnostics.add_warning(RevsetParseError::expression(
1367 "Multiple revisions are allowed by default; `all:` is planned for removal",
1368 modifier.name_span,
1369 ));
1370 RevsetModifier::All
1371 }
1372 _ => {
1373 return Err(RevsetParseError::with_span(
1374 RevsetParseErrorKind::NoSuchModifier(modifier.name.to_owned()),
1375 modifier.name_span,
1376 ));
1377 }
1378 };
1379 let parsed_body =
1380 lower_expression(diagnostics, &modifier.body, &context.to_lowering_context())?;
1381 Ok((parsed_body, Some(parsed_modifier)))
1382 }
1383 _ => {
1384 let parsed_body = lower_expression(diagnostics, node, &context.to_lowering_context())?;
1385 Ok((parsed_body, None))
1386 }
1387 })
1388 .map_err(|err| err.extend_function_candidates(context.aliases_map.function_names()))
1389}
1390
1391fn to_binary_expression<T: Clone>(
1394 expressions: &[T],
1395 unit: &impl Fn() -> T,
1396 binary: &impl Fn(&T, &T) -> T,
1397) -> T {
1398 match expressions {
1399 [] => unit(),
1400 [expression] => expression.clone(),
1401 _ => {
1402 let (left, right) = expressions.split_at(expressions.len() / 2);
1404 binary(
1405 &to_binary_expression(left, unit, binary),
1406 &to_binary_expression(right, unit, binary),
1407 )
1408 }
1409 }
1410}
1411
1412type TransformedExpression<St> = Option<Arc<RevsetExpression<St>>>;
1414type PreTransformedExpression<St> = ControlFlow<TransformedExpression<St>, ()>;
1417
1418fn transform_expression<St: ExpressionState>(
1420 expression: &Arc<RevsetExpression<St>>,
1421 mut pre: impl FnMut(&Arc<RevsetExpression<St>>) -> PreTransformedExpression<St>,
1422 mut post: impl FnMut(&Arc<RevsetExpression<St>>) -> TransformedExpression<St>,
1423) -> TransformedExpression<St> {
1424 let Ok(transformed) =
1425 try_transform_expression::<St, Infallible>(expression, |x| Ok(pre(x)), |x| Ok(post(x)));
1426 transformed
1427}
1428
1429fn transform_expression_bottom_up<St: ExpressionState>(
1431 expression: &Arc<RevsetExpression<St>>,
1432 post: impl FnMut(&Arc<RevsetExpression<St>>) -> TransformedExpression<St>,
1433) -> TransformedExpression<St> {
1434 transform_expression(expression, |_| ControlFlow::Continue(()), post)
1435}
1436
1437fn try_transform_expression<St: ExpressionState, E>(
1450 expression: &Arc<RevsetExpression<St>>,
1451 mut pre: impl FnMut(&Arc<RevsetExpression<St>>) -> Result<PreTransformedExpression<St>, E>,
1452 mut post: impl FnMut(&Arc<RevsetExpression<St>>) -> Result<TransformedExpression<St>, E>,
1453) -> Result<TransformedExpression<St>, E> {
1454 fn transform_child_rec<St: ExpressionState, E>(
1455 expression: &Arc<RevsetExpression<St>>,
1456 pre: &mut impl FnMut(&Arc<RevsetExpression<St>>) -> Result<PreTransformedExpression<St>, E>,
1457 post: &mut impl FnMut(&Arc<RevsetExpression<St>>) -> Result<TransformedExpression<St>, E>,
1458 ) -> Result<TransformedExpression<St>, E> {
1459 Ok(match expression.as_ref() {
1460 RevsetExpression::None => None,
1461 RevsetExpression::All => None,
1462 RevsetExpression::VisibleHeads => None,
1463 RevsetExpression::VisibleHeadsOrReferenced => None,
1464 RevsetExpression::Root => None,
1465 RevsetExpression::Commits(_) => None,
1466 RevsetExpression::CommitRef(_) => None,
1467 RevsetExpression::Ancestors {
1468 heads,
1469 generation,
1470 parents_range,
1471 } => transform_rec(heads, pre, post)?.map(|heads| RevsetExpression::Ancestors {
1472 heads,
1473 generation: generation.clone(),
1474 parents_range: parents_range.clone(),
1475 }),
1476 RevsetExpression::Descendants { roots, generation } => transform_rec(roots, pre, post)?
1477 .map(|roots| RevsetExpression::Descendants {
1478 roots,
1479 generation: generation.clone(),
1480 }),
1481 RevsetExpression::Range {
1482 roots,
1483 heads,
1484 generation,
1485 parents_range,
1486 } => transform_rec_pair((roots, heads), pre, post)?.map(|(roots, heads)| {
1487 RevsetExpression::Range {
1488 roots,
1489 heads,
1490 generation: generation.clone(),
1491 parents_range: parents_range.clone(),
1492 }
1493 }),
1494 RevsetExpression::DagRange { roots, heads } => {
1495 transform_rec_pair((roots, heads), pre, post)?
1496 .map(|(roots, heads)| RevsetExpression::DagRange { roots, heads })
1497 }
1498 RevsetExpression::Reachable { sources, domain } => {
1499 transform_rec_pair((sources, domain), pre, post)?
1500 .map(|(sources, domain)| RevsetExpression::Reachable { sources, domain })
1501 }
1502 RevsetExpression::Heads(candidates) => {
1503 transform_rec(candidates, pre, post)?.map(RevsetExpression::Heads)
1504 }
1505 RevsetExpression::HeadsRange {
1506 roots,
1507 heads,
1508 parents_range,
1509 filter,
1510 } => {
1511 let transformed_roots = transform_rec(roots, pre, post)?;
1512 let transformed_heads = transform_rec(heads, pre, post)?;
1513 let transformed_filter = transform_rec(filter, pre, post)?;
1514 (transformed_roots.is_some()
1515 || transformed_heads.is_some()
1516 || transformed_filter.is_some())
1517 .then(|| RevsetExpression::HeadsRange {
1518 roots: transformed_roots.unwrap_or_else(|| roots.clone()),
1519 heads: transformed_heads.unwrap_or_else(|| heads.clone()),
1520 parents_range: parents_range.clone(),
1521 filter: transformed_filter.unwrap_or_else(|| filter.clone()),
1522 })
1523 }
1524 RevsetExpression::Roots(candidates) => {
1525 transform_rec(candidates, pre, post)?.map(RevsetExpression::Roots)
1526 }
1527 RevsetExpression::ForkPoint(expression) => {
1528 transform_rec(expression, pre, post)?.map(RevsetExpression::ForkPoint)
1529 }
1530 RevsetExpression::Bisect(expression) => {
1531 transform_rec(expression, pre, post)?.map(RevsetExpression::Bisect)
1532 }
1533 RevsetExpression::HasSize { candidates, count } => {
1534 transform_rec(candidates, pre, post)?.map(|candidates| RevsetExpression::HasSize {
1535 candidates,
1536 count: *count,
1537 })
1538 }
1539 RevsetExpression::Latest { candidates, count } => transform_rec(candidates, pre, post)?
1540 .map(|candidates| RevsetExpression::Latest {
1541 candidates,
1542 count: *count,
1543 }),
1544 RevsetExpression::Filter(_) => None,
1545 RevsetExpression::AsFilter(candidates) => {
1546 transform_rec(candidates, pre, post)?.map(RevsetExpression::AsFilter)
1547 }
1548 RevsetExpression::AtOperation {
1549 operation,
1550 candidates,
1551 } => transform_rec(candidates, pre, post)?.map(|candidates| {
1552 RevsetExpression::AtOperation {
1553 operation: operation.clone(),
1554 candidates,
1555 }
1556 }),
1557 RevsetExpression::WithinReference {
1558 candidates,
1559 commits,
1560 } => transform_rec(candidates, pre, post)?.map(|candidates| {
1561 RevsetExpression::WithinReference {
1562 candidates,
1563 commits: commits.clone(),
1564 }
1565 }),
1566 RevsetExpression::WithinVisibility {
1567 candidates,
1568 visible_heads,
1569 } => transform_rec(candidates, pre, post)?.map(|candidates| {
1570 RevsetExpression::WithinVisibility {
1571 candidates,
1572 visible_heads: visible_heads.clone(),
1573 }
1574 }),
1575 RevsetExpression::Coalesce(expression1, expression2) => transform_rec_pair(
1576 (expression1, expression2),
1577 pre,
1578 post,
1579 )?
1580 .map(|(expression1, expression2)| RevsetExpression::Coalesce(expression1, expression2)),
1581 RevsetExpression::Present(candidates) => {
1582 transform_rec(candidates, pre, post)?.map(RevsetExpression::Present)
1583 }
1584 RevsetExpression::NotIn(complement) => {
1585 transform_rec(complement, pre, post)?.map(RevsetExpression::NotIn)
1586 }
1587 RevsetExpression::Union(expression1, expression2) => {
1588 transform_rec_pair((expression1, expression2), pre, post)?.map(
1589 |(expression1, expression2)| RevsetExpression::Union(expression1, expression2),
1590 )
1591 }
1592 RevsetExpression::Intersection(expression1, expression2) => {
1593 transform_rec_pair((expression1, expression2), pre, post)?.map(
1594 |(expression1, expression2)| {
1595 RevsetExpression::Intersection(expression1, expression2)
1596 },
1597 )
1598 }
1599 RevsetExpression::Difference(expression1, expression2) => {
1600 transform_rec_pair((expression1, expression2), pre, post)?.map(
1601 |(expression1, expression2)| {
1602 RevsetExpression::Difference(expression1, expression2)
1603 },
1604 )
1605 }
1606 }
1607 .map(Arc::new))
1608 }
1609
1610 #[expect(clippy::type_complexity)]
1611 fn transform_rec_pair<St: ExpressionState, E>(
1612 (expression1, expression2): (&Arc<RevsetExpression<St>>, &Arc<RevsetExpression<St>>),
1613 pre: &mut impl FnMut(&Arc<RevsetExpression<St>>) -> Result<PreTransformedExpression<St>, E>,
1614 post: &mut impl FnMut(&Arc<RevsetExpression<St>>) -> Result<TransformedExpression<St>, E>,
1615 ) -> Result<Option<(Arc<RevsetExpression<St>>, Arc<RevsetExpression<St>>)>, E> {
1616 match (
1617 transform_rec(expression1, pre, post)?,
1618 transform_rec(expression2, pre, post)?,
1619 ) {
1620 (Some(new_expression1), Some(new_expression2)) => {
1621 Ok(Some((new_expression1, new_expression2)))
1622 }
1623 (Some(new_expression1), None) => Ok(Some((new_expression1, expression2.clone()))),
1624 (None, Some(new_expression2)) => Ok(Some((expression1.clone(), new_expression2))),
1625 (None, None) => Ok(None),
1626 }
1627 }
1628
1629 fn transform_rec<St: ExpressionState, E>(
1630 expression: &Arc<RevsetExpression<St>>,
1631 pre: &mut impl FnMut(&Arc<RevsetExpression<St>>) -> Result<PreTransformedExpression<St>, E>,
1632 post: &mut impl FnMut(&Arc<RevsetExpression<St>>) -> Result<TransformedExpression<St>, E>,
1633 ) -> Result<TransformedExpression<St>, E> {
1634 if let ControlFlow::Break(transformed) = pre(expression)? {
1635 return Ok(transformed);
1636 }
1637 if let Some(new_expression) = transform_child_rec(expression, pre, post)? {
1638 Ok(Some(post(&new_expression)?.unwrap_or(new_expression)))
1640 } else {
1641 post(expression)
1642 }
1643 }
1644
1645 transform_rec(expression, &mut pre, &mut post)
1646}
1647
1648trait ExpressionStateFolder<InSt: ExpressionState, OutSt: ExpressionState> {
1653 type Error;
1654
1655 fn fold_expression(
1658 &mut self,
1659 expression: &RevsetExpression<InSt>,
1660 ) -> Result<Arc<RevsetExpression<OutSt>>, Self::Error> {
1661 fold_child_expression_state(self, expression)
1662 }
1663
1664 fn fold_commit_ref(
1666 &mut self,
1667 commit_ref: &InSt::CommitRef,
1668 ) -> Result<Arc<RevsetExpression<OutSt>>, Self::Error>;
1669
1670 fn fold_at_operation(
1672 &mut self,
1673 operation: &InSt::Operation,
1674 candidates: &RevsetExpression<InSt>,
1675 ) -> Result<Arc<RevsetExpression<OutSt>>, Self::Error>;
1676}
1677
1678fn fold_child_expression_state<InSt, OutSt, F>(
1680 folder: &mut F,
1681 expression: &RevsetExpression<InSt>,
1682) -> Result<Arc<RevsetExpression<OutSt>>, F::Error>
1683where
1684 InSt: ExpressionState,
1685 OutSt: ExpressionState,
1686 F: ExpressionStateFolder<InSt, OutSt> + ?Sized,
1687{
1688 let expression: Arc<_> = match expression {
1689 RevsetExpression::None => RevsetExpression::None.into(),
1690 RevsetExpression::All => RevsetExpression::All.into(),
1691 RevsetExpression::VisibleHeads => RevsetExpression::VisibleHeads.into(),
1692 RevsetExpression::VisibleHeadsOrReferenced => {
1693 RevsetExpression::VisibleHeadsOrReferenced.into()
1694 }
1695 RevsetExpression::Root => RevsetExpression::Root.into(),
1696 RevsetExpression::Commits(ids) => RevsetExpression::Commits(ids.clone()).into(),
1697 RevsetExpression::CommitRef(commit_ref) => folder.fold_commit_ref(commit_ref)?,
1698 RevsetExpression::Ancestors {
1699 heads,
1700 generation,
1701 parents_range,
1702 } => {
1703 let heads = folder.fold_expression(heads)?;
1704 let generation = generation.clone();
1705 let parents_range = parents_range.clone();
1706 RevsetExpression::Ancestors {
1707 heads,
1708 generation,
1709 parents_range,
1710 }
1711 .into()
1712 }
1713 RevsetExpression::Descendants { roots, generation } => {
1714 let roots = folder.fold_expression(roots)?;
1715 let generation = generation.clone();
1716 RevsetExpression::Descendants { roots, generation }.into()
1717 }
1718 RevsetExpression::Range {
1719 roots,
1720 heads,
1721 generation,
1722 parents_range,
1723 } => {
1724 let roots = folder.fold_expression(roots)?;
1725 let heads = folder.fold_expression(heads)?;
1726 let generation = generation.clone();
1727 let parents_range = parents_range.clone();
1728 RevsetExpression::Range {
1729 roots,
1730 heads,
1731 generation,
1732 parents_range,
1733 }
1734 .into()
1735 }
1736 RevsetExpression::DagRange { roots, heads } => {
1737 let roots = folder.fold_expression(roots)?;
1738 let heads = folder.fold_expression(heads)?;
1739 RevsetExpression::DagRange { roots, heads }.into()
1740 }
1741 RevsetExpression::Reachable { sources, domain } => {
1742 let sources = folder.fold_expression(sources)?;
1743 let domain = folder.fold_expression(domain)?;
1744 RevsetExpression::Reachable { sources, domain }.into()
1745 }
1746 RevsetExpression::Heads(heads) => {
1747 let heads = folder.fold_expression(heads)?;
1748 RevsetExpression::Heads(heads).into()
1749 }
1750 RevsetExpression::HeadsRange {
1751 roots,
1752 heads,
1753 parents_range,
1754 filter,
1755 } => {
1756 let roots = folder.fold_expression(roots)?;
1757 let heads = folder.fold_expression(heads)?;
1758 let parents_range = parents_range.clone();
1759 let filter = folder.fold_expression(filter)?;
1760 RevsetExpression::HeadsRange {
1761 roots,
1762 heads,
1763 parents_range,
1764 filter,
1765 }
1766 .into()
1767 }
1768 RevsetExpression::Roots(roots) => {
1769 let roots = folder.fold_expression(roots)?;
1770 RevsetExpression::Roots(roots).into()
1771 }
1772 RevsetExpression::ForkPoint(expression) => {
1773 let expression = folder.fold_expression(expression)?;
1774 RevsetExpression::ForkPoint(expression).into()
1775 }
1776 RevsetExpression::Bisect(expression) => {
1777 let expression = folder.fold_expression(expression)?;
1778 RevsetExpression::Bisect(expression).into()
1779 }
1780 RevsetExpression::HasSize { candidates, count } => {
1781 let candidates = folder.fold_expression(candidates)?;
1782 RevsetExpression::HasSize {
1783 candidates,
1784 count: *count,
1785 }
1786 .into()
1787 }
1788 RevsetExpression::Latest { candidates, count } => {
1789 let candidates = folder.fold_expression(candidates)?;
1790 let count = *count;
1791 RevsetExpression::Latest { candidates, count }.into()
1792 }
1793 RevsetExpression::Filter(predicate) => RevsetExpression::Filter(predicate.clone()).into(),
1794 RevsetExpression::AsFilter(candidates) => {
1795 let candidates = folder.fold_expression(candidates)?;
1796 RevsetExpression::AsFilter(candidates).into()
1797 }
1798 RevsetExpression::AtOperation {
1799 operation,
1800 candidates,
1801 } => folder.fold_at_operation(operation, candidates)?,
1802 RevsetExpression::WithinReference {
1803 candidates,
1804 commits,
1805 } => {
1806 let candidates = folder.fold_expression(candidates)?;
1807 let commits = commits.clone();
1808 RevsetExpression::WithinReference {
1809 candidates,
1810 commits,
1811 }
1812 .into()
1813 }
1814 RevsetExpression::WithinVisibility {
1815 candidates,
1816 visible_heads,
1817 } => {
1818 let candidates = folder.fold_expression(candidates)?;
1819 let visible_heads = visible_heads.clone();
1820 RevsetExpression::WithinVisibility {
1821 candidates,
1822 visible_heads,
1823 }
1824 .into()
1825 }
1826 RevsetExpression::Coalesce(expression1, expression2) => {
1827 let expression1 = folder.fold_expression(expression1)?;
1828 let expression2 = folder.fold_expression(expression2)?;
1829 RevsetExpression::Coalesce(expression1, expression2).into()
1830 }
1831 RevsetExpression::Present(candidates) => {
1832 let candidates = folder.fold_expression(candidates)?;
1833 RevsetExpression::Present(candidates).into()
1834 }
1835 RevsetExpression::NotIn(complement) => {
1836 let complement = folder.fold_expression(complement)?;
1837 RevsetExpression::NotIn(complement).into()
1838 }
1839 RevsetExpression::Union(expression1, expression2) => {
1840 let expression1 = folder.fold_expression(expression1)?;
1841 let expression2 = folder.fold_expression(expression2)?;
1842 RevsetExpression::Union(expression1, expression2).into()
1843 }
1844 RevsetExpression::Intersection(expression1, expression2) => {
1845 let expression1 = folder.fold_expression(expression1)?;
1846 let expression2 = folder.fold_expression(expression2)?;
1847 RevsetExpression::Intersection(expression1, expression2).into()
1848 }
1849 RevsetExpression::Difference(expression1, expression2) => {
1850 let expression1 = folder.fold_expression(expression1)?;
1851 let expression2 = folder.fold_expression(expression2)?;
1852 RevsetExpression::Difference(expression1, expression2).into()
1853 }
1854 };
1855 Ok(expression)
1856}
1857
1858fn resolve_referenced_commits<St: ExpressionState>(
1862 expression: &Arc<RevsetExpression<St>>,
1863) -> TransformedExpression<St> {
1864 if matches!(
1866 expression.as_ref(),
1867 RevsetExpression::WithinReference { .. }
1868 ) {
1869 return None;
1870 }
1871
1872 let mut inner_commits = Vec::new();
1874 let mut outer_commits = Vec::new();
1875 let transformed = transform_expression(
1876 expression,
1877 |expression| match expression.as_ref() {
1878 RevsetExpression::WithinReference { commits, .. } => {
1880 inner_commits.extend_from_slice(commits);
1881 ControlFlow::Break(None)
1882 }
1883 RevsetExpression::WithinVisibility {
1885 candidates,
1886 visible_heads,
1887 } => {
1888 inner_commits.extend_from_slice(visible_heads);
1890 let transformed = resolve_referenced_commits(candidates);
1891 if let RevsetExpression::WithinReference { commits, .. } =
1893 transformed.as_deref().unwrap_or(candidates)
1894 {
1895 inner_commits.extend_from_slice(commits);
1896 }
1897 ControlFlow::Break(transformed.map(|candidates| {
1898 Arc::new(RevsetExpression::WithinVisibility {
1899 candidates,
1900 visible_heads: visible_heads.clone(),
1901 })
1902 }))
1903 }
1904 _ => ControlFlow::Continue(()),
1905 },
1906 |expression| {
1907 if let RevsetExpression::Commits(commits) = expression.as_ref() {
1908 outer_commits.extend_from_slice(commits);
1909 }
1910 None
1911 },
1912 );
1913
1914 outer_commits.extend(inner_commits);
1917 if outer_commits.is_empty() {
1918 return transformed;
1920 }
1921 Some(Arc::new(RevsetExpression::WithinReference {
1922 candidates: transformed.unwrap_or_else(|| expression.clone()),
1923 commits: outer_commits,
1924 }))
1925}
1926
1927fn flatten_intersections<St: ExpressionState>(
1930 expression: &Arc<RevsetExpression<St>>,
1931) -> TransformedExpression<St> {
1932 fn flatten<St: ExpressionState>(
1933 expression1: &Arc<RevsetExpression<St>>,
1934 expression2: &Arc<RevsetExpression<St>>,
1935 ) -> TransformedExpression<St> {
1936 let recurse = |a, b| flatten(a, b).unwrap_or_else(|| a.intersection(b));
1937
1938 match expression2.as_ref() {
1939 RevsetExpression::Intersection(inner1, inner2) => {
1941 Some(recurse(expression1, inner1).intersection(inner2))
1942 }
1943 _ => None,
1944 }
1945 }
1946
1947 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
1948 RevsetExpression::Intersection(expression1, expression2) => {
1949 flatten(expression1, expression2)
1950 }
1951 _ => None,
1952 })
1953}
1954
1955fn sort_intersection_by_key<St: ExpressionState, T: Ord>(
1959 base: &Arc<RevsetExpression<St>>,
1960 expression: &Arc<RevsetExpression<St>>,
1961 mut get_key: impl FnMut(&RevsetExpression<St>) -> T,
1962) -> TransformedExpression<St> {
1963 fn sort_intersection_helper<St: ExpressionState, T: Ord>(
1966 base: &Arc<RevsetExpression<St>>,
1967 expression: &Arc<RevsetExpression<St>>,
1968 expression_key: T,
1969 mut get_key: impl FnMut(&RevsetExpression<St>) -> T,
1970 ) -> TransformedExpression<St> {
1971 if let RevsetExpression::Intersection(inner1, inner2) = base.as_ref() {
1972 (expression_key < get_key(inner2)).then(|| {
1974 sort_intersection_helper(inner1, expression, expression_key, get_key)
1975 .unwrap_or_else(|| inner1.intersection(expression))
1976 .intersection(inner2)
1977 })
1978 } else {
1979 (expression_key < get_key(base)).then(|| expression.intersection(base))
1981 }
1982 }
1983
1984 sort_intersection_helper(base, expression, get_key(expression), get_key)
1985}
1986
1987fn sort_negations_and_ancestors<St: ExpressionState>(
1992 expression: &Arc<RevsetExpression<St>>,
1993) -> TransformedExpression<St> {
1994 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1995 enum AncestorsOrder {
1996 NegatedAncestors,
1997 Ancestors,
1998 Other,
1999 NegatedOther,
2000 }
2001
2002 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2003 RevsetExpression::Intersection(expression1, expression2) => {
2004 sort_intersection_by_key(expression1, expression2, |expression| match expression {
2005 RevsetExpression::Ancestors {
2006 heads: _,
2007 generation: Range { end: u64::MAX, .. },
2008 parents_range: _,
2009 } => AncestorsOrder::Ancestors,
2010 RevsetExpression::NotIn(complement) => match complement.as_ref() {
2011 RevsetExpression::Ancestors {
2012 heads: _,
2013 generation: Range { end: u64::MAX, .. },
2014 parents_range: PARENTS_RANGE_FULL,
2017 } => AncestorsOrder::NegatedAncestors,
2018 _ => AncestorsOrder::NegatedOther,
2019 },
2020 _ => AncestorsOrder::Other,
2021 })
2022 }
2023 _ => None,
2024 })
2025}
2026
2027fn internalize_filter<St: ExpressionState>(
2036 expression: &Arc<RevsetExpression<St>>,
2037) -> TransformedExpression<St> {
2038 fn get_filter<St: ExpressionState>(
2039 expression: &Arc<RevsetExpression<St>>,
2040 ) -> Option<&Arc<RevsetExpression<St>>> {
2041 match expression.as_ref() {
2042 RevsetExpression::Filter(_) => Some(expression),
2043 RevsetExpression::AsFilter(candidates) => Some(candidates),
2044 _ => None,
2045 }
2046 }
2047
2048 fn mark_filter<St: ExpressionState>(
2049 expression: Arc<RevsetExpression<St>>,
2050 ) -> Arc<RevsetExpression<St>> {
2051 Arc::new(RevsetExpression::AsFilter(expression))
2052 }
2053
2054 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2055 RevsetExpression::Present(e) => get_filter(e).map(|f| mark_filter(f.present())),
2057 RevsetExpression::NotIn(e) => get_filter(e).map(|f| mark_filter(f.negated())),
2058 RevsetExpression::Union(e1, e2) => {
2059 let f1 = get_filter(e1);
2060 let f2 = get_filter(e2);
2061 (f1.is_some() || f2.is_some())
2062 .then(|| mark_filter(f1.unwrap_or(e1).union(f2.unwrap_or(e2))))
2063 }
2064 RevsetExpression::Intersection(e1, e2) => match (get_filter(e1), get_filter(e2)) {
2070 (Some(f1), Some(f2)) => Some(mark_filter(f1.intersection(f2))),
2072 (Some(_), None) => Some(e2.intersection(e1)),
2074 (None, Some(f2)) => match e1.as_ref() {
2076 RevsetExpression::Intersection(e1a, e1b) => {
2077 get_filter(e1b).map(|f1b| e1a.intersection(&mark_filter(f1b.intersection(f2))))
2078 }
2079 _ => None,
2080 },
2081 (None, None) => match e1.as_ref() {
2083 RevsetExpression::Intersection(e1a, e1b) => {
2084 get_filter(e1b).map(|_| e1a.intersection(e2).intersection(e1b))
2085 }
2086 _ => None,
2087 },
2088 },
2089 _ => None,
2091 })
2092}
2093
2094fn fold_redundant_expression<St: ExpressionState>(
2099 expression: &Arc<RevsetExpression<St>>,
2100) -> TransformedExpression<St> {
2101 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2102 RevsetExpression::Commits(commits) if commits.is_empty() => Some(RevsetExpression::none()),
2103 RevsetExpression::NotIn(outer) => match outer.as_ref() {
2104 RevsetExpression::NotIn(inner) => Some(inner.clone()),
2105 RevsetExpression::None => Some(RevsetExpression::all()),
2106 RevsetExpression::All => Some(RevsetExpression::none()),
2107 _ => None,
2108 },
2109 RevsetExpression::Union(expression1, expression2) => {
2110 match (expression1.as_ref(), expression2.as_ref()) {
2111 (_, RevsetExpression::None) => Some(expression1.clone()),
2112 (RevsetExpression::None, _) => Some(expression2.clone()),
2113 (RevsetExpression::All, _) => Some(RevsetExpression::all()),
2114 (_, RevsetExpression::All) => Some(RevsetExpression::all()),
2115 _ => None,
2116 }
2117 }
2118 RevsetExpression::Intersection(expression1, expression2) => {
2119 match (expression1.as_ref(), expression2.as_ref()) {
2120 (RevsetExpression::None, _) => Some(RevsetExpression::none()),
2121 (_, RevsetExpression::None) => Some(RevsetExpression::none()),
2122 (_, RevsetExpression::All) => Some(expression1.clone()),
2123 (RevsetExpression::All, _) => Some(expression2.clone()),
2124 _ => None,
2125 }
2126 }
2127 _ => None,
2128 })
2129}
2130
2131fn ancestors_to_heads<St: ExpressionState>(
2135 expression: &RevsetExpression<St>,
2136) -> Result<Arc<RevsetExpression<St>>, ()> {
2137 match ancestors_to_heads_and_parents_range(expression) {
2138 Ok((heads, PARENTS_RANGE_FULL)) => Ok(heads),
2139 _ => Err(()),
2140 }
2141}
2142
2143fn ancestors_to_heads_and_parents_range<St: ExpressionState>(
2144 expression: &RevsetExpression<St>,
2145) -> Result<(Arc<RevsetExpression<St>>, Range<u32>), ()> {
2146 match expression {
2147 RevsetExpression::Ancestors {
2148 heads,
2149 generation: GENERATION_RANGE_FULL,
2150 parents_range,
2151 } => Ok((heads.clone(), parents_range.clone())),
2152 RevsetExpression::Ancestors {
2153 heads,
2154 generation: Range {
2155 start,
2156 end: u64::MAX,
2157 },
2158 parents_range,
2159 } => Ok((
2160 Arc::new(RevsetExpression::Ancestors {
2161 heads: heads.clone(),
2162 generation: (*start)..start.saturating_add(1),
2163 parents_range: parents_range.clone(),
2164 }),
2165 parents_range.clone(),
2166 )),
2167 _ => Err(()),
2168 }
2169}
2170
2171fn fold_ancestors_union<St: ExpressionState>(
2176 expression: &Arc<RevsetExpression<St>>,
2177) -> TransformedExpression<St> {
2178 fn union_ancestors<St: ExpressionState>(
2179 expression1: &Arc<RevsetExpression<St>>,
2180 expression2: &Arc<RevsetExpression<St>>,
2181 ) -> TransformedExpression<St> {
2182 let heads1 = ancestors_to_heads(expression1).ok()?;
2183 let heads2 = ancestors_to_heads(expression2).ok()?;
2184 Some(heads1.union(&heads2).ancestors())
2185 }
2186
2187 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2188 RevsetExpression::Union(expression1, expression2) => {
2189 union_ancestors(expression1, expression2)
2191 }
2192 RevsetExpression::Intersection(expression1, expression2) => {
2193 match (expression1.as_ref(), expression2.as_ref()) {
2194 (RevsetExpression::NotIn(complement1), RevsetExpression::NotIn(complement2)) => {
2196 union_ancestors(complement1, complement2).map(|expression| expression.negated())
2197 }
2198 _ => None,
2199 }
2200 }
2201 _ => None,
2202 })
2203}
2204
2205fn fold_heads_range<St: ExpressionState>(
2212 expression: &Arc<RevsetExpression<St>>,
2213) -> TransformedExpression<St> {
2214 struct FilteredRange<St: ExpressionState> {
2216 roots: Arc<RevsetExpression<St>>,
2217 heads_and_parents_range: Option<(Arc<RevsetExpression<St>>, Range<u32>)>,
2218 filter: Arc<RevsetExpression<St>>,
2219 }
2220
2221 impl<St: ExpressionState> FilteredRange<St> {
2222 fn new(roots: Arc<RevsetExpression<St>>) -> Self {
2223 Self {
2225 roots,
2226 heads_and_parents_range: None,
2227 filter: RevsetExpression::all(),
2228 }
2229 }
2230
2231 fn add(mut self, expression: &Arc<RevsetExpression<St>>) -> Self {
2232 if self.heads_and_parents_range.is_none() {
2233 if let Ok(heads_and_parents_range) =
2235 ancestors_to_heads_and_parents_range(expression)
2236 {
2237 self.heads_and_parents_range = Some(heads_and_parents_range);
2238 return self;
2239 }
2240 }
2241 self.add_filter(expression)
2242 }
2243
2244 fn add_filter(mut self, expression: &Arc<RevsetExpression<St>>) -> Self {
2245 self.filter = if let RevsetExpression::All = self.filter.as_ref() {
2246 expression.clone()
2248 } else {
2249 self.filter.intersection(expression)
2250 };
2251 self
2252 }
2253 }
2254
2255 fn to_filtered_range<St: ExpressionState>(
2256 expression: &Arc<RevsetExpression<St>>,
2257 ) -> Option<FilteredRange<St>> {
2258 if let Ok(heads_and_parents_range) = ancestors_to_heads_and_parents_range(expression) {
2262 return Some(FilteredRange {
2263 roots: RevsetExpression::none(),
2264 heads_and_parents_range: Some(heads_and_parents_range),
2265 filter: RevsetExpression::all(),
2266 });
2267 }
2268 match expression.as_ref() {
2269 RevsetExpression::NotIn(complement) => {
2272 if let Ok(roots) = ancestors_to_heads(complement) {
2273 Some(FilteredRange::new(roots))
2274 } else {
2275 Some(FilteredRange::new(RevsetExpression::none()).add_filter(expression))
2278 }
2279 }
2280 RevsetExpression::All | RevsetExpression::Filter(_) | RevsetExpression::AsFilter(_) => {
2282 Some(FilteredRange::new(RevsetExpression::none()).add_filter(expression))
2283 }
2284 RevsetExpression::Intersection(expression1, expression2) => {
2287 to_filtered_range(expression1).map(|filtered_range| filtered_range.add(expression2))
2288 }
2289 _ => None,
2290 }
2291 }
2292
2293 fn to_heads_range<St: ExpressionState>(
2294 candidates: &Arc<RevsetExpression<St>>,
2295 ) -> Option<Arc<RevsetExpression<St>>> {
2296 to_filtered_range(candidates).map(|filtered_range| {
2297 let (heads, parents_range) =
2298 filtered_range.heads_and_parents_range.unwrap_or_else(|| {
2299 (
2300 RevsetExpression::visible_heads_or_referenced(),
2301 PARENTS_RANGE_FULL,
2302 )
2303 });
2304 RevsetExpression::HeadsRange {
2305 roots: filtered_range.roots,
2306 heads,
2307 parents_range,
2308 filter: filtered_range.filter,
2309 }
2310 .into()
2311 })
2312 }
2313
2314 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2315 RevsetExpression::Ancestors {
2318 heads,
2319 generation: GENERATION_RANGE_FULL,
2322 parents_range: PARENTS_RANGE_FULL,
2323 } => to_heads_range(heads).map(|heads| heads.ancestors()),
2324 RevsetExpression::Heads(candidates) => to_heads_range(candidates),
2327 _ => None,
2328 })
2329}
2330
2331fn to_difference_range<St: ExpressionState>(
2332 expression: &Arc<RevsetExpression<St>>,
2333 complement: &Arc<RevsetExpression<St>>,
2334) -> TransformedExpression<St> {
2335 let RevsetExpression::Ancestors {
2336 heads,
2337 generation,
2338 parents_range,
2339 } = expression.as_ref()
2340 else {
2341 return None;
2342 };
2343 let roots = ancestors_to_heads(complement).ok()?;
2344 Some(Arc::new(RevsetExpression::Range {
2347 roots,
2348 heads: heads.clone(),
2349 generation: generation.clone(),
2350 parents_range: parents_range.clone(),
2351 }))
2352}
2353
2354fn fold_difference<St: ExpressionState>(
2357 expression: &Arc<RevsetExpression<St>>,
2358) -> TransformedExpression<St> {
2359 fn to_difference<St: ExpressionState>(
2360 expression: &Arc<RevsetExpression<St>>,
2361 complement: &Arc<RevsetExpression<St>>,
2362 ) -> Arc<RevsetExpression<St>> {
2363 to_difference_range(expression, complement).unwrap_or_else(|| expression.minus(complement))
2364 }
2365
2366 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2367 RevsetExpression::Intersection(expression1, expression2) => {
2368 match (expression1.as_ref(), expression2.as_ref()) {
2369 (_, RevsetExpression::Filter(_) | RevsetExpression::AsFilter(_)) => None,
2371 (_, RevsetExpression::NotIn(complement)) => {
2372 Some(to_difference(expression1, complement))
2373 }
2374 (RevsetExpression::NotIn(complement), _) => {
2375 Some(to_difference(expression2, complement))
2376 }
2377 _ => None,
2378 }
2379 }
2380 _ => None,
2381 })
2382}
2383
2384fn fold_not_in_ancestors<St: ExpressionState>(
2389 expression: &Arc<RevsetExpression<St>>,
2390) -> TransformedExpression<St> {
2391 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2392 RevsetExpression::NotIn(complement)
2393 if matches!(complement.as_ref(), RevsetExpression::Ancestors { .. }) =>
2394 {
2395 to_difference_range(
2398 &RevsetExpression::visible_heads_or_referenced().ancestors(),
2399 complement,
2400 )
2401 }
2402 _ => None,
2403 })
2404}
2405
2406fn unfold_difference<St: ExpressionState>(
2411 expression: &Arc<RevsetExpression<St>>,
2412) -> TransformedExpression<St> {
2413 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2414 RevsetExpression::Range {
2416 roots,
2417 heads,
2418 parents_range,
2419 generation,
2420 } => {
2421 let heads_ancestors = Arc::new(RevsetExpression::Ancestors {
2422 heads: heads.clone(),
2423 generation: generation.clone(),
2424 parents_range: parents_range.clone(),
2425 });
2426 Some(heads_ancestors.intersection(&roots.ancestors().negated()))
2427 }
2428 RevsetExpression::Difference(expression1, expression2) => {
2429 Some(expression1.intersection(&expression2.negated()))
2430 }
2431 _ => None,
2432 })
2433}
2434
2435fn fold_generation<St: ExpressionState>(
2438 expression: &Arc<RevsetExpression<St>>,
2439) -> TransformedExpression<St> {
2440 fn add_generation(generation1: &Range<u64>, generation2: &Range<u64>) -> Range<u64> {
2441 if generation1.is_empty() || generation2.is_empty() {
2443 GENERATION_RANGE_EMPTY
2444 } else {
2445 let start = u64::saturating_add(generation1.start, generation2.start);
2446 let end = u64::saturating_add(generation1.end, generation2.end - 1);
2447 start..end
2448 }
2449 }
2450
2451 transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
2452 RevsetExpression::Ancestors {
2453 heads,
2454 generation: generation1,
2455 parents_range: parents1,
2456 } => {
2457 match heads.as_ref() {
2458 RevsetExpression::Ancestors {
2462 heads,
2463 generation: generation2,
2464 parents_range: parents2,
2465 } if parents2 == parents1 => Some(Arc::new(RevsetExpression::Ancestors {
2466 heads: heads.clone(),
2467 generation: add_generation(generation1, generation2),
2468 parents_range: parents1.clone(),
2469 })),
2470 _ => None,
2471 }
2472 }
2473 RevsetExpression::Descendants {
2474 roots,
2475 generation: generation1,
2476 } => {
2477 match roots.as_ref() {
2478 RevsetExpression::Descendants {
2482 roots,
2483 generation: generation2,
2484 } => Some(Arc::new(RevsetExpression::Descendants {
2485 roots: roots.clone(),
2486 generation: add_generation(generation1, generation2),
2487 })),
2488 _ => None,
2489 }
2490 }
2491 _ => None,
2493 })
2494}
2495
2496pub fn optimize<St: ExpressionState>(
2499 expression: Arc<RevsetExpression<St>>,
2500) -> Arc<RevsetExpression<St>> {
2501 let expression = resolve_referenced_commits(&expression).unwrap_or(expression);
2504 let expression = unfold_difference(&expression).unwrap_or(expression);
2505 let expression = fold_redundant_expression(&expression).unwrap_or(expression);
2506 let expression = fold_generation(&expression).unwrap_or(expression);
2507 let expression = flatten_intersections(&expression).unwrap_or(expression);
2508 let expression = sort_negations_and_ancestors(&expression).unwrap_or(expression);
2509 let expression = fold_ancestors_union(&expression).unwrap_or(expression);
2510 let expression = internalize_filter(&expression).unwrap_or(expression);
2511 let expression = fold_heads_range(&expression).unwrap_or(expression);
2512 let expression = fold_difference(&expression).unwrap_or(expression);
2513 fold_not_in_ancestors(&expression).unwrap_or(expression)
2514}
2515
2516pub fn walk_revs<'index>(
2520 repo: &'index dyn Repo,
2521 wanted: &[CommitId],
2522 unwanted: &[CommitId],
2523) -> Result<Box<dyn Revset + 'index>, RevsetEvaluationError> {
2524 RevsetExpression::commits(unwanted.to_vec())
2525 .range(&RevsetExpression::commits(wanted.to_vec()))
2526 .evaluate(repo)
2527}
2528
2529fn reload_repo_at_operation(
2530 repo: &dyn Repo,
2531 op_str: &str,
2532) -> Result<Arc<ReadonlyRepo>, RevsetResolutionError> {
2533 let base_repo = repo.base_repo();
2537 let operation = op_walk::resolve_op_with_repo(base_repo, op_str)
2538 .map_err(|err| RevsetResolutionError::Other(err.into()))?;
2539 base_repo.reload_at(&operation).map_err(|err| match err {
2540 RepoLoaderError::Backend(err) => RevsetResolutionError::Backend(err),
2541 RepoLoaderError::Index(_)
2542 | RepoLoaderError::IndexStore(_)
2543 | RepoLoaderError::OpHeadResolution(_)
2544 | RepoLoaderError::OpHeadsStoreError(_)
2545 | RepoLoaderError::OpStore(_)
2546 | RepoLoaderError::TransactionCommit(_) => RevsetResolutionError::Other(err.into()),
2547 })
2548}
2549
2550fn resolve_remote_bookmark(
2551 repo: &dyn Repo,
2552 symbol: RemoteRefSymbol<'_>,
2553) -> Result<CommitId, RevsetResolutionError> {
2554 let target = &repo.view().get_remote_bookmark(symbol).target;
2555 to_resolved_ref("remote_bookmark", symbol, target)?
2556 .ok_or_else(|| make_no_such_symbol_error(repo, symbol.to_string()))
2557}
2558
2559fn to_resolved_ref(
2560 kind: &'static str,
2561 symbol: impl ToString,
2562 target: &RefTarget,
2563) -> Result<Option<CommitId>, RevsetResolutionError> {
2564 match target.as_resolved() {
2565 Some(Some(id)) => Ok(Some(id.clone())),
2566 Some(None) => Ok(None),
2567 None => Err(RevsetResolutionError::ConflictedRef {
2568 kind,
2569 symbol: symbol.to_string(),
2570 targets: target.added_ids().cloned().collect(),
2571 }),
2572 }
2573}
2574
2575fn all_formatted_bookmark_symbols(
2576 repo: &dyn Repo,
2577 include_synced_remotes: bool,
2578) -> impl Iterator<Item = String> {
2579 let view = repo.view();
2580 view.bookmarks().flat_map(move |(name, bookmark_target)| {
2581 let local_target = bookmark_target.local_target;
2582 let local_symbol = local_target
2583 .is_present()
2584 .then(|| format_symbol(name.as_str()));
2585 let remote_symbols = bookmark_target
2586 .remote_refs
2587 .into_iter()
2588 .filter(move |&(_, remote_ref)| {
2589 include_synced_remotes
2590 || !remote_ref.is_tracked()
2591 || remote_ref.target != *local_target
2592 })
2593 .map(move |(remote, _)| format_remote_symbol(name.as_str(), remote.as_str()));
2594 local_symbol.into_iter().chain(remote_symbols)
2595 })
2596}
2597
2598fn make_no_such_symbol_error(repo: &dyn Repo, name: String) -> RevsetResolutionError {
2599 let bookmark_names = all_formatted_bookmark_symbols(repo, name.contains('@'));
2601 let candidates = collect_similar(&name, bookmark_names);
2602 RevsetResolutionError::NoSuchRevision { name, candidates }
2603}
2604
2605pub trait PartialSymbolResolver {
2609 fn resolve_symbol(
2610 &self,
2611 repo: &dyn Repo,
2612 symbol: &str,
2613 ) -> Result<Option<CommitId>, RevsetResolutionError>;
2614}
2615
2616struct TagResolver;
2617
2618impl PartialSymbolResolver for TagResolver {
2619 fn resolve_symbol(
2620 &self,
2621 repo: &dyn Repo,
2622 symbol: &str,
2623 ) -> Result<Option<CommitId>, RevsetResolutionError> {
2624 let target = repo.view().get_local_tag(symbol.as_ref());
2625 to_resolved_ref("tag", symbol, target)
2626 }
2627}
2628
2629struct BookmarkResolver;
2630
2631impl PartialSymbolResolver for BookmarkResolver {
2632 fn resolve_symbol(
2633 &self,
2634 repo: &dyn Repo,
2635 symbol: &str,
2636 ) -> Result<Option<CommitId>, RevsetResolutionError> {
2637 let target = repo.view().get_local_bookmark(symbol.as_ref());
2638 to_resolved_ref("bookmark", symbol, target)
2639 }
2640}
2641
2642struct GitRefResolver;
2643
2644impl PartialSymbolResolver for GitRefResolver {
2645 fn resolve_symbol(
2646 &self,
2647 repo: &dyn Repo,
2648 symbol: &str,
2649 ) -> Result<Option<CommitId>, RevsetResolutionError> {
2650 let view = repo.view();
2651 for git_ref_prefix in &["", "refs/"] {
2652 let target = view.get_git_ref([git_ref_prefix, symbol].concat().as_ref());
2653 if let Some(id) = to_resolved_ref("git_ref", symbol, target)? {
2654 return Ok(Some(id));
2655 }
2656 }
2657 Ok(None)
2658 }
2659}
2660
2661const DEFAULT_RESOLVERS: &[&dyn PartialSymbolResolver] =
2662 &[&TagResolver, &BookmarkResolver, &GitRefResolver];
2663
2664struct CommitPrefixResolver<'a> {
2665 context_repo: &'a dyn Repo,
2666 context: Option<&'a IdPrefixContext>,
2667}
2668
2669impl CommitPrefixResolver<'_> {
2670 fn try_resolve(
2671 &self,
2672 repo: &dyn Repo,
2673 prefix: &HexPrefix,
2674 ) -> Result<Option<CommitId>, RevsetResolutionError> {
2675 let index = self
2676 .context
2677 .map(|ctx| ctx.populate(self.context_repo))
2678 .transpose()
2679 .map_err(|err| RevsetResolutionError::Other(err.into()))?
2680 .unwrap_or(IdPrefixIndex::empty());
2681 match index
2682 .resolve_commit_prefix(repo, prefix)
2683 .map_err(|err| RevsetResolutionError::Other(err.into()))?
2684 {
2685 PrefixResolution::AmbiguousMatch => {
2686 Err(RevsetResolutionError::AmbiguousCommitIdPrefix(prefix.hex()))
2687 }
2688 PrefixResolution::SingleMatch(id) => Ok(Some(id)),
2689 PrefixResolution::NoMatch => Ok(None),
2690 }
2691 }
2692}
2693
2694impl PartialSymbolResolver for CommitPrefixResolver<'_> {
2695 fn resolve_symbol(
2696 &self,
2697 repo: &dyn Repo,
2698 symbol: &str,
2699 ) -> Result<Option<CommitId>, RevsetResolutionError> {
2700 if let Some(prefix) = HexPrefix::try_from_hex(symbol) {
2701 self.try_resolve(repo, &prefix)
2702 } else {
2703 Ok(None)
2704 }
2705 }
2706}
2707
2708struct ChangePrefixResolver<'a> {
2709 context_repo: &'a dyn Repo,
2710 context: Option<&'a IdPrefixContext>,
2711}
2712
2713impl ChangePrefixResolver<'_> {
2714 fn try_resolve(
2715 &self,
2716 repo: &dyn Repo,
2717 prefix: &HexPrefix,
2718 ) -> Result<Option<Vec<CommitId>>, RevsetResolutionError> {
2719 let index = self
2720 .context
2721 .map(|ctx| ctx.populate(self.context_repo))
2722 .transpose()
2723 .map_err(|err| RevsetResolutionError::Other(err.into()))?
2724 .unwrap_or(IdPrefixIndex::empty());
2725 match index
2726 .resolve_change_prefix(repo, prefix)
2727 .map_err(|err| RevsetResolutionError::Other(err.into()))?
2728 {
2729 PrefixResolution::AmbiguousMatch => Err(
2730 RevsetResolutionError::AmbiguousChangeIdPrefix(prefix.reverse_hex()),
2731 ),
2732 PrefixResolution::SingleMatch(ids) => Ok(Some(ids)),
2733 PrefixResolution::NoMatch => Ok(None),
2734 }
2735 }
2736}
2737
2738impl PartialSymbolResolver for ChangePrefixResolver<'_> {
2739 fn resolve_symbol(
2740 &self,
2741 repo: &dyn Repo,
2742 symbol: &str,
2743 ) -> Result<Option<CommitId>, RevsetResolutionError> {
2744 if let Some(prefix) = HexPrefix::try_from_reverse_hex(symbol) {
2745 match self.try_resolve(repo, &prefix)? {
2746 Some(targets) if targets.len() == 1 => Ok(targets.into_iter().next()),
2747 Some(targets) => Err(RevsetResolutionError::DivergentChangeId {
2748 symbol: symbol.to_owned(),
2749 targets,
2750 }),
2751 None => Ok(None),
2752 }
2753 } else {
2754 Ok(None)
2755 }
2756 }
2757}
2758
2759pub trait SymbolResolverExtension: Send + Sync {
2766 fn new_resolvers<'a>(
2771 &self,
2772 context_repo: &'a dyn Repo,
2773 ) -> Vec<Box<dyn PartialSymbolResolver + 'a>>;
2774}
2775
2776pub struct SymbolResolver<'a> {
2779 commit_id_resolver: CommitPrefixResolver<'a>,
2780 change_id_resolver: ChangePrefixResolver<'a>,
2781 extensions: Vec<Box<dyn PartialSymbolResolver + 'a>>,
2782}
2783
2784impl<'a> SymbolResolver<'a> {
2785 pub fn new(
2788 context_repo: &'a dyn Repo,
2789 extensions: &[impl AsRef<dyn SymbolResolverExtension>],
2790 ) -> Self {
2791 SymbolResolver {
2792 commit_id_resolver: CommitPrefixResolver {
2793 context_repo,
2794 context: None,
2795 },
2796 change_id_resolver: ChangePrefixResolver {
2797 context_repo,
2798 context: None,
2799 },
2800 extensions: extensions
2801 .iter()
2802 .flat_map(|ext| ext.as_ref().new_resolvers(context_repo))
2803 .collect(),
2804 }
2805 }
2806
2807 pub fn with_id_prefix_context(mut self, id_prefix_context: &'a IdPrefixContext) -> Self {
2808 self.commit_id_resolver.context = Some(id_prefix_context);
2809 self.change_id_resolver.context = Some(id_prefix_context);
2810 self
2811 }
2812
2813 fn partial_resolvers(&self) -> impl Iterator<Item = &(dyn PartialSymbolResolver + 'a)> {
2814 let prefix_resolvers: [&dyn PartialSymbolResolver; 2] =
2815 [&self.commit_id_resolver, &self.change_id_resolver];
2816 itertools::chain!(
2817 DEFAULT_RESOLVERS.iter().copied(),
2818 prefix_resolvers,
2819 self.extensions.iter().map(|e| e.as_ref())
2820 )
2821 }
2822
2823 pub fn resolve_symbol(
2825 &self,
2826 repo: &dyn Repo,
2827 symbol: &str,
2828 ) -> Result<CommitId, RevsetResolutionError> {
2829 if symbol.is_empty() {
2830 return Err(RevsetResolutionError::EmptyString);
2831 }
2832
2833 for partial_resolver in self.partial_resolvers() {
2834 if let Some(id) = partial_resolver.resolve_symbol(repo, symbol)? {
2835 return Ok(id);
2836 }
2837 }
2838
2839 Err(make_no_such_symbol_error(repo, format_symbol(symbol)))
2840 }
2841}
2842
2843fn resolve_commit_ref(
2844 repo: &dyn Repo,
2845 commit_ref: &RevsetCommitRef,
2846 symbol_resolver: &SymbolResolver,
2847) -> Result<Vec<CommitId>, RevsetResolutionError> {
2848 match commit_ref {
2849 RevsetCommitRef::Symbol(symbol) => {
2850 let commit_id = symbol_resolver.resolve_symbol(repo, symbol)?;
2851 Ok(vec![commit_id])
2852 }
2853 RevsetCommitRef::RemoteSymbol(symbol) => {
2854 let commit_id = resolve_remote_bookmark(repo, symbol.as_ref())?;
2855 Ok(vec![commit_id])
2856 }
2857 RevsetCommitRef::WorkingCopy(name) => {
2858 if let Some(commit_id) = repo.view().get_wc_commit_id(name) {
2859 Ok(vec![commit_id.clone()])
2860 } else {
2861 Err(RevsetResolutionError::WorkspaceMissingWorkingCopy { name: name.clone() })
2862 }
2863 }
2864 RevsetCommitRef::WorkingCopies => {
2865 let wc_commits = repo.view().wc_commit_ids().values().cloned().collect_vec();
2866 Ok(wc_commits)
2867 }
2868 RevsetCommitRef::ChangeId(prefix) => {
2869 let resolver = &symbol_resolver.change_id_resolver;
2870 Ok(resolver.try_resolve(repo, prefix)?.unwrap_or_else(Vec::new))
2871 }
2872 RevsetCommitRef::CommitId(prefix) => {
2873 let resolver = &symbol_resolver.commit_id_resolver;
2874 Ok(resolver.try_resolve(repo, prefix)?.into_iter().collect())
2875 }
2876 RevsetCommitRef::Bookmarks(expression) => {
2877 let commit_ids = repo
2878 .view()
2879 .local_bookmarks_matching(&expression.to_matcher())
2880 .flat_map(|(_, target)| target.added_ids())
2881 .cloned()
2882 .collect();
2883 Ok(commit_ids)
2884 }
2885 RevsetCommitRef::RemoteBookmarks {
2886 bookmark,
2887 remote,
2888 remote_ref_state,
2889 } => {
2890 let bookmark_matcher = bookmark.to_matcher();
2891 let remote_matcher = remote.to_matcher();
2892 let commit_ids = repo
2893 .view()
2894 .remote_bookmarks_matching(&bookmark_matcher, &remote_matcher)
2895 .filter(|(_, remote_ref)| {
2896 remote_ref_state.is_none_or(|state| remote_ref.state == state)
2897 })
2898 .flat_map(|(_, remote_ref)| remote_ref.target.added_ids())
2899 .cloned()
2900 .collect();
2901 Ok(commit_ids)
2902 }
2903 RevsetCommitRef::Tags(expression) => {
2904 let commit_ids = repo
2905 .view()
2906 .local_tags_matching(&expression.to_matcher())
2907 .flat_map(|(_, target)| target.added_ids())
2908 .cloned()
2909 .collect();
2910 Ok(commit_ids)
2911 }
2912 RevsetCommitRef::GitRefs => {
2913 let mut commit_ids = vec![];
2914 for ref_target in repo.view().git_refs().values() {
2915 commit_ids.extend(ref_target.added_ids().cloned());
2916 }
2917 Ok(commit_ids)
2918 }
2919 RevsetCommitRef::GitHead => Ok(repo.view().git_head().added_ids().cloned().collect()),
2920 }
2921}
2922
2923struct ExpressionSymbolResolver<'a, 'b> {
2925 base_repo: &'a dyn Repo,
2926 repo_stack: Vec<Arc<ReadonlyRepo>>,
2927 symbol_resolver: &'a SymbolResolver<'b>,
2928}
2929
2930impl<'a, 'b> ExpressionSymbolResolver<'a, 'b> {
2931 fn new(base_repo: &'a dyn Repo, symbol_resolver: &'a SymbolResolver<'b>) -> Self {
2932 Self {
2933 base_repo,
2934 repo_stack: vec![],
2935 symbol_resolver,
2936 }
2937 }
2938
2939 fn repo(&self) -> &dyn Repo {
2940 self.repo_stack
2941 .last()
2942 .map_or(self.base_repo, |repo| repo.as_ref())
2943 }
2944}
2945
2946impl ExpressionStateFolder<UserExpressionState, ResolvedExpressionState>
2947 for ExpressionSymbolResolver<'_, '_>
2948{
2949 type Error = RevsetResolutionError;
2950
2951 fn fold_expression(
2952 &mut self,
2953 expression: &UserRevsetExpression,
2954 ) -> Result<Arc<ResolvedRevsetExpression>, Self::Error> {
2955 match expression {
2956 RevsetExpression::Present(candidates) => {
2958 self.fold_expression(candidates).or_else(|err| match err {
2959 RevsetResolutionError::NoSuchRevision { .. }
2960 | RevsetResolutionError::WorkspaceMissingWorkingCopy { .. } => {
2961 Ok(RevsetExpression::none())
2962 }
2963 RevsetResolutionError::EmptyString
2964 | RevsetResolutionError::AmbiguousCommitIdPrefix(_)
2965 | RevsetResolutionError::AmbiguousChangeIdPrefix(_)
2966 | RevsetResolutionError::DivergentChangeId { .. }
2967 | RevsetResolutionError::ConflictedRef { .. }
2968 | RevsetResolutionError::Backend(_)
2969 | RevsetResolutionError::Other(_) => Err(err),
2970 })
2971 }
2972 _ => fold_child_expression_state(self, expression),
2973 }
2974 }
2975
2976 fn fold_commit_ref(
2977 &mut self,
2978 commit_ref: &RevsetCommitRef,
2979 ) -> Result<Arc<ResolvedRevsetExpression>, Self::Error> {
2980 let commit_ids = resolve_commit_ref(self.repo(), commit_ref, self.symbol_resolver)?;
2981 Ok(RevsetExpression::commits(commit_ids))
2982 }
2983
2984 fn fold_at_operation(
2985 &mut self,
2986 operation: &String,
2987 candidates: &UserRevsetExpression,
2988 ) -> Result<Arc<ResolvedRevsetExpression>, Self::Error> {
2989 let repo = reload_repo_at_operation(self.repo(), operation)?;
2990 self.repo_stack.push(repo);
2991 let candidates = self.fold_expression(candidates)?;
2992 let visible_heads = self.repo().view().heads().iter().cloned().collect();
2993 self.repo_stack.pop();
2994 Ok(Arc::new(RevsetExpression::WithinVisibility {
2995 candidates,
2996 visible_heads,
2997 }))
2998 }
2999}
3000
3001fn resolve_symbols(
3002 repo: &dyn Repo,
3003 expression: &UserRevsetExpression,
3004 symbol_resolver: &SymbolResolver,
3005) -> Result<Arc<ResolvedRevsetExpression>, RevsetResolutionError> {
3006 let mut resolver = ExpressionSymbolResolver::new(repo, symbol_resolver);
3007 resolver.fold_expression(expression)
3008}
3009
3010fn resolve_visibility(
3020 repo: &dyn Repo,
3021 expression: &ResolvedRevsetExpression,
3022) -> ResolvedExpression {
3023 let context = VisibilityResolutionContext {
3024 referenced_commits: &[],
3025 visible_heads: &repo.view().heads().iter().cloned().collect_vec(),
3026 root: repo.store().root_commit_id(),
3027 };
3028 context.resolve(expression)
3029}
3030
3031#[derive(Clone, Debug)]
3032struct VisibilityResolutionContext<'a> {
3033 referenced_commits: &'a [CommitId],
3034 visible_heads: &'a [CommitId],
3035 root: &'a CommitId,
3036}
3037
3038impl VisibilityResolutionContext<'_> {
3039 fn resolve(&self, expression: &ResolvedRevsetExpression) -> ResolvedExpression {
3041 match expression {
3042 RevsetExpression::None => ResolvedExpression::Commits(vec![]),
3043 RevsetExpression::All => self.resolve_all(),
3044 RevsetExpression::VisibleHeads => self.resolve_visible_heads(),
3045 RevsetExpression::VisibleHeadsOrReferenced => {
3046 self.resolve_visible_heads_or_referenced()
3047 }
3048 RevsetExpression::Root => self.resolve_root(),
3049 RevsetExpression::Commits(commit_ids) => {
3050 ResolvedExpression::Commits(commit_ids.clone())
3051 }
3052 RevsetExpression::CommitRef(commit_ref) => match *commit_ref {},
3053 RevsetExpression::Ancestors {
3054 heads,
3055 generation,
3056 parents_range,
3057 } => ResolvedExpression::Ancestors {
3058 heads: self.resolve(heads).into(),
3059 generation: generation.clone(),
3060 parents_range: parents_range.clone(),
3061 },
3062 RevsetExpression::Descendants { roots, generation } => ResolvedExpression::DagRange {
3063 roots: self.resolve(roots).into(),
3064 heads: self.resolve_visible_heads_or_referenced().into(),
3065 generation_from_roots: generation.clone(),
3066 },
3067 RevsetExpression::Range {
3068 roots,
3069 heads,
3070 generation,
3071 parents_range,
3072 } => ResolvedExpression::Range {
3073 roots: self.resolve(roots).into(),
3074 heads: self.resolve(heads).into(),
3075 generation: generation.clone(),
3076 parents_range: parents_range.clone(),
3077 },
3078 RevsetExpression::DagRange { roots, heads } => ResolvedExpression::DagRange {
3079 roots: self.resolve(roots).into(),
3080 heads: self.resolve(heads).into(),
3081 generation_from_roots: GENERATION_RANGE_FULL,
3082 },
3083 RevsetExpression::Reachable { sources, domain } => ResolvedExpression::Reachable {
3084 sources: self.resolve(sources).into(),
3085 domain: self.resolve(domain).into(),
3086 },
3087 RevsetExpression::Heads(candidates) => {
3088 ResolvedExpression::Heads(self.resolve(candidates).into())
3089 }
3090 RevsetExpression::HeadsRange {
3091 roots,
3092 heads,
3093 parents_range,
3094 filter,
3095 } => ResolvedExpression::HeadsRange {
3096 roots: self.resolve(roots).into(),
3097 heads: self.resolve(heads).into(),
3098 parents_range: parents_range.clone(),
3099 filter: (!matches!(filter.as_ref(), RevsetExpression::All))
3100 .then(|| self.resolve_predicate(filter)),
3101 },
3102 RevsetExpression::Roots(candidates) => {
3103 ResolvedExpression::Roots(self.resolve(candidates).into())
3104 }
3105 RevsetExpression::ForkPoint(expression) => {
3106 ResolvedExpression::ForkPoint(self.resolve(expression).into())
3107 }
3108 RevsetExpression::Bisect(expression) => {
3109 ResolvedExpression::Bisect(self.resolve(expression).into())
3110 }
3111 RevsetExpression::Latest { candidates, count } => ResolvedExpression::Latest {
3112 candidates: self.resolve(candidates).into(),
3113 count: *count,
3114 },
3115 RevsetExpression::HasSize { candidates, count } => ResolvedExpression::HasSize {
3116 candidates: self.resolve(candidates).into(),
3117 count: *count,
3118 },
3119 RevsetExpression::Filter(_) | RevsetExpression::AsFilter(_) => {
3120 ResolvedExpression::FilterWithin {
3123 candidates: self.resolve_all().into(),
3124 predicate: self.resolve_predicate(expression),
3125 }
3126 }
3127 RevsetExpression::AtOperation { operation, .. } => match *operation {},
3128 RevsetExpression::WithinReference {
3129 candidates,
3130 commits,
3131 } => {
3132 let context = VisibilityResolutionContext {
3133 referenced_commits: commits,
3134 visible_heads: self.visible_heads,
3135 root: self.root,
3136 };
3137 context.resolve(candidates)
3138 }
3139 RevsetExpression::WithinVisibility {
3140 candidates,
3141 visible_heads,
3142 } => {
3143 let context = VisibilityResolutionContext {
3144 referenced_commits: self.referenced_commits,
3145 visible_heads,
3146 root: self.root,
3147 };
3148 context.resolve(candidates)
3149 }
3150 RevsetExpression::Coalesce(expression1, expression2) => ResolvedExpression::Coalesce(
3151 self.resolve(expression1).into(),
3152 self.resolve(expression2).into(),
3153 ),
3154 RevsetExpression::Present(candidates) => self.resolve(candidates),
3156 RevsetExpression::NotIn(complement) => ResolvedExpression::Difference(
3157 self.resolve_all().into(),
3158 self.resolve(complement).into(),
3159 ),
3160 RevsetExpression::Union(expression1, expression2) => ResolvedExpression::Union(
3161 self.resolve(expression1).into(),
3162 self.resolve(expression2).into(),
3163 ),
3164 RevsetExpression::Intersection(expression1, expression2) => {
3165 match expression2.as_ref() {
3166 RevsetExpression::Filter(_) | RevsetExpression::AsFilter(_) => {
3167 ResolvedExpression::FilterWithin {
3168 candidates: self.resolve(expression1).into(),
3169 predicate: self.resolve_predicate(expression2),
3170 }
3171 }
3172 _ => ResolvedExpression::Intersection(
3173 self.resolve(expression1).into(),
3174 self.resolve(expression2).into(),
3175 ),
3176 }
3177 }
3178 RevsetExpression::Difference(expression1, expression2) => {
3179 ResolvedExpression::Difference(
3180 self.resolve(expression1).into(),
3181 self.resolve(expression2).into(),
3182 )
3183 }
3184 }
3185 }
3186
3187 fn resolve_all(&self) -> ResolvedExpression {
3188 ResolvedExpression::Ancestors {
3189 heads: self.resolve_visible_heads_or_referenced().into(),
3190 generation: GENERATION_RANGE_FULL,
3191 parents_range: PARENTS_RANGE_FULL,
3192 }
3193 }
3194
3195 fn resolve_visible_heads(&self) -> ResolvedExpression {
3196 ResolvedExpression::Commits(self.visible_heads.to_owned())
3197 }
3198
3199 fn resolve_visible_heads_or_referenced(&self) -> ResolvedExpression {
3200 let commits = itertools::chain(self.referenced_commits, self.visible_heads)
3205 .cloned()
3206 .collect();
3207 ResolvedExpression::Commits(commits)
3208 }
3209
3210 fn resolve_root(&self) -> ResolvedExpression {
3211 ResolvedExpression::Commits(vec![self.root.to_owned()])
3212 }
3213
3214 fn resolve_predicate(
3219 &self,
3220 expression: &ResolvedRevsetExpression,
3221 ) -> ResolvedPredicateExpression {
3222 match expression {
3223 RevsetExpression::None
3224 | RevsetExpression::All
3225 | RevsetExpression::VisibleHeads
3226 | RevsetExpression::VisibleHeadsOrReferenced
3227 | RevsetExpression::Root
3228 | RevsetExpression::Commits(_)
3229 | RevsetExpression::CommitRef(_)
3230 | RevsetExpression::Ancestors { .. }
3231 | RevsetExpression::Descendants { .. }
3232 | RevsetExpression::Range { .. }
3233 | RevsetExpression::DagRange { .. }
3234 | RevsetExpression::Reachable { .. }
3235 | RevsetExpression::Heads(_)
3236 | RevsetExpression::HeadsRange { .. }
3237 | RevsetExpression::Roots(_)
3238 | RevsetExpression::ForkPoint(_)
3239 | RevsetExpression::Bisect(_)
3240 | RevsetExpression::HasSize { .. }
3241 | RevsetExpression::Latest { .. } => {
3242 ResolvedPredicateExpression::Set(self.resolve(expression).into())
3243 }
3244 RevsetExpression::Filter(predicate) => {
3245 ResolvedPredicateExpression::Filter(predicate.clone())
3246 }
3247 RevsetExpression::AsFilter(candidates) => self.resolve_predicate(candidates),
3248 RevsetExpression::AtOperation { operation, .. } => match *operation {},
3249 RevsetExpression::WithinReference { .. }
3251 | RevsetExpression::WithinVisibility { .. } => {
3252 ResolvedPredicateExpression::Set(self.resolve(expression).into())
3253 }
3254 RevsetExpression::Coalesce(_, _) => {
3255 ResolvedPredicateExpression::Set(self.resolve(expression).into())
3256 }
3257 RevsetExpression::Present(candidates) => self.resolve_predicate(candidates),
3259 RevsetExpression::NotIn(complement) => {
3260 ResolvedPredicateExpression::NotIn(self.resolve_predicate(complement).into())
3261 }
3262 RevsetExpression::Union(expression1, expression2) => {
3263 let predicate1 = self.resolve_predicate(expression1);
3264 let predicate2 = self.resolve_predicate(expression2);
3265 ResolvedPredicateExpression::Union(predicate1.into(), predicate2.into())
3266 }
3267 RevsetExpression::Intersection(expression1, expression2) => {
3268 let predicate1 = self.resolve_predicate(expression1);
3269 let predicate2 = self.resolve_predicate(expression2);
3270 ResolvedPredicateExpression::Intersection(predicate1.into(), predicate2.into())
3271 }
3272 RevsetExpression::Difference(expression1, expression2) => {
3273 let predicate1 = self.resolve_predicate(expression1);
3274 let predicate2 = self.resolve_predicate(expression2);
3275 let predicate2 = ResolvedPredicateExpression::NotIn(predicate2.into());
3276 ResolvedPredicateExpression::Intersection(predicate1.into(), predicate2.into())
3277 }
3278 }
3279 }
3280}
3281
3282pub trait Revset: fmt::Debug {
3283 fn iter<'a>(&self) -> Box<dyn Iterator<Item = Result<CommitId, RevsetEvaluationError>> + 'a>
3285 where
3286 Self: 'a;
3287
3288 fn commit_change_ids<'a>(
3290 &self,
3291 ) -> Box<dyn Iterator<Item = Result<(CommitId, ChangeId), RevsetEvaluationError>> + 'a>
3292 where
3293 Self: 'a;
3294
3295 fn iter_graph<'a>(
3296 &self,
3297 ) -> Box<dyn Iterator<Item = Result<GraphNode<CommitId>, RevsetEvaluationError>> + 'a>
3298 where
3299 Self: 'a;
3300
3301 fn is_empty(&self) -> bool;
3303
3304 fn count_estimate(&self) -> Result<(usize, Option<usize>), RevsetEvaluationError>;
3309
3310 fn containing_fn<'a>(&self) -> Box<RevsetContainingFn<'a>>
3316 where
3317 Self: 'a;
3318}
3319
3320pub type RevsetContainingFn<'a> = dyn Fn(&CommitId) -> Result<bool, RevsetEvaluationError> + 'a;
3322
3323pub trait RevsetIteratorExt<I> {
3324 fn commits(self, store: &Arc<Store>) -> RevsetCommitIterator<I>;
3325}
3326
3327impl<I: Iterator<Item = Result<CommitId, RevsetEvaluationError>>> RevsetIteratorExt<I> for I {
3328 fn commits(self, store: &Arc<Store>) -> RevsetCommitIterator<I> {
3329 RevsetCommitIterator {
3330 iter: self,
3331 store: store.clone(),
3332 }
3333 }
3334}
3335
3336pub struct RevsetCommitIterator<I> {
3337 store: Arc<Store>,
3338 iter: I,
3339}
3340
3341impl<I: Iterator<Item = Result<CommitId, RevsetEvaluationError>>> Iterator
3342 for RevsetCommitIterator<I>
3343{
3344 type Item = Result<Commit, RevsetEvaluationError>;
3345
3346 fn next(&mut self) -> Option<Self::Item> {
3347 self.iter.next().map(|commit_id| {
3348 let commit_id = commit_id?;
3349 self.store
3350 .get_commit(&commit_id)
3351 .map_err(RevsetEvaluationError::Backend)
3352 })
3353 }
3354}
3355
3356pub struct RevsetExtensions {
3358 symbol_resolvers: Vec<Box<dyn SymbolResolverExtension>>,
3359 function_map: HashMap<&'static str, RevsetFunction>,
3360}
3361
3362impl Default for RevsetExtensions {
3363 fn default() -> Self {
3364 Self::new()
3365 }
3366}
3367
3368impl RevsetExtensions {
3369 pub fn new() -> Self {
3370 Self {
3371 symbol_resolvers: vec![],
3372 function_map: BUILTIN_FUNCTION_MAP.clone(),
3373 }
3374 }
3375
3376 pub fn symbol_resolvers(&self) -> &[Box<dyn SymbolResolverExtension>] {
3377 &self.symbol_resolvers
3378 }
3379
3380 pub fn add_symbol_resolver(&mut self, symbol_resolver: Box<dyn SymbolResolverExtension>) {
3381 self.symbol_resolvers.push(symbol_resolver);
3382 }
3383
3384 pub fn add_custom_function(&mut self, name: &'static str, func: RevsetFunction) {
3385 match self.function_map.entry(name) {
3386 hash_map::Entry::Occupied(_) => {
3387 panic!("Conflict registering revset function '{name}'")
3388 }
3389 hash_map::Entry::Vacant(v) => v.insert(func),
3390 };
3391 }
3392}
3393
3394#[derive(Clone)]
3396pub struct RevsetParseContext<'a> {
3397 pub aliases_map: &'a RevsetAliasesMap,
3398 pub local_variables: HashMap<&'a str, ExpressionNode<'a>>,
3399 pub user_email: &'a str,
3400 pub date_pattern_context: DatePatternContext,
3401 pub default_ignored_remote: Option<&'a RemoteName>,
3403 pub extensions: &'a RevsetExtensions,
3404 pub workspace: Option<RevsetWorkspaceContext<'a>>,
3405}
3406
3407impl<'a> RevsetParseContext<'a> {
3408 fn to_lowering_context(&self) -> LoweringContext<'a> {
3409 let RevsetParseContext {
3410 aliases_map: _,
3411 local_variables: _,
3412 user_email,
3413 date_pattern_context,
3414 default_ignored_remote,
3415 extensions,
3416 workspace,
3417 } = *self;
3418 LoweringContext {
3419 user_email,
3420 date_pattern_context,
3421 default_ignored_remote,
3422 extensions,
3423 workspace,
3424 }
3425 }
3426}
3427
3428#[derive(Clone)]
3430pub struct LoweringContext<'a> {
3431 user_email: &'a str,
3432 date_pattern_context: DatePatternContext,
3433 default_ignored_remote: Option<&'a RemoteName>,
3434 extensions: &'a RevsetExtensions,
3435 workspace: Option<RevsetWorkspaceContext<'a>>,
3436}
3437
3438impl<'a> LoweringContext<'a> {
3439 pub fn user_email(&self) -> &'a str {
3440 self.user_email
3441 }
3442
3443 pub fn date_pattern_context(&self) -> &DatePatternContext {
3444 &self.date_pattern_context
3445 }
3446
3447 pub fn symbol_resolvers(&self) -> &'a [impl AsRef<dyn SymbolResolverExtension> + use<>] {
3448 self.extensions.symbol_resolvers()
3449 }
3450}
3451
3452#[derive(Clone, Copy, Debug)]
3454pub struct RevsetWorkspaceContext<'a> {
3455 pub path_converter: &'a RepoPathUiConverter,
3456 pub workspace_name: &'a WorkspaceName,
3457}
3458
3459pub fn format_symbol(literal: &str) -> String {
3465 if revset_parser::is_identifier(literal) {
3466 literal.to_string()
3467 } else {
3468 format_string(literal)
3469 }
3470}
3471
3472pub fn format_string(literal: &str) -> String {
3474 format!(r#""{}""#, dsl_util::escape_string(literal))
3475}
3476
3477pub fn format_remote_symbol(name: &str, remote: &str) -> String {
3479 let name = format_symbol(name);
3480 let remote = format_symbol(remote);
3481 format!("{name}@{remote}")
3482}
3483
3484#[cfg(test)]
3485#[rustversion::attr(
3486 since(1.89),
3487 expect(clippy::cloned_ref_to_slice_refs, reason = "makes tests more readable")
3488)]
3489mod tests {
3490 use std::path::PathBuf;
3491
3492 use assert_matches::assert_matches;
3493
3494 use super::*;
3495
3496 fn parse(revset_str: &str) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
3497 parse_with_aliases(revset_str, [] as [(&str, &str); 0])
3498 }
3499
3500 fn parse_with_workspace(
3501 revset_str: &str,
3502 workspace_name: &WorkspaceName,
3503 ) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
3504 parse_with_aliases_and_workspace(revset_str, [] as [(&str, &str); 0], workspace_name)
3505 }
3506
3507 fn parse_with_aliases(
3508 revset_str: &str,
3509 aliases: impl IntoIterator<Item = (impl AsRef<str>, impl Into<String>)>,
3510 ) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
3511 let mut aliases_map = RevsetAliasesMap::new();
3512 for (decl, defn) in aliases {
3513 aliases_map.insert(decl, defn).unwrap();
3514 }
3515 let context = RevsetParseContext {
3516 aliases_map: &aliases_map,
3517 local_variables: HashMap::new(),
3518 user_email: "test.user@example.com",
3519 date_pattern_context: chrono::Utc::now().fixed_offset().into(),
3520 default_ignored_remote: Some("ignored".as_ref()),
3521 extensions: &RevsetExtensions::default(),
3522 workspace: None,
3523 };
3524 super::parse(&mut RevsetDiagnostics::new(), revset_str, &context)
3525 }
3526
3527 fn parse_with_aliases_and_workspace(
3528 revset_str: &str,
3529 aliases: impl IntoIterator<Item = (impl AsRef<str>, impl Into<String>)>,
3530 workspace_name: &WorkspaceName,
3531 ) -> Result<Arc<UserRevsetExpression>, RevsetParseError> {
3532 let path_converter = RepoPathUiConverter::Fs {
3534 cwd: PathBuf::from("/"),
3535 base: PathBuf::from("/"),
3536 };
3537 let workspace_ctx = RevsetWorkspaceContext {
3538 path_converter: &path_converter,
3539 workspace_name,
3540 };
3541 let mut aliases_map = RevsetAliasesMap::new();
3542 for (decl, defn) in aliases {
3543 aliases_map.insert(decl, defn).unwrap();
3544 }
3545 let context = RevsetParseContext {
3546 aliases_map: &aliases_map,
3547 local_variables: HashMap::new(),
3548 user_email: "test.user@example.com",
3549 date_pattern_context: chrono::Utc::now().fixed_offset().into(),
3550 default_ignored_remote: Some("ignored".as_ref()),
3551 extensions: &RevsetExtensions::default(),
3552 workspace: Some(workspace_ctx),
3553 };
3554 super::parse(&mut RevsetDiagnostics::new(), revset_str, &context)
3555 }
3556
3557 fn parse_with_modifier(
3558 revset_str: &str,
3559 ) -> Result<(Arc<UserRevsetExpression>, Option<RevsetModifier>), RevsetParseError> {
3560 parse_with_aliases_and_modifier(revset_str, [] as [(&str, &str); 0])
3561 }
3562
3563 fn parse_with_aliases_and_modifier(
3564 revset_str: &str,
3565 aliases: impl IntoIterator<Item = (impl AsRef<str>, impl Into<String>)>,
3566 ) -> Result<(Arc<UserRevsetExpression>, Option<RevsetModifier>), RevsetParseError> {
3567 let mut aliases_map = RevsetAliasesMap::new();
3568 for (decl, defn) in aliases {
3569 aliases_map.insert(decl, defn).unwrap();
3570 }
3571 let context = RevsetParseContext {
3572 aliases_map: &aliases_map,
3573 local_variables: HashMap::new(),
3574 user_email: "test.user@example.com",
3575 date_pattern_context: chrono::Utc::now().fixed_offset().into(),
3576 default_ignored_remote: Some("ignored".as_ref()),
3577 extensions: &RevsetExtensions::default(),
3578 workspace: None,
3579 };
3580 super::parse_with_modifier(&mut RevsetDiagnostics::new(), revset_str, &context)
3581 }
3582
3583 fn insta_settings() -> insta::Settings {
3584 let mut settings = insta::Settings::clone_current();
3585 for _ in 0..4 {
3588 settings.add_filter(
3589 r"(?x)
3590 \b([A-Z]\w*)\(\n
3591 \s*(.{1,60}),\n
3592 \s*\)",
3593 "$1($2)",
3594 );
3595 }
3596 settings
3597 }
3598
3599 #[test]
3600 #[expect(clippy::redundant_clone)] fn test_revset_expression_building() {
3602 let settings = insta_settings();
3603 let _guard = settings.bind_to_scope();
3604 let current_wc = UserRevsetExpression::working_copy(WorkspaceName::DEFAULT.to_owned());
3605 let foo_symbol = UserRevsetExpression::symbol("foo".to_string());
3606 let bar_symbol = UserRevsetExpression::symbol("bar".to_string());
3607 let baz_symbol = UserRevsetExpression::symbol("baz".to_string());
3608
3609 insta::assert_debug_snapshot!(
3610 current_wc,
3611 @r#"CommitRef(WorkingCopy(WorkspaceNameBuf("default")))"#);
3612 insta::assert_debug_snapshot!(
3613 current_wc.heads(),
3614 @r#"Heads(CommitRef(WorkingCopy(WorkspaceNameBuf("default"))))"#);
3615 insta::assert_debug_snapshot!(
3616 current_wc.roots(),
3617 @r#"Roots(CommitRef(WorkingCopy(WorkspaceNameBuf("default"))))"#);
3618 insta::assert_debug_snapshot!(
3619 current_wc.parents(), @r#"
3620 Ancestors {
3621 heads: CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3622 generation: 1..2,
3623 parents_range: 0..4294967295,
3624 }
3625 "#);
3626 insta::assert_debug_snapshot!(
3627 current_wc.ancestors(), @r#"
3628 Ancestors {
3629 heads: CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3630 generation: 0..18446744073709551615,
3631 parents_range: 0..4294967295,
3632 }
3633 "#);
3634 insta::assert_debug_snapshot!(
3635 foo_symbol.children(), @r#"
3636 Descendants {
3637 roots: CommitRef(Symbol("foo")),
3638 generation: 1..2,
3639 }
3640 "#);
3641 insta::assert_debug_snapshot!(
3642 foo_symbol.descendants(), @r#"
3643 Descendants {
3644 roots: CommitRef(Symbol("foo")),
3645 generation: 0..18446744073709551615,
3646 }
3647 "#);
3648 insta::assert_debug_snapshot!(
3649 foo_symbol.dag_range_to(¤t_wc), @r#"
3650 DagRange {
3651 roots: CommitRef(Symbol("foo")),
3652 heads: CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3653 }
3654 "#);
3655 insta::assert_debug_snapshot!(
3656 foo_symbol.connected(), @r#"
3657 DagRange {
3658 roots: CommitRef(Symbol("foo")),
3659 heads: CommitRef(Symbol("foo")),
3660 }
3661 "#);
3662 insta::assert_debug_snapshot!(
3663 foo_symbol.range(¤t_wc), @r#"
3664 Range {
3665 roots: CommitRef(Symbol("foo")),
3666 heads: CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3667 generation: 0..18446744073709551615,
3668 parents_range: 0..4294967295,
3669 }
3670 "#);
3671 insta::assert_debug_snapshot!(
3672 foo_symbol.negated(),
3673 @r#"NotIn(CommitRef(Symbol("foo")))"#);
3674 insta::assert_debug_snapshot!(
3675 foo_symbol.union(¤t_wc), @r#"
3676 Union(
3677 CommitRef(Symbol("foo")),
3678 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3679 )
3680 "#);
3681 insta::assert_debug_snapshot!(
3682 UserRevsetExpression::union_all(&[]),
3683 @"None");
3684 insta::assert_debug_snapshot!(
3685 RevsetExpression::union_all(&[current_wc.clone()]),
3686 @r#"CommitRef(WorkingCopy(WorkspaceNameBuf("default")))"#);
3687 insta::assert_debug_snapshot!(
3688 RevsetExpression::union_all(&[current_wc.clone(), foo_symbol.clone()]),
3689 @r#"
3690 Union(
3691 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3692 CommitRef(Symbol("foo")),
3693 )
3694 "#);
3695 insta::assert_debug_snapshot!(
3696 RevsetExpression::union_all(&[
3697 current_wc.clone(),
3698 foo_symbol.clone(),
3699 bar_symbol.clone(),
3700 ]),
3701 @r#"
3702 Union(
3703 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3704 Union(
3705 CommitRef(Symbol("foo")),
3706 CommitRef(Symbol("bar")),
3707 ),
3708 )
3709 "#);
3710 insta::assert_debug_snapshot!(
3711 RevsetExpression::union_all(&[
3712 current_wc.clone(),
3713 foo_symbol.clone(),
3714 bar_symbol.clone(),
3715 baz_symbol.clone(),
3716 ]),
3717 @r#"
3718 Union(
3719 Union(
3720 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3721 CommitRef(Symbol("foo")),
3722 ),
3723 Union(
3724 CommitRef(Symbol("bar")),
3725 CommitRef(Symbol("baz")),
3726 ),
3727 )
3728 "#);
3729 insta::assert_debug_snapshot!(
3730 foo_symbol.intersection(¤t_wc), @r#"
3731 Intersection(
3732 CommitRef(Symbol("foo")),
3733 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3734 )
3735 "#);
3736 insta::assert_debug_snapshot!(
3737 foo_symbol.minus(¤t_wc), @r#"
3738 Difference(
3739 CommitRef(Symbol("foo")),
3740 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3741 )
3742 "#);
3743 insta::assert_debug_snapshot!(
3744 UserRevsetExpression::coalesce(&[]),
3745 @"None");
3746 insta::assert_debug_snapshot!(
3747 RevsetExpression::coalesce(&[current_wc.clone()]),
3748 @r#"CommitRef(WorkingCopy(WorkspaceNameBuf("default")))"#);
3749 insta::assert_debug_snapshot!(
3750 RevsetExpression::coalesce(&[current_wc.clone(), foo_symbol.clone()]),
3751 @r#"
3752 Coalesce(
3753 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3754 CommitRef(Symbol("foo")),
3755 )
3756 "#);
3757 insta::assert_debug_snapshot!(
3758 RevsetExpression::coalesce(&[
3759 current_wc.clone(),
3760 foo_symbol.clone(),
3761 bar_symbol.clone(),
3762 ]),
3763 @r#"
3764 Coalesce(
3765 CommitRef(WorkingCopy(WorkspaceNameBuf("default"))),
3766 Coalesce(
3767 CommitRef(Symbol("foo")),
3768 CommitRef(Symbol("bar")),
3769 ),
3770 )
3771 "#);
3772 }
3773
3774 #[test]
3775 fn test_parse_revset() {
3776 let settings = insta_settings();
3777 let _guard = settings.bind_to_scope();
3778 let main_workspace_name = WorkspaceNameBuf::from("main");
3779 let other_workspace_name = WorkspaceNameBuf::from("other");
3780
3781 insta::assert_debug_snapshot!(
3783 parse("@").unwrap_err().kind(),
3784 @"WorkingCopyWithoutWorkspace");
3785 insta::assert_debug_snapshot!(
3786 parse("main@").unwrap(),
3787 @r#"CommitRef(WorkingCopy(WorkspaceNameBuf("main")))"#);
3788 insta::assert_debug_snapshot!(
3789 parse_with_workspace("@", &main_workspace_name).unwrap(),
3790 @r#"CommitRef(WorkingCopy(WorkspaceNameBuf("main")))"#);
3791 insta::assert_debug_snapshot!(
3792 parse_with_workspace("main@", &other_workspace_name).unwrap(),
3793 @r#"CommitRef(WorkingCopy(WorkspaceNameBuf("main")))"#);
3794 insta::assert_debug_snapshot!(
3796 parse("author_name(foo@)").unwrap_err().kind(),
3797 @r#"Expression("Invalid string expression")"#);
3798 insta::assert_debug_snapshot!(
3799 parse(r#"author_name("foo@")"#).unwrap(),
3800 @r#"Filter(AuthorName(Pattern(Substring("foo@"))))"#);
3801 insta::assert_debug_snapshot!(
3803 parse("foo").unwrap(),
3804 @r#"CommitRef(Symbol("foo"))"#);
3805 insta::assert_debug_snapshot!(
3807 parse("bookmarks()").unwrap(),
3808 @r#"CommitRef(Bookmarks(Pattern(Substring(""))))"#);
3809 insta::assert_debug_snapshot!(
3811 parse("tags()").unwrap(),
3812 @r#"CommitRef(Tags(Pattern(Substring(""))))"#);
3813 insta::assert_debug_snapshot!(parse("remote_bookmarks()").unwrap(), @r#"
3814 CommitRef(
3815 RemoteBookmarks {
3816 bookmark: Pattern(Substring("")),
3817 remote: NotIn(Pattern(Exact("ignored"))),
3818 remote_ref_state: None,
3819 },
3820 )
3821 "#);
3822 insta::assert_debug_snapshot!(parse("tracked_remote_bookmarks()").unwrap(), @r#"
3823 CommitRef(
3824 RemoteBookmarks {
3825 bookmark: Pattern(Substring("")),
3826 remote: NotIn(Pattern(Exact("ignored"))),
3827 remote_ref_state: Some(Tracked),
3828 },
3829 )
3830 "#);
3831 insta::assert_debug_snapshot!(parse("untracked_remote_bookmarks()").unwrap(), @r#"
3832 CommitRef(
3833 RemoteBookmarks {
3834 bookmark: Pattern(Substring("")),
3835 remote: NotIn(Pattern(Exact("ignored"))),
3836 remote_ref_state: Some(New),
3837 },
3838 )
3839 "#);
3840 insta::assert_debug_snapshot!(
3842 parse("'foo'").unwrap(),
3843 @r#"CommitRef(Symbol("foo"))"#);
3844 insta::assert_debug_snapshot!(parse("foo-").unwrap(), @r#"
3846 Ancestors {
3847 heads: CommitRef(Symbol("foo")),
3848 generation: 1..2,
3849 parents_range: 0..4294967295,
3850 }
3851 "#);
3852 insta::assert_debug_snapshot!(parse("foo+").unwrap(), @r#"
3854 Descendants {
3855 roots: CommitRef(Symbol("foo")),
3856 generation: 1..2,
3857 }
3858 "#);
3859 insta::assert_debug_snapshot!(parse("::foo").unwrap(), @r#"
3861 Ancestors {
3862 heads: CommitRef(Symbol("foo")),
3863 generation: 0..18446744073709551615,
3864 parents_range: 0..4294967295,
3865 }
3866 "#);
3867 insta::assert_debug_snapshot!(parse("foo::").unwrap(), @r#"
3869 Descendants {
3870 roots: CommitRef(Symbol("foo")),
3871 generation: 0..18446744073709551615,
3872 }
3873 "#);
3874 insta::assert_debug_snapshot!(parse("foo::bar").unwrap(), @r#"
3876 DagRange {
3877 roots: CommitRef(Symbol("foo")),
3878 heads: CommitRef(Symbol("bar")),
3879 }
3880 "#);
3881 insta::assert_debug_snapshot!(parse("::").unwrap(), @"All");
3883 insta::assert_debug_snapshot!(parse("..foo").unwrap(), @r#"
3885 Range {
3886 roots: Root,
3887 heads: CommitRef(Symbol("foo")),
3888 generation: 0..18446744073709551615,
3889 parents_range: 0..4294967295,
3890 }
3891 "#);
3892 insta::assert_debug_snapshot!(parse("foo..").unwrap(), @r#"
3893 NotIn(
3894 Ancestors {
3895 heads: CommitRef(Symbol("foo")),
3896 generation: 0..18446744073709551615,
3897 parents_range: 0..4294967295,
3898 },
3899 )
3900 "#);
3901 insta::assert_debug_snapshot!(parse("foo..bar").unwrap(), @r#"
3902 Range {
3903 roots: CommitRef(Symbol("foo")),
3904 heads: CommitRef(Symbol("bar")),
3905 generation: 0..18446744073709551615,
3906 parents_range: 0..4294967295,
3907 }
3908 "#);
3909 insta::assert_debug_snapshot!(parse("..").unwrap(), @r#"NotIn(Root)"#);
3911 insta::assert_debug_snapshot!(
3913 parse("~ foo").unwrap(),
3914 @r#"NotIn(CommitRef(Symbol("foo")))"#);
3915 insta::assert_debug_snapshot!(parse("foo & bar").unwrap(), @r#"
3917 Intersection(
3918 CommitRef(Symbol("foo")),
3919 CommitRef(Symbol("bar")),
3920 )
3921 "#);
3922 insta::assert_debug_snapshot!(parse("foo | bar").unwrap(), @r#"
3924 Union(
3925 CommitRef(Symbol("foo")),
3926 CommitRef(Symbol("bar")),
3927 )
3928 "#);
3929 insta::assert_debug_snapshot!(parse("foo ~ bar").unwrap(), @r#"
3931 Difference(
3932 CommitRef(Symbol("foo")),
3933 CommitRef(Symbol("bar")),
3934 )
3935 "#);
3936 }
3937
3938 #[test]
3939 fn test_parse_revset_with_modifier() {
3940 let settings = insta_settings();
3941 let _guard = settings.bind_to_scope();
3942
3943 insta::assert_debug_snapshot!(
3944 parse_with_modifier("all:foo").unwrap(), @r#"
3945 (
3946 CommitRef(Symbol("foo")),
3947 Some(All),
3948 )
3949 "#);
3950
3951 insta::assert_debug_snapshot!(
3953 parse_with_modifier(r#"exact:"foo""#).unwrap_err().kind(),
3954 @r#"NoSuchModifier("exact")"#);
3955 }
3956
3957 #[test]
3958 fn test_parse_string_pattern() {
3959 let settings = insta_settings();
3960 let _guard = settings.bind_to_scope();
3961
3962 insta::assert_debug_snapshot!(
3963 parse(r#"bookmarks("foo")"#).unwrap(),
3964 @r#"CommitRef(Bookmarks(Pattern(Substring("foo"))))"#);
3965 insta::assert_debug_snapshot!(
3966 parse(r#"bookmarks(exact:"foo")"#).unwrap(),
3967 @r#"CommitRef(Bookmarks(Pattern(Exact("foo"))))"#);
3968 insta::assert_debug_snapshot!(
3969 parse(r#"bookmarks(substring:"foo")"#).unwrap(),
3970 @r#"CommitRef(Bookmarks(Pattern(Substring("foo"))))"#);
3971 insta::assert_debug_snapshot!(
3972 parse(r#"bookmarks(bad:"foo")"#).unwrap_err().kind(),
3973 @r#"Expression("Invalid string pattern")"#);
3974 insta::assert_debug_snapshot!(
3975 parse(r#"bookmarks(exact::"foo")"#).unwrap_err().kind(),
3976 @r#"Expression("Invalid string expression")"#);
3977 insta::assert_debug_snapshot!(
3978 parse(r#"bookmarks(exact:"foo"+)"#).unwrap_err().kind(),
3979 @r#"Expression("Invalid string expression")"#);
3980
3981 insta::assert_debug_snapshot!(
3982 parse(r#"tags("foo")"#).unwrap(),
3983 @r#"CommitRef(Tags(Pattern(Substring("foo"))))"#);
3984 insta::assert_debug_snapshot!(
3985 parse(r#"tags(exact:"foo")"#).unwrap(),
3986 @r#"CommitRef(Tags(Pattern(Exact("foo"))))"#);
3987 insta::assert_debug_snapshot!(
3988 parse(r#"tags(substring:"foo")"#).unwrap(),
3989 @r#"CommitRef(Tags(Pattern(Substring("foo"))))"#);
3990 insta::assert_debug_snapshot!(
3991 parse(r#"tags(bad:"foo")"#).unwrap_err().kind(),
3992 @r#"Expression("Invalid string pattern")"#);
3993 insta::assert_debug_snapshot!(
3994 parse(r#"tags(exact::"foo")"#).unwrap_err().kind(),
3995 @r#"Expression("Invalid string expression")"#);
3996 insta::assert_debug_snapshot!(
3997 parse(r#"tags(exact:"foo"+)"#).unwrap_err().kind(),
3998 @r#"Expression("Invalid string expression")"#);
3999
4000 assert_matches!(
4002 parse(r#"(exact:"foo")"#).unwrap_err().kind(),
4003 RevsetParseErrorKind::NotInfixOperator { .. }
4004 );
4005 }
4006
4007 #[test]
4008 fn test_parse_compound_string_expression() {
4009 let settings = insta_settings();
4010 let _guard = settings.bind_to_scope();
4011
4012 insta::assert_debug_snapshot!(
4013 parse(r#"tags(~a)"#).unwrap(),
4014 @r#"
4015 CommitRef(
4016 Tags(NotIn(Pattern(Substring("a")))),
4017 )
4018 "#);
4019 insta::assert_debug_snapshot!(
4020 parse(r#"tags(a|b&c)"#).unwrap(),
4021 @r#"
4022 CommitRef(
4023 Tags(
4024 Union(
4025 Pattern(Substring("a")),
4026 Intersection(
4027 Pattern(Substring("b")),
4028 Pattern(Substring("c")),
4029 ),
4030 ),
4031 ),
4032 )
4033 "#);
4034 insta::assert_debug_snapshot!(
4035 parse(r#"tags(a|b|c)"#).unwrap(),
4036 @r#"
4037 CommitRef(
4038 Tags(
4039 Union(
4040 Pattern(Substring("a")),
4041 Union(
4042 Pattern(Substring("b")),
4043 Pattern(Substring("c")),
4044 ),
4045 ),
4046 ),
4047 )
4048 "#);
4049 insta::assert_debug_snapshot!(
4050 parse(r#"tags(a~(b|c))"#).unwrap(),
4051 @r#"
4052 CommitRef(
4053 Tags(
4054 Intersection(
4055 Pattern(Substring("a")),
4056 NotIn(
4057 Union(
4058 Pattern(Substring("b")),
4059 Pattern(Substring("c")),
4060 ),
4061 ),
4062 ),
4063 ),
4064 )
4065 "#);
4066 }
4067
4068 #[test]
4069 fn test_parse_revset_function() {
4070 let settings = insta_settings();
4071 let _guard = settings.bind_to_scope();
4072
4073 insta::assert_debug_snapshot!(
4074 parse("parents(foo)").unwrap(), @r#"
4075 Ancestors {
4076 heads: CommitRef(Symbol("foo")),
4077 generation: 1..2,
4078 parents_range: 0..4294967295,
4079 }
4080 "#);
4081 insta::assert_debug_snapshot!(
4082 parse("parents(\"foo\")").unwrap(), @r#"
4083 Ancestors {
4084 heads: CommitRef(Symbol("foo")),
4085 generation: 1..2,
4086 parents_range: 0..4294967295,
4087 }
4088 "#);
4089 insta::assert_debug_snapshot!(
4090 parse("ancestors(parents(foo))").unwrap(), @r#"
4091 Ancestors {
4092 heads: Ancestors {
4093 heads: CommitRef(Symbol("foo")),
4094 generation: 1..2,
4095 parents_range: 0..4294967295,
4096 },
4097 generation: 0..18446744073709551615,
4098 parents_range: 0..4294967295,
4099 }
4100 "#);
4101 insta::assert_debug_snapshot!(
4102 parse("parents(foo, bar, baz)").unwrap_err().kind(), @r#"
4103 InvalidFunctionArguments {
4104 name: "parents",
4105 message: "Expected 1 to 2 arguments",
4106 }
4107 "#);
4108 insta::assert_debug_snapshot!(
4109 parse("parents(foo, 2)").unwrap(), @r#"
4110 Ancestors {
4111 heads: CommitRef(Symbol("foo")),
4112 generation: 2..3,
4113 parents_range: 0..4294967295,
4114 }
4115 "#);
4116 insta::assert_debug_snapshot!(
4117 parse("root()").unwrap(),
4118 @"Root");
4119 assert!(parse("root(a)").is_err());
4120 insta::assert_debug_snapshot!(
4121 parse(r#"description("")"#).unwrap(),
4122 @r#"Filter(Description(Pattern(Substring(""))))"#);
4123 insta::assert_debug_snapshot!(
4124 parse("description(foo)").unwrap(),
4125 @r#"Filter(Description(Pattern(Substring("foo"))))"#);
4126 insta::assert_debug_snapshot!(
4127 parse("description(visible_heads())").unwrap_err().kind(),
4128 @r#"Expression("Invalid string expression")"#);
4129 insta::assert_debug_snapshot!(
4130 parse("description(\"(foo)\")").unwrap(),
4131 @r#"Filter(Description(Pattern(Substring("(foo)"))))"#);
4132 assert!(parse("mine(foo)").is_err());
4133 insta::assert_debug_snapshot!(
4134 parse_with_workspace("empty()", WorkspaceName::DEFAULT).unwrap(),
4135 @"NotIn(Filter(File(All)))");
4136 assert!(parse_with_workspace("empty(foo)", WorkspaceName::DEFAULT).is_err());
4137 assert!(parse_with_workspace("file()", WorkspaceName::DEFAULT).is_err());
4138 insta::assert_debug_snapshot!(
4139 parse_with_workspace("files(foo)", WorkspaceName::DEFAULT).unwrap(),
4140 @r#"Filter(File(Pattern(PrefixPath("foo"))))"#);
4141 insta::assert_debug_snapshot!(
4142 parse_with_workspace("files(all())", WorkspaceName::DEFAULT).unwrap(),
4143 @"Filter(File(All))");
4144 insta::assert_debug_snapshot!(
4145 parse_with_workspace(r#"files(file:"foo")"#, WorkspaceName::DEFAULT).unwrap(),
4146 @r#"Filter(File(Pattern(FilePath("foo"))))"#);
4147 insta::assert_debug_snapshot!(
4148 parse_with_workspace("files(foo|bar&baz)", WorkspaceName::DEFAULT).unwrap(), @r#"
4149 Filter(
4150 File(
4151 UnionAll(
4152 [
4153 Pattern(PrefixPath("foo")),
4154 Intersection(
4155 Pattern(PrefixPath("bar")),
4156 Pattern(PrefixPath("baz")),
4157 ),
4158 ],
4159 ),
4160 ),
4161 )
4162 "#);
4163 insta::assert_debug_snapshot!(
4164 parse_with_workspace(r#"files(~(foo))"#, WorkspaceName::DEFAULT).unwrap(),
4165 @r#"
4166 Filter(
4167 File(
4168 Difference(
4169 All,
4170 Pattern(PrefixPath("foo")),
4171 ),
4172 ),
4173 )
4174 "#);
4175 insta::assert_debug_snapshot!(parse("signed()").unwrap(), @"Filter(Signed)");
4176 }
4177
4178 #[test]
4179 fn test_parse_revset_change_commit_id_functions() {
4180 let settings = insta_settings();
4181 let _guard = settings.bind_to_scope();
4182
4183 insta::assert_debug_snapshot!(
4184 parse("change_id(z)").unwrap(),
4185 @r#"CommitRef(ChangeId(HexPrefix("0")))"#);
4186 insta::assert_debug_snapshot!(
4187 parse("change_id('zk')").unwrap(),
4188 @r#"CommitRef(ChangeId(HexPrefix("0f")))"#);
4189 insta::assert_debug_snapshot!(
4190 parse("change_id(01234)").unwrap_err().kind(),
4191 @r#"Expression("Invalid change ID prefix")"#);
4192
4193 insta::assert_debug_snapshot!(
4194 parse("commit_id(0)").unwrap(),
4195 @r#"CommitRef(CommitId(HexPrefix("0")))"#);
4196 insta::assert_debug_snapshot!(
4197 parse("commit_id('0f')").unwrap(),
4198 @r#"CommitRef(CommitId(HexPrefix("0f")))"#);
4199 insta::assert_debug_snapshot!(
4200 parse("commit_id(xyzzy)").unwrap_err().kind(),
4201 @r#"Expression("Invalid commit ID prefix")"#);
4202 }
4203
4204 #[test]
4205 fn test_parse_revset_author_committer_functions() {
4206 let settings = insta_settings();
4207 let _guard = settings.bind_to_scope();
4208
4209 insta::assert_debug_snapshot!(
4210 parse("author(foo)").unwrap(), @r#"
4211 Union(
4212 Filter(AuthorName(Pattern(Substring("foo")))),
4213 Filter(AuthorEmail(Pattern(Substring("foo")))),
4214 )
4215 "#);
4216 insta::assert_debug_snapshot!(
4217 parse("author_name(foo)").unwrap(),
4218 @r#"Filter(AuthorName(Pattern(Substring("foo"))))"#);
4219 insta::assert_debug_snapshot!(
4220 parse("author_email(foo)").unwrap(),
4221 @r#"Filter(AuthorEmail(Pattern(Substring("foo"))))"#);
4222
4223 insta::assert_debug_snapshot!(
4224 parse("committer(foo)").unwrap(), @r#"
4225 Union(
4226 Filter(CommitterName(Pattern(Substring("foo")))),
4227 Filter(CommitterEmail(Pattern(Substring("foo")))),
4228 )
4229 "#);
4230 insta::assert_debug_snapshot!(
4231 parse("committer_name(foo)").unwrap(),
4232 @r#"Filter(CommitterName(Pattern(Substring("foo"))))"#);
4233 insta::assert_debug_snapshot!(
4234 parse("committer_email(foo)").unwrap(),
4235 @r#"Filter(CommitterEmail(Pattern(Substring("foo"))))"#);
4236
4237 insta::assert_debug_snapshot!(
4238 parse("mine()").unwrap(),
4239 @r#"Filter(AuthorEmail(Pattern(ExactI("test.user@example.com"))))"#);
4240 }
4241
4242 #[test]
4243 fn test_parse_revset_keyword_arguments() {
4244 let settings = insta_settings();
4245 let _guard = settings.bind_to_scope();
4246
4247 insta::assert_debug_snapshot!(
4248 parse("remote_bookmarks(remote=foo)").unwrap(), @r#"
4249 CommitRef(
4250 RemoteBookmarks {
4251 bookmark: Pattern(Substring("")),
4252 remote: Pattern(Substring("foo")),
4253 remote_ref_state: None,
4254 },
4255 )
4256 "#);
4257 insta::assert_debug_snapshot!(
4258 parse("remote_bookmarks(foo, remote=bar)").unwrap(), @r#"
4259 CommitRef(
4260 RemoteBookmarks {
4261 bookmark: Pattern(Substring("foo")),
4262 remote: Pattern(Substring("bar")),
4263 remote_ref_state: None,
4264 },
4265 )
4266 "#);
4267 insta::assert_debug_snapshot!(
4268 parse("tracked_remote_bookmarks(foo, remote=bar)").unwrap(), @r#"
4269 CommitRef(
4270 RemoteBookmarks {
4271 bookmark: Pattern(Substring("foo")),
4272 remote: Pattern(Substring("bar")),
4273 remote_ref_state: Some(Tracked),
4274 },
4275 )
4276 "#);
4277 insta::assert_debug_snapshot!(
4278 parse("untracked_remote_bookmarks(foo, remote=bar)").unwrap(), @r#"
4279 CommitRef(
4280 RemoteBookmarks {
4281 bookmark: Pattern(Substring("foo")),
4282 remote: Pattern(Substring("bar")),
4283 remote_ref_state: Some(New),
4284 },
4285 )
4286 "#);
4287 insta::assert_debug_snapshot!(
4288 parse(r#"remote_bookmarks(remote=foo, bar)"#).unwrap_err().kind(),
4289 @r#"
4290 InvalidFunctionArguments {
4291 name: "remote_bookmarks",
4292 message: "Positional argument follows keyword argument",
4293 }
4294 "#);
4295 insta::assert_debug_snapshot!(
4296 parse(r#"remote_bookmarks("", foo, remote=bar)"#).unwrap_err().kind(),
4297 @r#"
4298 InvalidFunctionArguments {
4299 name: "remote_bookmarks",
4300 message: "Got multiple values for keyword \"remote\"",
4301 }
4302 "#);
4303 insta::assert_debug_snapshot!(
4304 parse(r#"remote_bookmarks(remote=bar, remote=bar)"#).unwrap_err().kind(),
4305 @r#"
4306 InvalidFunctionArguments {
4307 name: "remote_bookmarks",
4308 message: "Got multiple values for keyword \"remote\"",
4309 }
4310 "#);
4311 insta::assert_debug_snapshot!(
4312 parse(r#"remote_bookmarks(unknown=bar)"#).unwrap_err().kind(),
4313 @r#"
4314 InvalidFunctionArguments {
4315 name: "remote_bookmarks",
4316 message: "Unexpected keyword argument \"unknown\"",
4317 }
4318 "#);
4319 }
4320
4321 #[test]
4322 fn test_expand_symbol_alias() {
4323 let settings = insta_settings();
4324 let _guard = settings.bind_to_scope();
4325
4326 insta::assert_debug_snapshot!(
4327 parse_with_aliases("AB|c", [("AB", "a|b")]).unwrap(), @r#"
4328 Union(
4329 Union(
4330 CommitRef(Symbol("a")),
4331 CommitRef(Symbol("b")),
4332 ),
4333 CommitRef(Symbol("c")),
4334 )
4335 "#);
4336
4337 insta::assert_debug_snapshot!(
4339 parse_with_aliases_and_workspace("files(A)", [("A", "a")], WorkspaceName::DEFAULT)
4340 .unwrap(),
4341 @r#"Filter(File(Pattern(PrefixPath("a"))))"#);
4342
4343 insta::assert_debug_snapshot!(
4345 parse_with_aliases("author_name(A)", [("A", "a")]).unwrap(),
4346 @r#"Filter(AuthorName(Pattern(Substring("a"))))"#);
4347 insta::assert_debug_snapshot!(
4350 parse_with_aliases("author_name(A)", [("A", "(exact:a)")]).unwrap(),
4351 @r#"Filter(AuthorName(Pattern(Exact("a"))))"#);
4352
4353 insta::assert_debug_snapshot!(
4355 parse_with_aliases_and_modifier("A-", [("A", "all:a")]).unwrap_err().kind(),
4356 @r#"InAliasExpansion("A")"#);
4357 }
4358
4359 #[test]
4360 fn test_expand_function_alias() {
4361 let settings = insta_settings();
4362 let _guard = settings.bind_to_scope();
4363
4364 insta::assert_debug_snapshot!(
4366 parse_with_aliases("F(a)", [("F(x)", "author_name(x)|committer_name(x)")]).unwrap(),
4367 @r#"
4368 Union(
4369 Filter(AuthorName(Pattern(Substring("a")))),
4370 Filter(CommitterName(Pattern(Substring("a")))),
4371 )
4372 "#);
4373 }
4374
4375 #[test]
4376 fn test_transform_expression() {
4377 let settings = insta_settings();
4378 let _guard = settings.bind_to_scope();
4379
4380 insta::assert_debug_snapshot!(
4382 transform_expression(
4383 &ResolvedRevsetExpression::root(),
4384 |_| ControlFlow::Break(None),
4385 |_| Some(RevsetExpression::none()),
4386 ), @"None");
4387
4388 insta::assert_debug_snapshot!(
4390 transform_expression(
4391 &ResolvedRevsetExpression::root(),
4392 |_| ControlFlow::Break(Some(RevsetExpression::all())),
4393 |_| Some(RevsetExpression::none()),
4394 ), @"Some(All)");
4395
4396 insta::assert_debug_snapshot!(
4398 transform_expression(
4399 &ResolvedRevsetExpression::root().heads(),
4400 |_| ControlFlow::Continue(()),
4401 |x| match x.as_ref() {
4402 RevsetExpression::Root => Some(RevsetExpression::none()),
4403 _ => None,
4404 },
4405 ), @"Some(Heads(None))");
4406
4407 insta::assert_debug_snapshot!(
4409 transform_expression(
4410 &ResolvedRevsetExpression::root().heads(),
4411 |_| ControlFlow::Continue(()),
4412 |x| match x.as_ref() {
4413 RevsetExpression::Heads(y) => Some(y.clone()),
4414 _ => None,
4415 },
4416 ), @"Some(Root)");
4417 }
4418
4419 #[test]
4420 fn test_resolve_referenced_commits() {
4421 let settings = insta_settings();
4422 let _guard = settings.bind_to_scope();
4423
4424 let visibility1 = Arc::new(ResolvedRevsetExpression::WithinVisibility {
4425 candidates: RevsetExpression::commit(CommitId::from_hex("100001")),
4426 visible_heads: vec![CommitId::from_hex("100000")],
4427 });
4428 let visibility2 = Arc::new(ResolvedRevsetExpression::WithinVisibility {
4429 candidates: RevsetExpression::filter(RevsetFilterPredicate::HasConflict),
4430 visible_heads: vec![CommitId::from_hex("200000")],
4431 });
4432 let commit3 = RevsetExpression::commit(CommitId::from_hex("000003"));
4433
4434 insta::assert_debug_snapshot!(
4437 resolve_referenced_commits(&visibility1.intersection(&RevsetExpression::all())), @r#"
4438 Some(
4439 WithinReference {
4440 candidates: Intersection(
4441 WithinVisibility {
4442 candidates: WithinReference {
4443 candidates: Commits(
4444 [
4445 CommitId("100001"),
4446 ],
4447 ),
4448 commits: [
4449 CommitId("100001"),
4450 ],
4451 },
4452 visible_heads: [
4453 CommitId("100000"),
4454 ],
4455 },
4456 All,
4457 ),
4458 commits: [
4459 CommitId("100000"),
4460 CommitId("100001"),
4461 ],
4462 },
4463 )
4464 "#);
4465
4466 insta::assert_debug_snapshot!(
4468 resolve_referenced_commits(
4469 &visibility2
4470 .intersection(&RevsetExpression::all())
4471 .union(&commit3),
4472 ), @r#"
4473 Some(
4474 WithinReference {
4475 candidates: Union(
4476 Intersection(
4477 WithinVisibility {
4478 candidates: Filter(HasConflict),
4479 visible_heads: [
4480 CommitId("200000"),
4481 ],
4482 },
4483 All,
4484 ),
4485 Commits(
4486 [
4487 CommitId("000003"),
4488 ],
4489 ),
4490 ),
4491 commits: [
4492 CommitId("000003"),
4493 CommitId("200000"),
4494 ],
4495 },
4496 )
4497 "#);
4498
4499 insta::assert_debug_snapshot!(
4501 resolve_referenced_commits(
4502 &visibility1
4503 .union(&visibility2)
4504 .union(&commit3)
4505 .intersection(&RevsetExpression::all())
4506 ), @r#"
4507 Some(
4508 WithinReference {
4509 candidates: Intersection(
4510 Union(
4511 Union(
4512 WithinVisibility {
4513 candidates: WithinReference {
4514 candidates: Commits(
4515 [
4516 CommitId("100001"),
4517 ],
4518 ),
4519 commits: [
4520 CommitId("100001"),
4521 ],
4522 },
4523 visible_heads: [
4524 CommitId("100000"),
4525 ],
4526 },
4527 WithinVisibility {
4528 candidates: Filter(HasConflict),
4529 visible_heads: [
4530 CommitId("200000"),
4531 ],
4532 },
4533 ),
4534 Commits(
4535 [
4536 CommitId("000003"),
4537 ],
4538 ),
4539 ),
4540 All,
4541 ),
4542 commits: [
4543 CommitId("000003"),
4544 CommitId("100000"),
4545 CommitId("100001"),
4546 CommitId("200000"),
4547 ],
4548 },
4549 )
4550 "#);
4551
4552 insta::assert_debug_snapshot!(
4554 resolve_referenced_commits(&Arc::new(ResolvedRevsetExpression::WithinVisibility {
4555 candidates: visibility1.clone(),
4556 visible_heads: vec![CommitId::from_hex("400000")],
4557 })), @r#"
4558 Some(
4559 WithinReference {
4560 candidates: WithinVisibility {
4561 candidates: WithinReference {
4562 candidates: WithinVisibility {
4563 candidates: WithinReference {
4564 candidates: Commits(
4565 [
4566 CommitId("100001"),
4567 ],
4568 ),
4569 commits: [
4570 CommitId("100001"),
4571 ],
4572 },
4573 visible_heads: [
4574 CommitId("100000"),
4575 ],
4576 },
4577 commits: [
4578 CommitId("100000"),
4579 CommitId("100001"),
4580 ],
4581 },
4582 visible_heads: [
4583 CommitId("400000"),
4584 ],
4585 },
4586 commits: [
4587 CommitId("400000"),
4588 CommitId("100000"),
4589 CommitId("100001"),
4590 ],
4591 },
4592 )
4593 "#);
4594
4595 let resolved = Arc::new(ResolvedRevsetExpression::WithinReference {
4597 candidates: RevsetExpression::none(),
4600 commits: vec![CommitId::from_hex("100000")],
4601 });
4602 insta::assert_debug_snapshot!(
4603 resolve_referenced_commits(&resolved), @"None");
4604 insta::assert_debug_snapshot!(
4605 resolve_referenced_commits(&resolved.intersection(&RevsetExpression::all())), @r#"
4606 Some(
4607 WithinReference {
4608 candidates: Intersection(
4609 WithinReference {
4610 candidates: None,
4611 commits: [
4612 CommitId("100000"),
4613 ],
4614 },
4615 All,
4616 ),
4617 commits: [
4618 CommitId("100000"),
4619 ],
4620 },
4621 )
4622 "#);
4623 insta::assert_debug_snapshot!(
4624 resolve_referenced_commits(&Arc::new(ResolvedRevsetExpression::WithinVisibility {
4625 candidates: resolved.clone(),
4626 visible_heads: vec![CommitId::from_hex("400000")],
4627 })), @r#"
4628 Some(
4629 WithinReference {
4630 candidates: WithinVisibility {
4631 candidates: WithinReference {
4632 candidates: None,
4633 commits: [
4634 CommitId("100000"),
4635 ],
4636 },
4637 visible_heads: [
4638 CommitId("400000"),
4639 ],
4640 },
4641 commits: [
4642 CommitId("400000"),
4643 CommitId("100000"),
4644 ],
4645 },
4646 )
4647 "#);
4648 }
4649
4650 #[test]
4651 fn test_optimize_subtree() {
4652 let settings = insta_settings();
4653 let _guard = settings.bind_to_scope();
4654
4655 insta::assert_debug_snapshot!(
4659 optimize(parse("parents(bookmarks() & all())").unwrap()), @r#"
4660 Ancestors {
4661 heads: CommitRef(Bookmarks(Pattern(Substring("")))),
4662 generation: 1..2,
4663 parents_range: 0..4294967295,
4664 }
4665 "#);
4666 insta::assert_debug_snapshot!(
4667 optimize(parse("children(bookmarks() & all())").unwrap()), @r#"
4668 Descendants {
4669 roots: CommitRef(Bookmarks(Pattern(Substring("")))),
4670 generation: 1..2,
4671 }
4672 "#);
4673 insta::assert_debug_snapshot!(
4674 optimize(parse("ancestors(bookmarks() & all())").unwrap()), @r#"
4675 Ancestors {
4676 heads: CommitRef(Bookmarks(Pattern(Substring("")))),
4677 generation: 0..18446744073709551615,
4678 parents_range: 0..4294967295,
4679 }
4680 "#);
4681 insta::assert_debug_snapshot!(
4682 optimize(parse("descendants(bookmarks() & all())").unwrap()), @r#"
4683 Descendants {
4684 roots: CommitRef(Bookmarks(Pattern(Substring("")))),
4685 generation: 0..18446744073709551615,
4686 }
4687 "#);
4688
4689 insta::assert_debug_snapshot!(
4690 optimize(parse("(bookmarks() & all())..(all() & tags())").unwrap()), @r#"
4691 Range {
4692 roots: CommitRef(Bookmarks(Pattern(Substring("")))),
4693 heads: CommitRef(Tags(Pattern(Substring("")))),
4694 generation: 0..18446744073709551615,
4695 parents_range: 0..4294967295,
4696 }
4697 "#);
4698 insta::assert_debug_snapshot!(
4699 optimize(parse("(bookmarks() & all())::(all() & tags())").unwrap()), @r#"
4700 DagRange {
4701 roots: CommitRef(Bookmarks(Pattern(Substring("")))),
4702 heads: CommitRef(Tags(Pattern(Substring("")))),
4703 }
4704 "#);
4705
4706 insta::assert_debug_snapshot!(
4707 optimize(parse("heads(bookmarks() & all())").unwrap()),
4708 @r#"
4709 Heads(
4710 CommitRef(Bookmarks(Pattern(Substring("")))),
4711 )
4712 "#);
4713 insta::assert_debug_snapshot!(
4714 optimize(parse("roots(bookmarks() & all())").unwrap()),
4715 @r#"
4716 Roots(
4717 CommitRef(Bookmarks(Pattern(Substring("")))),
4718 )
4719 "#);
4720
4721 insta::assert_debug_snapshot!(
4722 optimize(parse("latest(bookmarks() & all(), 2)").unwrap()), @r#"
4723 Latest {
4724 candidates: CommitRef(Bookmarks(Pattern(Substring("")))),
4725 count: 2,
4726 }
4727 "#);
4728
4729 insta::assert_debug_snapshot!(
4730 optimize(parse("present(foo ~ bar)").unwrap()), @r#"
4731 Present(
4732 Difference(
4733 CommitRef(Symbol("foo")),
4734 CommitRef(Symbol("bar")),
4735 ),
4736 )
4737 "#);
4738 insta::assert_debug_snapshot!(
4739 optimize(parse("present(bookmarks() & all())").unwrap()),
4740 @r#"
4741 Present(
4742 CommitRef(Bookmarks(Pattern(Substring("")))),
4743 )
4744 "#);
4745
4746 insta::assert_debug_snapshot!(
4747 optimize(parse("at_operation(@-, bookmarks() & all())").unwrap()), @r#"
4748 AtOperation {
4749 operation: "@-",
4750 candidates: CommitRef(Bookmarks(Pattern(Substring("")))),
4751 }
4752 "#);
4753 insta::assert_debug_snapshot!(
4754 optimize(Arc::new(RevsetExpression::WithinReference {
4755 candidates: parse("bookmarks() & all()").unwrap(),
4756 commits: vec![CommitId::from_hex("012345")],
4757 })), @r#"
4758 WithinReference {
4759 candidates: CommitRef(Bookmarks(Pattern(Substring("")))),
4760 commits: [
4761 CommitId("012345"),
4762 ],
4763 }
4764 "#);
4765 insta::assert_debug_snapshot!(
4766 optimize(Arc::new(RevsetExpression::WithinVisibility {
4767 candidates: parse("bookmarks() & all()").unwrap(),
4768 visible_heads: vec![CommitId::from_hex("012345")],
4769 })), @r#"
4770 WithinReference {
4771 candidates: WithinVisibility {
4772 candidates: CommitRef(Bookmarks(Pattern(Substring("")))),
4773 visible_heads: [
4774 CommitId("012345"),
4775 ],
4776 },
4777 commits: [
4778 CommitId("012345"),
4779 ],
4780 }
4781 "#);
4782
4783 insta::assert_debug_snapshot!(
4784 optimize(parse("~bookmarks() & all()").unwrap()),
4785 @r#"
4786 NotIn(
4787 CommitRef(Bookmarks(Pattern(Substring("")))),
4788 )
4789 "#);
4790 insta::assert_debug_snapshot!(
4791 optimize(parse("(bookmarks() & all()) | (all() & tags())").unwrap()), @r#"
4792 Union(
4793 CommitRef(Bookmarks(Pattern(Substring("")))),
4794 CommitRef(Tags(Pattern(Substring("")))),
4795 )
4796 "#);
4797 insta::assert_debug_snapshot!(
4798 optimize(parse("(bookmarks() & all()) & (all() & tags())").unwrap()), @r#"
4799 Intersection(
4800 CommitRef(Bookmarks(Pattern(Substring("")))),
4801 CommitRef(Tags(Pattern(Substring("")))),
4802 )
4803 "#);
4804 insta::assert_debug_snapshot!(
4805 optimize(parse("(bookmarks() & all()) ~ (all() & tags())").unwrap()), @r#"
4806 Difference(
4807 CommitRef(Bookmarks(Pattern(Substring("")))),
4808 CommitRef(Tags(Pattern(Substring("")))),
4809 )
4810 "#);
4811 }
4812
4813 #[test]
4814 fn test_optimize_unchanged_subtree() {
4815 fn unwrap_union(
4816 expression: &UserRevsetExpression,
4817 ) -> (&Arc<UserRevsetExpression>, &Arc<UserRevsetExpression>) {
4818 match expression {
4819 RevsetExpression::Union(left, right) => (left, right),
4820 _ => panic!("unexpected expression: {expression:?}"),
4821 }
4822 }
4823
4824 let parsed = parse("foo-").unwrap();
4826 let optimized = optimize(parsed.clone());
4827 assert!(Arc::ptr_eq(&parsed, &optimized));
4828
4829 let parsed = parse("bookmarks() | tags()").unwrap();
4830 let optimized = optimize(parsed.clone());
4831 assert!(Arc::ptr_eq(&parsed, &optimized));
4832
4833 let parsed = parse("bookmarks() & tags()").unwrap();
4834 let optimized = optimize(parsed.clone());
4835 assert!(Arc::ptr_eq(&parsed, &optimized));
4836
4837 let parsed = parse("(bookmarks() & all()) | tags()").unwrap();
4839 let optimized = optimize(parsed.clone());
4840 assert_matches!(
4841 unwrap_union(&optimized).0.as_ref(),
4842 RevsetExpression::CommitRef(RevsetCommitRef::Bookmarks(_))
4843 );
4844 assert!(Arc::ptr_eq(
4845 unwrap_union(&parsed).1,
4846 unwrap_union(&optimized).1
4847 ));
4848
4849 let parsed = parse("bookmarks() | (all() & tags())").unwrap();
4851 let optimized = optimize(parsed.clone());
4852 assert!(Arc::ptr_eq(
4853 unwrap_union(&parsed).0,
4854 unwrap_union(&optimized).0
4855 ));
4856 assert_matches!(
4857 unwrap_union(&optimized).1.as_ref(),
4858 RevsetExpression::CommitRef(RevsetCommitRef::Tags(_))
4859 );
4860 }
4861
4862 #[test]
4863 fn test_optimize_basic() {
4864 let settings = insta_settings();
4865 let _guard = settings.bind_to_scope();
4866
4867 insta::assert_debug_snapshot!(optimize(parse("all() | none()").unwrap()), @"All");
4868 insta::assert_debug_snapshot!(optimize(parse("all() & none()").unwrap()), @"None");
4869 insta::assert_debug_snapshot!(optimize(parse("root() | all()").unwrap()), @"All");
4870 insta::assert_debug_snapshot!(optimize(parse("root() & all()").unwrap()), @"Root");
4871 insta::assert_debug_snapshot!(optimize(parse("none() | root()").unwrap()), @"Root");
4872 insta::assert_debug_snapshot!(optimize(parse("none() & root()").unwrap()), @"None");
4873 insta::assert_debug_snapshot!(optimize(parse("~none()").unwrap()), @"All");
4874 insta::assert_debug_snapshot!(optimize(parse("~~none()").unwrap()), @"None");
4875 insta::assert_debug_snapshot!(optimize(parse("~all()").unwrap()), @"None");
4876 insta::assert_debug_snapshot!(optimize(parse("~~all()").unwrap()), @"All");
4877 insta::assert_debug_snapshot!(optimize(parse("~~foo").unwrap()), @r#"CommitRef(Symbol("foo"))"#);
4878 insta::assert_debug_snapshot!(
4879 optimize(parse("(root() | none()) & (visible_heads() | ~~all())").unwrap()), @"Root");
4880 insta::assert_debug_snapshot!(
4881 optimize(UserRevsetExpression::commits(vec![])), @"None");
4882 insta::assert_debug_snapshot!(
4883 optimize(UserRevsetExpression::commits(vec![]).negated()), @"All");
4884 }
4885
4886 #[test]
4887 fn test_optimize_difference() {
4888 let settings = insta_settings();
4889 let _guard = settings.bind_to_scope();
4890
4891 insta::assert_debug_snapshot!(optimize(parse("foo & ~bar").unwrap()), @r#"
4892 Difference(
4893 CommitRef(Symbol("foo")),
4894 CommitRef(Symbol("bar")),
4895 )
4896 "#);
4897 insta::assert_debug_snapshot!(optimize(parse("~foo & bar").unwrap()), @r#"
4898 Difference(
4899 CommitRef(Symbol("bar")),
4900 CommitRef(Symbol("foo")),
4901 )
4902 "#);
4903 insta::assert_debug_snapshot!(optimize(parse("~foo & bar & ~baz").unwrap()), @r#"
4904 Difference(
4905 Difference(
4906 CommitRef(Symbol("bar")),
4907 CommitRef(Symbol("foo")),
4908 ),
4909 CommitRef(Symbol("baz")),
4910 )
4911 "#);
4912 insta::assert_debug_snapshot!(optimize(parse("(all() & ~foo) & bar").unwrap()), @r#"
4913 Difference(
4914 CommitRef(Symbol("bar")),
4915 CommitRef(Symbol("foo")),
4916 )
4917 "#);
4918
4919 insta::assert_debug_snapshot!(
4921 optimize(parse("all() ~ foo").unwrap()),
4922 @r#"NotIn(CommitRef(Symbol("foo")))"#);
4923 insta::assert_debug_snapshot!(optimize(parse("foo ~ bar").unwrap()), @r#"
4924 Difference(
4925 CommitRef(Symbol("foo")),
4926 CommitRef(Symbol("bar")),
4927 )
4928 "#);
4929 insta::assert_debug_snapshot!(optimize(parse("(all() ~ foo) & bar").unwrap()), @r#"
4930 Difference(
4931 CommitRef(Symbol("bar")),
4932 CommitRef(Symbol("foo")),
4933 )
4934 "#);
4935
4936 insta::assert_debug_snapshot!(optimize(parse("::foo & ~::bar").unwrap()), @r#"
4938 Range {
4939 roots: CommitRef(Symbol("bar")),
4940 heads: CommitRef(Symbol("foo")),
4941 generation: 0..18446744073709551615,
4942 parents_range: 0..4294967295,
4943 }
4944 "#);
4945 insta::assert_debug_snapshot!(optimize(parse("~::foo & ::bar").unwrap()), @r#"
4946 Range {
4947 roots: CommitRef(Symbol("foo")),
4948 heads: CommitRef(Symbol("bar")),
4949 generation: 0..18446744073709551615,
4950 parents_range: 0..4294967295,
4951 }
4952 "#);
4953 insta::assert_debug_snapshot!(optimize(parse("foo..").unwrap()), @r#"
4954 Range {
4955 roots: CommitRef(Symbol("foo")),
4956 heads: VisibleHeadsOrReferenced,
4957 generation: 0..18446744073709551615,
4958 parents_range: 0..4294967295,
4959 }
4960 "#);
4961 insta::assert_debug_snapshot!(optimize(parse("foo..bar").unwrap()), @r#"
4962 Range {
4963 roots: CommitRef(Symbol("foo")),
4964 heads: CommitRef(Symbol("bar")),
4965 generation: 0..18446744073709551615,
4966 parents_range: 0..4294967295,
4967 }
4968 "#);
4969 insta::assert_debug_snapshot!(optimize(parse("foo.. & ::bar").unwrap()), @r#"
4970 Range {
4971 roots: CommitRef(Symbol("foo")),
4972 heads: CommitRef(Symbol("bar")),
4973 generation: 0..18446744073709551615,
4974 parents_range: 0..4294967295,
4975 }
4976 "#);
4977 insta::assert_debug_snapshot!(optimize(parse("foo.. & first_ancestors(bar)").unwrap()), @r#"
4978 Range {
4979 roots: CommitRef(Symbol("foo")),
4980 heads: CommitRef(Symbol("bar")),
4981 generation: 0..18446744073709551615,
4982 parents_range: 0..1,
4983 }
4984 "#);
4985
4986 insta::assert_debug_snapshot!(optimize(parse("foo & ~~bar").unwrap()), @r#"
4988 Intersection(
4989 CommitRef(Symbol("foo")),
4990 CommitRef(Symbol("bar")),
4991 )
4992 "#);
4993 insta::assert_debug_snapshot!(optimize(parse("foo & ~~~bar").unwrap()), @r#"
4994 Difference(
4995 CommitRef(Symbol("foo")),
4996 CommitRef(Symbol("bar")),
4997 )
4998 "#);
4999 insta::assert_debug_snapshot!(optimize(parse("~(all() & ~foo) & bar").unwrap()), @r#"
5000 Intersection(
5001 CommitRef(Symbol("foo")),
5002 CommitRef(Symbol("bar")),
5003 )
5004 "#);
5005
5006 insta::assert_debug_snapshot!(optimize(parse("~foo & ~bar").unwrap()), @r#"
5008 Difference(
5009 NotIn(CommitRef(Symbol("foo"))),
5010 CommitRef(Symbol("bar")),
5011 )
5012 "#);
5013
5014 insta::assert_debug_snapshot!(optimize(parse("a..b & c..d").unwrap()), @r#"
5016 Intersection(
5017 Range {
5018 roots: Union(
5019 CommitRef(Symbol("a")),
5020 CommitRef(Symbol("c")),
5021 ),
5022 heads: CommitRef(Symbol("b")),
5023 generation: 0..18446744073709551615,
5024 parents_range: 0..4294967295,
5025 },
5026 Ancestors {
5027 heads: CommitRef(Symbol("d")),
5028 generation: 0..18446744073709551615,
5029 parents_range: 0..4294967295,
5030 },
5031 )
5032 "#);
5033
5034 insta::assert_debug_snapshot!(optimize(parse("foo..bar ~ first_ancestors(baz)").unwrap()), @r#"
5036 Difference(
5037 Range {
5038 roots: CommitRef(Symbol("foo")),
5039 heads: CommitRef(Symbol("bar")),
5040 generation: 0..18446744073709551615,
5041 parents_range: 0..4294967295,
5042 },
5043 Ancestors {
5044 heads: CommitRef(Symbol("baz")),
5045 generation: 0..18446744073709551615,
5046 parents_range: 0..1,
5047 },
5048 )
5049 "#);
5050
5051 insta::assert_debug_snapshot!(optimize(parse("foo ~ ::a & (::b & bar & ::c) & (baz ~ ::d)").unwrap()), @r#"
5054 Intersection(
5055 Intersection(
5056 Intersection(
5057 Intersection(
5058 Range {
5059 roots: Union(
5060 CommitRef(Symbol("a")),
5061 CommitRef(Symbol("d")),
5062 ),
5063 heads: CommitRef(Symbol("b")),
5064 generation: 0..18446744073709551615,
5065 parents_range: 0..4294967295,
5066 },
5067 Ancestors {
5068 heads: CommitRef(Symbol("c")),
5069 generation: 0..18446744073709551615,
5070 parents_range: 0..4294967295,
5071 },
5072 ),
5073 CommitRef(Symbol("foo")),
5074 ),
5075 CommitRef(Symbol("bar")),
5076 ),
5077 CommitRef(Symbol("baz")),
5078 )
5079 "#);
5080 }
5081
5082 #[test]
5083 fn test_optimize_not_in_ancestors() {
5084 let settings = insta_settings();
5085 let _guard = settings.bind_to_scope();
5086
5087 insta::assert_debug_snapshot!(optimize(parse("~(::foo)").unwrap()), @r#"
5089 Range {
5090 roots: CommitRef(Symbol("foo")),
5091 heads: VisibleHeadsOrReferenced,
5092 generation: 0..18446744073709551615,
5093 parents_range: 0..4294967295,
5094 }
5095 "#);
5096
5097 insta::assert_debug_snapshot!(optimize(parse("~(::foo-)").unwrap()), @r#"
5099 Range {
5100 roots: Ancestors {
5101 heads: CommitRef(Symbol("foo")),
5102 generation: 1..2,
5103 parents_range: 0..4294967295,
5104 },
5105 heads: VisibleHeadsOrReferenced,
5106 generation: 0..18446744073709551615,
5107 parents_range: 0..4294967295,
5108 }
5109 "#);
5110 insta::assert_debug_snapshot!(optimize(parse("~(::foo--)").unwrap()), @r#"
5111 Range {
5112 roots: Ancestors {
5113 heads: CommitRef(Symbol("foo")),
5114 generation: 2..3,
5115 parents_range: 0..4294967295,
5116 },
5117 heads: VisibleHeadsOrReferenced,
5118 generation: 0..18446744073709551615,
5119 parents_range: 0..4294967295,
5120 }
5121 "#);
5122
5123 insta::assert_debug_snapshot!(optimize(parse("~ancestors(foo, 1)").unwrap()), @r#"
5125 NotIn(
5126 Ancestors {
5127 heads: CommitRef(Symbol("foo")),
5128 generation: 0..1,
5129 parents_range: 0..4294967295,
5130 },
5131 )
5132 "#);
5133 insta::assert_debug_snapshot!(optimize(parse("~ancestors(foo-, 1)").unwrap()), @r#"
5134 NotIn(
5135 Ancestors {
5136 heads: CommitRef(Symbol("foo")),
5137 generation: 1..2,
5138 parents_range: 0..4294967295,
5139 },
5140 )
5141 "#);
5142 }
5143
5144 #[test]
5145 fn test_optimize_filter_difference() {
5146 let settings = insta_settings();
5147 let _guard = settings.bind_to_scope();
5148
5149 insta::assert_debug_snapshot!(optimize(parse("~empty()").unwrap()), @"Filter(File(All))");
5151
5152 insta::assert_debug_snapshot!(
5154 optimize(parse("(author_name(foo) & ~bar) & baz").unwrap()), @r#"
5155 Intersection(
5156 Difference(
5157 CommitRef(Symbol("baz")),
5158 CommitRef(Symbol("bar")),
5159 ),
5160 Filter(AuthorName(Pattern(Substring("foo")))),
5161 )
5162 "#);
5163
5164 insta::assert_debug_snapshot!(
5166 optimize(parse("~foo & author_name(bar)").unwrap()), @r#"
5167 Intersection(
5168 NotIn(CommitRef(Symbol("foo"))),
5169 Filter(AuthorName(Pattern(Substring("bar")))),
5170 )
5171 "#);
5172 insta::assert_debug_snapshot!(
5173 optimize(parse("~foo & (author_name(bar) | baz)").unwrap()), @r#"
5174 Intersection(
5175 NotIn(CommitRef(Symbol("foo"))),
5176 AsFilter(
5177 Union(
5178 Filter(AuthorName(Pattern(Substring("bar")))),
5179 CommitRef(Symbol("baz")),
5180 ),
5181 ),
5182 )
5183 "#);
5184
5185 insta::assert_debug_snapshot!(
5187 optimize(parse("author_name(foo) ~ bar").unwrap()), @r#"
5188 Intersection(
5189 NotIn(CommitRef(Symbol("bar"))),
5190 Filter(AuthorName(Pattern(Substring("foo")))),
5191 )
5192 "#);
5193 }
5194
5195 #[test]
5196 fn test_optimize_filter_intersection() {
5197 let settings = insta_settings();
5198 let _guard = settings.bind_to_scope();
5199
5200 insta::assert_debug_snapshot!(
5201 optimize(parse("author_name(foo)").unwrap()),
5202 @r#"Filter(AuthorName(Pattern(Substring("foo"))))"#);
5203
5204 insta::assert_debug_snapshot!(optimize(parse("foo & description(bar)").unwrap()), @r#"
5205 Intersection(
5206 CommitRef(Symbol("foo")),
5207 Filter(Description(Pattern(Substring("bar")))),
5208 )
5209 "#);
5210 insta::assert_debug_snapshot!(optimize(parse("author_name(foo) & bar").unwrap()), @r#"
5211 Intersection(
5212 CommitRef(Symbol("bar")),
5213 Filter(AuthorName(Pattern(Substring("foo")))),
5214 )
5215 "#);
5216 insta::assert_debug_snapshot!(
5217 optimize(parse("author_name(foo) & committer_name(bar)").unwrap()), @r#"
5218 AsFilter(
5219 Intersection(
5220 Filter(AuthorName(Pattern(Substring("foo")))),
5221 Filter(CommitterName(Pattern(Substring("bar")))),
5222 ),
5223 )
5224 "#);
5225
5226 insta::assert_debug_snapshot!(
5227 optimize(parse("foo & description(bar) & author_name(baz)").unwrap()), @r#"
5228 Intersection(
5229 CommitRef(Symbol("foo")),
5230 AsFilter(
5231 Intersection(
5232 Filter(Description(Pattern(Substring("bar")))),
5233 Filter(AuthorName(Pattern(Substring("baz")))),
5234 ),
5235 ),
5236 )
5237 "#);
5238 insta::assert_debug_snapshot!(
5239 optimize(parse("committer_name(foo) & bar & author_name(baz)").unwrap()), @r#"
5240 Intersection(
5241 CommitRef(Symbol("bar")),
5242 AsFilter(
5243 Intersection(
5244 Filter(CommitterName(Pattern(Substring("foo")))),
5245 Filter(AuthorName(Pattern(Substring("baz")))),
5246 ),
5247 ),
5248 )
5249 "#);
5250 insta::assert_debug_snapshot!(
5251 optimize(parse_with_workspace(
5252 "committer_name(foo) & files(bar) & baz",
5253 WorkspaceName::DEFAULT).unwrap(),
5254 ), @r#"
5255 Intersection(
5256 CommitRef(Symbol("baz")),
5257 AsFilter(
5258 Intersection(
5259 Filter(CommitterName(Pattern(Substring("foo")))),
5260 Filter(File(Pattern(PrefixPath("bar")))),
5261 ),
5262 ),
5263 )
5264 "#);
5265 insta::assert_debug_snapshot!(
5266 optimize(parse_with_workspace(
5267 "committer_name(foo) & files(bar) & author_name(baz)",
5268 WorkspaceName::DEFAULT).unwrap(),
5269 ), @r#"
5270 AsFilter(
5271 Intersection(
5272 Intersection(
5273 Filter(CommitterName(Pattern(Substring("foo")))),
5274 Filter(File(Pattern(PrefixPath("bar")))),
5275 ),
5276 Filter(AuthorName(Pattern(Substring("baz")))),
5277 ),
5278 )
5279 "#);
5280 insta::assert_debug_snapshot!(
5281 optimize(parse_with_workspace(
5282 "foo & files(bar) & baz",
5283 WorkspaceName::DEFAULT).unwrap(),
5284 ), @r#"
5285 Intersection(
5286 Intersection(
5287 CommitRef(Symbol("foo")),
5288 CommitRef(Symbol("baz")),
5289 ),
5290 Filter(File(Pattern(PrefixPath("bar")))),
5291 )
5292 "#);
5293
5294 insta::assert_debug_snapshot!(
5295 optimize(parse("foo & description(bar) & author_name(baz) & qux").unwrap()), @r#"
5296 Intersection(
5297 Intersection(
5298 CommitRef(Symbol("foo")),
5299 CommitRef(Symbol("qux")),
5300 ),
5301 AsFilter(
5302 Intersection(
5303 Filter(Description(Pattern(Substring("bar")))),
5304 Filter(AuthorName(Pattern(Substring("baz")))),
5305 ),
5306 ),
5307 )
5308 "#);
5309 insta::assert_debug_snapshot!(
5310 optimize(parse("foo & description(bar) & parents(author_name(baz)) & qux").unwrap()),
5311 @r#"
5312 Intersection(
5313 Intersection(
5314 Intersection(
5315 CommitRef(Symbol("foo")),
5316 Ancestors {
5317 heads: Filter(AuthorName(Pattern(Substring("baz")))),
5318 generation: 1..2,
5319 parents_range: 0..4294967295,
5320 },
5321 ),
5322 CommitRef(Symbol("qux")),
5323 ),
5324 Filter(Description(Pattern(Substring("bar")))),
5325 )
5326 "#);
5327 insta::assert_debug_snapshot!(
5328 optimize(parse("foo & description(bar) & parents(author_name(baz) & qux)").unwrap()),
5329 @r#"
5330 Intersection(
5331 Intersection(
5332 CommitRef(Symbol("foo")),
5333 Ancestors {
5334 heads: Intersection(
5335 CommitRef(Symbol("qux")),
5336 Filter(AuthorName(Pattern(Substring("baz")))),
5337 ),
5338 generation: 1..2,
5339 parents_range: 0..4294967295,
5340 },
5341 ),
5342 Filter(Description(Pattern(Substring("bar")))),
5343 )
5344 "#);
5345
5346 insta::assert_debug_snapshot!(
5348 optimize(parse("(a & author_name(A)) & (b & author_name(B)) & (c & author_name(C))").unwrap()),
5349 @r#"
5350 Intersection(
5351 Intersection(
5352 Intersection(
5353 CommitRef(Symbol("a")),
5354 CommitRef(Symbol("b")),
5355 ),
5356 CommitRef(Symbol("c")),
5357 ),
5358 AsFilter(
5359 Intersection(
5360 Intersection(
5361 Filter(AuthorName(Pattern(Substring("A")))),
5362 Filter(AuthorName(Pattern(Substring("B")))),
5363 ),
5364 Filter(AuthorName(Pattern(Substring("C")))),
5365 ),
5366 ),
5367 )
5368 "#);
5369 insta::assert_debug_snapshot!(
5370 optimize(parse("(a & author_name(A)) & ((b & author_name(B)) & (c & author_name(C))) & d").unwrap()),
5371 @r#"
5372 Intersection(
5373 Intersection(
5374 Intersection(
5375 Intersection(
5376 CommitRef(Symbol("a")),
5377 CommitRef(Symbol("b")),
5378 ),
5379 CommitRef(Symbol("c")),
5380 ),
5381 CommitRef(Symbol("d")),
5382 ),
5383 AsFilter(
5384 Intersection(
5385 Intersection(
5386 Filter(AuthorName(Pattern(Substring("A")))),
5387 Filter(AuthorName(Pattern(Substring("B")))),
5388 ),
5389 Filter(AuthorName(Pattern(Substring("C")))),
5390 ),
5391 ),
5392 )
5393 "#);
5394
5395 insta::assert_debug_snapshot!(
5397 optimize(parse("foo & (all() & description(bar)) & (author_name(baz) & all())").unwrap()),
5398 @r#"
5399 Intersection(
5400 CommitRef(Symbol("foo")),
5401 AsFilter(
5402 Intersection(
5403 Filter(Description(Pattern(Substring("bar")))),
5404 Filter(AuthorName(Pattern(Substring("baz")))),
5405 ),
5406 ),
5407 )
5408 "#);
5409
5410 insta::assert_debug_snapshot!(
5412 optimize(parse("author_name(foo) & bar & at_operation(@-, committer_name(baz))").unwrap()),
5413 @r#"
5414 Intersection(
5415 Intersection(
5416 CommitRef(Symbol("bar")),
5417 AtOperation {
5418 operation: "@-",
5419 candidates: Filter(CommitterName(Pattern(Substring("baz")))),
5420 },
5421 ),
5422 Filter(AuthorName(Pattern(Substring("foo")))),
5423 )
5424 "#);
5425 }
5426
5427 #[test]
5428 fn test_optimize_filter_subtree() {
5429 let settings = insta_settings();
5430 let _guard = settings.bind_to_scope();
5431
5432 insta::assert_debug_snapshot!(
5433 optimize(parse("(author_name(foo) | bar) & baz").unwrap()), @r#"
5434 Intersection(
5435 CommitRef(Symbol("baz")),
5436 AsFilter(
5437 Union(
5438 Filter(AuthorName(Pattern(Substring("foo")))),
5439 CommitRef(Symbol("bar")),
5440 ),
5441 ),
5442 )
5443 "#);
5444
5445 insta::assert_debug_snapshot!(
5447 optimize(parse("merges() & foo | bar").unwrap()), @r#"
5448 Union(
5449 Intersection(
5450 CommitRef(Symbol("foo")),
5451 Filter(ParentCount(2..4294967295)),
5452 ),
5453 CommitRef(Symbol("bar")),
5454 )
5455 "#);
5456
5457 insta::assert_debug_snapshot!(
5460 optimize(parse("merges() & foo | conflicts()").unwrap()), @r#"
5461 AsFilter(
5462 Union(
5463 Intersection(
5464 CommitRef(Symbol("foo")),
5465 Filter(ParentCount(2..4294967295)),
5466 ),
5467 Filter(HasConflict),
5468 ),
5469 )
5470 "#);
5471
5472 insta::assert_debug_snapshot!(
5474 optimize(parse("foo | conflicts() & merges() & signed()").unwrap()), @r#"
5475 AsFilter(
5476 Union(
5477 CommitRef(Symbol("foo")),
5478 Intersection(
5479 Intersection(
5480 Filter(HasConflict),
5481 Filter(ParentCount(2..4294967295)),
5482 ),
5483 Filter(Signed),
5484 ),
5485 ),
5486 )
5487 "#);
5488
5489 insta::assert_debug_snapshot!(
5490 optimize(parse("(foo | committer_name(bar)) & description(baz) & qux").unwrap()), @r#"
5491 Intersection(
5492 CommitRef(Symbol("qux")),
5493 AsFilter(
5494 Intersection(
5495 Union(
5496 CommitRef(Symbol("foo")),
5497 Filter(CommitterName(Pattern(Substring("bar")))),
5498 ),
5499 Filter(Description(Pattern(Substring("baz")))),
5500 ),
5501 ),
5502 )
5503 "#);
5504
5505 insta::assert_debug_snapshot!(
5506 optimize(parse(
5507 "(~present(author_name(foo) & description(bar)) | baz) & qux").unwrap()), @r#"
5508 Intersection(
5509 CommitRef(Symbol("qux")),
5510 AsFilter(
5511 Union(
5512 NotIn(
5513 Present(
5514 Intersection(
5515 Filter(AuthorName(Pattern(Substring("foo")))),
5516 Filter(Description(Pattern(Substring("bar")))),
5517 ),
5518 ),
5519 ),
5520 CommitRef(Symbol("baz")),
5521 ),
5522 ),
5523 )
5524 "#);
5525
5526 insta::assert_debug_snapshot!(
5528 optimize(parse(
5529 "(a & (author_name(A) | 0)) & (b & (author_name(B) | 1)) & (c & (author_name(C) | 2))").unwrap()),
5530 @r#"
5531 Intersection(
5532 Intersection(
5533 Intersection(
5534 CommitRef(Symbol("a")),
5535 CommitRef(Symbol("b")),
5536 ),
5537 CommitRef(Symbol("c")),
5538 ),
5539 AsFilter(
5540 Intersection(
5541 Intersection(
5542 Union(
5543 Filter(AuthorName(Pattern(Substring("A")))),
5544 CommitRef(Symbol("0")),
5545 ),
5546 Union(
5547 Filter(AuthorName(Pattern(Substring("B")))),
5548 CommitRef(Symbol("1")),
5549 ),
5550 ),
5551 Union(
5552 Filter(AuthorName(Pattern(Substring("C")))),
5553 CommitRef(Symbol("2")),
5554 ),
5555 ),
5556 ),
5557 )
5558 "#);
5559
5560 insta::assert_debug_snapshot!(optimize(parse("::foo | ::author_name(bar)").unwrap()), @r#"
5562 Ancestors {
5563 heads: HeadsRange {
5564 roots: None,
5565 heads: VisibleHeadsOrReferenced,
5566 parents_range: 0..4294967295,
5567 filter: AsFilter(
5568 Union(
5569 CommitRef(Symbol("foo")),
5570 Filter(AuthorName(Pattern(Substring("bar")))),
5571 ),
5572 ),
5573 },
5574 generation: 0..18446744073709551615,
5575 parents_range: 0..4294967295,
5576 }
5577 "#);
5578 }
5579
5580 #[test]
5581 fn test_optimize_ancestors() {
5582 let settings = insta_settings();
5583 let _guard = settings.bind_to_scope();
5584
5585 insta::assert_debug_snapshot!(optimize(parse("foo--").unwrap()), @r#"
5587 Ancestors {
5588 heads: CommitRef(Symbol("foo")),
5589 generation: 2..3,
5590 parents_range: 0..4294967295,
5591 }
5592 "#);
5593 insta::assert_debug_snapshot!(optimize(parse("::(foo---)").unwrap()), @r#"
5594 Ancestors {
5595 heads: CommitRef(Symbol("foo")),
5596 generation: 3..18446744073709551615,
5597 parents_range: 0..4294967295,
5598 }
5599 "#);
5600 insta::assert_debug_snapshot!(optimize(parse("(::foo)---").unwrap()), @r#"
5601 Ancestors {
5602 heads: CommitRef(Symbol("foo")),
5603 generation: 3..18446744073709551615,
5604 parents_range: 0..4294967295,
5605 }
5606 "#);
5607
5608 insta::assert_debug_snapshot!(optimize(parse("foo---+").unwrap()), @r#"
5610 Descendants {
5611 roots: Ancestors {
5612 heads: CommitRef(Symbol("foo")),
5613 generation: 3..4,
5614 parents_range: 0..4294967295,
5615 },
5616 generation: 1..2,
5617 }
5618 "#);
5619
5620 insta::assert_debug_snapshot!(optimize(parse("foo..(bar--)").unwrap()), @r#"
5622 Range {
5623 roots: CommitRef(Symbol("foo")),
5624 heads: CommitRef(Symbol("bar")),
5625 generation: 2..18446744073709551615,
5626 parents_range: 0..4294967295,
5627 }
5628 "#);
5629 insta::assert_debug_snapshot!(optimize(parse("(foo--)..(bar---)").unwrap()), @r#"
5631 Range {
5632 roots: Ancestors {
5633 heads: CommitRef(Symbol("foo")),
5634 generation: 2..3,
5635 parents_range: 0..4294967295,
5636 },
5637 heads: CommitRef(Symbol("bar")),
5638 generation: 3..18446744073709551615,
5639 parents_range: 0..4294967295,
5640 }
5641 "#);
5642 insta::assert_debug_snapshot!(
5644 optimize(parse("~ancestors(foo, 2) & ::bar").unwrap()), @r#"
5645 Difference(
5646 Ancestors {
5647 heads: CommitRef(Symbol("bar")),
5648 generation: 0..18446744073709551615,
5649 parents_range: 0..4294967295,
5650 },
5651 Ancestors {
5652 heads: CommitRef(Symbol("foo")),
5653 generation: 0..2,
5654 parents_range: 0..4294967295,
5655 },
5656 )
5657 "#);
5658
5659 insta::assert_debug_snapshot!(optimize(parse("(foo..bar)--").unwrap()), @r#"
5662 Ancestors {
5663 heads: Range {
5664 roots: CommitRef(Symbol("foo")),
5665 heads: CommitRef(Symbol("bar")),
5666 generation: 0..18446744073709551615,
5667 parents_range: 0..4294967295,
5668 },
5669 generation: 2..3,
5670 parents_range: 0..4294967295,
5671 }
5672 "#);
5673 insta::assert_debug_snapshot!(optimize(parse("foo..(bar..baz)").unwrap()), @r#"
5674 Range {
5675 roots: CommitRef(Symbol("foo")),
5676 heads: HeadsRange {
5677 roots: CommitRef(Symbol("bar")),
5678 heads: CommitRef(Symbol("baz")),
5679 parents_range: 0..4294967295,
5680 filter: All,
5681 },
5682 generation: 0..18446744073709551615,
5683 parents_range: 0..4294967295,
5684 }
5685 "#);
5686
5687 insta::assert_debug_snapshot!(
5689 optimize(parse("ancestors(ancestors(foo), 0)").unwrap()), @r#"
5690 Ancestors {
5691 heads: CommitRef(Symbol("foo")),
5692 generation: 0..0,
5693 parents_range: 0..4294967295,
5694 }
5695 "#
5696 );
5697 insta::assert_debug_snapshot!(
5698 optimize(parse("ancestors(ancestors(foo, 0))").unwrap()), @r#"
5699 Ancestors {
5700 heads: CommitRef(Symbol("foo")),
5701 generation: 0..0,
5702 parents_range: 0..4294967295,
5703 }
5704 "#
5705 );
5706
5707 insta::assert_debug_snapshot!(
5709 optimize(parse("first_ancestors(first_ancestors(foo, 5), 5)").unwrap()), @r#"
5710 Ancestors {
5711 heads: CommitRef(Symbol("foo")),
5712 generation: 0..9,
5713 parents_range: 0..1,
5714 }
5715 "#
5716 );
5717 insta::assert_debug_snapshot!(
5718 optimize(parse("first_ancestors(first_parent(foo), 5)").unwrap()), @r#"
5719 Ancestors {
5720 heads: CommitRef(Symbol("foo")),
5721 generation: 1..6,
5722 parents_range: 0..1,
5723 }
5724 "#
5725 );
5726 insta::assert_debug_snapshot!(
5727 optimize(parse("first_ancestors(ancestors(foo, 5), 5)").unwrap()), @r#"
5728 Ancestors {
5729 heads: Ancestors {
5730 heads: CommitRef(Symbol("foo")),
5731 generation: 0..5,
5732 parents_range: 0..4294967295,
5733 },
5734 generation: 0..5,
5735 parents_range: 0..1,
5736 }
5737 "#
5738 );
5739 insta::assert_debug_snapshot!(
5740 optimize(parse("ancestors(first_ancestors(foo, 5), 5)").unwrap()), @r#"
5741 Ancestors {
5742 heads: Ancestors {
5743 heads: CommitRef(Symbol("foo")),
5744 generation: 0..5,
5745 parents_range: 0..1,
5746 },
5747 generation: 0..5,
5748 parents_range: 0..4294967295,
5749 }
5750 "#
5751 );
5752 }
5753
5754 #[test]
5755 fn test_optimize_descendants() {
5756 let settings = insta_settings();
5757 let _guard = settings.bind_to_scope();
5758
5759 insta::assert_debug_snapshot!(optimize(parse("foo++").unwrap()), @r#"
5761 Descendants {
5762 roots: CommitRef(Symbol("foo")),
5763 generation: 2..3,
5764 }
5765 "#);
5766 insta::assert_debug_snapshot!(optimize(parse("(foo+++)::").unwrap()), @r#"
5767 Descendants {
5768 roots: CommitRef(Symbol("foo")),
5769 generation: 3..18446744073709551615,
5770 }
5771 "#);
5772 insta::assert_debug_snapshot!(optimize(parse("(foo::)+++").unwrap()), @r#"
5773 Descendants {
5774 roots: CommitRef(Symbol("foo")),
5775 generation: 3..18446744073709551615,
5776 }
5777 "#);
5778
5779 insta::assert_debug_snapshot!(optimize(parse("foo+++-").unwrap()), @r#"
5781 Ancestors {
5782 heads: Descendants {
5783 roots: CommitRef(Symbol("foo")),
5784 generation: 3..4,
5785 },
5786 generation: 1..2,
5787 parents_range: 0..4294967295,
5788 }
5789 "#);
5790
5791 insta::assert_debug_snapshot!(optimize(parse("(foo++)::bar").unwrap()), @r#"
5795 DagRange {
5796 roots: Descendants {
5797 roots: CommitRef(Symbol("foo")),
5798 generation: 2..3,
5799 },
5800 heads: CommitRef(Symbol("bar")),
5801 }
5802 "#);
5803 }
5804
5805 #[test]
5806 fn test_optimize_flatten_intersection() {
5807 let settings = insta_settings();
5808 let _guard = settings.bind_to_scope();
5809
5810 insta::assert_debug_snapshot!(optimize(parse("a & ((b & c) & (d & e))").unwrap()), @r#"
5812 Intersection(
5813 Intersection(
5814 Intersection(
5815 Intersection(
5816 CommitRef(Symbol("a")),
5817 CommitRef(Symbol("b")),
5818 ),
5819 CommitRef(Symbol("c")),
5820 ),
5821 CommitRef(Symbol("d")),
5822 ),
5823 CommitRef(Symbol("e")),
5824 )
5825 "#);
5826 }
5827
5828 #[test]
5829 fn test_optimize_ancestors_union() {
5830 let settings = insta_settings();
5831 let _guard = settings.bind_to_scope();
5832
5833 insta::assert_debug_snapshot!(optimize(parse("::a | ::b | ::c | ::d").unwrap()), @r#"
5835 Ancestors {
5836 heads: Union(
5837 Union(
5838 CommitRef(Symbol("a")),
5839 CommitRef(Symbol("b")),
5840 ),
5841 Union(
5842 CommitRef(Symbol("c")),
5843 CommitRef(Symbol("d")),
5844 ),
5845 ),
5846 generation: 0..18446744073709551615,
5847 parents_range: 0..4294967295,
5848 }
5849 "#);
5850 insta::assert_debug_snapshot!(optimize(parse("ancestors(a-) | ancestors(b)").unwrap()), @r#"
5851 Ancestors {
5852 heads: Union(
5853 Ancestors {
5854 heads: CommitRef(Symbol("a")),
5855 generation: 1..2,
5856 parents_range: 0..4294967295,
5857 },
5858 CommitRef(Symbol("b")),
5859 ),
5860 generation: 0..18446744073709551615,
5861 parents_range: 0..4294967295,
5862 }
5863 "#);
5864
5865 insta::assert_debug_snapshot!(optimize(parse("~::a- & ~::b & ~::c & ::d").unwrap()), @r#"
5867 Range {
5868 roots: Union(
5869 Union(
5870 Ancestors {
5871 heads: CommitRef(Symbol("a")),
5872 generation: 1..2,
5873 parents_range: 0..4294967295,
5874 },
5875 CommitRef(Symbol("b")),
5876 ),
5877 CommitRef(Symbol("c")),
5878 ),
5879 heads: CommitRef(Symbol("d")),
5880 generation: 0..18446744073709551615,
5881 parents_range: 0..4294967295,
5882 }
5883 "#);
5884 insta::assert_debug_snapshot!(optimize(parse("a..b ~ ::c- ~ ::d").unwrap()), @r#"
5885 Range {
5886 roots: Union(
5887 Union(
5888 CommitRef(Symbol("a")),
5889 Ancestors {
5890 heads: CommitRef(Symbol("c")),
5891 generation: 1..2,
5892 parents_range: 0..4294967295,
5893 },
5894 ),
5895 CommitRef(Symbol("d")),
5896 ),
5897 heads: CommitRef(Symbol("b")),
5898 generation: 0..18446744073709551615,
5899 parents_range: 0..4294967295,
5900 }
5901 "#);
5902
5903 insta::assert_debug_snapshot!(optimize(parse("ancestors(a, 2) | ancestors(b)").unwrap()), @r#"
5905 Union(
5906 Ancestors {
5907 heads: CommitRef(Symbol("a")),
5908 generation: 0..2,
5909 parents_range: 0..4294967295,
5910 },
5911 Ancestors {
5912 heads: CommitRef(Symbol("b")),
5913 generation: 0..18446744073709551615,
5914 parents_range: 0..4294967295,
5915 },
5916 )
5917 "#);
5918 }
5919
5920 #[test]
5921 fn test_optimize_sort_negations_and_ancestors() {
5922 let settings = insta_settings();
5923 let _guard = settings.bind_to_scope();
5924
5925 insta::assert_debug_snapshot!(optimize(parse("~a & ::b & ~::c & d ~ e & f & ::g & ~::h").unwrap()), @r#"
5928 Difference(
5929 Difference(
5930 Intersection(
5931 Intersection(
5932 Intersection(
5933 Range {
5934 roots: Union(
5935 CommitRef(Symbol("c")),
5936 CommitRef(Symbol("h")),
5937 ),
5938 heads: CommitRef(Symbol("b")),
5939 generation: 0..18446744073709551615,
5940 parents_range: 0..4294967295,
5941 },
5942 Ancestors {
5943 heads: CommitRef(Symbol("g")),
5944 generation: 0..18446744073709551615,
5945 parents_range: 0..4294967295,
5946 },
5947 ),
5948 CommitRef(Symbol("d")),
5949 ),
5950 CommitRef(Symbol("f")),
5951 ),
5952 CommitRef(Symbol("a")),
5953 ),
5954 CommitRef(Symbol("e")),
5955 )
5956 "#);
5957 }
5958
5959 #[test]
5960 fn test_optimize_heads_range() {
5961 let settings = insta_settings();
5962 let _guard = settings.bind_to_scope();
5963
5964 insta::assert_debug_snapshot!(optimize(parse("heads(::)").unwrap()), @r"
5966 HeadsRange {
5967 roots: None,
5968 heads: VisibleHeadsOrReferenced,
5969 parents_range: 0..4294967295,
5970 filter: All,
5971 }
5972 ");
5973 insta::assert_debug_snapshot!(optimize(parse("heads(::foo)").unwrap()), @r#"
5974 HeadsRange {
5975 roots: None,
5976 heads: CommitRef(Symbol("foo")),
5977 parents_range: 0..4294967295,
5978 filter: All,
5979 }
5980 "#);
5981 insta::assert_debug_snapshot!(optimize(parse("heads(..)").unwrap()), @r"
5984 HeadsRange {
5985 roots: None,
5986 heads: VisibleHeadsOrReferenced,
5987 parents_range: 0..4294967295,
5988 filter: NotIn(Root),
5989 }
5990 ");
5991 insta::assert_debug_snapshot!(optimize(parse("heads(foo..)").unwrap()), @r#"
5992 HeadsRange {
5993 roots: CommitRef(Symbol("foo")),
5994 heads: VisibleHeadsOrReferenced,
5995 parents_range: 0..4294967295,
5996 filter: All,
5997 }
5998 "#);
5999 insta::assert_debug_snapshot!(optimize(parse("heads(..bar)").unwrap()), @r#"
6000 HeadsRange {
6001 roots: Root,
6002 heads: CommitRef(Symbol("bar")),
6003 parents_range: 0..4294967295,
6004 filter: All,
6005 }
6006 "#);
6007 insta::assert_debug_snapshot!(optimize(parse("heads(foo..bar)").unwrap()), @r#"
6008 HeadsRange {
6009 roots: CommitRef(Symbol("foo")),
6010 heads: CommitRef(Symbol("bar")),
6011 parents_range: 0..4294967295,
6012 filter: All,
6013 }
6014 "#);
6015 insta::assert_debug_snapshot!(optimize(parse("heads(~::foo & ::bar)").unwrap()), @r#"
6016 HeadsRange {
6017 roots: CommitRef(Symbol("foo")),
6018 heads: CommitRef(Symbol("bar")),
6019 parents_range: 0..4294967295,
6020 filter: All,
6021 }
6022 "#);
6023 insta::assert_debug_snapshot!(optimize(parse("heads(~::foo)").unwrap()), @r#"
6024 HeadsRange {
6025 roots: CommitRef(Symbol("foo")),
6026 heads: VisibleHeadsOrReferenced,
6027 parents_range: 0..4294967295,
6028 filter: All,
6029 }
6030 "#);
6031 insta::assert_debug_snapshot!(optimize(parse("heads(a..b & c..d)").unwrap()), @r#"
6032 HeadsRange {
6033 roots: Union(
6034 CommitRef(Symbol("a")),
6035 CommitRef(Symbol("c")),
6036 ),
6037 heads: CommitRef(Symbol("b")),
6038 parents_range: 0..4294967295,
6039 filter: Ancestors {
6040 heads: CommitRef(Symbol("d")),
6041 generation: 0..18446744073709551615,
6042 parents_range: 0..4294967295,
6043 },
6044 }
6045 "#);
6046
6047 insta::assert_debug_snapshot!(optimize(parse("heads(first_ancestors(foo))").unwrap()), @r#"
6049 HeadsRange {
6050 roots: None,
6051 heads: CommitRef(Symbol("foo")),
6052 parents_range: 0..1,
6053 filter: All,
6054 }
6055 "#);
6056 insta::assert_debug_snapshot!(optimize(parse("heads(first_ancestors(foo) & bar..)").unwrap()), @r#"
6057 HeadsRange {
6058 roots: CommitRef(Symbol("bar")),
6059 heads: CommitRef(Symbol("foo")),
6060 parents_range: 0..1,
6061 filter: All,
6062 }
6063 "#);
6064 insta::assert_debug_snapshot!(optimize(parse("heads(foo.. & first_ancestors(bar) & ::baz)").unwrap()), @r#"
6065 HeadsRange {
6066 roots: CommitRef(Symbol("foo")),
6067 heads: CommitRef(Symbol("bar")),
6068 parents_range: 0..1,
6069 filter: Ancestors {
6070 heads: CommitRef(Symbol("baz")),
6071 generation: 0..18446744073709551615,
6072 parents_range: 0..4294967295,
6073 },
6074 }
6075 "#);
6076
6077 insta::assert_debug_snapshot!(optimize(parse("heads(ancestors(foo, 2))").unwrap()), @r#"
6079 Heads(
6080 Ancestors {
6081 heads: CommitRef(Symbol("foo")),
6082 generation: 0..2,
6083 parents_range: 0..4294967295,
6084 },
6085 )
6086 "#);
6087 insta::assert_debug_snapshot!(optimize(parse("heads(first_ancestors(foo, 2))").unwrap()), @r#"
6088 Heads(
6089 Ancestors {
6090 heads: CommitRef(Symbol("foo")),
6091 generation: 0..2,
6092 parents_range: 0..1,
6093 },
6094 )
6095 "#);
6096
6097 insta::assert_debug_snapshot!(optimize(parse("heads(ancestors(foo--))").unwrap()), @r#"
6099 HeadsRange {
6100 roots: None,
6101 heads: Ancestors {
6102 heads: CommitRef(Symbol("foo")),
6103 generation: 2..3,
6104 parents_range: 0..4294967295,
6105 },
6106 parents_range: 0..4294967295,
6107 filter: All,
6108 }
6109 "#);
6110 insta::assert_debug_snapshot!(optimize(parse("heads(first_ancestors(first_parent(foo, 2)))").unwrap()), @r#"
6111 HeadsRange {
6112 roots: None,
6113 heads: Ancestors {
6114 heads: CommitRef(Symbol("foo")),
6115 generation: 2..3,
6116 parents_range: 0..1,
6117 },
6118 parents_range: 0..1,
6119 filter: All,
6120 }
6121 "#);
6122
6123 insta::assert_debug_snapshot!(optimize(parse("heads(author_name(A) | author_name(B))").unwrap()), @r#"
6125 HeadsRange {
6126 roots: None,
6127 heads: VisibleHeadsOrReferenced,
6128 parents_range: 0..4294967295,
6129 filter: AsFilter(
6130 Union(
6131 Filter(AuthorName(Pattern(Substring("A")))),
6132 Filter(AuthorName(Pattern(Substring("B")))),
6133 ),
6134 ),
6135 }
6136 "#);
6137 insta::assert_debug_snapshot!(optimize(parse("heads(~author_name(A))").unwrap()), @r#"
6138 HeadsRange {
6139 roots: None,
6140 heads: VisibleHeadsOrReferenced,
6141 parents_range: 0..4294967295,
6142 filter: AsFilter(
6143 NotIn(
6144 Filter(AuthorName(Pattern(Substring("A")))),
6145 ),
6146 ),
6147 }
6148 "#);
6149 insta::assert_debug_snapshot!(optimize(parse("heads(~foo)").unwrap()), @r#"
6150 HeadsRange {
6151 roots: None,
6152 heads: VisibleHeadsOrReferenced,
6153 parents_range: 0..4294967295,
6154 filter: NotIn(CommitRef(Symbol("foo"))),
6155 }
6156 "#);
6157
6158 insta::assert_debug_snapshot!(optimize(parse("heads(author_name(A) & ::foo ~ author_name(B))").unwrap()), @r#"
6160 HeadsRange {
6161 roots: None,
6162 heads: CommitRef(Symbol("foo")),
6163 parents_range: 0..4294967295,
6164 filter: AsFilter(
6165 Difference(
6166 Filter(AuthorName(Pattern(Substring("A")))),
6167 Filter(AuthorName(Pattern(Substring("B")))),
6168 ),
6169 ),
6170 }
6171 "#);
6172
6173 insta::assert_debug_snapshot!(optimize(parse("heads(~foo & ~roots(bar) & ::baz)").unwrap()), @r#"
6175 HeadsRange {
6176 roots: None,
6177 heads: CommitRef(Symbol("baz")),
6178 parents_range: 0..4294967295,
6179 filter: Difference(
6180 NotIn(CommitRef(Symbol("foo"))),
6181 Roots(CommitRef(Symbol("bar"))),
6182 ),
6183 }
6184 "#);
6185 }
6186
6187 #[test]
6188 fn test_optimize_ancestors_heads_range() {
6189 insta::assert_debug_snapshot!(optimize(parse("::description(bar)").unwrap()), @r#"
6191 Ancestors {
6192 heads: HeadsRange {
6193 roots: None,
6194 heads: VisibleHeadsOrReferenced,
6195 parents_range: 0..4294967295,
6196 filter: Filter(
6197 Description(
6198 Pattern(
6199 Substring(
6200 "bar",
6201 ),
6202 ),
6203 ),
6204 ),
6205 },
6206 generation: 0..18446744073709551615,
6207 parents_range: 0..4294967295,
6208 }
6209 "#);
6210 insta::assert_debug_snapshot!(optimize(parse("author_name(foo)..").unwrap()), @r#"
6211 Range {
6212 roots: HeadsRange {
6213 roots: None,
6214 heads: VisibleHeadsOrReferenced,
6215 parents_range: 0..4294967295,
6216 filter: Filter(
6217 AuthorName(
6218 Pattern(
6219 Substring(
6220 "foo",
6221 ),
6222 ),
6223 ),
6224 ),
6225 },
6226 heads: VisibleHeadsOrReferenced,
6227 generation: 0..18446744073709551615,
6228 parents_range: 0..4294967295,
6229 }
6230 "#);
6231
6232 insta::assert_debug_snapshot!(optimize(parse("::(foo..bar)").unwrap()), @r#"
6234 Ancestors {
6235 heads: HeadsRange {
6236 roots: CommitRef(
6237 Symbol(
6238 "foo",
6239 ),
6240 ),
6241 heads: CommitRef(
6242 Symbol(
6243 "bar",
6244 ),
6245 ),
6246 parents_range: 0..4294967295,
6247 filter: All,
6248 },
6249 generation: 0..18446744073709551615,
6250 parents_range: 0..4294967295,
6251 }
6252 "#);
6253
6254 insta::assert_debug_snapshot!(optimize(parse("ancestors(author_name(foo), 5)").unwrap()), @r#"
6256 Ancestors {
6257 heads: Filter(
6258 AuthorName(
6259 Pattern(
6260 Substring(
6261 "foo",
6262 ),
6263 ),
6264 ),
6265 ),
6266 generation: 0..5,
6267 parents_range: 0..4294967295,
6268 }
6269 "#);
6270 insta::assert_debug_snapshot!(optimize(parse("first_ancestors(author_name(foo))").unwrap()), @r#"
6271 Ancestors {
6272 heads: Filter(
6273 AuthorName(
6274 Pattern(
6275 Substring(
6276 "foo",
6277 ),
6278 ),
6279 ),
6280 ),
6281 generation: 0..18446744073709551615,
6282 parents_range: 0..1,
6283 }
6284 "#);
6285 }
6286
6287 #[test]
6288 fn test_escape_string_literal() {
6289 assert_eq!(format_symbol("foo"), "foo");
6291 assert_eq!(format_symbol("foo.bar"), "foo.bar");
6292
6293 assert_eq!(format_symbol("foo@bar"), r#""foo@bar""#);
6295 assert_eq!(format_symbol("foo bar"), r#""foo bar""#);
6296 assert_eq!(format_symbol(" foo "), r#"" foo ""#);
6297 assert_eq!(format_symbol("(foo)"), r#""(foo)""#);
6298 assert_eq!(format_symbol("all:foo"), r#""all:foo""#);
6299
6300 assert_eq!(format_symbol("foo\"bar"), r#""foo\"bar""#);
6302 assert_eq!(format_symbol("foo\\bar"), r#""foo\\bar""#);
6303 assert_eq!(format_symbol("foo\\\"bar"), r#""foo\\\"bar""#);
6304 assert_eq!(format_symbol("foo\nbar"), r#""foo\nbar""#);
6305
6306 assert_eq!(format_symbol("foo\"bar"), r#""foo\"bar""#);
6309 assert_eq!(format_symbol("foo\\bar"), r#""foo\\bar""#);
6310 assert_eq!(format_symbol("foo\\\"bar"), r#""foo\\\"bar""#);
6311 assert_eq!(format_symbol("foo \x01 bar"), r#""foo \x01 bar""#);
6312 }
6313
6314 #[test]
6315 fn test_escape_remote_symbol() {
6316 assert_eq!(format_remote_symbol("foo", "bar"), "foo@bar");
6317 assert_eq!(
6318 format_remote_symbol(" foo ", "bar:baz"),
6319 r#"" foo "@"bar:baz""#
6320 );
6321 }
6322}