Skip to main content

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}