Skip to content

Commit a67210e

Browse files
committed
error handling
1 parent 1a8ae07 commit a67210e

File tree

3 files changed

+75
-67
lines changed

3 files changed

+75
-67
lines changed

rustecal-core/src/core.rs

Lines changed: 47 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -14,117 +14,97 @@
1414
//! application and [`Ecal::finalize`] at shutdown.
1515
1616
use std::ffi::{CStr, CString};
17+
use std::ptr;
18+
1719
use crate::components::EcalComponents;
20+
use crate::error::{check, RustecalError};
1821
use crate::types::Version;
1922

20-
/// Provides access to the core initialization, shutdown, and state-checking functions of eCAL.
23+
/// Provides access to the core initialization, shutdown, and statechecking functions of eCAL.
2124
pub struct Ecal;
2225

2326
impl Ecal {
2427
/// Initializes the eCAL runtime system.
2528
///
26-
/// This function must be called before using any publisher, subscriber, or service functionality.
27-
///
2829
/// # Arguments
2930
///
30-
/// * `unit_name` - Optional name to identify this process in eCAL (e.g. in monitoring).
31-
/// * `components` - Bitmask of which subsystems (e.g. pub/sub, monitoring) to enable.
32-
///
33-
/// # Returns
34-
///
35-
/// Returns `Ok(())` on success, or `Err(code)` with a non-zero error code.
36-
pub fn initialize(unit_name: Option<&str>, components: EcalComponents) -> Result<(), i32> {
37-
let cstr = unit_name.map(|s| CString::new(s).unwrap());
38-
let ptr = cstr.as_ref().map_or(std::ptr::null(), |c| c.as_ptr());
39-
40-
let result = unsafe {
41-
rustecal_sys::eCAL_Initialize(ptr, &components.bits(), std::ptr::null())
31+
/// * `unit_name` – Optional name to identify this process in eCAL.
32+
/// * `components` – Bitmask of which subsystems to enable.
33+
///
34+
/// # Errors
35+
///
36+
/// Returns `Err(RustecalError::Ecal{..})` on any non‑zero C return code,
37+
/// or `RustecalError::Internal` if the unit name contains an interior NUL.
38+
pub fn initialize(
39+
unit_name: Option<&str>,
40+
components: EcalComponents,
41+
) -> Result<(), RustecalError> {
42+
// Convert the unit name (if any), mapping CString errors
43+
let (name_ptr, _): ( *const i8, Option<CString> ) = if let Some(name) = unit_name {
44+
let c = CString::new(name)
45+
.map_err(|e| RustecalError::Internal(format!("invalid unit name: {}", e)))?;
46+
(c.as_ptr(), Some(c))
47+
} else {
48+
(ptr::null(), None)
4249
};
4350

44-
if result == 0 {
45-
Ok(())
46-
} else {
47-
Err(result)
48-
}
51+
// Call the C API and map its return code
52+
let ret = unsafe { rustecal_sys::eCAL_Initialize(name_ptr, &components.bits(), ptr::null()) };
53+
check(ret)
4954
}
5055

5156
/// Finalizes and shuts down the eCAL runtime system.
5257
///
53-
/// After calling this function, all publishers, subscribers, and services are invalidated.
58+
/// After calling this, all publishers, subscribers, and services are invalidated.
5459
pub fn finalize() {
5560
unsafe {
5661
rustecal_sys::eCAL_Finalize();
5762
}
5863
}
5964

60-
/// Checks if the eCAL system is initialized and running properly.
61-
///
62-
/// This can be used as the main loop condition in long-running processes.
63-
///
64-
/// # Returns
65-
///
66-
/// `true` if the system is operational, `false` otherwise.
65+
/// Returns `true` if the eCAL system is currently operational.
6766
pub fn ok() -> bool {
6867
unsafe { rustecal_sys::eCAL_Ok() != 0 }
6968
}
7069

71-
/// Checks if the eCAL system has been initialized.
72-
///
73-
/// This function checks whether any components of the middleware have been initialized.
74-
///
75-
/// # Returns
76-
///
77-
/// `true` if initialization has occurred, `false` otherwise.
70+
/// Returns `true` if *any* eCAL components have been initialized.
7871
pub fn is_initialized() -> bool {
7972
unsafe { rustecal_sys::eCAL_IsInitialized() != 0 }
8073
}
8174

82-
/// Checks if specific components of eCAL are initialized.
83-
///
84-
/// This allows querying the status of individual middleware components like pub/sub, monitoring, etc.
85-
///
86-
/// # Arguments
87-
///
88-
/// * `components` - Bitmask of components to check.
89-
///
90-
/// # Returns
91-
///
92-
/// `true` if the given components are initialized, `false` otherwise.
75+
/// Returns `true` if the specified components are initialized.
9376
pub fn is_component_initialized(components: EcalComponents) -> bool {
9477
unsafe { rustecal_sys::eCAL_IsComponentInitialized(components.bits()) != 0 }
9578
}
9679

97-
/// Returns the version string of the eCAL runtime.
98-
///
99-
/// # Returns
80+
/// Returns the eCAL version string (e.g. `"6.0.0"`).
10081
///
101-
/// A static string slice with the full version string, e.g. `"5.11.0"`.
82+
/// This is infallible: if the C pointer is null or contains invalid UTF‑8,
83+
/// it returns `"unknown"`.
10284
pub fn version_string() -> &'static str {
103-
unsafe {
104-
CStr::from_ptr(rustecal_sys::eCAL_GetVersionString())
105-
.to_str()
106-
.unwrap_or("unknown")
85+
// SAFETY: eCAL guarantees a static, valid C string here.
86+
let ptr = unsafe { rustecal_sys::eCAL_GetVersionString() };
87+
if ptr.is_null() {
88+
"unknown"
89+
} else {
90+
unsafe { CStr::from_ptr(ptr).to_str().unwrap_or("unknown") }
10791
}
10892
}
10993

110-
/// Returns the build date string of the eCAL runtime.
94+
/// Returns the eCAL build‑date string (e.g. `"01.05.2025"`).
11195
///
112-
/// # Returns
113-
///
114-
/// A static string slice with the build date string, e.g. `"Apr 2025"`.
96+
/// This is infallible: if the C pointer is null or contains invalid UTF‑8,
97+
/// it returns `"unknown"`.
11598
pub fn version_date_string() -> &'static str {
116-
unsafe {
117-
CStr::from_ptr(rustecal_sys::eCAL_GetVersionDateString())
118-
.to_str()
119-
.unwrap_or("unknown")
99+
let ptr = unsafe { rustecal_sys::eCAL_GetVersionDateString() };
100+
if ptr.is_null() {
101+
"unknown"
102+
} else {
103+
unsafe { CStr::from_ptr(ptr).to_str().unwrap_or("unknown") }
120104
}
121105
}
122106

123-
/// Returns the version of the eCAL runtime as structured integers.
124-
///
125-
/// # Returns
126-
///
127-
/// A [`Version`] struct with fields `major`, `minor`, and `patch`.
107+
/// Returns the eCAL version as a structured `Version { major, minor, patch }`.
128108
pub fn version_struct() -> Version {
129109
unsafe { rustecal_sys::eCAL_GetVersion().into() }
130110
}

rustecal-core/src/error.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use thiserror::Error;
2+
3+
/// All error types returned by rustecal‑core.
4+
#[derive(Debug, Error)]
5+
pub enum RustecalError {
6+
/// A non‑zero return code from the eCAL C API.
7+
#[error("eCAL error code {0}")]
8+
Ecal(i32),
9+
10+
/// Unexpected null pointer from C.
11+
#[error("unexpected null pointer")]
12+
NullPointer,
13+
14+
/// A catch‑all for any other internal Rust error.
15+
#[error("internal error: {0}")]
16+
Internal(String),
17+
}
18+
19+
/// Check a C return code: `0` → `Ok(())`, non‑zero → `Err(RustecalError::Ecal)`.
20+
pub fn check(code: i32) -> Result<(), RustecalError> {
21+
if code == 0 {
22+
Ok(())
23+
} else {
24+
Err(RustecalError::Ecal(code))
25+
}
26+
}

rustecal-core/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
1212
pub mod core;
1313
pub mod components;
14+
pub mod error;
1415
pub mod types;
1516
pub mod log;
1617
pub mod log_level;
@@ -19,6 +20,7 @@ pub mod core_types;
1920
// Re‑exports for ergonomic access:
2021
pub use core::Ecal;
2122
pub use components::EcalComponents;
23+
pub use error::RustecalError;
2224
pub use log::Log;
2325
pub use log_level::LogLevel;
2426
pub use core_types::logging::LogMessage;

0 commit comments

Comments
 (0)