reifydb_sub_flow/operator/
extern_rust.rs1use std::{
5 collections::HashMap,
6 path::{Path, PathBuf},
7 sync::OnceLock,
8};
9
10use libloading::Symbol;
11use reifydb_core::interface::catalog::flow::OperatorId;
12use reifydb_extension::loader::extern_load::ExternLoad;
13use reifydb_flow::operator::BoxedHostOperator;
14use reifydb_runtime::sync::rwlock::RwLock;
15use reifydb_value::{Result, config::Config, error::Error, value::constraint::TypeConstraint};
16
17use crate::error::ExternOperatorError;
18
19pub const EXTERN_RUST_OPERATOR_MAGIC: u32 = 0x5244_424E;
20
21pub const EXTERN_RUST_ABI_TAG: u32 = 0x0308;
22
23pub type ExternRustOperatorCreateFn = fn(OperatorId, &Config) -> Result<BoxedHostOperator>;
24
25pub struct ExternRustOperatorColumn {
26 pub name: String,
27 pub field_type: TypeConstraint,
28 pub description: String,
29}
30
31pub struct ExternRustOperatorDescriptor {
32 pub abi_tag: u32,
33 pub name: String,
34 pub version: String,
35 pub description: String,
36 pub capabilities: u32,
37 pub input_columns: Vec<ExternRustOperatorColumn>,
38 pub output_columns: Vec<ExternRustOperatorColumn>,
39}
40
41pub fn extern_rust_operator_magic() -> u32 {
42 EXTERN_RUST_OPERATOR_MAGIC
43}
44
45pub fn check_extern_rust_abi_tag(abi_tag: u32) -> Result<()> {
46 if abi_tag != EXTERN_RUST_ABI_TAG {
47 return Err(Error::from(ExternOperatorError::AbiTagMismatch {
48 plugin: abi_tag,
49 host: EXTERN_RUST_ABI_TAG,
50 }));
51 }
52 Ok(())
53}
54
55pub struct LoadedExternRustOperatorInfo {
56 pub operator: String,
57 pub library_path: PathBuf,
58 pub abi_tag: u32,
59 pub version: String,
60 pub description: String,
61 pub input_columns: Vec<ExternRustOperatorColumn>,
62 pub output_columns: Vec<ExternRustOperatorColumn>,
63 pub capabilities: u32,
64}
65
66static GLOBAL_EXTERN_RUST_OPERATOR_LOADER: OnceLock<RwLock<ExternRustOperatorLoader>> = OnceLock::new();
67
68pub fn extern_rust_operator_loader() -> &'static RwLock<ExternRustOperatorLoader> {
69 GLOBAL_EXTERN_RUST_OPERATOR_LOADER.get_or_init(|| RwLock::new(ExternRustOperatorLoader::new()))
70}
71
72pub struct ExternRustOperatorLoader {
73 cache: ExternLoad,
74 operator_paths: HashMap<String, PathBuf>,
75}
76
77impl ExternRustOperatorLoader {
78 fn new() -> Self {
79 Self {
80 cache: ExternLoad::new(),
81 operator_paths: HashMap::new(),
82 }
83 }
84
85 fn load_library(&mut self, path: &Path) -> Result<bool> {
86 self.cache
87 .check_magic(path, b"reifydb_extern_rust_operator_magic\0", EXTERN_RUST_OPERATOR_MAGIC)
88 .map_err(|_e| {
89 Error::from(ExternOperatorError::LibraryNotLoaded {
90 path: path.display().to_string(),
91 })
92 })
93 }
94
95 fn descriptor(&self, path: &Path) -> Result<ExternRustOperatorDescriptor> {
96 let library = self.cache.get(path).ok_or_else(|| {
97 Error::from(ExternOperatorError::LibraryNotLoaded {
98 path: path.display().to_string(),
99 })
100 })?;
101
102 let descriptor = unsafe {
106 let get_descriptor: Symbol<fn() -> ExternRustOperatorDescriptor> =
107 library.get(b"reifydb_extern_rust_operator_descriptor\0").map_err(|e| {
108 Error::from(ExternOperatorError::SymbolNotFound {
109 symbol: "reifydb_extern_rust_operator_descriptor",
110 cause: e.to_string(),
111 })
112 })?;
113 get_descriptor()
114 };
115
116 check_extern_rust_abi_tag(descriptor.abi_tag)?;
117
118 Ok(descriptor)
119 }
120
121 pub fn register_operator(&mut self, path: &Path) -> Result<Option<LoadedExternRustOperatorInfo>> {
122 if !self.load_library(path)? {
123 return Ok(None);
124 }
125
126 let descriptor = self.descriptor(path)?;
127 self.operator_paths.insert(descriptor.name.clone(), path.to_path_buf());
128
129 Ok(Some(LoadedExternRustOperatorInfo {
130 operator: descriptor.name,
131 library_path: path.to_path_buf(),
132 abi_tag: descriptor.abi_tag,
133 version: descriptor.version,
134 description: descriptor.description,
135 input_columns: descriptor.input_columns,
136 output_columns: descriptor.output_columns,
137 capabilities: descriptor.capabilities,
138 }))
139 }
140
141 pub fn has_operator(&self, operator: &str) -> bool {
142 self.operator_paths.contains_key(operator)
143 }
144
145 pub fn create_operator_by_name(
146 &mut self,
147 operator: &str,
148 operator_id: OperatorId,
149 config: &Config,
150 ) -> Result<BoxedHostOperator> {
151 let path = self
152 .operator_paths
153 .get(operator)
154 .ok_or_else(|| {
155 Error::from(ExternOperatorError::OperatorNotFound {
156 operator: operator.to_string(),
157 })
158 })?
159 .clone();
160
161 if !self.load_library(&path)? {
162 return Err(Error::from(ExternOperatorError::LibraryNotLoaded {
163 path: operator.to_string(),
164 }));
165 }
166
167 self.descriptor(&path)?;
168
169 let library = self.cache.library(&path).map_err(|_| {
170 Error::from(ExternOperatorError::LibraryNotLoaded {
171 path: operator.to_string(),
172 })
173 })?;
174 let create: ExternRustOperatorCreateFn = unsafe {
178 let create_symbol: Symbol<ExternRustOperatorCreateFn> =
179 library.get(b"reifydb_extern_rust_operator_create\0").map_err(|e| {
180 Error::from(ExternOperatorError::SymbolNotFound {
181 symbol: "reifydb_extern_rust_operator_create",
182 cause: e.to_string(),
183 })
184 })?;
185 *create_symbol
186 };
187
188 create(operator_id, config)
189 }
190}
191
192impl Default for ExternRustOperatorLoader {
193 fn default() -> Self {
194 Self::new()
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use reifydb_extension::operator::extern_c::loader::check_operator_abi_tag;
201 use reifydb_sdk::flow::operator::extern_c::wire::types::OPERATOR_ABI_TAG;
202
203 use super::{EXTERN_RUST_ABI_TAG, check_extern_rust_abi_tag};
204
205 #[test]
206 fn extern_rust_abi_tag_accepts_match_rejects_mismatch() {
207 assert!(check_extern_rust_abi_tag(EXTERN_RUST_ABI_TAG).is_ok());
209 assert!(check_extern_rust_abi_tag(EXTERN_RUST_ABI_TAG ^ 0x1).is_err());
210 assert!(check_extern_rust_abi_tag(0).is_err());
211 }
212
213 #[test]
214 fn extern_c_abi_tag_accepts_match_rejects_mismatch() {
215 assert!(check_operator_abi_tag(OPERATOR_ABI_TAG).is_ok());
216 assert!(check_operator_abi_tag(OPERATOR_ABI_TAG ^ 0x1).is_err());
217 assert!(check_operator_abi_tag(0).is_err());
218 }
219
220 #[test]
221 fn extern_rust_and_extern_c_tags_do_not_accept_each_other() {
222 assert_ne!(EXTERN_RUST_ABI_TAG, OPERATOR_ABI_TAG);
224 assert!(check_extern_rust_abi_tag(OPERATOR_ABI_TAG).is_err());
225 assert!(check_operator_abi_tag(EXTERN_RUST_ABI_TAG).is_err());
226 }
227}