dag/
ops.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8//! DAG and Id operations (mostly traits)
9
10use std::sync::Arc;
11
12use futures::StreamExt;
13use futures::TryStreamExt;
14
15use crate::clone::CloneData;
16use crate::default_impl;
17use crate::errors::NotFoundError;
18use crate::id::Group;
19use crate::id::Id;
20use crate::id::VertexName;
21pub use crate::iddag::IdDagAlgorithm;
22use crate::namedag::MemNameDag;
23use crate::nameset::id_lazy::IdLazySet;
24use crate::nameset::id_static::IdStaticSet;
25use crate::nameset::NameSet;
26use crate::IdSet;
27use crate::Result;
28use crate::VerLink;
29use crate::VertexListWithOptions;
30
31/// DAG related read-only algorithms.
32#[async_trait::async_trait]
33pub trait DagAlgorithm: Send + Sync {
34    /// Sort a `NameSet` topologically.
35    ///
36    /// The returned set should have `dag` and `id_map` hints set to associated
37    /// with this dag or its previous compatible version. For example, if a
38    /// `set` is sorted on another dag but not in this dag, it should be resorted
39    /// using this dag.  If a `set` is empty and not associated to the current
40    /// `dag` in its hints, the return value should be a different empty `set`
41    /// that has the `dag` and `id_map` hints set to this dag.
42    async fn sort(&self, set: &NameSet) -> Result<NameSet>;
43
44    /// Re-create the graph so it looks better when rendered.
45    async fn beautify(&self, main_branch: Option<NameSet>) -> Result<MemNameDag> {
46        default_impl::beautify(self, main_branch).await
47    }
48
49    /// Extract a sub graph containing only specified vertexes.
50    async fn subdag(&self, set: NameSet) -> Result<MemNameDag> {
51        default_impl::subdag(self, set).await
52    }
53
54    /// Get ordered parent vertexes.
55    async fn parent_names(&self, name: VertexName) -> Result<Vec<VertexName>>;
56
57    /// Returns a set that covers all vertexes tracked by this DAG.
58    async fn all(&self) -> Result<NameSet>;
59
60    /// Returns a set that covers all vertexes in the master group.
61    async fn master_group(&self) -> Result<NameSet>;
62
63    /// Calculates all ancestors reachable from any name from the given set.
64    async fn ancestors(&self, set: NameSet) -> Result<NameSet>;
65
66    /// Calculates parents of the given set.
67    ///
68    /// Note: Parent order is not preserved. Use [`NameDag::parent_names`]
69    /// to preserve order.
70    async fn parents(&self, set: NameSet) -> Result<NameSet> {
71        default_impl::parents(self, set).await
72    }
73
74    /// Calculates the n-th first ancestor.
75    async fn first_ancestor_nth(&self, name: VertexName, n: u64) -> Result<Option<VertexName>> {
76        default_impl::first_ancestor_nth(self, name, n).await
77    }
78
79    /// Calculates ancestors but only follows the first parent.
80    async fn first_ancestors(&self, set: NameSet) -> Result<NameSet> {
81        default_impl::first_ancestors(self, set).await
82    }
83
84    /// Calculates heads of the given set.
85    async fn heads(&self, set: NameSet) -> Result<NameSet> {
86        default_impl::heads(self, set).await
87    }
88
89    /// Calculates children of the given set.
90    async fn children(&self, set: NameSet) -> Result<NameSet>;
91
92    /// Calculates roots of the given set.
93    async fn roots(&self, set: NameSet) -> Result<NameSet> {
94        default_impl::roots(self, set).await
95    }
96
97    /// Calculates merges of the selected set (vertexes with >=2 parents).
98    async fn merges(&self, set: NameSet) -> Result<NameSet> {
99        default_impl::merges(self, set).await
100    }
101
102    /// Calculates one "greatest common ancestor" of the given set.
103    ///
104    /// If there are no common ancestors, return None.
105    /// If there are multiple greatest common ancestors, pick one arbitrarily.
106    /// Use `gca_all` to get all of them.
107    async fn gca_one(&self, set: NameSet) -> Result<Option<VertexName>> {
108        default_impl::gca_one(self, set).await
109    }
110
111    /// Calculates all "greatest common ancestor"s of the given set.
112    /// `gca_one` is faster if an arbitrary answer is ok.
113    async fn gca_all(&self, set: NameSet) -> Result<NameSet> {
114        default_impl::gca_all(self, set).await
115    }
116
117    /// Calculates all common ancestors of the given set.
118    async fn common_ancestors(&self, set: NameSet) -> Result<NameSet> {
119        default_impl::common_ancestors(self, set).await
120    }
121
122    /// Tests if `ancestor` is an ancestor of `descendant`.
123    async fn is_ancestor(&self, ancestor: VertexName, descendant: VertexName) -> Result<bool> {
124        default_impl::is_ancestor(self, ancestor, descendant).await
125    }
126
127    /// Calculates "heads" of the ancestors of the given set. That is,
128    /// Find Y, which is the smallest subset of set X, where `ancestors(Y)` is
129    /// `ancestors(X)`.
130    ///
131    /// This is faster than calculating `heads(ancestors(set))` in certain
132    /// implementations like segmented changelog.
133    ///
134    /// This is different from `heads`. In case set contains X and Y, and Y is
135    /// an ancestor of X, but not the immediate ancestor, `heads` will include
136    /// Y while this function won't.
137    async fn heads_ancestors(&self, set: NameSet) -> Result<NameSet> {
138        default_impl::heads_ancestors(self, set).await
139    }
140
141    /// Calculates the "dag range" - vertexes reachable from both sides.
142    async fn range(&self, roots: NameSet, heads: NameSet) -> Result<NameSet>;
143
144    /// Calculates `ancestors(reachable) - ancestors(unreachable)`.
145    async fn only(&self, reachable: NameSet, unreachable: NameSet) -> Result<NameSet> {
146        default_impl::only(self, reachable, unreachable).await
147    }
148
149    /// Calculates `ancestors(reachable) - ancestors(unreachable)`, and
150    /// `ancestors(unreachable)`.
151    /// This might be faster in some implementations than calculating `only` and
152    /// `ancestors` separately.
153    async fn only_both(
154        &self,
155        reachable: NameSet,
156        unreachable: NameSet,
157    ) -> Result<(NameSet, NameSet)> {
158        default_impl::only_both(self, reachable, unreachable).await
159    }
160
161    /// Calculates the descendants of the given set.
162    async fn descendants(&self, set: NameSet) -> Result<NameSet>;
163
164    /// Calculates `roots` that are reachable from `heads` without going
165    /// through other `roots`. For example, given the following graph:
166    ///
167    /// ```plain,ignore
168    ///   F
169    ///   |\
170    ///   C E
171    ///   | |
172    ///   B D
173    ///   |/
174    ///   A
175    /// ```
176    ///
177    /// `reachable_roots(roots=[A, B, C], heads=[F])` returns `[A, C]`.
178    /// `B` is not included because it cannot be reached without going
179    /// through another root `C` from `F`. `A` is included because it
180    /// can be reached via `F -> E -> D -> A` that does not go through
181    /// other roots.
182    ///
183    /// The can be calculated as
184    /// `roots & (heads | parents(only(heads, roots & ancestors(heads))))`.
185    /// Actual implementation might have faster paths.
186    ///
187    /// The `roots & ancestors(heads)` portion filters out bogus roots for
188    /// compatibility, if the callsite does not provide bogus roots, it
189    /// could be simplified to just `roots`.
190    async fn reachable_roots(&self, roots: NameSet, heads: NameSet) -> Result<NameSet> {
191        default_impl::reachable_roots(self, roots, heads).await
192    }
193
194    /// Vertexes buffered in memory, not yet written to disk.
195    async fn dirty(&self) -> Result<NameSet>;
196
197    /// Returns true if the vertex names might need to be resolved remotely.
198    fn is_vertex_lazy(&self) -> bool;
199
200    /// Get a snapshot of the current graph that can operate separately.
201    ///
202    /// This makes it easier to fight with borrowck.
203    fn dag_snapshot(&self) -> Result<Arc<dyn DagAlgorithm + Send + Sync>>;
204
205    /// Get a snapshot of the `IdDag` that can operate separately.
206    ///
207    /// This is for advanced use-cases. For example, if callsite wants to
208    /// do some graph calculation without network, and control how to
209    /// batch the vertex name lookups precisely.
210    fn id_dag_snapshot(&self) -> Result<Arc<dyn IdDagAlgorithm + Send + Sync>> {
211        Err(crate::errors::BackendError::Generic(format!(
212            "id_dag_snapshot() is not supported for {}",
213            std::any::type_name::<Self>()
214        ))
215        .into())
216    }
217
218    /// Identity of the dag.
219    fn dag_id(&self) -> &str;
220
221    /// Version of the dag. Useful to figure out compatibility between two dags.
222    fn dag_version(&self) -> &VerLink;
223}
224
225#[async_trait::async_trait]
226pub trait Parents: Send + Sync {
227    async fn parent_names(&self, name: VertexName) -> Result<Vec<VertexName>>;
228
229    /// A hint of a sub-graph for inserting `heads`.
230    ///
231    /// This is used to reduce remote fetches in a lazy graph.
232    /// The roots will be checked first, if a root is unknown locally then
233    /// all its descendants will be considered unknown locally.
234    ///
235    /// The returned graph is only used to optimize network fetches in
236    /// `assign_head`. It is not used to be actually inserted to the graph. So
237    /// returning an empty or "incorrect" graph does not hurt correctness. But
238    /// might hurt performance.
239    async fn hint_subdag_for_insertion(&self, _heads: &[VertexName]) -> Result<MemNameDag>;
240}
241
242#[async_trait::async_trait]
243impl Parents for Arc<dyn DagAlgorithm + Send + Sync> {
244    async fn parent_names(&self, name: VertexName) -> Result<Vec<VertexName>> {
245        DagAlgorithm::parent_names(self, name).await
246    }
247
248    async fn hint_subdag_for_insertion(&self, heads: &[VertexName]) -> Result<MemNameDag> {
249        let scope = self.dirty().await?;
250        default_impl::hint_subdag_for_insertion(self, &scope, heads).await
251    }
252}
253
254#[async_trait::async_trait]
255impl Parents for &(dyn DagAlgorithm + Send + Sync) {
256    async fn parent_names(&self, name: VertexName) -> Result<Vec<VertexName>> {
257        DagAlgorithm::parent_names(*self, name).await
258    }
259
260    async fn hint_subdag_for_insertion(&self, heads: &[VertexName]) -> Result<MemNameDag> {
261        let scope = self.dirty().await?;
262        default_impl::hint_subdag_for_insertion(self, &scope, heads).await
263    }
264}
265
266#[async_trait::async_trait]
267impl<'a> Parents for Box<dyn Fn(VertexName) -> Result<Vec<VertexName>> + Send + Sync + 'a> {
268    async fn parent_names(&self, name: VertexName) -> Result<Vec<VertexName>> {
269        (self)(name)
270    }
271
272    async fn hint_subdag_for_insertion(&self, _heads: &[VertexName]) -> Result<MemNameDag> {
273        // No clear way to detect the "dirty" scope.
274        Ok(MemNameDag::new())
275    }
276}
277
278#[async_trait::async_trait]
279impl Parents for std::collections::HashMap<VertexName, Vec<VertexName>> {
280    async fn parent_names(&self, name: VertexName) -> Result<Vec<VertexName>> {
281        match self.get(&name) {
282            Some(v) => Ok(v.clone()),
283            None => name.not_found(),
284        }
285    }
286
287    async fn hint_subdag_for_insertion(&self, heads: &[VertexName]) -> Result<MemNameDag> {
288        let mut keys: Vec<VertexName> = self.keys().cloned().collect();
289        keys.sort_unstable();
290        let scope = NameSet::from_static_names(keys);
291        default_impl::hint_subdag_for_insertion(self, &scope, heads).await
292    }
293}
294
295/// Add vertexes recursively to the DAG.
296#[async_trait::async_trait]
297pub trait DagAddHeads {
298    /// Add vertexes and their ancestors to the DAG. This does not persistent
299    /// changes to disk.
300    async fn add_heads(
301        &mut self,
302        parents: &dyn Parents,
303        heads: &VertexListWithOptions,
304    ) -> Result<bool>;
305}
306
307/// Remove vertexes and their descendants from the DAG.
308#[async_trait::async_trait]
309pub trait DagStrip {
310    /// Remove the given `set` and their descendants.
311    ///
312    /// This will reload the DAG from its source (ex. filesystem) and writes
313    /// changes back with a lock so there are no other processes adding
314    /// new descendants of the stripped set.
315    ///
316    /// After strip, the `self` graph might contain new vertexes because of
317    /// the reload.
318    async fn strip(&mut self, set: &NameSet) -> Result<()>;
319}
320
321/// Import a generated `CloneData` object into an empty DAG.
322#[async_trait::async_trait]
323pub trait DagImportCloneData {
324    /// Updates the DAG using a `CloneData` object.
325    async fn import_clone_data(&mut self, clone_data: CloneData<VertexName>) -> Result<()>;
326}
327
328/// Import a generated incremental `CloneData` object into an existing DAG.
329/// Ids in the passed CloneData might not match ids in existing DAG.
330#[async_trait::async_trait]
331pub trait DagImportPullData {
332    /// Updates the DAG using a `CloneData` object.
333    ///
334    /// Only import the given `heads`.
335    async fn import_pull_data(
336        &mut self,
337        clone_data: CloneData<VertexName>,
338        heads: &VertexListWithOptions,
339    ) -> Result<()>;
340}
341
342#[async_trait::async_trait]
343pub trait DagExportCloneData {
344    /// Export `CloneData` for vertexes in the master group.
345    async fn export_clone_data(&self) -> Result<CloneData<VertexName>>;
346}
347
348#[async_trait::async_trait]
349pub trait DagExportPullData {
350    /// Export `CloneData` for vertexes in the given set.
351    /// The set is typcially calculated by `only(heads, common)`.
352    async fn export_pull_data(&self, set: &NameSet) -> Result<CloneData<VertexName>>;
353}
354
355/// Persistent the DAG on disk.
356#[async_trait::async_trait]
357pub trait DagPersistent {
358    /// Write in-memory DAG to disk. This might also pick up changes to
359    /// the DAG by other processes.
360    async fn flush(&mut self, master_heads: &VertexListWithOptions) -> Result<()>;
361
362    /// Write in-memory IdMap that caches Id <-> Vertex translation from
363    /// remote service to disk.
364    async fn flush_cached_idmap(&self) -> Result<()>;
365
366    /// A faster path for add_heads, followed by flush.
367    async fn add_heads_and_flush(
368        &mut self,
369        parent_names_func: &dyn Parents,
370        heads: &VertexListWithOptions,
371    ) -> Result<()>;
372
373    /// Import from another (potentially large) DAG. Write to disk immediately.
374    async fn import_and_flush(
375        &mut self,
376        dag: &dyn DagAlgorithm,
377        master_heads: NameSet,
378    ) -> Result<()> {
379        let heads = dag.heads(dag.all().await?).await?;
380        let non_master_heads = heads - master_heads.clone();
381        let master_heads: Vec<VertexName> =
382            master_heads.iter().await?.try_collect::<Vec<_>>().await?;
383        let non_master_heads: Vec<VertexName> = non_master_heads
384            .iter()
385            .await?
386            .try_collect::<Vec<_>>()
387            .await?;
388        let heads = VertexListWithOptions::from(master_heads)
389            .with_highest_group(Group::MASTER)
390            .chain(non_master_heads);
391        self.add_heads_and_flush(&dag.dag_snapshot()?, &heads).await
392    }
393}
394
395/// Import ASCII graph to DAG.
396pub trait ImportAscii {
397    /// Import vertexes described in an ASCII graph.
398    /// `heads` optionally specifies the order of heads to insert.
399    /// Useful for testing. Panic if the input is invalid.
400    fn import_ascii_with_heads(
401        &mut self,
402        text: &str,
403        heads: Option<&[impl AsRef<str>]>,
404    ) -> Result<()>;
405
406    /// Import vertexes described in an ASCII graph.
407    fn import_ascii(&mut self, text: &str) -> Result<()> {
408        self.import_ascii_with_heads(text, <Option<&[&str]>>::None)
409    }
410}
411
412/// Lookup vertexes by prefixes.
413#[async_trait::async_trait]
414pub trait PrefixLookup {
415    /// Lookup vertexes by hex prefix.
416    async fn vertexes_by_hex_prefix(
417        &self,
418        hex_prefix: &[u8],
419        limit: usize,
420    ) -> Result<Vec<VertexName>>;
421}
422
423/// Convert between `Vertex` and `Id`.
424#[async_trait::async_trait]
425pub trait IdConvert: PrefixLookup + Sync {
426    async fn vertex_id(&self, name: VertexName) -> Result<Id>;
427    async fn vertex_id_with_max_group(
428        &self,
429        name: &VertexName,
430        max_group: Group,
431    ) -> Result<Option<Id>>;
432    async fn vertex_name(&self, id: Id) -> Result<VertexName>;
433    async fn contains_vertex_name(&self, name: &VertexName) -> Result<bool>;
434
435    /// Test if an `id` is present locally. Do not trigger remote fetching.
436    async fn contains_vertex_id_locally(&self, id: &[Id]) -> Result<Vec<bool>>;
437
438    /// Test if an `name` is present locally. Do not trigger remote fetching.
439    async fn contains_vertex_name_locally(&self, name: &[VertexName]) -> Result<Vec<bool>>;
440
441    async fn vertex_id_optional(&self, name: &VertexName) -> Result<Option<Id>> {
442        self.vertex_id_with_max_group(name, Group::NON_MASTER).await
443    }
444
445    /// Convert [`Id`]s to [`VertexName`]s in batch.
446    async fn vertex_name_batch(&self, ids: &[Id]) -> Result<Vec<Result<VertexName>>> {
447        // This is not an efficient implementation in an async context.
448        let mut names = Vec::with_capacity(ids.len());
449        for &id in ids {
450            names.push(self.vertex_name(id).await);
451        }
452        Ok(names)
453    }
454
455    /// Convert [`VertexName`]s to [`Id`]s in batch.
456    async fn vertex_id_batch(&self, names: &[VertexName]) -> Result<Vec<Result<Id>>> {
457        // This is not an efficient implementation in an async context.
458        let mut ids = Vec::with_capacity(names.len());
459        for name in names {
460            ids.push(self.vertex_id(name.clone()).await);
461        }
462        Ok(ids)
463    }
464
465    /// Identity of the map.
466    fn map_id(&self) -> &str;
467
468    /// Version of the map. Useful to figure out compatibility between two maps.
469    fn map_version(&self) -> &VerLink;
470}
471
472/// Integrity check functions.
473#[async_trait::async_trait]
474pub trait CheckIntegrity {
475    /// Verify that universally known `Id`s (parents of merges) are actually
476    /// known locally.
477    ///
478    /// Returns set of `Id`s that should be universally known but missing.
479    /// An empty set means all universally known `Id`s are known locally.
480    ///
481    /// Check `FirstAncestorConstraint::KnownUniversally` for concepts of
482    /// "universally known".
483    async fn check_universal_ids(&self) -> Result<Vec<Id>>;
484
485    /// Check segment properties: no cycles, no overlaps, no gaps etc.
486    /// This is only about the `Id`s, not about the vertex names.
487    ///
488    /// Returns human readable messages about problems.
489    /// No messages indicates there are no problems detected.
490    async fn check_segments(&self) -> Result<Vec<String>>;
491
492    /// Check that the subset of the current graph (ancestors of `heads`)
493    /// is isomorphic with the subset in the `other` graph.
494    ///
495    /// Returns messages about where two graphs are different.
496    /// No messages indicates two graphs are likely isomorphic.
497    ///
498    /// Note: For performance, this function only checks the "shape"
499    /// of the graph, without checking the (potentially lazy) vertex
500    /// names.
501    async fn check_isomorphic_graph(
502        &self,
503        other: &dyn DagAlgorithm,
504        heads: NameSet,
505    ) -> Result<Vec<String>>;
506}
507
508impl<T> ImportAscii for T
509where
510    T: DagAddHeads,
511{
512    fn import_ascii_with_heads(
513        &mut self,
514        text: &str,
515        heads: Option<&[impl AsRef<str>]>,
516    ) -> Result<()> {
517        let parents = drawdag::parse(&text);
518        let heads: Vec<_> = match heads {
519            Some(heads) => heads
520                .iter()
521                .map(|s| VertexName::copy_from(s.as_ref().as_bytes()))
522                .collect(),
523            None => {
524                let mut heads: Vec<_> = parents
525                    .keys()
526                    .map(|s| VertexName::copy_from(s.as_bytes()))
527                    .collect();
528                heads.sort();
529                heads
530            }
531        };
532
533        let v = |s: String| VertexName::copy_from(s.as_bytes());
534        let parents: std::collections::HashMap<VertexName, Vec<VertexName>> = parents
535            .into_iter()
536            .map(|(k, vs)| (v(k), vs.into_iter().map(v).collect()))
537            .collect();
538        nonblocking::non_blocking_result(self.add_heads(&parents, &heads[..].into()))?;
539        Ok(())
540    }
541}
542
543#[async_trait::async_trait]
544pub trait ToIdSet {
545    /// Converts [`NameSet`] to [`IdSet`].
546    async fn to_id_set(&self, set: &NameSet) -> Result<IdSet>;
547}
548
549pub trait ToSet {
550    /// Converts [`IdSet`] to [`NameSet`].
551    fn to_set(&self, set: &IdSet) -> Result<NameSet>;
552}
553
554pub trait IdMapSnapshot {
555    /// Get a snapshot of IdMap.
556    fn id_map_snapshot(&self) -> Result<Arc<dyn IdConvert + Send + Sync>>;
557}
558
559/// Describes how to persist state to disk.
560pub trait Persist {
561    /// Return type of `lock()`.
562    type Lock: Send + Sync;
563
564    /// Obtain an exclusive lock for writing.
565    /// This should prevent other writers.
566    fn lock(&mut self) -> Result<Self::Lock>;
567
568    /// Reload from the source of truth. Drop pending changes.
569    ///
570    /// This requires a lock and is usually called before `persist()`.
571    fn reload(&mut self, _lock: &Self::Lock) -> Result<()>;
572
573    /// Write pending changes to the source of truth.
574    ///
575    /// This requires a lock.
576    fn persist(&mut self, _lock: &Self::Lock) -> Result<()>;
577}
578
579/// Address that can be used to open things.
580///
581/// The address type decides the return type of `open`.
582pub trait Open: Clone {
583    type OpenTarget;
584
585    fn open(&self) -> Result<Self::OpenTarget>;
586}
587
588/// Has an integer tuple version that can be used to test if the data was
589/// changed. If the first number changes, it means incompatible changes.
590/// If only the second number increases, it means append-only changes.
591pub trait IntVersion {
592    fn int_version(&self) -> (u64, u64);
593}
594
595/// Fallible clone.
596pub trait TryClone {
597    fn try_clone(&self) -> Result<Self>
598    where
599        Self: Sized;
600}
601
602impl<T: Clone> TryClone for T {
603    fn try_clone(&self) -> Result<Self> {
604        Ok(self.clone())
605    }
606}
607
608#[async_trait::async_trait]
609impl<T: IdConvert + IdMapSnapshot> ToIdSet for T {
610    /// Converts [`NameSet`] to [`IdSet`].
611    async fn to_id_set(&self, set: &NameSet) -> Result<IdSet> {
612        let version = set.hints().id_map_version();
613
614        // Fast path: extract IdSet from IdStaticSet.
615        if let Some(set) = set.as_any().downcast_ref::<IdStaticSet>() {
616            if None < version && version <= Some(self.map_version()) {
617                return Ok(set.spans.clone());
618            }
619        }
620
621        // Convert IdLazySet to IdStaticSet. Bypass hash lookups.
622        if let Some(set) = set.as_any().downcast_ref::<IdLazySet>() {
623            if None < version && version <= Some(self.map_version()) {
624                let set: IdStaticSet = set.to_static()?;
625                return Ok(set.spans);
626            }
627        }
628
629        // Slow path: iterate through the set and convert it to a non-lazy
630        // IdSet. Does not bypass hash lookups.
631        let mut spans = IdSet::empty();
632        let mut iter = set.iter().await?.chunks(1 << 17);
633        while let Some(names) = iter.next().await {
634            let names = names.into_iter().collect::<Result<Vec<_>>>()?;
635            let ids = self.vertex_id_batch(&names).await?;
636            for id in ids {
637                spans.push(id?);
638            }
639        }
640        Ok(spans)
641    }
642}
643
644impl IdMapSnapshot for Arc<dyn IdConvert + Send + Sync> {
645    fn id_map_snapshot(&self) -> Result<Arc<dyn IdConvert + Send + Sync>> {
646        Ok(self.clone())
647    }
648}
649
650impl<T: IdMapSnapshot + DagAlgorithm> ToSet for T {
651    /// Converts [`IdSet`] to [`NameSet`].
652    fn to_set(&self, set: &IdSet) -> Result<NameSet> {
653        NameSet::from_spans_dag(set.clone(), self)
654    }
655}