scion_stack/path/
strategy.rs

1// Copyright 2025 Anapaya Systems
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Path Selection defines how paths are filtered and ranked.
16
17use std::{cmp::Ordering, sync::Arc};
18
19use crate::path::{policy::PathPolicy, ranking::PathRanking, types::PathManagerPath};
20
21pub mod policy;
22pub mod ranking;
23
24/// PathStrategy combines multiple path operations into a single strategy.
25#[derive(Default)]
26pub struct PathStrategy {
27    /// The path policies to apply.
28    pub policies: Vec<Arc<dyn PathPolicy>>,
29    /// The path ranking functions to apply.
30    pub ranking: Vec<Arc<dyn PathRanking>>,
31}
32impl PathStrategy {
33    /// Appends a path policy to the list of policies.
34    pub fn add_policy(&mut self, policy: impl PathPolicy) {
35        self.policies.push(Arc::new(policy));
36    }
37
38    /// Appends a path ranking function to the list of ranking functions.
39    pub fn add_ranking(&mut self, ranking: impl PathRanking) {
40        self.ranking.push(Arc::new(ranking));
41    }
42
43    /// Returns true if the given path is accepted by all policies.
44    ///
45    /// If no policies are added, all paths are accepted.
46    pub fn predicate(&self, path: &PathManagerPath) -> bool {
47        self.policies.iter().all(|policy| policy.predicate(path))
48    }
49
50    /// Ranks the order of two paths based on preference.
51    ///
52    /// # Return
53    /// Returns the **preference ordering** between two paths.
54    ///
55    /// - `Ordering::Less` if `this` is preferred over `other`
56    /// - `Ordering::Greater` if `other` is preferred over `this`
57    /// - `Ordering::Equal` if both paths are equally preferred
58    pub fn rank_order(&self, this: &PathManagerPath, other: &PathManagerPath) -> Ordering {
59        for ranker in &self.ranking {
60            match ranker.rank_order(this, other) {
61                Ordering::Equal => continue,
62                ord => return ord,
63            }
64        }
65        Ordering::Equal
66    }
67
68    /// Filters the given paths based on all policies, removing paths that are not accepted.
69    pub fn filter_inplace<'path: 'iter, 'iter>(&self, paths: &mut Vec<PathManagerPath>) {
70        paths.retain(|p| self.predicate(p));
71    }
72
73    /// Sorts the given paths in place, placing the most preferred paths first.
74    ///
75    /// Uses the ranking functions in the order they were added.
76    ///
77    /// If no ranking functions are added, the paths are not modified.
78    pub fn rank_inplace<'path: 'iter, 'iter>(&self, path: &mut [PathManagerPath]) {
79        path.sort_by(|a, b| self.rank_order(a, b));
80    }
81}