Skip to main content

mysql_handler/runtime/
registry.rs

1// Copyright (C) 2026 ren-yamanashi
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License, version 2.0,
5// as published by the Free Software Foundation.
6//
7// This program is designed to work with certain software (including
8// but not limited to OpenSSL) that is licensed under separate terms,
9// as designated in a particular file or component or in included license
10// documentation. The authors of this program hereby grant you an additional
11// permission to link the program and your derivative works with the
12// separately licensed software that they have either included with
13// the program or referenced in the documentation.
14//
15// This program is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with this program; if not, see <https://www.gnu.org/licenses/>.
22
23//! Process-wide engine factory registry.
24
25use std::sync::OnceLock;
26
27use super::context::EngineContext;
28use crate::engine::StorageEngine;
29
30/// Factory closure that produces a fresh engine instance per opened table
31pub type EngineFactory = fn() -> Box<dyn StorageEngine>;
32
33/// Process-wide singleton holding the engine factory. The plugin's
34/// `rust__plugin_init` registers the factory once at startup;
35/// `rust__create_engine` reads back through the same registry on every
36/// handler instantiation.
37#[derive(Debug)]
38#[non_exhaustive]
39pub(crate) struct EngineRegistry {
40    factory: OnceLock<EngineFactory>,
41}
42
43impl EngineRegistry {
44    pub(crate) const fn new() -> Self {
45        Self {
46            factory: OnceLock::new(),
47        }
48    }
49
50    pub(crate) fn register(&self, factory: EngineFactory) {
51        match self.factory.set(factory) {
52            Ok(()) => {}
53            Err(_) => {
54                tracing::debug!(
55                    "engine factory already registered; ignoring duplicate registration"
56                );
57            }
58        }
59    }
60
61    pub(crate) fn create_context(&self) -> Option<EngineContext> {
62        let factory = self.factory.get().copied()?;
63        Some(EngineContext::new(factory()))
64    }
65}
66
67impl Default for EngineRegistry {
68    fn default() -> Self {
69        Self::new()
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use std::ffi::CStr;
76
77    use super::*;
78    use crate::engine::{EngineError, EngineResult};
79
80    struct MockEngine;
81
82    impl StorageEngine for MockEngine {
83        fn table_type(&self) -> &'static CStr {
84            c"MOCK"
85        }
86        fn table_flags(&self) -> u64 {
87            0
88        }
89        fn index_flags(&self, _idx: u32, _part: u32, _all_parts: bool) -> u32 {
90            0
91        }
92        fn create(
93            &mut self,
94            _name: &str,
95            _table_def: Option<&crate::sys::DdTable>,
96        ) -> EngineResult {
97            Ok(())
98        }
99        fn open(
100            &mut self,
101            _name: &str,
102            _mode: i32,
103            _table_def: Option<&crate::sys::DdTable>,
104        ) -> EngineResult {
105            Ok(())
106        }
107        fn close(&mut self) -> EngineResult {
108            Ok(())
109        }
110        fn rnd_init(&mut self, _scan: bool) -> EngineResult {
111            Ok(())
112        }
113        fn rnd_next(&mut self, _buf: &mut [u8]) -> EngineResult {
114            Err(EngineError::EndOfFile)
115        }
116        fn rnd_pos(&mut self, _buf: &mut [u8], _pos: &[u8]) -> EngineResult {
117            Err(EngineError::WrongCommand)
118        }
119        fn position(&mut self, _record: &[u8], _ref_out: &mut [u8]) {}
120        fn info(&mut self, _flag: u32) -> EngineResult {
121            Ok(())
122        }
123    }
124
125    #[test]
126    fn create_context_is_none_before_register() {
127        let registry = EngineRegistry::new();
128        assert!(registry.create_context().is_none());
129    }
130
131    #[test]
132    fn register_then_create_yields_context() {
133        let registry = EngineRegistry::new();
134        registry.register(|| Box::new(MockEngine));
135        assert!(registry.create_context().is_some());
136    }
137
138    #[test]
139    fn duplicate_register_is_ignored() {
140        let registry = EngineRegistry::new();
141        registry.register(|| Box::new(MockEngine));
142        registry.register(|| Box::new(MockEngine));
143        assert!(registry.create_context().is_some());
144    }
145}