kunquant_rs/library.rs
1use crate::error::{KunQuantError, Result};
2use crate::ffi;
3use std::ffi::CString;
4use std::path::Path;
5
6/// A loaded KunQuant library containing compiled factor modules.
7///
8/// A `Library` represents a dynamically loaded shared library (.so/.dll/.dylib)
9/// that contains one or more compiled factor computation modules. These libraries
10/// are typically generated using KunQuant's Python compilation interface.
11///
12/// # Library Structure
13///
14/// Each library can contain multiple named modules, where each module represents
15/// a complete factor computation graph with defined inputs and outputs.
16///
17/// # Memory Management
18///
19/// The library automatically manages its resources using RAII. The underlying
20/// C handle and loaded library are properly cleaned up when dropped.
21///
22/// # Thread Safety
23///
24/// Libraries are thread-safe and can be shared across multiple threads.
25/// Multiple modules can be retrieved and used concurrently from the same library.
26pub struct Library {
27 handle: ffi::KunLibraryHandle,
28}
29
30impl Library {
31 /// Loads a KunQuant factor library from the specified file path.
32 ///
33 /// This method dynamically loads a compiled factor library and makes its
34 /// modules available for computation. The library must be compiled with
35 /// compatible settings for the intended computation mode (batch vs streaming).
36 ///
37 /// # Arguments
38 ///
39 /// * `path` - Path to the compiled library file. Can be any type that implements
40 /// `AsRef<str>` (e.g., `&str`, `String`, `PathBuf`, etc.)
41 ///
42 /// # Returns
43 ///
44 /// Returns `Ok(Library)` on successful loading, or an error if:
45 /// - The file doesn't exist or isn't accessible
46 /// - The file isn't a valid KunQuant library
47 /// - The library has incompatible version or architecture
48 /// - System resources are insufficient for loading
49 ///
50 /// # Examples
51 ///
52 /// ```rust,no_run
53 /// use kunquant_rs::Library;
54 ///
55 /// # fn main() -> kunquant_rs::Result<()> {
56 /// // Load library from relative path
57 /// let library = Library::load("factors/my_factors.so")?;
58 ///
59 /// // Load library from absolute path
60 /// let library = Library::load("/opt/factors/alpha_factors.so")?;
61 ///
62 /// // Works with String as well
63 /// let path = String::from("./test_factors.so");
64 /// let library = Library::load(path)?;
65 /// # Ok(())
66 /// # }
67 /// ```
68 ///
69 /// # Library Requirements
70 ///
71 /// - Must be compiled with compatible KunQuant version
72 /// - Architecture must match the target platform
73 /// - All dependencies must be available in the system
74 /// - File must have appropriate read permissions
75 ///
76 /// # Performance Notes
77 ///
78 /// - Library loading is a one-time cost during initialization
79 /// - Loaded libraries are cached by the system loader
80 /// - Multiple `Library` instances of the same file share underlying resources
81 pub fn load<P: AsRef<str>>(path: P) -> Result<Self> {
82 if !Path::new(path.as_ref()).exists() {
83 return Err(KunQuantError::LibraryLoadFailed {
84 path: path.as_ref().to_string(),
85 });
86 }
87 let path_str = path.as_ref();
88 let c_path = CString::new(path_str)?;
89
90 let handle = unsafe { ffi::kunLoadLibrary(c_path.as_ptr()) };
91 if handle.is_null() {
92 return Err(KunQuantError::LibraryLoadFailed {
93 path: path_str.to_string(),
94 });
95 }
96
97 Ok(Library { handle })
98 }
99
100 /// Retrieves a named factor module from the loaded library.
101 ///
102 /// Each library can contain multiple factor modules, each representing a
103 /// complete computation graph with defined inputs and outputs. This method
104 /// provides access to a specific module by name.
105 ///
106 /// # Arguments
107 ///
108 /// * `name` - The name of the module as defined during compilation. Can be any
109 /// type that implements `AsRef<str>` (e.g., `&str`, `String`, etc.)
110 ///
111 /// # Returns
112 ///
113 /// Returns `Ok(Module)` on success, or an error if:
114 /// - No module with the specified name exists in the library
115 /// - The library handle is invalid
116 /// - The C library call fails
117 ///
118 /// # Examples
119 ///
120 /// ```rust,no_run
121 /// use kunquant_rs::Library;
122 ///
123 /// # fn main() -> kunquant_rs::Result<()> {
124 /// let library = Library::load("factors.so")?;
125 ///
126 /// // Get a specific factor module
127 /// let alpha001 = library.get_module("alpha001")?;
128 /// let momentum = library.get_module("momentum_factor")?;
129 ///
130 /// // Works with String as well
131 /// let module_name = String::from("mean_reversion");
132 /// let mean_rev = library.get_module(module_name)?;
133 /// # Ok(())
134 /// # }
135 /// ```
136 ///
137 /// # Module Naming
138 ///
139 /// - Module names are defined during factor compilation
140 /// - Names are case-sensitive and must match exactly
141 /// - Common naming conventions include factor names (e.g., "alpha001")
142 /// or descriptive names (e.g., "price_momentum", "volume_weighted_return")
143 ///
144 /// # Lifetime Management
145 ///
146 /// The returned `Module` maintains a reference to the parent `Library`,
147 /// ensuring the library remains loaded for the module's lifetime.
148 pub fn get_module<N: AsRef<str>>(&self, name: N) -> Result<Module> {
149 let name_str = name.as_ref();
150 let c_name = CString::new(name_str)?;
151
152 let module_handle = unsafe { ffi::kunGetModuleFromLibrary(self.handle, c_name.as_ptr()) };
153 if module_handle.is_null() {
154 return Err(KunQuantError::ModuleNotFound {
155 name: name_str.to_string(),
156 });
157 }
158
159 Ok(Module {
160 handle: module_handle,
161 _library: self, // Keep library alive
162 })
163 }
164}
165
166impl Drop for Library {
167 fn drop(&mut self) {
168 if !self.handle.is_null() {
169 unsafe {
170 ffi::kunUnloadLibrary(self.handle);
171 }
172 }
173 }
174}
175
176/// A KunQuant module containing factor computation logic.
177///
178/// A `Module` represents a compiled factor computation graph with defined inputs
179/// and outputs. It encapsulates the mathematical operations, data flow, and
180/// optimization strategies for a specific factor or set of related factors.
181///
182/// # Computation Modes
183///
184/// Modules can be compiled for different execution modes:
185/// - **Batch Mode**: Optimized for processing historical time series data
186/// - **Streaming Mode**: Optimized for real-time, low-latency computation
187///
188/// # Lifetime Parameters
189///
190/// * `'a` - The lifetime of the parent library reference
191///
192/// # Thread Safety
193///
194/// Modules are thread-safe and can be used concurrently across multiple threads.
195/// Each computation context (batch or streaming) maintains its own state.
196///
197/// # Memory Management
198///
199/// The module maintains a reference to its parent library, ensuring the library
200/// remains loaded for the module's entire lifetime. This prevents use-after-free
201/// errors and ensures computation integrity.
202pub struct Module<'a> {
203 handle: ffi::KunModuleHandle,
204 _library: &'a Library, // Keep library alive
205}
206
207impl<'a> Module<'a> {
208 /// Get the raw handle (for internal use)
209 pub(crate) fn handle(&self) -> ffi::KunModuleHandle {
210 self.handle
211 }
212}