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}