Skip to main content

pylon_core/cast/
mod.rs

1//
2// This source file is part of the Pylon open source project.
3//
4// Copyright (c) 2026 Jaldis B.V.
5//
6// Licensed under the MIT OR Apache-2.0 license (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10//     https://opensource.org/licenses/MIT
11//     https://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS,
15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16// See the License for the specific language governing permissions and
17// limitations under the License.
18//
19
20use crate::stdlib::PylonType;
21use std::sync::OnceLock;
22
23mod matrix;
24
25// ── Cast strategy ─────────────────────────────────────────────────────────────
26
27/// How the transpiler emits a `<type>expr` cast.
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub enum CastStrategy {
30    /// Inserted silently by the type checker; user never writes `<type>expr`.
31    Implicit,
32    /// Transpiler emits `expr::pg_type` — no `_pylon` function required.
33    Sql(&'static str),
34    /// Transpiler resolves to the named stdlib function and uses its `ImplStrategy`.
35    /// The string is the unqualified function name, e.g. `"to_int16"`.
36    Function(&'static str),
37}
38
39// ── Cast entry ────────────────────────────────────────────────────────────────
40
41/// One entry in the closed cast whitelist.
42///
43/// Any `(source, target)` pair absent from the matrix is a compile-time type
44/// error — the type checker rejects it before SQL is emitted.
45#[derive(Debug, Clone)]
46pub struct CastEntry {
47    pub source: PylonType,
48    pub target: PylonType,
49    pub strategy: CastStrategy,
50}
51
52// ── Static matrix ─────────────────────────────────────────────────────────────
53
54static CAST_MATRIX: OnceLock<Vec<CastEntry>> = OnceLock::new();
55
56/// Return the full cast matrix, initializing it on first call.
57pub fn cast_matrix() -> &'static [CastEntry] {
58    CAST_MATRIX.get_or_init(matrix::build)
59}
60
61/// Look up a `(source, target)` pair. Returns `None` for unknown pairs — the
62/// transpiler must treat those as compile-time type errors.
63pub fn lookup_cast(source: &PylonType, target: &PylonType) -> Option<&'static CastEntry> {
64    cast_matrix()
65        .iter()
66        .find(|e| &e.source == source && &e.target == target)
67}
68
69/// True when the cast is inserted silently by the type checker.
70pub fn is_implicit(source: &PylonType, target: &PylonType) -> bool {
71    matches!(
72        lookup_cast(source, target),
73        Some(e) if e.strategy == CastStrategy::Implicit
74    )
75}