scribe_selection/
simple_router.rs1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum SelectionStrategy {
11 ImportanceGreedy,
13 DependencyAware,
15 CoverageOptimized,
17 Random,
19 TwoPassSpeculative,
21 QuotaManaged,
23}
24
25impl SelectionStrategy {
26 pub fn name(&self) -> &'static str {
27 match self {
28 Self::ImportanceGreedy => "importance_greedy",
29 Self::DependencyAware => "dependency_aware",
30 Self::CoverageOptimized => "coverage_optimized",
31 Self::Random => "random",
32 Self::TwoPassSpeculative => "two_pass_speculative",
33 Self::QuotaManaged => "quota_managed",
34 }
35 }
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub enum ProjectSize {
41 Small, Medium, Large, }
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub enum TimeConstraint {
49 Tight, Normal, Relaxed, }
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct SelectionContext {
57 pub file_count: usize,
59 pub avg_importance: f64,
61 pub dependency_density: f64,
63 pub budget_ratio: f64,
65 pub dominant_file_type: String,
67 pub project_size: ProjectSize,
69 pub time_constraint: TimeConstraint,
71}
72
73#[derive(Debug, Clone)]
75pub struct RoutingDecision {
76 pub strategy: SelectionStrategy,
78 pub reason: String,
80}
81
82pub struct SimpleRouter;
84
85impl SimpleRouter {
86 pub fn new() -> Self {
88 Self
89 }
90
91 pub fn route_selection(&self, context: &SelectionContext) -> RoutingDecision {
99 if context.budget_ratio < 0.3 {
101 return RoutingDecision {
102 strategy: SelectionStrategy::QuotaManaged,
103 reason: format!(
104 "Low budget ratio ({:.2}) requires quota management",
105 context.budget_ratio
106 ),
107 };
108 }
109
110 if matches!(context.time_constraint, TimeConstraint::Tight) {
112 if context.dependency_density > 0.5 {
114 return RoutingDecision {
115 strategy: SelectionStrategy::DependencyAware,
116 reason: format!(
117 "Tight time constraint with high dependency density ({:.2}) needs dependency-aware selection",
118 context.dependency_density
119 ),
120 };
121 }
122 return RoutingDecision {
124 strategy: SelectionStrategy::ImportanceGreedy,
125 reason: "Tight time constraint requires fast importance-based selection".to_string(),
126 };
127 }
128
129 if matches!(context.project_size, ProjectSize::Small) {
131 return RoutingDecision {
132 strategy: SelectionStrategy::ImportanceGreedy,
133 reason: format!(
134 "Small project ({} files) works well with importance-based selection",
135 context.file_count
136 ),
137 };
138 }
139
140 if context.dependency_density > 0.7 {
142 return RoutingDecision {
143 strategy: SelectionStrategy::DependencyAware,
144 reason: format!(
145 "High dependency density ({:.2}) requires dependency-aware selection",
146 context.dependency_density
147 ),
148 };
149 }
150
151 if context.file_count > 200 {
153 return RoutingDecision {
154 strategy: SelectionStrategy::TwoPassSpeculative,
155 reason: format!(
156 "Large project ({} files) benefits from two-pass speculative selection",
157 context.file_count
158 ),
159 };
160 }
161
162 RoutingDecision {
164 strategy: SelectionStrategy::CoverageOptimized,
165 reason: "Standard context: using coverage-optimized selection for balanced results".to_string(),
166 }
167 }
168}
169
170impl Default for SimpleRouter {
171 fn default() -> Self {
172 Self::new()
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 fn create_test_context() -> SelectionContext {
181 SelectionContext {
182 file_count: 100,
183 avg_importance: 0.5,
184 dependency_density: 0.3,
185 budget_ratio: 0.8,
186 dominant_file_type: "source".to_string(),
187 project_size: ProjectSize::Medium,
188 time_constraint: TimeConstraint::Normal,
189 }
190 }
191
192 #[test]
193 fn test_low_budget_uses_quota_managed() {
194 let router = SimpleRouter::new();
195 let mut context = create_test_context();
196 context.budget_ratio = 0.2;
197
198 let decision = router.route_selection(&context);
199 assert_eq!(decision.strategy, SelectionStrategy::QuotaManaged);
200 assert!(decision.reason.contains("budget"));
201 }
202
203 #[test]
204 fn test_tight_time_with_dependencies_uses_dependency_aware() {
205 let router = SimpleRouter::new();
206 let mut context = create_test_context();
207 context.time_constraint = TimeConstraint::Tight;
208 context.dependency_density = 0.6;
209
210 let decision = router.route_selection(&context);
211 assert_eq!(decision.strategy, SelectionStrategy::DependencyAware);
212 }
213
214 #[test]
215 fn test_tight_time_without_dependencies_uses_importance_greedy() {
216 let router = SimpleRouter::new();
217 let mut context = create_test_context();
218 context.time_constraint = TimeConstraint::Tight;
219 context.dependency_density = 0.2;
220
221 let decision = router.route_selection(&context);
222 assert_eq!(decision.strategy, SelectionStrategy::ImportanceGreedy);
223 }
224
225 #[test]
226 fn test_small_project_uses_importance_greedy() {
227 let router = SimpleRouter::new();
228 let mut context = create_test_context();
229 context.project_size = ProjectSize::Small;
230 context.file_count = 30;
231
232 let decision = router.route_selection(&context);
233 assert_eq!(decision.strategy, SelectionStrategy::ImportanceGreedy);
234 }
235
236 #[test]
237 fn test_high_dependency_density_uses_dependency_aware() {
238 let router = SimpleRouter::new();
239 let mut context = create_test_context();
240 context.dependency_density = 0.8;
241
242 let decision = router.route_selection(&context);
243 assert_eq!(decision.strategy, SelectionStrategy::DependencyAware);
244 }
245
246 #[test]
247 fn test_large_file_count_uses_two_pass() {
248 let router = SimpleRouter::new();
249 let mut context = create_test_context();
250 context.file_count = 250;
251
252 let decision = router.route_selection(&context);
253 assert_eq!(decision.strategy, SelectionStrategy::TwoPassSpeculative);
254 }
255
256 #[test]
257 fn test_default_uses_coverage_optimized() {
258 let router = SimpleRouter::new();
259 let context = create_test_context();
260
261 let decision = router.route_selection(&context);
262 assert_eq!(decision.strategy, SelectionStrategy::CoverageOptimized);
263 }
264
265 #[test]
266 fn test_priority_order_budget_over_time() {
267 let router = SimpleRouter::new();
268 let mut context = create_test_context();
269 context.budget_ratio = 0.2;
270 context.time_constraint = TimeConstraint::Tight;
271
272 let decision = router.route_selection(&context);
273 assert_eq!(decision.strategy, SelectionStrategy::QuotaManaged);
275 }
276
277 #[test]
278 fn test_priority_order_time_over_size() {
279 let router = SimpleRouter::new();
280 let mut context = create_test_context();
281 context.time_constraint = TimeConstraint::Tight;
282 context.project_size = ProjectSize::Small;
283 context.dependency_density = 0.2;
284
285 let decision = router.route_selection(&context);
286 assert_eq!(decision.strategy, SelectionStrategy::ImportanceGreedy);
288 }
289}