openjph_core/message.rs
1//! Message handling system — port of `ojph_message.h/cpp`.
2//!
3//! The C++ library uses a global mutable message handler with three severity
4//! levels. This module provides a similar mechanism using a trait object
5//! behind a [`std::sync::Mutex`].
6
7use std::sync::Mutex;
8
9// ---------------------------------------------------------------------------
10// Severity level
11// ---------------------------------------------------------------------------
12
13/// Message severity level.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum MsgLevel {
16 /// Informational message.
17 Info = 0,
18 /// Warning — non-fatal but potentially problematic.
19 Warn = 1,
20 /// Error — the operation cannot continue.
21 Error = 2,
22}
23
24// ---------------------------------------------------------------------------
25// Handler trait
26// ---------------------------------------------------------------------------
27
28/// Trait for receiving diagnostic messages from the codec.
29///
30/// Implement this to redirect messages to a custom logging framework.
31pub trait MessageHandler: Send + Sync {
32 /// Handle a diagnostic message.
33 fn handle(&self, level: MsgLevel, code: u32, msg: &str);
34}
35
36/// Default handler that writes to stderr.
37struct StderrHandler;
38
39impl MessageHandler for StderrHandler {
40 fn handle(&self, level: MsgLevel, code: u32, msg: &str) {
41 let tag = match level {
42 MsgLevel::Info => "INFO",
43 MsgLevel::Warn => "WARN",
44 MsgLevel::Error => "ERROR",
45 };
46 eprintln!("[openjph {}] (0x{:08x}) {}", tag, code, msg);
47 }
48}
49
50// ---------------------------------------------------------------------------
51// Global handler
52// ---------------------------------------------------------------------------
53
54static HANDLER: Mutex<Option<Box<dyn MessageHandler>>> = Mutex::new(None);
55
56/// Dispatches a message to the currently installed handler.
57pub fn dispatch_message(level: MsgLevel, code: u32, msg: &str) {
58 let guard = HANDLER.lock().unwrap();
59 match guard.as_ref() {
60 Some(h) => h.handle(level, code, msg),
61 None => {
62 // Fallback: use the default stderr handler.
63 StderrHandler.handle(level, code, msg);
64 }
65 }
66}
67
68/// Replaces the global message handler.
69///
70/// Pass `None` to restore the default stderr handler.
71pub fn set_message_handler(handler: Option<Box<dyn MessageHandler>>) {
72 let mut guard = HANDLER.lock().unwrap();
73 *guard = handler;
74}
75
76// ---------------------------------------------------------------------------
77// Convenience macros
78// ---------------------------------------------------------------------------
79
80/// Emit an informational message.
81#[macro_export]
82macro_rules! ojph_info {
83 ($code:expr, $($arg:tt)*) => {
84 $crate::message::dispatch_message(
85 $crate::message::MsgLevel::Info,
86 $code,
87 &format!($($arg)*),
88 )
89 };
90}
91
92/// Emit a warning message.
93#[macro_export]
94macro_rules! ojph_warn {
95 ($code:expr, $($arg:tt)*) => {
96 $crate::message::dispatch_message(
97 $crate::message::MsgLevel::Warn,
98 $code,
99 &format!($($arg)*),
100 )
101 };
102}
103
104/// Emit an error message and return an `OjphError::Codec`.
105#[macro_export]
106macro_rules! ojph_error {
107 ($code:expr, $($arg:tt)*) => {{
108 let msg = format!($($arg)*);
109 $crate::message::dispatch_message(
110 $crate::message::MsgLevel::Error,
111 $code,
112 &msg,
113 );
114 $crate::error::OjphError::Codec { code: $code, message: msg }
115 }};
116}