Skip to main content

mars_agents/resolve/
context.rs

1use std::collections::{BTreeMap, HashMap};
2
3use indexmap::IndexMap;
4
5use super::filter::push_filter_constraint;
6use super::{
7    PackageResolutionState, PackageVersions, PendingItem, RegisteredPackage, ResolvedGraph,
8    ResolvedNode, RootedSourceRef, VersionConstraint, VisitedSet,
9};
10use crate::config::FilterMode;
11use crate::source::ResolvedRef;
12use crate::types::{SourceId, SourceName};
13
14/// Mutable resolver state threaded through bottom-up resolution and DFS traversal.
15pub struct ResolverContext {
16    registry: IndexMap<SourceName, RegisteredPackage>,
17    package_states: HashMap<SourceName, PackageResolutionState>,
18    id_index: HashMap<SourceId, SourceName>,
19    version_constraints: HashMap<SourceName, Vec<(String, VersionConstraint)>>,
20    materialization_filters: HashMap<SourceName, Vec<FilterMode>>,
21    stack: Vec<PendingItem>,
22    visited: VisitedSet,
23    package_versions: PackageVersions,
24    /// Version overrides carried from a prior restart pass.
25    ///
26    /// When a restart is triggered because package X would resolve to a different
27    /// version under the full accumulated constraint set, the driver carries the
28    /// correct (new) ref into the fresh context via this map. The first-resolution
29    /// branch in `resolve_package_bottom_up` checks this map and uses the override
30    /// directly, so the same constraint-accumulation pattern does NOT re-trigger a
31    /// restart on the next pass.
32    version_overrides: HashMap<
33        SourceName,
34        (
35            ResolvedRef,
36            RootedSourceRef,
37            crate::staging::HookSurfaceState,
38        ),
39    >,
40    /// Pending restart info set by `resolve_package_bottom_up` just before it returns
41    /// `ResolutionRestartNeeded`. The driver reads this before discarding the context.
42    pending_restart: Option<(
43        SourceName,
44        ResolvedRef,
45        RootedSourceRef,
46        crate::staging::HookSurfaceState,
47    )>,
48    unreadable_hook_surfaces: BTreeMap<SourceName, std::collections::BTreeSet<String>>,
49}
50
51impl Default for ResolverContext {
52    fn default() -> Self {
53        Self::new()
54    }
55}
56
57impl ResolverContext {
58    pub fn new() -> Self {
59        Self {
60            registry: IndexMap::new(),
61            package_states: HashMap::new(),
62            id_index: HashMap::new(),
63            version_constraints: HashMap::new(),
64            materialization_filters: HashMap::new(),
65            stack: Vec::new(),
66            visited: VisitedSet::new(),
67            package_versions: PackageVersions::new(),
68            version_overrides: HashMap::new(),
69            pending_restart: None,
70            unreadable_hook_surfaces: BTreeMap::new(),
71        }
72    }
73
74    /// Set version overrides from a prior restart pass.
75    /// These are used by `resolve_package_bottom_up` to skip re-resolution for
76    /// packages where the correct version was already computed.
77    pub(super) fn set_version_overrides(
78        &mut self,
79        overrides: HashMap<
80            SourceName,
81            (
82                ResolvedRef,
83                RootedSourceRef,
84                crate::staging::HookSurfaceState,
85            ),
86        >,
87    ) {
88        self.version_overrides = overrides;
89    }
90
91    /// Look up an override for the first resolution of `name`.
92    /// Returns the pre-computed ref and rooted package if present.
93    pub(super) fn version_override(
94        &self,
95        name: &SourceName,
96    ) -> Option<(
97        ResolvedRef,
98        RootedSourceRef,
99        crate::staging::HookSurfaceState,
100    )> {
101        self.version_overrides.get(name).cloned()
102    }
103
104    pub(super) fn set_version_override(
105        &mut self,
106        name: SourceName,
107        value: (
108            ResolvedRef,
109            RootedSourceRef,
110            crate::staging::HookSurfaceState,
111        ),
112    ) {
113        self.version_overrides.insert(name, value);
114    }
115
116    pub(super) fn version_overrides(
117        &self,
118    ) -> &HashMap<
119        SourceName,
120        (
121            ResolvedRef,
122            RootedSourceRef,
123            crate::staging::HookSurfaceState,
124        ),
125    > {
126        &self.version_overrides
127    }
128
129    /// Record the restart info: the package that triggered a restart and the ref
130    /// it should be resolved to on the next pass. Called by `resolve_package_bottom_up`
131    /// just before returning `ResolutionRestartNeeded`.
132    pub(super) fn set_pending_restart(
133        &mut self,
134        package: SourceName,
135        new_ref: ResolvedRef,
136        new_rooted: RootedSourceRef,
137        hook_surface: crate::staging::HookSurfaceState,
138    ) {
139        self.pending_restart = Some((package, new_ref, new_rooted, hook_surface));
140    }
141
142    /// Drain the pending restart info. Called by the driver after catching the signal.
143    pub(super) fn take_pending_restart(
144        &mut self,
145    ) -> Option<(
146        SourceName,
147        ResolvedRef,
148        RootedSourceRef,
149        crate::staging::HookSurfaceState,
150    )> {
151        self.pending_restart.take()
152    }
153
154    pub(super) fn set_hook_surface(
155        &mut self,
156        source_name: &SourceName,
157        state: crate::staging::HookSurfaceState,
158    ) {
159        if let crate::staging::HookSurfaceState::Unreadable { hook_names } = state {
160            self.unreadable_hook_surfaces
161                .insert(source_name.clone(), hook_names);
162        }
163    }
164
165    pub(super) fn registry(&self) -> &IndexMap<SourceName, RegisteredPackage> {
166        &self.registry
167    }
168
169    pub(super) fn registry_mut(&mut self) -> &mut IndexMap<SourceName, RegisteredPackage> {
170        &mut self.registry
171    }
172
173    pub(super) fn package_states(&self) -> &HashMap<SourceName, PackageResolutionState> {
174        &self.package_states
175    }
176
177    pub(super) fn package_states_mut(
178        &mut self,
179    ) -> &mut HashMap<SourceName, PackageResolutionState> {
180        &mut self.package_states
181    }
182
183    pub(super) fn id_index(&self) -> &HashMap<SourceId, SourceName> {
184        &self.id_index
185    }
186
187    pub(super) fn id_index_mut(&mut self) -> &mut HashMap<SourceId, SourceName> {
188        &mut self.id_index
189    }
190
191    pub(super) fn version_constraints(
192        &self,
193    ) -> &HashMap<SourceName, Vec<(String, VersionConstraint)>> {
194        &self.version_constraints
195    }
196
197    pub(super) fn materialization_filters(&self) -> &HashMap<SourceName, Vec<FilterMode>> {
198        &self.materialization_filters
199    }
200
201    pub(super) fn visited(&self) -> &VisitedSet {
202        &self.visited
203    }
204
205    pub(super) fn visited_mut(&mut self) -> &mut VisitedSet {
206        &mut self.visited
207    }
208
209    pub(super) fn package_versions_mut(&mut self) -> &mut PackageVersions {
210        &mut self.package_versions
211    }
212
213    pub fn add_version_constraint(
214        &mut self,
215        package: &SourceName,
216        requester: &str,
217        constraint: VersionConstraint,
218    ) {
219        self.version_constraints
220            .entry(package.clone())
221            .or_default()
222            .push((requester.to_string(), constraint));
223    }
224
225    pub fn add_filter(&mut self, package: &SourceName, filter: FilterMode) {
226        push_filter_constraint(&mut self.materialization_filters, package, &filter);
227    }
228
229    pub fn push_pending(&mut self, item: PendingItem) {
230        self.stack.push(item);
231    }
232
233    pub fn pop_pending(&mut self) -> Option<PendingItem> {
234        self.stack.pop()
235    }
236
237    pub fn into_graph(self) -> ResolvedGraph {
238        let mut nodes: IndexMap<SourceName, ResolvedNode> = IndexMap::new();
239        for (name, package) in self.registry {
240            nodes.insert(name, package.node);
241        }
242
243        let mut order: Vec<SourceName> = nodes.keys().cloned().collect();
244        order.sort();
245
246        ResolvedGraph {
247            nodes,
248            order,
249            filters: self.materialization_filters,
250            version_constraints: self.version_constraints,
251            unreadable_hook_surfaces: self.unreadable_hook_surfaces,
252        }
253    }
254}