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