Skip to content

Commit 64b17b2

Browse files
committed
Refactor Map and Plugin error handling, improve safety and usability
- Updated Map::new() to return a Result type, handling creation failures. - Enhanced error enumeration in MapError and PluginError for better clarity. - Modified key retrieval and string conversion methods to return Result types, improving error handling. - Adjusted tests to accommodate changes in Map creation and error handling. - Refactored Plugin function invocation to return Result types, ensuring better error propagation. - Removed unnecessary panic calls and replaced them with proper error handling. - Improved iterator methods to handle potential errors gracefully.
1 parent 911cf3c commit 64b17b2

11 files changed

Lines changed: 149 additions & 146 deletions

File tree

rspipe/src/main.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ fn main() {
138138
}
139139
}
140140

141-
let mut vars_map = Map::new();
141+
let mut vars_map = Map::new().unwrap();
142142
for (key, value) in script_args {
143143
if let Err(e) = vars_map.set(&key, &value) {
144144
eprintln!("Failed to set script variable {}: {}", key, e);
@@ -181,11 +181,11 @@ fn main() {
181181
};
182182

183183
// Determine frame range
184-
let start_frame = matches.get_one::<usize>("start").copied().unwrap_or(0);
184+
let start_frame = matches.get_one::<i32>("start").copied().unwrap_or(0);
185185
let end_frame = matches
186-
.get_one::<usize>("end")
186+
.get_one::<i32>("end")
187187
.copied()
188-
.unwrap_or((video_info.num_frames - 1) as usize);
188+
.unwrap_or(video_info.num_frames - 1);
189189

190190
if start_frame > end_frame {
191191
eprintln!("Start frame cannot be greater than end frame");
@@ -236,15 +236,15 @@ fn main() {
236236
fn process_frames_concurrent(
237237
node: &rustsynth::node::Node,
238238
writer: &mut OutputWriter,
239-
start_frame: usize,
240-
end_frame: usize,
239+
start_frame: i32,
240+
end_frame: i32,
241241
num_requests: usize,
242242
progress: &mut ProgressTracker,
243243
) {
244244
use std::sync::mpsc;
245245

246246
let total_frames = end_frame - start_frame + 1;
247-
let (tx, rx) = mpsc::channel::<(usize, Result<rustsynth::frame::Frame, String>)>();
247+
let (tx, rx) = mpsc::channel::<(i32, Result<rustsynth::frame::Frame, String>)>();
248248
let node_clone = node.clone();
249249

250250
// Track pending requests
@@ -254,7 +254,7 @@ fn process_frames_concurrent(
254254
let mut next_request = start_frame;
255255

256256
// Request initial batch
257-
for _ in 0..num_requests.min(total_frames) {
257+
for _ in 0..num_requests.min(total_frames.try_into().unwrap()) {
258258
*pending_requests.lock().unwrap() += 1;
259259
let tx_clone = tx.clone();
260260
let node_clone = node_clone.clone();

rspipe/src/progress.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ use std::io::{self, Write};
22
use std::time::Instant;
33

44
pub struct ProgressTracker {
5-
total_frames: usize,
5+
total_frames: i32,
66
start_time: Instant,
77
last_update: Instant,
88
verbose: bool,
99
}
1010

1111
impl ProgressTracker {
12-
pub fn new(total_frames: usize, verbose: bool) -> Self {
12+
pub fn new(total_frames: i32, verbose: bool) -> Self {
1313
let now = Instant::now();
1414
ProgressTracker {
1515
total_frames,
@@ -19,7 +19,7 @@ impl ProgressTracker {
1919
}
2020
}
2121

22-
pub fn update(&mut self, completed_frames: usize) {
22+
pub fn update(&mut self, completed_frames: i32) {
2323
let now = Instant::now();
2424

2525
// Only update progress every 100ms to avoid spam

rustsynth/src/core.rs

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,41 @@ use crate::{
55
format::VideoFormat,
66
frame::{Frame, FrameContext},
77
log::{log_handler_callback, LogHandle, LogHandler, MessageType},
8-
map::Map,
8+
map::{Map, MapError},
9+
node::Node,
910
plugin::Plugin,
1011
};
1112
use bitflags::bitflags;
12-
use core::fmt;
1313
use rustsynth_sys as ffi;
14+
use std::fmt;
1415
use std::{
15-
ffi::{CStr, CString},
16+
ffi::{CStr, CString, NulError},
1617
marker::PhantomData,
1718
ptr::NonNull,
1819
};
20+
use thiserror::Error;
1921

2022
#[cfg(test)]
2123
mod tests;
2224

25+
/// The error type for `Core` operations.
26+
#[derive(Error, Debug)]
27+
pub enum CoreError {
28+
#[error("Failed to create map: {0}")]
29+
MapCreationFailed(#[from] MapError),
30+
#[error("Invalid filter name: {0}")]
31+
InvalidFilterName(#[from] NulError),
32+
#[error("Failed to create video filter")]
33+
VideoFilterCreationFailed,
34+
#[error("Failed to create audio filter")]
35+
AudioFilterCreationFailed,
36+
#[error("{0}")]
37+
Custom(String),
38+
}
39+
40+
/// A specialized `Result` type for `Core` operations.
41+
pub type CoreResult<T> = Result<T, CoreError>;
42+
2343
bitflags! {
2444
/// Options when creating a core.
2545
pub struct CoreCreationFlags: u32 {
@@ -208,13 +228,13 @@ impl<'core> CoreRef<'core> {
208228
}
209229

210230
/// Create a video filter using the Filter trait
211-
pub fn create_video_filter<F>(&self, filter: &F) -> Result<Map<'_>, String>
231+
pub fn create_video_filter<F>(&self, filter: &F) -> CoreResult<Map<'_>>
212232
where
213233
F: Filter<'core>,
214234
{
215-
let out = Map::new();
235+
let out = Map::new()?;
216236
// Get video info from the filter
217-
let video_info = filter.get_video_info()?;
237+
let video_info = filter.get_video_info().map_err(CoreError::Custom)?;
218238
let dependencies = filter.get_dependencies();
219239

220240
// Convert dependencies to FFI format
@@ -226,7 +246,7 @@ impl<'core> CoreRef<'core> {
226246
let instance_data = Box::into_raw(filter_box) as *mut std::ffi::c_void;
227247

228248
// Create C strings for name
229-
let name_cstr = CString::new(F::NAME).map_err(|_| "Invalid filter name")?;
249+
let name_cstr = CString::new(F::NAME)?;
230250

231251
unsafe {
232252
API::get_cached().create_video_filter(
@@ -247,12 +267,12 @@ impl<'core> CoreRef<'core> {
247267
}
248268

249269
/// Create a video filter using the Filter trait (returns node directly)
250-
pub fn create_video_filter2<F>(&self, filter: &F) -> Result<crate::node::Node<'core>, String>
270+
pub fn create_video_filter2<F>(&self, filter: &F) -> CoreResult<crate::node::Node<'core>>
251271
where
252272
F: Filter<'core>,
253273
{
254274
// Get video info from the filter
255-
let video_info = filter.get_video_info()?;
275+
let video_info = filter.get_video_info().map_err(CoreError::Custom)?;
256276
let dependencies = filter.get_dependencies();
257277

258278
// Convert dependencies to FFI format
@@ -264,7 +284,7 @@ impl<'core> CoreRef<'core> {
264284
let instance_data = Box::into_raw(filter_box) as *mut std::ffi::c_void;
265285

266286
// Create C strings for name
267-
let name_cstr = CString::new(F::NAME).map_err(|_| "Invalid filter name")?;
287+
let name_cstr = CString::new(F::NAME)?;
268288

269289
let node_ptr = unsafe {
270290
API::get_cached().create_video_filter2(
@@ -281,20 +301,20 @@ impl<'core> CoreRef<'core> {
281301
};
282302

283303
if node_ptr.is_null() {
284-
return Err("Failed to create video filter".to_string());
304+
return Err(CoreError::VideoFilterCreationFailed);
285305
}
286306

287307
Ok(unsafe { crate::node::Node::from_ptr(node_ptr) })
288308
}
289309

290310
/// Create a audio filter using the Filter trait
291-
pub fn create_audio_filter<F>(&self, filter: &F) -> Result<Map<'_>, String>
311+
pub fn create_audio_filter<F>(&self, filter: &F) -> CoreResult<Map<'_>>
292312
where
293313
F: Filter<'core>,
294314
{
295-
let out = Map::new();
315+
let out = Map::new()?;
296316
// Get audio info from the filter
297-
let audio_info = filter.get_audio_info()?;
317+
let audio_info = filter.get_audio_info().map_err(CoreError::Custom)?;
298318
let dependencies = filter.get_dependencies();
299319

300320
// Convert dependencies to FFI format
@@ -306,7 +326,7 @@ impl<'core> CoreRef<'core> {
306326
let instance_data = Box::into_raw(filter_box) as *mut std::ffi::c_void;
307327

308328
// Create C strings for name
309-
let name_cstr = CString::new(F::NAME).map_err(|_| "Invalid filter name")?;
329+
let name_cstr = CString::new(F::NAME)?;
310330

311331
unsafe {
312332
API::get_cached().create_audio_filter(
@@ -327,12 +347,12 @@ impl<'core> CoreRef<'core> {
327347
}
328348

329349
/// Create an audio filter using the Filter trait (returns node directly)
330-
pub fn create_audio_filter2<F>(&self, filter: &F) -> Result<crate::node::Node<'core>, String>
350+
pub fn create_audio_filter2<F>(&self, filter: &F) -> CoreResult<Node<'core>>
331351
where
332352
F: Filter<'core>,
333353
{
334354
// Get audio info from the filter
335-
let audio_info = filter.get_audio_info()?;
355+
let audio_info = filter.get_audio_info().map_err(CoreError::Custom)?;
336356
let dependencies = filter.get_dependencies();
337357

338358
// Convert dependencies to FFI format
@@ -344,7 +364,7 @@ impl<'core> CoreRef<'core> {
344364
let instance_data = Box::into_raw(filter_box) as *mut std::ffi::c_void;
345365

346366
// Create C strings for name
347-
let name_cstr = CString::new(F::NAME).map_err(|_| "Invalid filter name")?;
367+
let name_cstr = CString::new(F::NAME)?;
348368

349369
let node_ptr = unsafe {
350370
API::get_cached().create_audio_filter2(
@@ -361,7 +381,7 @@ impl<'core> CoreRef<'core> {
361381
};
362382

363383
if node_ptr.is_null() {
364-
return Err("Failed to create audio filter".to_string());
384+
return Err(CoreError::AudioFilterCreationFailed);
365385
}
366386

367387
Ok(unsafe { crate::node::Node::from_ptr(node_ptr) })

rustsynth/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pub fn api_version() -> i32 {
5959
macro_rules! owned_map {
6060
($({$key:literal: $x:expr }),*) => {
6161
{
62-
let mut temp_map = $crate::map::Map::new();
62+
let mut temp_map = $crate::map::Map::new().unwrap();
6363
$(
6464
temp_map.set($key, $x).unwrap();
6565
)*

rustsynth/src/map/errors.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ pub enum MapError {
1616
InvalidKey(#[from] InvalidKeyError),
1717
#[error("Couldn't convert to a CString")]
1818
CStringConversion(#[from] NulError),
19+
#[error("Failed to create map")]
20+
CreationFailed,
1921
#[error("Unknown error (see Map::error())")]
2022
Error,
23+
#[error("UTF-8 conversion error: {0}")]
24+
Utf8Error(#[from] std::string::FromUtf8Error),
2125
}
2226

2327
impl From<MapError> for String {

rustsynth/src/map/iterators.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl<'map, 'elem> Iterator for Keys<'map, 'elem> {
2929
return None;
3030
}
3131

32-
let key = self.map.key(self.index);
32+
let key = self.map.key(self.index).ok()?;
3333
self.index += 1;
3434
Some(key)
3535
}

rustsynth/src/map/mod.rs

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,14 @@ fn handle_append_prop_error(error: i32) -> MapResult<()> {
8888
}
8989

9090
impl<'elem> Map<'elem> {
91-
pub fn new() -> Self {
91+
pub fn new() -> MapResult<Self> {
9292
let handle = unsafe { API::get_cached().create_map() };
93-
if handle.is_null() {
94-
panic!("Failed to create VSMap");
95-
}
93+
let handle = NonNull::new(handle).ok_or(MapError::CreationFailed)?;
9694

97-
Self {
98-
handle: NonNull::new(handle).unwrap(),
95+
Ok(Self {
96+
handle,
9997
_elem: PhantomData,
100-
}
98+
})
10199
}
102100

103101
pub const fn as_ptr(&self) -> *mut ffi::VSMap {
@@ -150,7 +148,7 @@ impl<'elem> Map<'elem> {
150148
#[inline]
151149
pub(crate) fn make_raw_key(key: &str) -> MapResult<CString> {
152150
Map::is_key_valid(key)?;
153-
Ok(CString::new(key).unwrap())
151+
Ok(CString::new(key)?)
154152
}
155153

156154
/// Clears the map.
@@ -163,14 +161,10 @@ impl<'elem> Map<'elem> {
163161

164162
/// Returns the error message contained in the map, if any.
165163
#[inline]
166-
pub fn error(&'_ self) -> Option<Cow<'_, str>> {
164+
pub fn error(&'_ self) -> Result<&str, std::str::Utf8Error> {
167165
let error_message = unsafe { API::get_cached().map_get_error(self) };
168-
if error_message.is_null() {
169-
return None;
170-
}
171-
172166
let error_message = unsafe { CStr::from_ptr(error_message) };
173-
Some(error_message.to_string_lossy())
167+
error_message.to_str()
174168
}
175169

176170
/// Adds an error message to a map. The map is cleared first.
@@ -208,8 +202,8 @@ impl<'elem> Map<'elem> {
208202
/// # Panics
209203
/// Panics if `index >= self.key_count()`.
210204
#[inline]
211-
pub fn key(&self, index: usize) -> &str {
212-
self.key_raw(index).to_str().unwrap()
205+
pub fn key(&self, index: usize) -> Result<&str, std::str::Utf8Error> {
206+
self.key_raw(index).to_str()
213207
}
214208

215209
/// Returns an iterator over all keys in a map.
@@ -1088,11 +1082,3 @@ impl<'elem> Map<'elem> {
10881082
pub trait IntoOwnedMap {
10891083
fn into_owned_map<'elem>(self) -> Map<'elem>;
10901084
}
1091-
1092-
1093-
impl Default for Map<'_> {
1094-
#[inline]
1095-
fn default() -> Self {
1096-
Self::new()
1097-
}
1098-
}

0 commit comments

Comments
 (0)