1use sim_kernel::{Cx, Expr, Result, Symbol};
10
11use crate::contract::LensKind;
12use crate::dispatch::{DispatchContext, DispatchReason, LensRegistry, best_shape_score};
13
14#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct LensStackEntry {
17 pub lens_id: Symbol,
19 pub reason: DispatchReason,
21}
22
23impl LensRegistry {
24 pub fn lens_stack(
28 &self,
29 cx: &mut Cx,
30 kind: LensKind,
31 target: &Expr,
32 ctx: &DispatchContext,
33 ) -> Result<Vec<LensStackEntry>> {
34 let mut ranked: Vec<(i32, i32, i32, i32, Symbol, DispatchReason)> = Vec::new();
36 for lens in self.lenses() {
37 if lens.meta.kind != kind || !self.allowed(lens, ctx) {
38 continue;
39 }
40 let meta = &lens.meta;
41 if meta.universal_default {
42 ranked.push((
43 0,
44 0,
45 meta.quality,
46 -meta.cost,
47 meta.id.clone(),
48 DispatchReason::UniversalDefault,
49 ));
50 } else if let Some(score) = best_shape_score(cx, lens, target)? {
51 ranked.push((
52 2,
53 score,
54 meta.quality,
55 -meta.cost,
56 meta.id.clone(),
57 DispatchReason::ShapeMatch(score),
58 ));
59 } else if class_matches(meta, ctx) {
60 ranked.push((
61 1,
62 0,
63 meta.quality,
64 -meta.cost,
65 meta.id.clone(),
66 DispatchReason::ClassMatch,
67 ));
68 }
69 }
70 ranked.sort_by_key(|entry| std::cmp::Reverse((entry.0, entry.1, entry.2, entry.3)));
72 Ok(ranked
73 .into_iter()
74 .map(|(_, _, _, _, lens_id, reason)| LensStackEntry { lens_id, reason })
75 .collect())
76 }
77}
78
79fn class_matches(meta: &crate::contract::LensMeta, ctx: &DispatchContext) -> bool {
80 match &ctx.value_class {
81 Some(class) => meta.claimed_classes.contains(class),
82 None => false,
83 }
84}