summaryrefslogtreecommitdiff
path: root/src/c_api/messages.rs
blob: 5b227cb80fde6d7a818fe64d26b6da0222e18ba3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//! Logging functions, plus Rust versions of `g_return_if_fail()`.
//!
//! Glib's `g_return_if_fail()`, `g_warning()`, etc. are all C macros, so they cannot be
//! used from Rust.  This module defines equivalent functions or macros with an `rsvg_`
//! prefix, to be clear that they should only be used from the implementation of the C API
//! and not from the main Rust code of the library.

use glib::ffi::{g_log_structured_array, GLogField, G_LOG_LEVEL_CRITICAL, G_LOG_LEVEL_WARNING};
use glib::translate::*;

/*
  G_LOG_LEVEL_CRITICAL          = 1 << 3,
  G_LOG_LEVEL_WARNING           = 1 << 4,

#define g_critical(...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
                                                   __FILE__, G_STRINGIFY (__LINE__), \
                                                   G_STRFUNC, __VA_ARGS__)
#define g_warning(...)  g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
                                                   __FILE__, G_STRINGIFY (__LINE__), \
                                                   G_STRFUNC, __VA_ARGS__)
  GLogField fields[] =
    {
      { "PRIORITY", log_level_to_priority (log_level), -1 },
      { "CODE_FILE", file, -1 },
      { "CODE_LINE", line, -1 },
      { "CODE_FUNC", func, -1 },
      /* Filled in later: */
      { "MESSAGE", NULL, -1 },
      /* If @log_domain is %NULL, we will not pass this field: */
      { "GLIB_DOMAIN", log_domain, -1 },
    };

  g_log_structured_array (log_level, fields, n_fields);
 */

/// Helper function for converting string literals to C char pointers.
#[macro_export]
macro_rules! rsvg_c_str {
    ($txt:expr) => {
        // SAFETY: it's important that the type we pass to `from_bytes_with_nul` is 'static,
        // so that the storage behind the returned pointer doesn't get freed while it's still
        // being used. We get that by only allowing string literals.
        std::ffi::CStr::from_bytes_with_nul(concat!($txt, "\0").as_bytes())
            .unwrap()
            .as_ptr()
    };
}

/// Helper for `rsvg_g_warning` and `rsvg_g_critical`
///
/// This simulates what in C would be a call to the g_warning() or g_critical()
/// macros, but with the underlying function g_log_structured_array().
///
/// If the implementation of g_warning() or g_critical() changes, we'll have
/// to change this function.
fn rsvg_g_log(level: glib::ffi::GLogLevelFlags, msg: &str) {
    // stolen from gmessages.c:log_level_to_priority()
    let priority = match level {
        G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL => rsvg_c_str!("4"),
        _ => unreachable!("please add another log level priority to rsvg_g_log()"),
    };

    let c_msg = msg.to_glib_none();
    let c_char_msg: *const libc::c_char = c_msg.0;

    // Glib's g_log_structured_standard() adds a few more fields for the source
    // file, line number, etc., but those are not terribly useful without a stack
    // trace.  So, we'll omit them.
    let fields = [
        GLogField {
            key: rsvg_c_str!("PRIORITY"),
            value: priority as *const _,
            length: -1,
        },
        GLogField {
            key: rsvg_c_str!("MESSAGE"),
            value: c_char_msg as *const _,
            length: msg.len() as _,
        },
        // This is the G_LOG_DOMAIN set from the Makefile
        GLogField {
            key: rsvg_c_str!("GLIB_DOMAIN"),
            value: rsvg_c_str!("librsvg") as *const _,
            length: -1,
        },
    ];

    unsafe {
        g_log_structured_array(level, fields.as_ptr(), fields.len());
    }
}

/// Replacement for `g_warning()`.
///
/// Use this to signal an error condition in the following situations:
///
/// * The C API does not have an adequate error code for the error in question (and cannot
///   be changed to have one, for ABI compatibility reasons).
///
/// * Applications using the C API would be prone to ignoring an important error,
///   so it's best to have a warning on the console to at least have a hope of someone
///   noticing the error.
pub(crate) fn rsvg_g_warning(msg: &str) {
    rsvg_g_log(glib::ffi::G_LOG_LEVEL_WARNING, msg);
}

/// Replacement for `g_critical()`.
///
/// Use this to signal a programming error from the caller of the C API, like passing
/// incorrect arguments or calling the API out of order.  Rust code conventionally panics
/// in such situations, but C/Glib code does not, so it's best to "do nothing", print a
/// critical message, and return.  Development versions of GNOME will crash the program
/// if this happens; release versions will ignore the error.
pub(crate) fn rsvg_g_critical(msg: &str) {
    rsvg_g_log(glib::ffi::G_LOG_LEVEL_CRITICAL, msg);
}

/// Replacement for `g_return_if_fail()`.
// Once Rust has a function! macro that gives us the current function name, we
// can remove the $func_name argument.
#[macro_export]
macro_rules! rsvg_return_if_fail {
    {
        $func_name:ident;
        $($condition:expr,)+
    } => {
        $(
            if !$condition {
                glib::ffi::g_return_if_fail_warning(
                    rsvg_c_str!("librsvg"),
                    rsvg_c_str!(stringify!($func_name)),
                    rsvg_c_str!(stringify!($condition)),
                );
                return;
            }
        )+
    }
}

/// Replacement for `g_return_val_if_fail()`.
#[macro_export]
macro_rules! rsvg_return_val_if_fail {
    {
        $func_name:ident => $retval:expr;
        $($condition:expr,)+
    } => {
        $(
            if !$condition {
                glib::ffi::g_return_if_fail_warning(
                    rsvg_c_str!("librsvg"),
                    rsvg_c_str!(stringify!($func_name)),
                    rsvg_c_str!(stringify!($condition)),
                );
                return $retval;
            }
        )+
    }
}