Skip to content

Commit 6d4e417

Browse files
author
toasteater
committed
Improve error handling, explain ZST pointer
Refactored error macros to forward to a high-level interface. Methods and argument errors now contain site information for error reporting. Strings in arguments errors replaced with `Cow<'a, str>` to avoid lifetime issues.
1 parent 958b274 commit 6d4e417

File tree

6 files changed

+338
-111
lines changed

6 files changed

+338
-111
lines changed

gdnative-core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ mod init;
4141
#[cfg(feature = "nativescript")]
4242
pub mod nativescript;
4343

44+
pub mod log;
4445
mod new_ref;
4546
pub mod object;
4647
pub mod ref_kind;

gdnative-core/src/log.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//! Functions for using the engine's logging system in the editor.
2+
use std::ffi::CStr;
3+
use std::fmt::{self, Display};
4+
5+
use crate::core_types::GodotString;
6+
use crate::private;
7+
8+
/// Value representing a call site for errors and warnings. Can be constructed
9+
/// using the `godot_site!` macro, or manually.
10+
#[derive(Copy, Clone, Debug)]
11+
pub struct Site<'a> {
12+
file: &'a CStr,
13+
func: &'a CStr,
14+
line: u32,
15+
}
16+
17+
impl<'a> Site<'a> {
18+
/// Construct a new `Site` value using values provided manually.
19+
#[inline]
20+
pub const fn new(file: &'a CStr, func: &'a CStr, line: u32) -> Self {
21+
Site { file, func, line }
22+
}
23+
}
24+
25+
impl<'a> Default for Site<'a> {
26+
#[inline]
27+
fn default() -> Self {
28+
let unset = unsafe { CStr::from_bytes_with_nul_unchecked(b"<unset>\0") };
29+
Site::new(unset, unset, 0)
30+
}
31+
}
32+
33+
impl<'a> Display for Site<'a> {
34+
#[inline]
35+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36+
write!(
37+
f,
38+
"file {}, {}, line {}",
39+
self.file.to_string_lossy(),
40+
self.func.to_string_lossy(),
41+
self.line
42+
)
43+
}
44+
}
45+
46+
/// Print a message to the Godot console.
47+
///
48+
/// # Panics
49+
///
50+
/// If the API isn't initialized.
51+
#[inline]
52+
pub fn print<S: Display>(msg: S) {
53+
unsafe {
54+
let msg = GodotString::from_str(&msg.to_string());
55+
(private::get_api().godot_print)(&msg.to_sys() as *const _);
56+
}
57+
}
58+
59+
/// Print a warning to the Godot console.
60+
///
61+
/// # Panics
62+
///
63+
/// If the API isn't initialized, or if the message contains any NUL-bytes.
64+
#[inline]
65+
pub fn warn<S: Display>(site: Site<'_>, msg: S) {
66+
let msg = msg.to_string();
67+
let msg = ::std::ffi::CString::new(msg).unwrap();
68+
69+
unsafe {
70+
(private::get_api().godot_print_warning)(
71+
msg.as_ptr(),
72+
site.func.as_ptr(),
73+
site.file.as_ptr(),
74+
site.line as libc::c_int,
75+
);
76+
}
77+
}
78+
79+
/// Print an error to the Godot console.
80+
///
81+
/// # Panics
82+
///
83+
/// If the API isn't initialized, or if the message contains any NUL-bytes.
84+
#[inline]
85+
pub fn error<S: Display>(site: Site<'_>, msg: S) {
86+
let msg = msg.to_string();
87+
let msg = ::std::ffi::CString::new(msg).unwrap();
88+
89+
unsafe {
90+
(private::get_api().godot_print_error)(
91+
msg.as_ptr(),
92+
site.func.as_ptr(),
93+
site.file.as_ptr(),
94+
site.line as libc::c_int,
95+
);
96+
}
97+
}

gdnative-core/src/macros.rs

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,7 @@ macro_rules! godot_gdnative_terminate {
102102
#[macro_export]
103103
macro_rules! godot_print {
104104
($($args:tt)*) => ({
105-
let msg = format!($($args)*);
106-
107-
#[allow(unused_unsafe)]
108-
unsafe {
109-
let msg = $crate::core_types::GodotString::from_str(msg);
110-
($crate::private::get_api().godot_print)(&msg.to_sys() as *const _);
111-
}
105+
$crate::log::print(::std::format_args!($($args)*));
112106
});
113107
}
114108

@@ -139,6 +133,51 @@ macro_rules! godot_dbg {
139133
};
140134
}
141135

