Skip to main content

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}