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}