Skip to main content

ri/c/
core.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of Ri.
4//! The Ri project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (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//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18//! # Core Module C API
19//!
20//! This module provides C language bindings for Ri's core application infrastructure.
21//! The core module serves as the foundation for building Ri applications, providing
22//! application lifecycle management, configuration handling, and initialization routines.
23//! This C API enables C/C++ applications to leverage Ri's powerful application builder
24//! and configuration management capabilities.
25//!
26//! ## Module Architecture
27//!
28//! The core module comprises two essential components that form the backbone of any
29//! Ri application:
30//!
31//! - **RiAppBuilder**: Fluent builder pattern implementation for constructing Ri
32//!   applications with type-safe configuration. The builder supports registration of
33//!   modules, services, and middleware components through a declarative API. It handles
34//!   dependency injection, service discovery, and lifecycle coordination across all
35//!   registered components. The builder produces a fully initialized RiApp instance
36//!   ready for execution.
37//!
38//! - **RiConfig**: Unified configuration management interface supporting multiple
39//!   configuration sources including environment variables, command-line arguments,
40//!   configuration files (YAML, TOML, JSON), and remote configuration services.
41//!   The configuration system provides type-safe value retrieval with automatic
42//!   type conversion, validation, and hot-reload capabilities for dynamic
43//!   configuration updates in running applications.
44//!
45//! ## Application Lifecycle
46//!
47//! Ri applications follow a well-defined lifecycle:
48//!
49//! 1. **Initialization Phase**: Application builder creates and configures components.
50//!    Services are registered, dependencies are wired, and configuration is loaded.
51//!
52//! 2. **Startup Phase**: All registered services and modules are initialized in
53//!    dependency order. Health checks verify component readiness.
54//!
55//! 3. **Running Phase**: Application enters active state, processing requests or
56//!    executing scheduled tasks. Components operate according to their configured
57//!    behavior.
58//!
59//! 4. **Shutdown Phase**: Graceful shutdown initiates component cleanup in reverse
60//!    dependency order. Resources are released, connections are closed, and state
61//!    is persisted as needed.
62//!
63//! ## Configuration System
64//!
65//! The configuration module implements a hierarchical configuration model:
66//!
67//! - **Sources**: Environment variables override file-based settings, which override
68//!   default values. Multiple sources can coexist with configurable precedence.
69//!
70//! - **Formats**: Native support for YAML, TOML, JSON configuration files. Each
71//!   format has optimized parsing and schema validation.
72//!
73//! - **Validation**: Configuration values undergo validation against defined schemas.
74//!   Type mismatches, missing required fields, and constraint violations are detected.
75//!
76//! - **Hot Reload**: Configuration files are monitored for changes. Updates are
77//!   applied atomically without application restart. Subscribers are notified of changes.
78//!
79//! ## Memory Management
80//!
81//! All C API objects use opaque pointers with manual memory management:
82//!
83//! - Constructor functions allocate new instances on the heap
84//! - Destructor functions must be called to release memory
85//! - Objects must not be used after being freed
86//! - Null pointer checks are required before all operations
87//!
88//! ## Thread Safety
89//!
90//! The underlying Rust implementations are thread-safe:
91//!
92//! - Application builder operations require external synchronization
93//! - Configuration reads are concurrent-safe after initialization
94//! - Configuration writes require exclusive access
95//!
96//! ## Usage Example
97//!
98//! ```c
99//! // Create application configuration
100//! CRiConfig* config = ri_config_new();
101//!
102//! // Load configuration from file
103//! int result = ri_config_load_file(config, "config.yaml");
104//!
105//! // Get configuration value
106//! char* value = ri_config_get_string(config, "database.url");
107//!
108//! // Create application builder
109//! CRiAppBuilder* builder = ri_app_builder_new();
110//!
111//! // Configure builder with configuration
112//! ri_app_builder_configure(builder, config);
113//!
114//! // Build and run application
115//! ri_app_builder_build(builder);
116//!
117//! // Cleanup
118//! ri_app_builder_free(builder);
119//! ri_config_free(config);
120//! ri_string_free(value);
121//! ```
122//!
123//! ## Dependencies
124//!
125//! This module depends on the following Ri components:
126//!
127//! - `crate::core`: Rust core module implementation
128//! - `crate::prelude`: Common types and traits
129//!
130//! ## Feature Flags
131//!
132//! The core module is always enabled as it provides fundamental infrastructure
133//! required by all other Ri components.
134
135use crate::prelude::{RiAppBuilder, RiConfig};
136use crate::core::{RiServiceContext, RiHealthStatus, RiHealthCheckResult, RiHealthCheckConfig};
137use crate::core::error_chain::RiErrorChain;
138use crate::core::lock::RiLockError;
139use std::ffi::{c_char, CString, c_int};
140use std::time::SystemTime;
141
142/// Opaque C wrapper structure for RiAppBuilder.
143///
144/// Provides C-compatible interface to the Rust application builder implementation.
145/// The builder uses the fluent builder pattern to construct Ri applications with
146/// proper dependency injection and lifecycle management.
147///
148/// # Builder Responsibilities
149///
150/// The application builder handles:
151///
152/// - Service registration and dependency management
153/// - Middleware composition and ordering
154/// - Configuration propagation to components
155/// - Lifecycle event registration
156/// - Application initialization and startup coordination
157///
158/// # Builder Pattern
159///
160/// The builder implements a fluent interface allowing method chaining:
161///
162/// ```c
163/// ri_app_builder_register_module(builder, module_a);
164/// ri_app_builder_register_module(builder, module_b);
165/// ri_app_builder_configure(builder, config);
166/// ri_app_builder_with_middleware(builder, middleware_1);
167/// ri_app_builder_with_middleware(builder, middleware_2);
168/// ```
169///
170/// # Thread Safety
171///
172/// The builder is not thread-safe. All builder operations should occur from a
173/// single thread before application startup. Concurrent builder access results
174/// in undefined behavior.
175#[repr(C)]
176pub struct CRiAppBuilder {
177    inner: RiAppBuilder,
178}
179
180/// Opaque C wrapper structure for RiConfig.
181///
182/// Provides C-compatible interface to the unified configuration management system.
183/// The configuration object provides type-safe access to configuration values
184/// from multiple sources with automatic type conversion and validation.
185///
186/// # Configuration Sources
187///
188/// The configuration system aggregates values from:
189///
190/// - Default values defined in code
191/// - Configuration files (YAML, TOML, JSON)
192/// - Environment variables
193/// - Command-line arguments
194/// - Remote configuration services (etcd, Consul)
195///
196/// # Value Resolution
197///
198/// Configuration values are resolved using precedence order:
199///
200/// 1. Environment variables (highest priority)
201/// 2. Command-line arguments
202/// 3. Remote configuration
203/// 4. Configuration files
204/// 5. Default values (lowest priority)
205///
206/// # Type Safety
207///
208/// The configuration system provides type-safe value retrieval:
209///
210/// - get_string(): Retrieve string values
211/// - get_int(): Retrieve integer values with automatic conversion
212/// - get_bool(): Retrieve boolean values
213/// - get_float(): Retrieve floating-point values
214/// - get_list(): Retrieve array values
215///
216/// Invalid type requests return default values or trigger validation errors.
217#[repr(C)]
218pub struct CRiConfig {
219    inner: RiConfig,
220}
221
222/// Creates a new CRiAppBuilder instance.
223///
224/// Initializes an empty application builder ready for component registration.
225/// The builder starts with default configuration and no registered modules.
226///
227/// # Returns
228///
229/// Pointer to newly allocated CRiAppBuilder on success. Never returns NULL
230/// as the implementation uses infallible construction. The returned pointer
231/// must be freed using ri_app_builder_free().
232///
233/// # Initial State
234///
235/// A newly created builder:
236///
237/// - Has no registered modules
238/// - Has no configured middleware
239/// - Uses default application configuration
240/// - Has not initiated application startup
241///
242/// # Usage Pattern
243///
244/// ```c
245/// CRiAppBuilder* builder = ri_app_builder_new();
246/// if (builder == NULL) {
247///     // Handle allocation failure
248///     return ERROR_MEMORY_ALLOCATION;
249/// }
250///
251/// // Register modules and configure
252/// ri_app_builder_register_module(builder, http_module);
253/// ri_app_builder_register_module(builder, database_module);
254///
255/// // Build and run
256/// ri_app_builder_build(builder);
257///
258/// // Cleanup
259/// ri_app_builder_free(builder);
260/// ```
261#[no_mangle]
262pub extern "C" fn ri_app_builder_new() -> *mut CRiAppBuilder {
263    let builder = CRiAppBuilder {
264        inner: RiAppBuilder::new(),
265    };
266    Box::into_raw(Box::new(builder))
267}
268
269/// Frees a previously allocated CRiAppBuilder instance.
270///
271/// Releases all memory associated with the builder including any registered
272/// configurations, module references, or internal state. After this function
273/// returns, the pointer becomes invalid.
274///
275/// # Parameters
276///
277/// - `builder`: Pointer to CRiAppBuilder to free. If NULL, the function
278///   returns immediately without error.
279///
280/// # Behavior
281///
282/// The destructor:
283///
284/// - Clears all registered modules
285/// - Releases internal configuration
286/// - Frees allocated memory
287/// - Invalidates the pointer
288///
289/// # Safety
290///
291/// This function is safe to call with NULL. Calling with a pointer that has
292/// already been freed results in undefined behavior.
293#[no_mangle]
294pub extern "C" fn ri_app_builder_free(builder: *mut CRiAppBuilder) {
295    if !builder.is_null() {
296        unsafe {
297            let _ = Box::from_raw(builder);
298        }
299    }
300}
301
302/// Creates a new CRiConfig instance.
303///
304/// Initializes an empty configuration object with no loaded sources.
305/// The configuration starts with default values and requires explicit
306/// loading from configuration sources.
307///
308/// # Returns
309///
310/// Pointer to newly allocated CRiConfig on success. Never returns NULL
311/// as the implementation uses infallible construction. The returned pointer
312/// must be freed using ri_config_free().
313///
314/// # Initial State
315///
316/// A newly created configuration:
317///
318/// - Contains no loaded values
319/// - Has default values for all known keys
320/// - Has no active configuration sources
321/// - Is not watching for changes
322///
323/// # Usage Pattern
324///
325/// ```c
326/// CRiConfig* config = ri_config_new();
327/// if (config == NULL) {
328///     // Handle allocation failure
329///     return ERROR_MEMORY_ALLOCATION;
330/// }
331///
332/// // Load from file
333/// int load_result = ri_config_load_file(config, "config.yaml");
334/// if (load_result != 0) {
335///     // Handle load failure
336/// }
337///
338/// // Access configuration values
339/// char* host = ri_config_get_string(config, "server.host");
340/// int port = ri_config_get_int(config, "server.port");
341///
342/// // Cleanup
343/// ri_config_free(config);
344/// ri_string_free(host);
345/// ```
346#[no_mangle]
347pub extern "C" fn ri_config_new() -> *mut CRiConfig {
348    let config = CRiConfig {
349        inner: RiConfig::new(),
350    };
351    Box::into_raw(Box::new(config))
352}
353
354/// Frees a previously allocated CRiConfig instance.
355///
356/// Releases all memory associated with the configuration including any
357/// loaded values, watched files, or internal caches. After this function
358/// returns, the pointer becomes invalid.
359///
360/// # Parameters
361///
362/// - `config`: Pointer to CRiConfig to free. If NULL, the function returns
363///   immediately without error.
364///
365/// # Behavior
366///
367/// The destructor:
368///
369/// - Clears all configuration values
370/// - Stops file watchers if active
371/// - Releases internal caches
372/// - Invalidates the pointer
373///
374/// # Safety
375///
376/// This function is safe to call with NULL. Calling with a pointer that has
377/// already been freed results in undefined behavior.
378#[no_mangle]
379pub extern "C" fn ri_config_free(config: *mut CRiConfig) {
380    if !config.is_null() {
381        unsafe {
382            let _ = Box::from_raw(config);
383        }
384    }
385}
386
387/// Retrieves a string configuration value by key.
388///
389/// Looks up the specified key in the configuration hierarchy and returns
390/// the associated string value if found. The function performs type-safe
391/// retrieval with automatic conversion from compatible types.
392///
393/// # Parameters
394///
395/// - `config`: Pointer to CRiConfig containing the configuration. Must not
396///   be NULL. If NULL, the function returns NULL.
397/// - `key`: Pointer to null-terminated C string specifying the configuration key.
398///   Keys use dot notation for hierarchical access (e.g., "database.connections.max").
399///   Must not be NULL. If NULL, the function returns NULL.
400///
401/// # Returns
402///
403/// Pointer to newly allocated C string containing the configuration value on
404/// success. The caller is responsible for freeing the returned string using
405/// ri_string_free(). Returns NULL if:
406///
407/// - `config` is NULL
408/// - `key` is NULL
409/// - Key does not exist in configuration
410/// - Value exists but is not a string type
411/// - String conversion fails (invalid UTF-8)
412///
413/// # Key Format
414///
415/// Configuration keys support hierarchical access:
416///
417/// - Simple keys: "timeout"
418/// - Nested keys: "server.http.port"
419/// - Array indices: "servers.0.host"
420///
421/// # Example
422///
423/// ```c
424/// char* database_url = ri_config_get_string(config, "database.url");
425/// if (database_url != NULL) {
426///     printf("Database URL: %s\n", database_url);
427///     ri_string_free(database_url);
428/// } else {
429///     printf("Database URL not configured\n");
430/// }
431/// ```
432///
433/// # Memory Management
434///
435/// The returned string is newly allocated. Callers must release it using
436/// ri_string_free() to prevent memory leaks. Do not use free() directly.
437#[no_mangle]
438pub extern "C" fn ri_config_get_string(
439    config: *mut CRiConfig,
440    key: *const c_char,
441) -> *mut c_char {
442    if config.is_null() || key.is_null() {
443        return std::ptr::null_mut();
444    }
445
446    unsafe {
447        let c = &(*config).inner;
448        let key_str = match std::ffi::CStr::from_ptr(key).to_str() {
449            Ok(s) => s,
450            Err(_) => return std::ptr::null_mut(),
451        };
452
453        match c.get_str(key_str) {
454            Some(val) => match CString::new(val) {
455                Ok(c_str) => c_str.into_raw(),
456                Err(_) => std::ptr::null_mut(),
457            },
458            None => std::ptr::null_mut(),
459        }
460    }
461}
462
463#[repr(C)]
464pub struct CRiServiceContext {
465    inner: RiServiceContext,
466}
467
468#[no_mangle]
469pub extern "C" fn ri_service_context_new() -> *mut CRiServiceContext {
470    match RiServiceContext::new_default() {
471        Ok(ctx) => {
472            let context = CRiServiceContext { inner: ctx };
473            Box::into_raw(Box::new(context))
474        }
475        Err(_) => std::ptr::null_mut(),
476    }
477}
478
479#[no_mangle]
480pub extern "C" fn ri_service_context_free(ctx: *mut CRiServiceContext) {
481    if !ctx.is_null() {
482        unsafe {
483            let _ = Box::from_raw(ctx);
484        }
485    }
486}
487
488#[repr(C)]
489pub struct CRiHealthStatus {
490    pub status: c_int,
491}
492
493pub const RI_HEALTH_STATUS_HEALTHY: c_int = 0;
494pub const RI_HEALTH_STATUS_DEGRADED: c_int = 1;
495pub const RI_HEALTH_STATUS_UNHEALTHY: c_int = 2;
496pub const RI_HEALTH_STATUS_UNKNOWN: c_int = 3;
497
498#[no_mangle]
499pub extern "C" fn ri_health_status_new(status: c_int) -> CRiHealthStatus {
500    CRiHealthStatus { status }
501}
502
503#[no_mangle]
504pub extern "C" fn ri_health_status_is_healthy(status: *const CRiHealthStatus) -> bool {
505    if status.is_null() {
506        return false;
507    }
508    unsafe {
509        matches!((*status).status, RI_HEALTH_STATUS_HEALTHY | RI_HEALTH_STATUS_DEGRADED)
510    }
511}
512
513#[no_mangle]
514pub extern "C" fn ri_health_status_requires_attention(status: *const CRiHealthStatus) -> bool {
515    if status.is_null() {
516        return false;
517    }
518    unsafe { (*status).status == RI_HEALTH_STATUS_UNHEALTHY }
519}
520
521#[repr(C)]
522pub struct CRiHealthCheckResult {
523    pub name: *mut c_char,
524    pub status: CRiHealthStatus,
525    pub message: *mut c_char,
526    pub timestamp_secs: u64,
527    pub timestamp_nanos: u64,
528    pub duration_secs: u64,
529    pub duration_nanos: u64,
530}
531
532#[no_mangle]
533pub extern "C" fn ri_health_check_result_new(
534    name: *const c_char,
535    status: c_int,
536    message: *const c_char,
537) -> *mut CRiHealthCheckResult {
538    if name.is_null() {
539        return std::ptr::null_mut();
540    }
541
542    unsafe {
543        let name_str = match std::ffi::CStr::from_ptr(name).to_str() {
544            Ok(s) => s.to_string(),
545            Err(_) => return std::ptr::null_mut(),
546        };
547
548        let msg_str = if message.is_null() {
549            None
550        } else {
551            match std::ffi::CStr::from_ptr(message).to_str() {
552                Ok(s) => Some(s.to_string()),
553                Err(_) => None,
554            }
555        };
556
557        let rust_status = match status {
558            RI_HEALTH_STATUS_HEALTHY => RiHealthStatus::Healthy,
559            RI_HEALTH_STATUS_DEGRADED => RiHealthStatus::Degraded,
560            RI_HEALTH_STATUS_UNHEALTHY => RiHealthStatus::Unhealthy,
561            _ => RiHealthStatus::Unknown,
562        };
563
564        let result = RiHealthCheckResult {
565            name: name_str,
566            status: rust_status,
567            message: msg_str,
568            timestamp: SystemTime::now(),
569            duration: std::time::Duration::ZERO,
570        };
571
572        let c_result = convert_health_check_result_to_c(result);
573        Box::into_raw(Box::new(c_result))
574    }
575}
576
577fn convert_health_check_result_to_c(result: RiHealthCheckResult) -> CRiHealthCheckResult {
578    let name = match CString::new(result.name) {
579        Ok(s) => s.into_raw(),
580        Err(_) => std::ptr::null_mut(),
581    };
582
583    let message = match result.message {
584        Some(msg) => match CString::new(msg) {
585            Ok(s) => s.into_raw(),
586            Err(_) => std::ptr::null_mut(),
587        },
588        None => std::ptr::null_mut(),
589    };
590
591    let status = CRiHealthStatus {
592        status: match result.status {
593            RiHealthStatus::Healthy => RI_HEALTH_STATUS_HEALTHY,
594            RiHealthStatus::Degraded => RI_HEALTH_STATUS_DEGRADED,
595            RiHealthStatus::Unhealthy => RI_HEALTH_STATUS_UNHEALTHY,
596            RiHealthStatus::Unknown => RI_HEALTH_STATUS_UNKNOWN,
597        },
598    };
599
600    let duration = result.duration;
601    let timestamp = result.timestamp
602        .duration_since(SystemTime::UNIX_EPOCH)
603        .unwrap_or_default();
604
605    CRiHealthCheckResult {
606        name,
607        status,
608        message,
609        timestamp_secs: timestamp.as_secs(),
610        timestamp_nanos: timestamp.subsec_nanos() as u64,
611        duration_secs: duration.as_secs(),
612        duration_nanos: duration.subsec_nanos() as u64,
613    }
614}
615
616#[no_mangle]
617pub extern "C" fn ri_health_check_result_free(result: *mut CRiHealthCheckResult) {
618    if !result.is_null() {
619        unsafe {
620            if !(*result).name.is_null() {
621                let _ = CString::from_raw((*result).name);
622            }
623            if !(*result).message.is_null() {
624                let _ = CString::from_raw((*result).message);
625            }
626            let _ = Box::from_raw(result);
627        }
628    }
629}
630
631#[repr(C)]
632pub struct CRiHealthCheckConfig {
633    pub check_interval_secs: u64,
634    pub timeout_secs: u64,
635    pub failure_threshold: u32,
636    pub success_threshold: u32,
637    pub enabled: bool,
638}
639
640#[no_mangle]
641pub extern "C" fn ri_health_check_config_new(
642    check_interval_secs: u64,
643    timeout_secs: u64,
644    failure_threshold: u32,
645    success_threshold: u32,
646    enabled: bool,
647) -> CRiHealthCheckConfig {
648    CRiHealthCheckConfig {
649        check_interval_secs,
650        timeout_secs,
651        failure_threshold,
652        success_threshold,
653        enabled,
654    }
655}
656
657#[no_mangle]
658pub extern "C" fn ri_health_check_config_default() -> CRiHealthCheckConfig {
659    let default_config = RiHealthCheckConfig::default();
660    CRiHealthCheckConfig {
661        check_interval_secs: default_config.check_interval.as_secs(),
662        timeout_secs: default_config.timeout.as_secs(),
663        failure_threshold: default_config.failure_threshold,
664        success_threshold: default_config.success_threshold,
665        enabled: default_config.enabled,
666    }
667}
668
669#[repr(C)]
670pub struct CRiHealthReport {
671    pub overall_status: CRiHealthStatus,
672    pub total_components: usize,
673    pub healthy_count: usize,
674    pub degraded_count: usize,
675    pub unhealthy_count: usize,
676    pub unknown_count: usize,
677}
678
679#[no_mangle]
680pub extern "C" fn ri_health_report_new() -> *mut CRiHealthReport {
681    let report = CRiHealthReport {
682        overall_status: CRiHealthStatus { status: RI_HEALTH_STATUS_UNKNOWN },
683        total_components: 0,
684        healthy_count: 0,
685        degraded_count: 0,
686        unhealthy_count: 0,
687        unknown_count: 0,
688    };
689    Box::into_raw(Box::new(report))
690}
691
692#[no_mangle]
693pub extern "C" fn ri_health_report_free(report: *mut CRiHealthReport) {
694    if !report.is_null() {
695        unsafe {
696            let _ = Box::from_raw(report);
697        }
698    }
699}
700
701#[repr(C)]
702pub struct CRiErrorChain {
703    inner: RiErrorChain,
704}
705
706#[no_mangle]
707pub extern "C" fn ri_error_chain_new(message: *const c_char) -> *mut CRiErrorChain {
708    if message.is_null() {
709        return std::ptr::null_mut();
710    }
711
712    unsafe {
713        let msg = match std::ffi::CStr::from_ptr(message).to_str() {
714            Ok(s) => s.to_string(),
715            Err(_) => return std::ptr::null_mut(),
716        };
717
718        let chain = crate::core::error_chain::utils::chain_from_msg(msg);
719        Box::into_raw(Box::new(CRiErrorChain { inner: chain }))
720    }
721}
722
723#[no_mangle]
724pub extern "C" fn ri_error_chain_free(chain: *mut CRiErrorChain) {
725    if !chain.is_null() {
726        unsafe {
727            let _ = Box::from_raw(chain);
728        }
729    }
730}
731
732#[no_mangle]
733pub extern "C" fn ri_error_chain_get_context(chain: *const CRiErrorChain) -> *mut c_char {
734    if chain.is_null() {
735        return std::ptr::null_mut();
736    }
737
738    unsafe {
739        let context = (*chain).inner.get_context();
740        match CString::new(context) {
741            Ok(s) => s.into_raw(),
742            Err(_) => std::ptr::null_mut(),
743        }
744    }
745}
746
747#[no_mangle]
748pub extern "C" fn ri_error_chain_pretty_format(chain: *const CRiErrorChain) -> *mut c_char {
749    if chain.is_null() {
750        return std::ptr::null_mut();
751    }
752
753    unsafe {
754        let formatted = (*chain).inner.pretty_format();
755        match CString::new(formatted) {
756            Ok(s) => s.into_raw(),
757            Err(_) => std::ptr::null_mut(),
758        }
759    }
760}
761
762#[repr(C)]
763pub struct CRiLockError {
764    pub context: *mut c_char,
765    pub is_poisoned: bool,
766}
767
768#[no_mangle]
769pub extern "C" fn ri_lock_error_new(context: *const c_char, is_poisoned: bool) -> *mut CRiLockError {
770    if context.is_null() {
771        return std::ptr::null_mut();
772    }
773
774    unsafe {
775        let ctx = match std::ffi::CStr::from_ptr(context).to_str() {
776            Ok(s) => s.to_string(),
777            Err(_) => return std::ptr::null_mut(),
778        };
779
780        let error = if is_poisoned {
781            RiLockError::poisoned(&ctx)
782        } else {
783            RiLockError::new(&ctx)
784        };
785
786        let c_context = match CString::new(error.get_context()) {
787            Ok(s) => s.into_raw(),
788            Err(_) => return std::ptr::null_mut(),
789        };
790
791        Box::into_raw(Box::new(CRiLockError {
792            context: c_context,
793            is_poisoned: error.is_poisoned(),
794        }))
795    }
796}
797
798#[no_mangle]
799pub extern "C" fn ri_lock_error_free(error: *mut CRiLockError) {
800    if !error.is_null() {
801        unsafe {
802            if !(*error).context.is_null() {
803                let _ = CString::from_raw((*error).context);
804            }
805            let _ = Box::from_raw(error);
806        }
807    }
808}
809
810#[no_mangle]
811pub extern "C" fn ri_lock_error_get_context(error: *const CRiLockError) -> *mut c_char {
812    if error.is_null() {
813        return std::ptr::null_mut();
814    }
815
816    unsafe {
817        if (*error).context.is_null() {
818            return std::ptr::null_mut();
819        }
820        match CString::new(std::ffi::CStr::from_ptr((*error).context).to_bytes()) {
821            Ok(s) => s.into_raw(),
822            Err(_) => std::ptr::null_mut(),
823        }
824    }
825}
826
827#[no_mangle]
828pub extern "C" fn ri_lock_error_is_poisoned(error: *const CRiLockError) -> bool {
829    if error.is_null() {
830        return false;
831    }
832    unsafe { (*error).is_poisoned }
833}