SttError -> Error

This commit is contained in:
Emil Lerch 2025-10-01 10:03:47 -07:00
parent e2ffff8130
commit d818a5fc82
Signed by: lobo
GPG key ID: A7B62D657EF764F8
3 changed files with 164 additions and 164 deletions

View file

@ -174,7 +174,7 @@ const SpeechHandler = struct {
} }
/// Handle basic errors (fallback for compatibility) /// Handle basic errors (fallback for compatibility)
fn onError(ctx: *anyopaque, error_code: stt.SttError, message: []const u8) void { fn onError(ctx: *anyopaque, error_code: stt.Error, message: []const u8) void {
if (builtin.is_test) return; // Suppress output during tests if (builtin.is_test) return; // Suppress output during tests
const self: *SpeechHandler = @ptrCast(@alignCast(ctx)); const self: *SpeechHandler = @ptrCast(@alignCast(ctx));
@ -186,14 +186,14 @@ const SpeechHandler = struct {
} }
/// Handle detailed errors with comprehensive information /// Handle detailed errors with comprehensive information
fn onDetailedError(ctx: *anyopaque, error_info: stt.SttErrorInfo) void { fn onDetailedError(ctx: *anyopaque, error_info: stt.ErrorInfo) void {
const self: *SpeechHandler = @ptrCast(@alignCast(ctx)); const self: *SpeechHandler = @ptrCast(@alignCast(ctx));
logDetail(self, error_info) catch |e| logDetail(self, error_info) catch |e|
std.log.err("Error writing error {}. Original message: {s}", .{ e, error_info.message }); std.log.err("Error writing error {}. Original message: {s}", .{ e, error_info.message });
} }
fn logDetail(self: *SpeechHandler, error_info: stt.SttErrorInfo) !void { fn logDetail(self: *SpeechHandler, error_info: stt.ErrorInfo) !void {
const log = std.log.scoped(.stt); const log = std.log.scoped(.stt);
// Categorize the error for statistics // Categorize the error for statistics
if (error_info.recoverable) if (error_info.recoverable)
@ -234,14 +234,14 @@ const SpeechHandler = struct {
// Determine and call appropriate log function once // Determine and call appropriate log function once
switch (error_info.error_code) { switch (error_info.error_code) {
stt.SttError.InternalError => if (error_info.recoverable) { stt.Error.InternalError => if (error_info.recoverable) {
log.info("{s}", .{message}); log.info("{s}", .{message});
} else { } else {
log.warn("{s}", .{message}); log.warn("{s}", .{message});
}, },
stt.SttError.OutOfMemory, stt.Error.OutOfMemory,
stt.SttError.ModelLoadError, stt.Error.ModelLoadError,
stt.SttError.InitializationFailed, stt.Error.InitializationFailed,
=> log.err("{s}", .{message}), => log.err("{s}", .{message}),
else => if (error_info.recoverable) else => if (error_info.recoverable)
log.warn("{s}", .{message}) log.warn("{s}", .{message})
@ -430,13 +430,13 @@ pub fn main() !void {
session.start_listening() catch |err| { session.start_listening() catch |err| {
std.log.err("Failed to start listening: {}", .{err}); std.log.err("Failed to start listening: {}", .{err});
switch (err) { switch (err) {
stt.SttError.AudioDeviceError => { stt.Error.AudioDeviceError => {
std.log.err("Audio device error. Please check:", .{}); std.log.err("Audio device error. Please check:", .{});
std.log.err(" - Device '{s}' exists and is accessible", .{options.audio_device}); std.log.err(" - Device '{s}' exists and is accessible", .{options.audio_device});
std.log.err(" - No other application is using the device", .{}); std.log.err(" - No other application is using the device", .{});
std.log.err(" - You have permission to access audio devices", .{}); std.log.err(" - You have permission to access audio devices", .{});
}, },
stt.SttError.ThreadingError => { stt.Error.ThreadingError => {
std.log.err("Threading error. System may be under heavy load.", .{}); std.log.err("Threading error. System may be under heavy load.", .{});
}, },
else => { else => {
@ -494,7 +494,7 @@ test "handler callbacks" {
// Test that callbacks can be invoked without crashing // Test that callbacks can be invoked without crashing
speech_handler.onSpeech("test speech"); speech_handler.onSpeech("test speech");
speech_handler.onError(stt.SttError.AudioDeviceError, "test error"); speech_handler.onError(stt.Error.AudioDeviceError, "test error");
// If we get here without crashing, the test passes // If we get here without crashing, the test passes
try testing.expect(true); try testing.expect(true);

View file

@ -10,7 +10,7 @@ const c = @cImport({
}); });
/// Core error types for the STT library /// Core error types for the STT library
pub const SttError = error{ pub const Error = error{
/// Failed to initialize the library (model loading, audio setup, etc.) /// Failed to initialize the library (model loading, audio setup, etc.)
InitializationFailed, InitializationFailed,
/// Audio device access or configuration error /// Audio device access or configuration error
@ -57,9 +57,9 @@ pub const SttError = error{
}; };
/// Detailed error information structure /// Detailed error information structure
pub const SttErrorInfo = struct { pub const ErrorInfo = struct {
/// The error code /// The error code
error_code: SttError, error_code: Error,
/// Human-readable error message /// Human-readable error message
message: []const u8, message: []const u8,
/// Optional system error code (errno, ALSA error, etc.) /// Optional system error code (errno, ALSA error, etc.)
@ -74,8 +74,8 @@ pub const SttErrorInfo = struct {
recovery_suggestion: ?[]const u8 = null, recovery_suggestion: ?[]const u8 = null,
/// Create a new error info structure /// Create a new error info structure
pub fn init(error_code: SttError, message: []const u8) SttErrorInfo { pub fn init(error_code: Error, message: []const u8) ErrorInfo {
return SttErrorInfo{ return ErrorInfo{
.error_code = error_code, .error_code = error_code,
.message = message, .message = message,
.timestamp = std.time.timestamp(), .timestamp = std.time.timestamp(),
@ -83,8 +83,8 @@ pub const SttErrorInfo = struct {
} }
/// Create error info with system error code /// Create error info with system error code
pub fn initWithSystemError(error_code: SttError, message: []const u8, system_error: i32) SttErrorInfo { pub fn initWithSystemError(error_code: Error, message: []const u8, system_error: i32) ErrorInfo {
return SttErrorInfo{ return ErrorInfo{
.error_code = error_code, .error_code = error_code,
.message = message, .message = message,
.system_error = system_error, .system_error = system_error,
@ -93,8 +93,8 @@ pub const SttErrorInfo = struct {
} }
/// Create error info with context /// Create error info with context
pub fn initWithContext(error_code: SttError, message: []const u8, context: []const u8) SttErrorInfo { pub fn initWithContext(error_code: Error, message: []const u8, context: []const u8) ErrorInfo {
return SttErrorInfo{ return ErrorInfo{
.error_code = error_code, .error_code = error_code,
.message = message, .message = message,
.context = context, .context = context,
@ -103,8 +103,8 @@ pub const SttErrorInfo = struct {
} }
/// Create recoverable error info with suggestion /// Create recoverable error info with suggestion
pub fn initRecoverable(error_code: SttError, message: []const u8, suggestion: []const u8) SttErrorInfo { pub fn initRecoverable(error_code: Error, message: []const u8, suggestion: []const u8) ErrorInfo {
return SttErrorInfo{ return ErrorInfo{
.error_code = error_code, .error_code = error_code,
.message = message, .message = message,
.timestamp = std.time.timestamp(), .timestamp = std.time.timestamp(),
@ -127,14 +127,14 @@ pub const SpeechCallback = *const fn (text: [*:0]const u8, user_data: ?*anyopaqu
/// - error_code: The specific error that occurred /// - error_code: The specific error that occurred
/// - message: Null-terminated string with error details /// - message: Null-terminated string with error details
/// - user_data: Optional user-provided context data /// - user_data: Optional user-provided context data
pub const ErrorCallback = *const fn (error_code: SttError, message: [*:0]const u8, user_data: ?*anyopaque) void; pub const ErrorCallback = *const fn (error_code: Error, message: [*:0]const u8, user_data: ?*anyopaque) void;
/// Enhanced callback function type for detailed error events /// Enhanced callback function type for detailed error events
/// ///
/// Parameters: /// Parameters:
/// - error_info: Detailed error information structure /// - error_info: Detailed error information structure
/// - user_data: Optional user-provided context data /// - user_data: Optional user-provided context data
pub const DetailedErrorCallback = *const fn (error_info: SttErrorInfo, user_data: ?*anyopaque) void; pub const DetailedErrorCallback = *const fn (error_info: ErrorInfo, user_data: ?*anyopaque) void;
/// Speech event handler interface pattern /// Speech event handler interface pattern
/// ///
@ -144,9 +144,9 @@ pub const SpeechEventHandler = struct {
/// Function to call when speech is detected /// Function to call when speech is detected
onSpeechFn: *const fn (ctx: *anyopaque, text: []const u8) void, onSpeechFn: *const fn (ctx: *anyopaque, text: []const u8) void,
/// Function to call when an error occurs /// Function to call when an error occurs
onErrorFn: *const fn (ctx: *anyopaque, error_code: SttError, message: []const u8) void, onErrorFn: *const fn (ctx: *anyopaque, error_code: Error, message: []const u8) void,
/// Optional function to call for detailed error information /// Optional function to call for detailed error information
onDetailedErrorFn: ?*const fn (ctx: *anyopaque, error_info: SttErrorInfo) void = null, onDetailedErrorFn: ?*const fn (ctx: *anyopaque, error_info: ErrorInfo) void = null,
/// Context pointer passed to callback functions /// Context pointer passed to callback functions
ctx: *anyopaque, ctx: *anyopaque,
@ -159,12 +159,12 @@ pub const SpeechEventHandler = struct {
} }
/// Invoke the error callback /// Invoke the error callback
pub fn onError(self: SpeechEventHandler, error_code: SttError, message: []const u8) void { pub fn onError(self: SpeechEventHandler, error_code: Error, message: []const u8) void {
self.onErrorFn(self.ctx, error_code, message); self.onErrorFn(self.ctx, error_code, message);
} }
/// Invoke the detailed error callback with comprehensive error information /// Invoke the detailed error callback with comprehensive error information
pub fn onDetailedError(self: SpeechEventHandler, error_info: SttErrorInfo) void { pub fn onDetailedError(self: SpeechEventHandler, error_info: ErrorInfo) void {
if (self.onDetailedErrorFn) |detailed_fn| { if (self.onDetailedErrorFn) |detailed_fn| {
detailed_fn(self.ctx, error_info); detailed_fn(self.ctx, error_info);
} else { } else {
@ -174,7 +174,7 @@ pub const SpeechEventHandler = struct {
} }
/// Internal helper to report errors with proper fallback /// Internal helper to report errors with proper fallback
fn reportError(self: SpeechEventHandler, error_code: SttError, error_info: SttErrorInfo) void { fn reportError(self: SpeechEventHandler, error_code: Error, error_info: ErrorInfo) void {
if (self.onDetailedErrorFn) |detailed_fn| { if (self.onDetailedErrorFn) |detailed_fn| {
detailed_fn(self.ctx, error_info); detailed_fn(self.ctx, error_info);
} else { } else {
@ -379,7 +379,7 @@ pub const AlsaCapture = struct {
pub fn open(self: *Self) !void { pub fn open(self: *Self) !void {
// Convert device name to null-terminated string // Convert device name to null-terminated string
const device_cstr = self.allocator.dupeZ(u8, self.device_name) catch { const device_cstr = self.allocator.dupeZ(u8, self.device_name) catch {
return SttError.OutOfMemory; return Error.OutOfMemory;
}; };
defer self.allocator.free(device_cstr); defer self.allocator.free(device_cstr);
@ -387,12 +387,12 @@ pub const AlsaCapture = struct {
var err = c.snd_pcm_open(&self.pcm_handle, device_cstr.ptr, c.SND_PCM_STREAM_CAPTURE, 0); var err = c.snd_pcm_open(&self.pcm_handle, device_cstr.ptr, c.SND_PCM_STREAM_CAPTURE, 0);
if (err < 0) { if (err < 0) {
return switch (err) { return switch (err) {
-c.ENOENT => SttError.AudioDeviceNotFound, -c.ENOENT => Error.AudioDeviceNotFound,
-c.EBUSY => SttError.AudioDeviceBusy, -c.EBUSY => Error.AudioDeviceBusy,
-c.EACCES => SttError.PermissionDenied, -c.EACCES => Error.PermissionDenied,
-c.ENOMEM => SttError.OutOfMemory, -c.ENOMEM => Error.OutOfMemory,
-c.EMFILE, -c.ENFILE => SttError.SystemResourcesExhausted, -c.EMFILE, -c.ENFILE => Error.SystemResourcesExhausted,
else => SttError.AudioDeviceError, else => Error.AudioDeviceError,
}; };
} }
@ -400,20 +400,20 @@ pub const AlsaCapture = struct {
var hw_params: ?*c.snd_pcm_hw_params_t = null; var hw_params: ?*c.snd_pcm_hw_params_t = null;
err = c.snd_pcm_hw_params_malloc(@ptrCast(&hw_params)); err = c.snd_pcm_hw_params_malloc(@ptrCast(&hw_params));
errdefer self.close(); errdefer self.close();
if (err < 0) return SttError.AudioDeviceError; if (err < 0) return Error.AudioDeviceError;
defer c.snd_pcm_hw_params_free(hw_params); defer c.snd_pcm_hw_params_free(hw_params);
// Initialize hardware parameters // Initialize hardware parameters
err = c.snd_pcm_hw_params_any(self.pcm_handle, hw_params); err = c.snd_pcm_hw_params_any(self.pcm_handle, hw_params);
if (err < 0) return SttError.AudioDeviceError; if (err < 0) return Error.AudioDeviceError;
// Set access type to interleaved // Set access type to interleaved
err = c.snd_pcm_hw_params_set_access(self.pcm_handle, hw_params, c.SND_PCM_ACCESS_RW_INTERLEAVED); err = c.snd_pcm_hw_params_set_access(self.pcm_handle, hw_params, c.SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0) return SttError.SetAccessError; if (err < 0) return Error.SetAccessError;
// Set sample format to 16-bit signed little endian // Set sample format to 16-bit signed little endian
err = c.snd_pcm_hw_params_set_format(self.pcm_handle, hw_params, c.SND_PCM_FORMAT_S16_LE); err = c.snd_pcm_hw_params_set_format(self.pcm_handle, hw_params, c.SND_PCM_FORMAT_S16_LE);
if (err < 0) return SttError.SetFormatError; if (err < 0) return Error.SetFormatError;
// Set number of channels // Set number of channels
err = c.snd_pcm_hw_params_set_channels(self.pcm_handle, hw_params, self.channels); err = c.snd_pcm_hw_params_set_channels(self.pcm_handle, hw_params, self.channels);
@ -423,35 +423,35 @@ pub const AlsaCapture = struct {
// SAFETY: min/max is set in c calls before use just below // SAFETY: min/max is set in c calls before use just below
var max: c_uint = undefined; var max: c_uint = undefined;
err = c.snd_pcm_hw_params_get_channels_min(hw_params, &min); err = c.snd_pcm_hw_params_get_channels_min(hw_params, &min);
if (err < 0) return SttError.SetChannelError; if (err < 0) return Error.SetChannelError;
err = c.snd_pcm_hw_params_get_channels_max(hw_params, &max); err = c.snd_pcm_hw_params_get_channels_max(hw_params, &max);
if (err < 0) return SttError.SetChannelError; if (err < 0) return Error.SetChannelError;
std.log.err("error setting number of channels. Must be between {d} and {d}", .{ min, max }); std.log.err("error setting number of channels. Must be between {d} and {d}", .{ min, max });
return SttError.SetChannelError; return Error.SetChannelError;
} }
// Set sample rate // Set sample rate
var actual_rate = self.sample_rate; var actual_rate = self.sample_rate;
err = c.snd_pcm_hw_params_set_rate_near(self.pcm_handle, hw_params, &actual_rate, null); err = c.snd_pcm_hw_params_set_rate_near(self.pcm_handle, hw_params, &actual_rate, null);
if (err < 0) return SttError.SetSampleRateError; if (err < 0) return Error.SetSampleRateError;
// Set buffer size // Set buffer size
var actual_buffer_size: c.snd_pcm_uframes_t = self.buffer_size; var actual_buffer_size: c.snd_pcm_uframes_t = self.buffer_size;
err = c.snd_pcm_hw_params_set_buffer_size_near(self.pcm_handle, hw_params, &actual_buffer_size); err = c.snd_pcm_hw_params_set_buffer_size_near(self.pcm_handle, hw_params, &actual_buffer_size);
if (err < 0) return SttError.SetBufferSizeError; if (err < 0) return Error.SetBufferSizeError;
// Set period size // Set period size
var actual_period_size: c.snd_pcm_uframes_t = self.period_size; var actual_period_size: c.snd_pcm_uframes_t = self.period_size;
err = c.snd_pcm_hw_params_set_period_size_near(self.pcm_handle, hw_params, &actual_period_size, null); err = c.snd_pcm_hw_params_set_period_size_near(self.pcm_handle, hw_params, &actual_period_size, null);
if (err < 0) return SttError.SetPeriodSizeError; if (err < 0) return Error.SetPeriodSizeError;
// Apply hardware parameters // Apply hardware parameters
err = c.snd_pcm_hw_params(self.pcm_handle, hw_params); err = c.snd_pcm_hw_params(self.pcm_handle, hw_params);
if (err < 0) return SttError.ApplyParametersError; if (err < 0) return Error.ApplyParametersError;
// Prepare the PCM for use // Prepare the PCM for use
err = c.snd_pcm_prepare(self.pcm_handle); err = c.snd_pcm_prepare(self.pcm_handle);
if (err < 0) return SttError.PcmPrepareError; if (err < 0) return Error.PcmPrepareError;
} }
/// Close ALSA device /// Close ALSA device
@ -465,7 +465,7 @@ pub const AlsaCapture = struct {
/// Read audio data from ALSA device and process it /// Read audio data from ALSA device and process it
pub fn readAudio(self: *Self) !usize { pub fn readAudio(self: *Self) !usize {
if (self.pcm_handle == null) { if (self.pcm_handle == null) {
return SttError.AudioDeviceError; return Error.AudioDeviceError;
} }
// Read audio data from ALSA // Read audio data from ALSA
@ -476,9 +476,9 @@ pub const AlsaCapture = struct {
if (frames_read == -c.EPIPE) { if (frames_read == -c.EPIPE) {
// Underrun occurred, try to recover // Underrun occurred, try to recover
const err = c.snd_pcm_prepare(self.pcm_handle); const err = c.snd_pcm_prepare(self.pcm_handle);
if (err < 0) return SttError.AudioDeviceError; if (err < 0) return Error.AudioDeviceError;
return 0; // No data read this time return 0; // No data read this time
} else return SttError.AudioDeviceError; } else return Error.AudioDeviceError;
} }
const samples_read = @as(usize, @intCast(frames_read)) * self.channels; const samples_read = @as(usize, @intCast(frames_read)) * self.channels;
@ -575,13 +575,13 @@ pub const Session = struct {
/// ///
/// Returns: /// Returns:
/// - Session instance on success /// - Session instance on success
/// - SttError on failure /// - Error on failure
pub fn init(allocator: std.mem.Allocator, options: Options) SttError!Session { pub fn init(allocator: std.mem.Allocator, options: Options) Error!Session {
// Validate options first with detailed error reporting // Validate options first with detailed error reporting
validateOptions(options) catch |err| { validateOptions(options) catch |err| {
const error_info = switch (err) { const error_info = switch (err) {
SttError.InvalidParameter => SttErrorInfo.initWithContext(err, "Invalid initialization parameters provided", "Check model path, audio device, sample rate, and other parameters"), Error.InvalidParameter => ErrorInfo.initWithContext(err, "Invalid initialization parameters provided", "Check model path, audio device, sample rate, and other parameters"),
else => SttErrorInfo.init(err, "Parameter validation failed"), else => ErrorInfo.init(err, "Parameter validation failed"),
}; };
options.event_handler.onDetailedError(error_info); options.event_handler.onDetailedError(error_info);
return err; return err;
@ -589,9 +589,9 @@ pub const Session = struct {
// Allocate processing buffer for audio samples (1 second worth of samples) // Allocate processing buffer for audio samples (1 second worth of samples)
const processing_buffer = allocator.alloc(i16, options.sample_rate) catch { const processing_buffer = allocator.alloc(i16, options.sample_rate) catch {
const error_info = SttErrorInfo.init(SttError.OutOfMemory, "Failed to allocate processing buffer during initialization"); const error_info = ErrorInfo.init(Error.OutOfMemory, "Failed to allocate processing buffer during initialization");
options.event_handler.onDetailedError(error_info); options.event_handler.onDetailedError(error_info);
return SttError.OutOfMemory; return Error.OutOfMemory;
}; };
errdefer allocator.free(processing_buffer); errdefer allocator.free(processing_buffer);
@ -604,10 +604,10 @@ pub const Session = struct {
options.buffer_size, options.buffer_size,
) catch |err| { ) catch |err| {
const error_info = switch (err) { const error_info = switch (err) {
error.OutOfMemory => SttErrorInfo.init(SttError.OutOfMemory, "Out of memory while initializing audio capture"), error.OutOfMemory => ErrorInfo.init(Error.OutOfMemory, "Out of memory while initializing audio capture"),
}; };
options.event_handler.onDetailedError(error_info); options.event_handler.onDetailedError(error_info);
return SttError.OutOfMemory; return Error.OutOfMemory;
}; };
errdefer { errdefer {
var alsa_capture_mut = alsa_capture; var alsa_capture_mut = alsa_capture;
@ -616,9 +616,9 @@ pub const Session = struct {
// Initialize Vosk audio buffer (larger buffer for processing) // Initialize Vosk audio buffer (larger buffer for processing)
const vosk_audio_buffer = AudioBuffer.init(allocator, options.sample_rate * 2) catch { const vosk_audio_buffer = AudioBuffer.init(allocator, options.sample_rate * 2) catch {
const error_info = SttErrorInfo.init(SttError.OutOfMemory, "Failed to allocate Vosk audio buffer during initialization"); const error_info = ErrorInfo.init(Error.OutOfMemory, "Failed to allocate Vosk audio buffer during initialization");
options.event_handler.onDetailedError(error_info); options.event_handler.onDetailedError(error_info);
return SttError.OutOfMemory; return Error.OutOfMemory;
}; };
errdefer { errdefer {
var vosk_audio_buffer_mut = vosk_audio_buffer; var vosk_audio_buffer_mut = vosk_audio_buffer;
@ -636,9 +636,9 @@ pub const Session = struct {
// Initialize Vosk model and recognizer with detailed error reporting // Initialize Vosk model and recognizer with detailed error reporting
session.initVosk() catch |err| { session.initVosk() catch |err| {
const error_info = switch (err) { const error_info = switch (err) {
SttError.ModelLoadError => SttErrorInfo.initWithContext(err, "Failed to load Vosk speech recognition model", options.model_path), Error.ModelLoadError => ErrorInfo.initWithContext(err, "Failed to load Vosk speech recognition model", options.model_path),
SttError.OutOfMemory => SttErrorInfo.init(err, "Out of memory while loading Vosk model"), Error.OutOfMemory => ErrorInfo.init(err, "Out of memory while loading Vosk model"),
else => SttErrorInfo.initWithContext(SttError.InitializationFailed, "Unexpected error during Vosk initialization", options.model_path), else => ErrorInfo.initWithContext(Error.InitializationFailed, "Unexpected error during Vosk initialization", options.model_path),
}; };
options.event_handler.onDetailedError(error_info); options.event_handler.onDetailedError(error_info);
session.deinitPartial(); session.deinitPartial();
@ -648,7 +648,7 @@ pub const Session = struct {
session.initialized = true; session.initialized = true;
// Report successful initialization // Report successful initialization
const success_info = SttErrorInfo.initRecoverable(SttError.InternalError, "STT library initialized successfully", "Ready to start speech recognition"); const success_info = ErrorInfo.initRecoverable(Error.InternalError, "STT library initialized successfully", "Ready to start speech recognition");
options.event_handler.onDetailedError(success_info); options.event_handler.onDetailedError(success_info);
return session; return session;
@ -668,7 +668,7 @@ pub const Session = struct {
// Load Vosk model // Load Vosk model
self.vosk_model = c.vosk_model_new(model_path_cstr.ptr); self.vosk_model = c.vosk_model_new(model_path_cstr.ptr);
if (self.vosk_model == null) { if (self.vosk_model == null) {
return SttError.ModelLoadError; return Error.ModelLoadError;
} }
// Create Vosk recognizer // Create Vosk recognizer
@ -678,7 +678,7 @@ pub const Session = struct {
c.vosk_model_free(model); c.vosk_model_free(model);
self.vosk_model = null; self.vosk_model = null;
} }
return SttError.ModelLoadError; return Error.ModelLoadError;
} }
} }
@ -723,12 +723,12 @@ pub const Session = struct {
// Create detailed error information // Create detailed error information
const error_info = switch (err) { const error_info = switch (err) {
SttError.AudioDeviceNotFound => SttErrorInfo.initWithContext(err, "Audio device not found", self.options.audio_device), Error.AudioDeviceNotFound => ErrorInfo.initWithContext(err, "Audio device not found", self.options.audio_device),
SttError.AudioDeviceBusy => SttErrorInfo.initRecoverable(err, "Audio device is busy", "Close other applications using the audio device"), Error.AudioDeviceBusy => ErrorInfo.initRecoverable(err, "Audio device is busy", "Close other applications using the audio device"),
SttError.PermissionDenied => SttErrorInfo.initWithContext(err, "Permission denied accessing audio device", self.options.audio_device), Error.PermissionDenied => ErrorInfo.initWithContext(err, "Permission denied accessing audio device", self.options.audio_device),
SttError.OutOfMemory => SttErrorInfo.init(err, "Out of memory while opening audio device"), Error.OutOfMemory => ErrorInfo.init(err, "Out of memory while opening audio device"),
SttError.SystemResourcesExhausted => SttErrorInfo.initRecoverable(err, "System resources exhausted", "Close other applications to free system resources"), Error.SystemResourcesExhausted => ErrorInfo.initRecoverable(err, "System resources exhausted", "Close other applications to free system resources"),
else => SttErrorInfo.initWithContext(err, "Failed to open audio device", self.options.audio_device), else => ErrorInfo.initWithContext(err, "Failed to open audio device", self.options.audio_device),
}; };
if (retry_count >= max_retries) { if (retry_count >= max_retries) {
@ -765,26 +765,26 @@ pub const Session = struct {
// Create detailed error information based on error type // Create detailed error information based on error type
const error_info = switch (err) { const error_info = switch (err) {
SttError.AudioDeviceError => blk: { Error.AudioDeviceError => blk: {
// Try to determine if device was disconnected // Try to determine if device was disconnected
if (consecutive_errors > 5) { if (consecutive_errors > 5) {
break :blk SttErrorInfo.initRecoverable(SttError.AudioDeviceError, "Audio device may have been disconnected", "Check audio device connection and restart application"); break :blk ErrorInfo.initRecoverable(Error.AudioDeviceError, "Audio device may have been disconnected", "Check audio device connection and restart application");
} else { } else {
break :blk SttErrorInfo.initRecoverable(err, "Audio capture error, attempting recovery", "Audio device will be automatically reopened"); break :blk ErrorInfo.initRecoverable(err, "Audio capture error, attempting recovery", "Audio device will be automatically reopened");
} }
}, },
SttError.OutOfMemory => SttErrorInfo.init(err, "Out of memory during audio processing"), Error.OutOfMemory => ErrorInfo.init(err, "Out of memory during audio processing"),
else => SttErrorInfo.initWithContext(err, "Unexpected audio capture error", self.options.audio_device), else => ErrorInfo.initWithContext(err, "Unexpected audio capture error", self.options.audio_device),
}; };
// Report error with context // Report error with context
self.options.event_handler.onDetailedError(error_info); self.options.event_handler.onDetailedError(error_info);
// Handle different error types appropriately // Handle different error types appropriately
if (err == SttError.AudioDeviceError) { if (err == Error.AudioDeviceError) {
retry_count += 1; retry_count += 1;
if (retry_count >= max_retries or consecutive_errors >= max_consecutive_errors) { if (retry_count >= max_retries or consecutive_errors >= max_consecutive_errors) {
const final_error = SttErrorInfo.init(SttError.AudioDeviceError, "Audio capture failed permanently, stopping audio thread"); const final_error = ErrorInfo.init(Error.AudioDeviceError, "Audio capture failed permanently, stopping audio thread");
self.options.event_handler.onDetailedError(final_error); self.options.event_handler.onDetailedError(final_error);
break; break;
} }
@ -793,15 +793,15 @@ pub const Session = struct {
self.recoverAudioDevice() catch |recovery_err| { self.recoverAudioDevice() catch |recovery_err| {
// Recovery failed, log the error and continue with retry logic // Recovery failed, log the error and continue with retry logic
const recovery_error_info = switch (recovery_err) { const recovery_error_info = switch (recovery_err) {
SttError.AudioDeviceError => SttErrorInfo.init(SttError.AudioDeviceError, "Audio device recovery failed"), Error.AudioDeviceError => ErrorInfo.init(Error.AudioDeviceError, "Audio device recovery failed"),
else => SttErrorInfo.init(SttError.AudioDeviceError, "Audio device recovery failed with unknown error"), else => ErrorInfo.init(Error.AudioDeviceError, "Audio device recovery failed with unknown error"),
}; };
self.options.event_handler.onDetailedError(recovery_error_info); self.options.event_handler.onDetailedError(recovery_error_info);
}; };
std.Thread.sleep(retry_delay_ms * std.time.ns_per_ms * retry_count); std.Thread.sleep(retry_delay_ms * std.time.ns_per_ms * retry_count);
continue; continue;
} else if (err == SttError.OutOfMemory) { } else if (err == Error.OutOfMemory) {
// Memory error is usually fatal // Memory error is usually fatal
break; break;
} else { } else {
@ -824,7 +824,7 @@ pub const Session = struct {
const written = self.vosk_audio_buffer.write(self.processing_buffer[0..samples_read]); const written = self.vosk_audio_buffer.write(self.processing_buffer[0..samples_read]);
if (written < samples_read) { if (written < samples_read) {
// Buffer overflow - report warning and clear buffer // Buffer overflow - report warning and clear buffer
const warning = SttErrorInfo.initRecoverable(SttError.InternalError, "Audio buffer overflow, clearing buffer to prevent data loss", "Consider increasing buffer size if this happens frequently"); const warning = ErrorInfo.initRecoverable(Error.InternalError, "Audio buffer overflow, clearing buffer to prevent data loss", "Consider increasing buffer size if this happens frequently");
self.options.event_handler.onDetailedError(warning); self.options.event_handler.onDetailedError(warning);
self.vosk_audio_buffer.clear(); self.vosk_audio_buffer.clear();
@ -849,7 +849,7 @@ pub const Session = struct {
const min_chunk_size = 1024; // Minimum chunk size for processing const min_chunk_size = 1024; // Minimum chunk size for processing
var vosk_buffer = self.allocator.alloc(i16, vosk_chunk_size) catch { var vosk_buffer = self.allocator.alloc(i16, vosk_chunk_size) catch {
const error_info = SttErrorInfo.init(SttError.OutOfMemory, "Failed to allocate Vosk processing buffer"); const error_info = ErrorInfo.init(Error.OutOfMemory, "Failed to allocate Vosk processing buffer");
self.options.event_handler.onDetailedError(error_info); self.options.event_handler.onDetailedError(error_info);
return; return;
}; };
@ -879,28 +879,28 @@ pub const Session = struct {
// Create detailed error information // Create detailed error information
const error_info = switch (err) { const error_info = switch (err) {
SttError.InvalidState => SttErrorInfo.initRecoverable(err, "Vosk recognizer is in invalid state", "Recognizer will be reinitialized"), Error.InvalidState => ErrorInfo.initRecoverable(err, "Vosk recognizer is in invalid state", "Recognizer will be reinitialized"),
SttError.OutOfMemory => SttErrorInfo.init(err, "Out of memory during speech processing"), Error.OutOfMemory => ErrorInfo.init(err, "Out of memory during speech processing"),
SttError.CallbackError => SttErrorInfo.initWithContext(err, "Error in speech detection callback", "Check callback implementation"), Error.CallbackError => ErrorInfo.initWithContext(err, "Error in speech detection callback", "Check callback implementation"),
else => SttErrorInfo.init(err, "Unexpected error during speech processing"), else => ErrorInfo.init(err, "Unexpected error during speech processing"),
}; };
self.options.event_handler.onDetailedError(error_info); self.options.event_handler.onDetailedError(error_info);
// Handle different error scenarios // Handle different error scenarios
if (error_count >= max_errors) { if (error_count >= max_errors) {
const fatal_error = SttErrorInfo.init(SttError.CallbackError, "Too many Vosk processing errors, stopping processing thread"); const fatal_error = ErrorInfo.init(Error.CallbackError, "Too many Vosk processing errors, stopping processing thread");
self.options.event_handler.onDetailedError(fatal_error); self.options.event_handler.onDetailedError(fatal_error);
break; break;
} }
if (consecutive_failures >= max_consecutive_failures) { if (consecutive_failures >= max_consecutive_failures) {
// Try to recover by reinitializing Vosk // Try to recover by reinitializing Vosk
const recovery_info = SttErrorInfo.initRecoverable(SttError.InternalError, "Multiple consecutive processing failures, attempting recovery", "Vosk recognizer will be reinitialized"); const recovery_info = ErrorInfo.initRecoverable(Error.InternalError, "Multiple consecutive processing failures, attempting recovery", "Vosk recognizer will be reinitialized");
self.options.event_handler.onDetailedError(recovery_info); self.options.event_handler.onDetailedError(recovery_info);
self.reinitializeVosk() catch { self.reinitializeVosk() catch {
const recovery_failed = SttErrorInfo.init(SttError.ModelLoadError, "Failed to recover Vosk recognizer, stopping processing"); const recovery_failed = ErrorInfo.init(Error.ModelLoadError, "Failed to recover Vosk recognizer, stopping processing");
self.options.event_handler.onDetailedError(recovery_failed); self.options.event_handler.onDetailedError(recovery_failed);
break; break;
}; };
@ -946,7 +946,7 @@ pub const Session = struct {
if (samples_read > 0) { if (samples_read > 0) {
self.processVoskAudio(vosk_buffer[0..samples_read]) catch { self.processVoskAudio(vosk_buffer[0..samples_read]) catch {
// Ignore errors during shutdown, but log them // Ignore errors during shutdown, but log them
const shutdown_error = SttErrorInfo.init(SttError.InternalError, "Error during final audio processing at shutdown"); const shutdown_error = ErrorInfo.init(Error.InternalError, "Error during final audio processing at shutdown");
self.options.event_handler.onDetailedError(shutdown_error); self.options.event_handler.onDetailedError(shutdown_error);
}; };
} }
@ -956,7 +956,7 @@ pub const Session = struct {
/// Process audio chunk with Vosk and handle results /// Process audio chunk with Vosk and handle results
fn processVoskAudio(self: *Session, audio_data: []const i16) !void { fn processVoskAudio(self: *Session, audio_data: []const i16) !void {
if (self.vosk_recognizer == null) { if (self.vosk_recognizer == null) {
return SttError.InvalidState; return Error.InvalidState;
} }
// Convert i16 samples to bytes for Vosk // Convert i16 samples to bytes for Vosk
@ -989,8 +989,8 @@ pub const Session = struct {
self.parseVoskPartialResult(partial_str) catch |parse_err| { self.parseVoskPartialResult(partial_str) catch |parse_err| {
// Log partial result parsing errors but continue processing // Log partial result parsing errors but continue processing
const parse_error_info = switch (parse_err) { const parse_error_info = switch (parse_err) {
SttError.CallbackError => SttErrorInfo.init(SttError.CallbackError, "Failed to parse partial speech result"), Error.CallbackError => ErrorInfo.init(Error.CallbackError, "Failed to parse partial speech result"),
else => SttErrorInfo.init(SttError.CallbackError, "Unexpected error parsing partial speech result"), else => ErrorInfo.init(Error.CallbackError, "Unexpected error parsing partial speech result"),
}; };
self.options.event_handler.onDetailedError(parse_error_info); self.options.event_handler.onDetailedError(parse_error_info);
}; };
@ -1049,7 +1049,7 @@ pub const Session = struct {
} }
/// Attempt to recover from audio device errors with detailed error reporting /// Attempt to recover from audio device errors with detailed error reporting
fn recoverAudioDevice(self: *Session) SttError!void { fn recoverAudioDevice(self: *Session) Error!void {
if (self.alsa_capture) |*capture| { if (self.alsa_capture) |*capture| {
// Close the current device handle // Close the current device handle
capture.close(); capture.close();
@ -1060,10 +1060,10 @@ pub const Session = struct {
// Try to reopen the device with detailed error handling // Try to reopen the device with detailed error handling
capture.open() catch |err| { capture.open() catch |err| {
const recovery_error = switch (err) { const recovery_error = switch (err) {
SttError.AudioDeviceNotFound => SttErrorInfo.initWithContext(err, "Audio device not found during recovery", self.options.audio_device), Error.AudioDeviceNotFound => ErrorInfo.initWithContext(err, "Audio device not found during recovery", self.options.audio_device),
SttError.AudioDeviceBusy => SttErrorInfo.initRecoverable(err, "Audio device busy during recovery", "Wait for other applications to release the device"), Error.AudioDeviceBusy => ErrorInfo.initRecoverable(err, "Audio device busy during recovery", "Wait for other applications to release the device"),
SttError.PermissionDenied => SttErrorInfo.initWithContext(err, "Permission denied during audio device recovery", self.options.audio_device), Error.PermissionDenied => ErrorInfo.initWithContext(err, "Permission denied during audio device recovery", self.options.audio_device),
else => SttErrorInfo.initWithContext(err, "Failed to recover audio device", self.options.audio_device), else => ErrorInfo.initWithContext(err, "Failed to recover audio device", self.options.audio_device),
}; };
self.options.event_handler.onDetailedError(recovery_error); self.options.event_handler.onDetailedError(recovery_error);
@ -1073,13 +1073,13 @@ pub const Session = struct {
// Clear audio buffers after successful recovery // Clear audio buffers after successful recovery
capture.audio_buffer.clear(); capture.audio_buffer.clear();
const recovery_success = SttErrorInfo.initRecoverable(SttError.InternalError, "Audio device recovered successfully", "Audio capture will resume normally"); const recovery_success = ErrorInfo.initRecoverable(Error.InternalError, "Audio device recovered successfully", "Audio capture will resume normally");
self.options.event_handler.onDetailedError(recovery_success); self.options.event_handler.onDetailedError(recovery_success);
} }
} }
/// Reinitialize Vosk recognizer for error recovery /// Reinitialize Vosk recognizer for error recovery
fn reinitializeVosk(self: *Session) SttError!void { fn reinitializeVosk(self: *Session) Error!void {
// Clean up existing Vosk resources // Clean up existing Vosk resources
if (self.vosk_recognizer) |recognizer| { if (self.vosk_recognizer) |recognizer| {
c.vosk_recognizer_free(recognizer); c.vosk_recognizer_free(recognizer);
@ -1090,18 +1090,18 @@ pub const Session = struct {
if (self.vosk_model) |model| { if (self.vosk_model) |model| {
self.vosk_recognizer = c.vosk_recognizer_new(model, @floatFromInt(self.options.sample_rate)); self.vosk_recognizer = c.vosk_recognizer_new(model, @floatFromInt(self.options.sample_rate));
if (self.vosk_recognizer == null) { if (self.vosk_recognizer == null) {
const error_info = SttErrorInfo.init(SttError.ModelLoadError, "Failed to reinitialize Vosk recognizer"); const error_info = ErrorInfo.init(Error.ModelLoadError, "Failed to reinitialize Vosk recognizer");
self.options.event_handler.onDetailedError(error_info); self.options.event_handler.onDetailedError(error_info);
return SttError.ModelLoadError; return Error.ModelLoadError;
} }
// Clear processing buffer // Clear processing buffer
self.vosk_audio_buffer.clear(); self.vosk_audio_buffer.clear();
const success_info = SttErrorInfo.initRecoverable(SttError.InternalError, "Vosk recognizer reinitialized successfully", "Speech processing will resume normally"); const success_info = ErrorInfo.initRecoverable(Error.InternalError, "Vosk recognizer reinitialized successfully", "Speech processing will resume normally");
self.options.event_handler.onDetailedError(success_info); self.options.event_handler.onDetailedError(success_info);
} else { } else {
return SttError.InvalidState; return Error.InvalidState;
} }
} }
@ -1121,26 +1121,26 @@ pub const Session = struct {
} }
/// Validate session options before initialization /// Validate session options before initialization
fn validateOptions(options: Options) SttError!void { fn validateOptions(options: Options) Error!void {
if (options.model_path.len == 0) { if (options.model_path.len == 0) {
return SttError.InvalidParameter; return Error.InvalidParameter;
} }
if (options.audio_device.len == 0) { if (options.audio_device.len == 0) {
return SttError.InvalidParameter; return Error.InvalidParameter;
} }
if (options.sample_rate == 0 or options.sample_rate > 48000) { if (options.sample_rate == 0 or options.sample_rate > 48000) {
return SttError.InvalidParameter; return Error.InvalidParameter;
} }
if (options.channels == 0 or options.channels > 8) { if (options.channels == 0 or options.channels > 8) {
return SttError.InvalidParameter; return Error.InvalidParameter;
} }
if (options.buffer_size == 0 or options.buffer_size > 8192) { if (options.buffer_size == 0 or options.buffer_size > 8192) {
return SttError.InvalidParameter; return Error.InvalidParameter;
} }
} }
/// Reinitialize the session after an error (recovery mechanism) /// Reinitialize the session after an error (recovery mechanism)
pub fn reinitialize(self: *Session) SttError!void { pub fn reinitialize(self: *Session) Error!void {
if (self.listening) { if (self.listening) {
self.stop_listening(); self.stop_listening();
} }
@ -1175,13 +1175,13 @@ pub const Session = struct {
/// ///
/// Returns: /// Returns:
/// - void on success /// - void on success
/// - SttError on failure /// - Error on failure
pub fn start_listening(self: *Session) SttError!void { pub fn start_listening(self: *Session) Error!void {
if (!self.initialized) { if (!self.initialized) {
return SttError.InvalidState; return Error.InvalidState;
} }
if (self.listening) { if (self.listening) {
return SttError.InvalidState; return Error.InvalidState;
} }
// Clear any existing audio buffers // Clear any existing audio buffers
@ -1197,8 +1197,8 @@ pub const Session = struct {
self.audio_thread = std.Thread.spawn(.{}, audioThreadFn, .{self}) catch |err| { self.audio_thread = std.Thread.spawn(.{}, audioThreadFn, .{self}) catch |err| {
self.should_stop.store(true, .release); self.should_stop.store(true, .release);
return switch (err) { return switch (err) {
error.SystemResources, error.ThreadQuotaExceeded => SttError.ThreadingError, error.SystemResources, error.ThreadQuotaExceeded => Error.ThreadingError,
else => SttError.ThreadingError, else => Error.ThreadingError,
}; };
}; };
@ -1211,8 +1211,8 @@ pub const Session = struct {
self.audio_thread = null; self.audio_thread = null;
} }
return switch (err) { return switch (err) {
error.SystemResources, error.ThreadQuotaExceeded => SttError.ThreadingError, error.SystemResources, error.ThreadQuotaExceeded => Error.ThreadingError,
else => SttError.ThreadingError, else => Error.ThreadingError,
}; };
}; };
@ -1328,8 +1328,8 @@ pub const Session = struct {
/// ///
/// Returns: /// Returns:
/// - Session instance on success /// - Session instance on success
/// - SttError on failure /// - Error on failure
pub fn init(allocator: std.mem.Allocator, options: Options) SttError!Session { pub fn init(allocator: std.mem.Allocator, options: Options) Error!Session {
return Session.init(allocator, options); return Session.init(allocator, options);
} }
@ -1390,15 +1390,15 @@ pub export fn stt_deinit(handle: *SttHandle) void {
} }
// Tests // Tests
test "SttError enum" { test "Error enum" {
const testing = std.testing; const testing = std.testing;
// Test that error types can be created and compared // Test that error types can be created and compared
const err1 = SttError.InitializationFailed; const err1 = Error.InitializationFailed;
const err2 = SttError.AudioDeviceError; const err2 = Error.AudioDeviceError;
try testing.expect(err1 != err2); try testing.expect(err1 != err2);
try testing.expect(err1 == SttError.InitializationFailed); try testing.expect(err1 == Error.InitializationFailed);
} }
test "Options validation" { test "Options validation" {
@ -1410,7 +1410,7 @@ test "Options validation" {
_ = ctx; _ = ctx;
_ = text; _ = text;
} }
fn onError(ctx: *anyopaque, error_code: SttError, message: []const u8) void { fn onError(ctx: *anyopaque, error_code: Error, message: []const u8) void {
_ = ctx; _ = ctx;
_ = message; _ = message;
// Can't discard error types with _, so we just don't use it // Can't discard error types with _, so we just don't use it
@ -1447,7 +1447,7 @@ test "Session state management" {
_ = ctx; _ = ctx;
_ = text; _ = text;
} }
fn onError(ctx: *anyopaque, error_code: SttError, message: []const u8) void { fn onError(ctx: *anyopaque, error_code: Error, message: []const u8) void {
_ = ctx; _ = ctx;
_ = message; _ = message;
// Can't discard error types with _, so we just don't use it // Can't discard error types with _, so we just don't use it
@ -1483,7 +1483,7 @@ test "SpeechEventHandler interface" {
speech_called: bool = false, speech_called: bool = false,
error_called: bool = false, error_called: bool = false,
last_text: []const u8 = "", last_text: []const u8 = "",
last_error: SttError = SttError.InitializationFailed, last_error: Error = Error.InitializationFailed,
fn onSpeech(ctx: *anyopaque, text: []const u8) void { fn onSpeech(ctx: *anyopaque, text: []const u8) void {
const self: *@This() = @ptrCast(@alignCast(ctx)); const self: *@This() = @ptrCast(@alignCast(ctx));
@ -1491,7 +1491,7 @@ test "SpeechEventHandler interface" {
self.last_text = text; self.last_text = text;
} }
fn onError(ctx: *anyopaque, error_code: SttError, message: []const u8) void { fn onError(ctx: *anyopaque, error_code: Error, message: []const u8) void {
const self: *@This() = @ptrCast(@alignCast(ctx)); const self: *@This() = @ptrCast(@alignCast(ctx));
self.error_called = true; self.error_called = true;
self.last_error = error_code; self.last_error = error_code;
@ -1512,9 +1512,9 @@ test "SpeechEventHandler interface" {
try testing.expectEqualStrings("hello world", handler.last_text); try testing.expectEqualStrings("hello world", handler.last_text);
// Test error callback // Test error callback
event_handler.onError(SttError.AudioDeviceError, "test error"); event_handler.onError(Error.AudioDeviceError, "test error");
try testing.expect(handler.error_called); try testing.expect(handler.error_called);
try testing.expect(handler.last_error == SttError.AudioDeviceError); try testing.expect(handler.last_error == Error.AudioDeviceError);
} }
test "Vosk integration with valid model" { test "Vosk integration with valid model" {
@ -1676,7 +1676,7 @@ test "Session session management API" {
_ = text; _ = text;
} }
fn onError(ctx: *anyopaque, error_code: SttError, message: []const u8) void { fn onError(ctx: *anyopaque, error_code: Error, message: []const u8) void {
const self: *@This() = @ptrCast(@alignCast(ctx)); const self: *@This() = @ptrCast(@alignCast(ctx));
self.error_count += 1; self.error_count += 1;
switch (error_code) { switch (error_code) {
@ -1712,7 +1712,7 @@ test "Session session management API" {
}; };
const invalid_result = init(allocator, invalid_options); const invalid_result = init(allocator, invalid_options);
try testing.expectError(SttError.InvalidParameter, invalid_result); try testing.expectError(Error.InvalidParameter, invalid_result);
} }
test "Session status and recovery" { test "Session status and recovery" {

View file

@ -17,11 +17,11 @@ var test_allocator = std.testing.allocator;
const TestEventHandler = struct { const TestEventHandler = struct {
speech_events: std.ArrayList([]const u8), speech_events: std.ArrayList([]const u8),
error_events: std.ArrayList(TestError), error_events: std.ArrayList(TestError),
detailed_error_events: std.ArrayList(stt.SttErrorInfo), detailed_error_events: std.ArrayList(stt.ErrorInfo),
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
const TestError = struct { const TestError = struct {
error_code: stt.SttError, error_code: stt.Error,
message: []const u8, message: []const u8,
}; };
@ -29,7 +29,7 @@ const TestEventHandler = struct {
return TestEventHandler{ return TestEventHandler{
.speech_events = std.ArrayList([]const u8){}, .speech_events = std.ArrayList([]const u8){},
.error_events = std.ArrayList(TestError){}, .error_events = std.ArrayList(TestError){},
.detailed_error_events = std.ArrayList(stt.SttErrorInfo){}, .detailed_error_events = std.ArrayList(stt.ErrorInfo){},
.allocator = allocator, .allocator = allocator,
}; };
} }
@ -63,7 +63,7 @@ const TestEventHandler = struct {
self.speech_events.append(self.allocator, owned_text) catch return; self.speech_events.append(self.allocator, owned_text) catch return;
} }
fn onError(ctx: *anyopaque, error_code: stt.SttError, message: []const u8) void { fn onError(ctx: *anyopaque, error_code: stt.Error, message: []const u8) void {
const self: *TestEventHandler = @ptrCast(@alignCast(ctx)); const self: *TestEventHandler = @ptrCast(@alignCast(ctx));
const owned_message = self.allocator.dupe(u8, message) catch return; const owned_message = self.allocator.dupe(u8, message) catch return;
const error_event = TestError{ const error_event = TestError{
@ -73,7 +73,7 @@ const TestEventHandler = struct {
self.error_events.append(self.allocator, error_event) catch return; self.error_events.append(self.allocator, error_event) catch return;
} }
fn onDetailedError(ctx: *anyopaque, error_info: stt.SttErrorInfo) void { fn onDetailedError(ctx: *anyopaque, error_info: stt.ErrorInfo) void {
const self: *TestEventHandler = @ptrCast(@alignCast(ctx)); const self: *TestEventHandler = @ptrCast(@alignCast(ctx));
// Create owned copies of strings // Create owned copies of strings
@ -128,25 +128,25 @@ const TestEventHandler = struct {
} }
}; };
test "SttError types and SttErrorInfo" { test "Error types and ErrorInfo" {
// Test basic error info creation // Test basic error info creation
const basic_error = stt.SttErrorInfo.init(stt.SttError.AudioDeviceError, "Test error message"); const basic_error = stt.ErrorInfo.init(stt.Error.AudioDeviceError, "Test error message");
try testing.expect(basic_error.error_code == stt.SttError.AudioDeviceError); try testing.expect(basic_error.error_code == stt.Error.AudioDeviceError);
try testing.expectEqualStrings("Test error message", basic_error.message); try testing.expectEqualStrings("Test error message", basic_error.message);
try testing.expect(basic_error.system_error == null); try testing.expect(basic_error.system_error == null);
try testing.expect(basic_error.context == null); try testing.expect(basic_error.context == null);
try testing.expect(basic_error.recoverable == false); try testing.expect(basic_error.recoverable == false);
// Test error info with system error // Test error info with system error
const system_error = stt.SttErrorInfo.initWithSystemError(stt.SttError.AudioDeviceError, "System error", -1); const system_error = stt.ErrorInfo.initWithSystemError(stt.Error.AudioDeviceError, "System error", -1);
try testing.expect(system_error.system_error.? == -1); try testing.expect(system_error.system_error.? == -1);
// Test error info with context // Test error info with context
const context_error = stt.SttErrorInfo.initWithContext(stt.SttError.ModelLoadError, "Context error", "/path/to/model"); const context_error = stt.ErrorInfo.initWithContext(stt.Error.ModelLoadError, "Context error", "/path/to/model");
try testing.expectEqualStrings("/path/to/model", context_error.context.?); try testing.expectEqualStrings("/path/to/model", context_error.context.?);
// Test recoverable error info // Test recoverable error info
const recoverable_error = stt.SttErrorInfo.initRecoverable(stt.SttError.AudioDeviceBusy, "Recoverable error", "Try again later"); const recoverable_error = stt.ErrorInfo.initRecoverable(stt.Error.AudioDeviceBusy, "Recoverable error", "Try again later");
try testing.expect(recoverable_error.recoverable == true); try testing.expect(recoverable_error.recoverable == true);
try testing.expectEqualStrings("Try again later", recoverable_error.recovery_suggestion.?); try testing.expectEqualStrings("Try again later", recoverable_error.recovery_suggestion.?);
} }
@ -163,16 +163,16 @@ test "SpeechEventHandler callback invocation" {
try testing.expectEqualStrings("Hello world", test_handler.speech_events.items[0]); try testing.expectEqualStrings("Hello world", test_handler.speech_events.items[0]);
// Test error callback // Test error callback
speech_handler.onError(stt.SttError.AudioDeviceError, "Test error"); speech_handler.onError(stt.Error.AudioDeviceError, "Test error");
try testing.expect(test_handler.error_events.items.len == 1); try testing.expect(test_handler.error_events.items.len == 1);
try testing.expect(test_handler.error_events.items[0].error_code == stt.SttError.AudioDeviceError); try testing.expect(test_handler.error_events.items[0].error_code == stt.Error.AudioDeviceError);
try testing.expectEqualStrings("Test error", test_handler.error_events.items[0].message); try testing.expectEqualStrings("Test error", test_handler.error_events.items[0].message);
// Test detailed error callback // Test detailed error callback
const error_info = stt.SttErrorInfo.initWithContext(stt.SttError.ModelLoadError, "Detailed error", "test context"); const error_info = stt.ErrorInfo.initWithContext(stt.Error.ModelLoadError, "Detailed error", "test context");
speech_handler.onDetailedError(error_info); speech_handler.onDetailedError(error_info);
try testing.expect(test_handler.detailed_error_events.items.len == 1); try testing.expect(test_handler.detailed_error_events.items.len == 1);
try testing.expect(test_handler.detailed_error_events.items[0].error_code == stt.SttError.ModelLoadError); try testing.expect(test_handler.detailed_error_events.items[0].error_code == stt.Error.ModelLoadError);
try testing.expectEqualStrings("Detailed error", test_handler.detailed_error_events.items[0].message); try testing.expectEqualStrings("Detailed error", test_handler.detailed_error_events.items[0].message);
try testing.expectEqualStrings("test context", test_handler.detailed_error_events.items[0].context.?); try testing.expectEqualStrings("test context", test_handler.detailed_error_events.items[0].context.?);
} }
@ -340,14 +340,14 @@ test "Error recovery and handling" {
// Test different error types and their recovery suggestions // Test different error types and their recovery suggestions
const errors_to_test = [_]struct { const errors_to_test = [_]struct {
error_code: stt.SttError, error_code: stt.Error,
message: []const u8, message: []const u8,
should_be_recoverable: bool, should_be_recoverable: bool,
}{ }{
.{ .error_code = stt.SttError.AudioDeviceBusy, .message = "Device busy", .should_be_recoverable = true }, .{ .error_code = stt.Error.AudioDeviceBusy, .message = "Device busy", .should_be_recoverable = true },
.{ .error_code = stt.SttError.OutOfMemory, .message = "Out of memory", .should_be_recoverable = false }, .{ .error_code = stt.Error.OutOfMemory, .message = "Out of memory", .should_be_recoverable = false },
.{ .error_code = stt.SttError.ModelLoadError, .message = "Model load failed", .should_be_recoverable = false }, .{ .error_code = stt.Error.ModelLoadError, .message = "Model load failed", .should_be_recoverable = false },
.{ .error_code = stt.SttError.AudioDeviceNotFound, .message = "Device not found", .should_be_recoverable = true }, .{ .error_code = stt.Error.AudioDeviceNotFound, .message = "Device not found", .should_be_recoverable = true },
}; };
for (errors_to_test) |error_test| { for (errors_to_test) |error_test| {
@ -355,9 +355,9 @@ test "Error recovery and handling" {
// Create error info based on error type // Create error info based on error type
const error_info = if (error_test.should_be_recoverable) const error_info = if (error_test.should_be_recoverable)
stt.SttErrorInfo.initRecoverable(error_test.error_code, error_test.message, "Try again later") stt.ErrorInfo.initRecoverable(error_test.error_code, error_test.message, "Try again later")
else else
stt.SttErrorInfo.init(error_test.error_code, error_test.message); stt.ErrorInfo.init(error_test.error_code, error_test.message);
speech_handler.onDetailedError(error_info); speech_handler.onDetailedError(error_info);
@ -386,9 +386,9 @@ test "Callback error handling robustness" {
// Test mixed callback types // Test mixed callback types
speech_handler.onSpeech("Final speech"); speech_handler.onSpeech("Final speech");
speech_handler.onError(stt.SttError.CallbackError, "Callback error"); speech_handler.onError(stt.Error.CallbackError, "Callback error");
const final_error = stt.SttErrorInfo.init(stt.SttError.InternalError, "Internal error"); const final_error = stt.ErrorInfo.init(stt.Error.InternalError, "Internal error");
speech_handler.onDetailedError(final_error); speech_handler.onDetailedError(final_error);
try testing.expect(test_handler.speech_events.items.len == 101); try testing.expect(test_handler.speech_events.items.len == 101);
@ -416,9 +416,9 @@ test "Memory management and resource cleanup" {
const speech_handler = test_handler.getSpeechEventHandler(); const speech_handler = test_handler.getSpeechEventHandler();
speech_handler.onSpeech("Test speech"); speech_handler.onSpeech("Test speech");
speech_handler.onError(stt.SttError.AudioDeviceError, "Test error"); speech_handler.onError(stt.Error.AudioDeviceError, "Test error");
const error_info = stt.SttErrorInfo.initWithContext(stt.SttError.ModelLoadError, "Test detailed error", "test context"); const error_info = stt.ErrorInfo.initWithContext(stt.Error.ModelLoadError, "Test detailed error", "test context");
speech_handler.onDetailedError(error_info); speech_handler.onDetailedError(error_info);
} }
} }
@ -473,7 +473,7 @@ test "Complete workflow simulation" {
// Simulate a complete speech recognition workflow // Simulate a complete speech recognition workflow
// 1. Initialization phase // 1. Initialization phase
const init_error = stt.SttErrorInfo.initRecoverable(stt.SttError.InternalError, "STT library initialized", "Ready for speech recognition"); const init_error = stt.ErrorInfo.initRecoverable(stt.Error.InternalError, "STT library initialized", "Ready for speech recognition");
speech_handler.onDetailedError(init_error); speech_handler.onDetailedError(init_error);
// 2. Audio processing phase // 2. Audio processing phase
@ -493,14 +493,14 @@ test "Complete workflow simulation" {
speech_handler.onSpeech("This is a test"); speech_handler.onSpeech("This is a test");
// 4. Error handling phase // 4. Error handling phase
const recoverable_error = stt.SttErrorInfo.initRecoverable(stt.SttError.AudioDeviceBusy, "Audio device temporarily busy", "Retrying in 100ms"); const recoverable_error = stt.ErrorInfo.initRecoverable(stt.Error.AudioDeviceBusy, "Audio device temporarily busy", "Retrying in 100ms");
speech_handler.onDetailedError(recoverable_error); speech_handler.onDetailedError(recoverable_error);
// 5. Recovery phase // 5. Recovery phase
speech_handler.onSpeech("Speech recognition resumed"); speech_handler.onSpeech("Speech recognition resumed");
// 6. Cleanup phase // 6. Cleanup phase
const cleanup_info = stt.SttErrorInfo.initRecoverable(stt.SttError.InternalError, "STT session cleanup completed", "All resources freed"); const cleanup_info = stt.ErrorInfo.initRecoverable(stt.Error.InternalError, "STT session cleanup completed", "All resources freed");
speech_handler.onDetailedError(cleanup_info); speech_handler.onDetailedError(cleanup_info);
// Verify the complete workflow // Verify the complete workflow