136+
/// Creates a `gdnative::log::Site` value from the current position in code,
137+
/// optionally with a function path for identification.
138+
///
139+
/// # Examples
140+
///
141+
/// ```ignore
142+
/// // WARN: <unset>: foo At: path/to/file.rs:123
143+
/// gdnative::log::warn(godot_site!(), "foo");
144+
/// // WARN: Foo::my_func: bar At: path/to/file.rs:123
145+
/// gdnative::log::error(godot_site!(Foo::my_func), "bar");
146+
/// ```
147+
#[macro_export]
148+
macro_rules! godot_site {
149+
() => {{
150+
// SAFETY: I guess we can assume that all sane file systems don't allow
151+
// NUL-bytes in paths?
152+
#[allow(unused_unsafe)]
153+
let site: $crate::log::Site<'static> = unsafe {
154+
let file = ::std::ffi::CStr::from_bytes_with_nul_unchecked(
155+
::std::concat!(::std::file!(), "\0").as_bytes(),
156+
);
157+
let func = ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"<unset>\0");
158+
$crate::log::Site::new(file, func, ::std::line!())
159+
};
160+
161+
site
162+
}};
163+
($($path:tt)+) => {{
164+
// SAFETY: I guess we can assume that all sane file systems don't allow
165+
// NUL-bytes in paths?
166+
#[allow(unused_unsafe)]
167+
let site: $crate::log::Site<'static> = unsafe {
168+
let file = ::std::ffi::CStr::from_bytes_with_nul_unchecked(
169+
::std::concat!(::std::file!(), "\0").as_bytes(),
170+
);
171+
let func = ::std::ffi::CStr::from_bytes_with_nul_unchecked(
172+
::std::concat!(::std::stringify!($($path)+), "\0").as_bytes(),
173+
);
174+
$crate::log::Site::new(file, func, ::std::line!())
175+
};
176+
177+
site
178+
}};
179+
}
180+
142181
/// Print a warning using the engine's logging system (visible in the editor).
143182
///
144183
/// # Guarantees
@@ -150,22 +189,8 @@ macro_rules! godot_dbg {
150189
#[macro_export]
151190
macro_rules! godot_warn {
152191
($($args:tt)*) => ({
153-
let msg = format!($($args)*);
154-
let line = line!();
155-
let file = file!();
156-
#[allow(unused_unsafe)]
157-
unsafe {
158-
let msg = ::std::ffi::CString::new(msg).unwrap();
159-
let file = ::std::ffi::CString::new(file).unwrap();
160-
let func = b"<native>\0";
161-
($crate::private::get_api().godot_print_warning)(
162-
msg.as_ptr() as *const _,
163-
func.as_ptr() as *const _,
164-
file.as_ptr() as *const _,
165-
line as _,
166-
);
167-
}
168-
})
192+
$crate::log::warn($crate::godot_site!(), ::std::format_args!($($args)*));
193+
});
169194
}
170195

171196
/// Print an error using the engine's logging system (visible in the editor).
@@ -179,22 +204,8 @@ macro_rules! godot_warn {
179204
#[macro_export]
180205
macro_rules! godot_error {
181206
($($args:tt)*) => ({
182-
let msg = format!($($args)*);
183-
let line = line!();
184-
let file = file!();
185-
#[allow(unused_unsafe)]
186-
unsafe {
187-
let msg = ::std::ffi::CString::new(msg).unwrap();
188-
let file = ::std::ffi::CString::new(file).unwrap();
189-
let func = b"<native>\0";
190-
($crate::private::get_api().godot_print_error)(
191-
msg.as_ptr() as *const _,
192-
func.as_ptr() as *const _,
193-
file.as_ptr() as *const _,
194-
line as _,
195-
);
196-
}
197-
})
207+
$crate::log::error($crate::godot_site!(), ::std::format_args!($($args)*));
208+
});
198209
}
199210

200211
macro_rules! impl_basic_trait_as_sys {

0 commit comments

Comments
 (0)