Skip to main content

icydb_core/db/predicate/
coercion.rs

1//! Module: predicate::coercion
2//! Responsibility: coercion identifiers/specs and family support matching.
3//! Does not own: predicate AST evaluation or schema literal validation.
4//! Boundary: consumed by predicate schema/semantics/runtime layers.
5
6use crate::value::CoercionFamily;
7use std::collections::BTreeMap;
8
9///
10/// CoercionId
11///
12/// Identifier for an explicit comparison coercion policy.
13///
14/// Coercions express *how* values may be compared, not whether a comparison
15/// is valid for a given field. Validation and planning enforce legality.
16///
17
18#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
19pub enum CoercionId {
20    Strict,
21    NumericWiden,
22    TextCasefold,
23    CollectionElement,
24}
25
26impl CoercionId {
27    /// Stable tag used by plan hash encodings (fingerprint/continuation).
28    #[must_use]
29    pub const fn plan_hash_tag(self) -> u8 {
30        match self {
31            Self::Strict => 0x01,
32            Self::NumericWiden => 0x02,
33            Self::TextCasefold => 0x04,
34            Self::CollectionElement => 0x05,
35        }
36    }
37}
38
39///
40/// CoercionSpec
41///
42/// Fully-specified coercion policy for predicate comparisons.
43///
44
45#[derive(Clone, Debug, Eq, PartialEq)]
46pub struct CoercionSpec {
47    pub id: CoercionId,
48    pub params: BTreeMap<String, String>,
49}
50
51impl CoercionSpec {
52    #[must_use]
53    pub const fn new(id: CoercionId) -> Self {
54        Self {
55            id,
56            params: BTreeMap::new(),
57        }
58    }
59}
60
61impl Default for CoercionSpec {
62    fn default() -> Self {
63        Self::new(CoercionId::Strict)
64    }
65}
66
67///
68/// CoercionRuleFamily
69///
70/// Rule-side matcher for coercion routing families.
71///
72
73#[derive(Clone, Copy, Debug, Eq, PartialEq)]
74pub(crate) enum CoercionRuleFamily {
75    Any,
76    Family(CoercionFamily),
77}
78
79///
80/// CoercionRule
81///
82/// Declarative coercion routing rule between value families.
83///
84
85#[derive(Clone, Copy, Debug, Eq, PartialEq)]
86pub(crate) struct CoercionRule {
87    pub left: CoercionRuleFamily,
88    pub right: CoercionRuleFamily,
89    pub id: CoercionId,
90}
91
92pub(crate) const COERCION_TABLE: &[CoercionRule] = &[
93    CoercionRule {
94        left: CoercionRuleFamily::Any,
95        right: CoercionRuleFamily::Any,
96        id: CoercionId::Strict,
97    },
98    CoercionRule {
99        left: CoercionRuleFamily::Family(CoercionFamily::Numeric),
100        right: CoercionRuleFamily::Family(CoercionFamily::Numeric),
101        id: CoercionId::NumericWiden,
102    },
103    CoercionRule {
104        left: CoercionRuleFamily::Family(CoercionFamily::Textual),
105        right: CoercionRuleFamily::Family(CoercionFamily::Textual),
106        id: CoercionId::TextCasefold,
107    },
108    CoercionRule {
109        left: CoercionRuleFamily::Any,
110        right: CoercionRuleFamily::Any,
111        id: CoercionId::CollectionElement,
112    },
113];
114
115/// Returns whether a coercion rule exists for the provided routing families.
116#[must_use]
117pub(in crate::db) fn supports_coercion(
118    left: CoercionFamily,
119    right: CoercionFamily,
120    id: CoercionId,
121) -> bool {
122    COERCION_TABLE.iter().any(|rule| {
123        rule.id == id && family_matches(rule.left, left) && family_matches(rule.right, right)
124    })
125}
126
127fn family_matches(rule: CoercionRuleFamily, value: CoercionFamily) -> bool {
128    match rule {
129        CoercionRuleFamily::Any => true,
130        CoercionRuleFamily::Family(expected) => expected == value,
131    }
132}