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