diff --git a/src/main.zig b/src/main.zig index fc2eca2..7e45265 100644 --- a/src/main.zig +++ b/src/main.zig @@ -174,7 +174,7 @@ const SpeechHandler = struct { } /// 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 const self: *SpeechHandler = @ptrCast(@alignCast(ctx)); @@ -186,14 +186,14 @@ const SpeechHandler = struct { } /// 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)); logDetail(self, error_info) catch |e| 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); // Categorize the error for statistics if (error_info.recoverable) @@ -234,14 +234,14 @@ const SpeechHandler = struct { // Determine and call appropriate log function once switch (error_info.error_code) { - stt.SttError.InternalError => if (error_info.recoverable) { + stt.Error.InternalError => if (error_info.recoverable) { log.info("{s}", .{message}); } else { log.warn("{s}", .{message}); }, - stt.SttError.OutOfMemory, - stt.SttError.ModelLoadError, - stt.SttError.InitializationFailed, + stt.Error.OutOfMemory, + stt.Error.ModelLoadError, + stt.Error.InitializationFailed, => log.err("{s}", .{message}), else => if (error_info.recoverable) log.warn("{s}", .{message}) @@ -430,13 +430,13 @@ pub fn main() !void { session.start_listening() catch |err| { std.log.err("Failed to start listening: {}", .{err}); switch (err) { - stt.SttError.AudioDeviceError => { + stt.Error.AudioDeviceError => { std.log.err("Audio device error. Please check:", .{}); 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(" - You have permission to access audio devices", .{}); }, - stt.SttError.ThreadingError => { + stt.Error.ThreadingError => { std.log.err("Threading error. System may be under heavy load.", .{}); }, else => { @@ -494,7 +494,7 @@ test "handler callbacks" { // Test that callbacks can be invoked without crashing 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 try testing.expect(true); diff --git a/src/stt.zig b/src/stt.zig index 70ebe85..a783779 100644 --- a/src/stt.zig +++ b/src/stt.zig @@ -10,7 +10,7 @@ const c = @cImport({ }); /// 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.) InitializationFailed, /// Audio device access or configuration error @@ -57,9 +57,9 @@ pub const SttError = error{ }; /// Detailed error information structure -pub const SttErrorInfo = struct { +pub const ErrorInfo = struct { /// The error code - error_code: SttError, + error_code: Error, /// Human-readable error message message: []const u8, /// Optional system error code (errno, ALSA error, etc.) @@ -74,8 +74,8 @@ pub const SttErrorInfo = struct { recovery_suggestion: ?[]const u8 = null, /// Create a new error info structure - pub fn init(error_code: SttError, message: []const u8) SttErrorInfo { - return SttErrorInfo{ + pub fn init(error_code: Error, message: []const u8) ErrorInfo { + return ErrorInfo{ .error_code = error_code, .message = message, .timestamp = std.time.timestamp(), @@ -83,8 +83,8 @@ pub const SttErrorInfo = struct { } /// Create error info with system error code - pub fn initWithSystemError(error_code: SttError, message: []const u8, system_error: i32) SttErrorInfo { - return SttErrorInfo{ + pub fn initWithSystemError(error_code: Error, message: []const u8, system_error: i32) ErrorInfo { + return ErrorInfo{ .error_code = error_code, .message = message, .system_error = system_error, @@ -93,8 +93,8 @@ pub const SttErrorInfo = struct { } /// Create error info with context - pub fn initWithContext(error_code: SttError, message: []const u8, context: []const u8) SttErrorInfo { - return SttErrorInfo{ + pub fn initWithContext(error_code: Error, message: []const u8, context: []const u8) ErrorInfo { + return ErrorInfo{ .error_code = error_code, .message = message, .context = context, @@ -103,8 +103,8 @@ pub const SttErrorInfo = struct { } /// Create recoverable error info with suggestion - pub fn initRecoverable(error_code: SttError, message: []const u8, suggestion: []const u8) SttErrorInfo { - return SttErrorInfo{ + pub fn initRecoverable(error_code: Error, message: []const u8, suggestion: []const u8) ErrorInfo { + return ErrorInfo{ .error_code = error_code, .message = message, .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 /// - message: Null-terminated string with error details /// - 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 /// /// Parameters: /// - error_info: Detailed error information structure /// - 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 /// @@ -144,9 +144,9 @@ pub const SpeechEventHandler = struct { /// Function to call when speech is detected onSpeechFn: *const fn (ctx: *anyopaque, text: []const u8) void, /// 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 - 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 ctx: *anyopaque, @@ -159,12 +159,12 @@ pub const SpeechEventHandler = struct { } /// 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); } /// 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| { detailed_fn(self.ctx, error_info); } else { @@ -174,7 +174,7 @@ pub const SpeechEventHandler = struct { } /// 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| { detailed_fn(self.ctx, error_info); } else { @@ -379,7 +379,7 @@ pub const AlsaCapture = struct { pub fn open(self: *Self) !void { // Convert device name to null-terminated string const device_cstr = self.allocator.dupeZ(u8, self.device_name) catch { - return SttError.OutOfMemory; + return Error.OutOfMemory; }; 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); if (err < 0) { return switch (err) { - -c.ENOENT => SttError.AudioDeviceNotFound, - -c.EBUSY => SttError.AudioDeviceBusy, - -c.EACCES => SttError.PermissionDenied, - -c.ENOMEM => SttError.OutOfMemory, - -c.EMFILE, -c.ENFILE => SttError.SystemResourcesExhausted, - else => SttError.AudioDeviceError, + -c.ENOENT => Error.AudioDeviceNotFound, + -c.EBUSY => Error.AudioDeviceBusy, + -c.EACCES => Error.PermissionDenied, + -c.ENOMEM => Error.OutOfMemory, + -c.EMFILE, -c.ENFILE => Error.SystemResourcesExhausted, + else => Error.AudioDeviceError, }; } @@ -400,20 +400,20 @@ pub const AlsaCapture = struct { var hw_params: ?*c.snd_pcm_hw_params_t = null; err = c.snd_pcm_hw_params_malloc(@ptrCast(&hw_params)); errdefer self.close(); - if (err < 0) return SttError.AudioDeviceError; + if (err < 0) return Error.AudioDeviceError; defer c.snd_pcm_hw_params_free(hw_params); // Initialize hardware parameters 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 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 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 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 var max: c_uint = undefined; 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); - 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 }); - return SttError.SetChannelError; + return Error.SetChannelError; } // Set 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); - if (err < 0) return SttError.SetSampleRateError; + if (err < 0) return Error.SetSampleRateError; // Set 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); - if (err < 0) return SttError.SetBufferSizeError; + if (err < 0) return Error.SetBufferSizeError; // Set 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); - if (err < 0) return SttError.SetPeriodSizeError; + if (err < 0) return Error.SetPeriodSizeError; // Apply hardware parameters 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 err = c.snd_pcm_prepare(self.pcm_handle); - if (err < 0) return SttError.PcmPrepareError; + if (err < 0) return Error.PcmPrepareError; } /// Close ALSA device @@ -465,7 +465,7 @@ pub const AlsaCapture = struct { /// Read audio data from ALSA device and process it pub fn readAudio(self: *Self) !usize { if (self.pcm_handle == null) { - return SttError.AudioDeviceError; + return Error.AudioDeviceError; } // Read audio data from ALSA @@ -476,9 +476,9 @@ pub const AlsaCapture = struct { if (frames_read == -c.EPIPE) { // Underrun occurred, try to recover 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 - } else return SttError.AudioDeviceError; + } else return Error.AudioDeviceError; } const samples_read = @as(usize, @intCast(frames_read)) * self.channels; @@ -575,13 +575,13 @@ pub const Session = struct { /// /// Returns: /// - Session instance on success - /// - SttError on failure - pub fn init(allocator: std.mem.Allocator, options: Options) SttError!Session { + /// - Error on failure + pub fn init(allocator: std.mem.Allocator, options: Options) Error!Session { // Validate options first with detailed error reporting validateOptions(options) catch |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"), - else => SttErrorInfo.init(err, "Parameter validation failed"), + Error.InvalidParameter => ErrorInfo.initWithContext(err, "Invalid initialization parameters provided", "Check model path, audio device, sample rate, and other parameters"), + else => ErrorInfo.init(err, "Parameter validation failed"), }; options.event_handler.onDetailedError(error_info); return err; @@ -589,9 +589,9 @@ pub const Session = struct { // Allocate processing buffer for audio samples (1 second worth of samples) 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); - return SttError.OutOfMemory; + return Error.OutOfMemory; }; errdefer allocator.free(processing_buffer); @@ -604,10 +604,10 @@ pub const Session = struct { options.buffer_size, ) catch |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); - return SttError.OutOfMemory; + return Error.OutOfMemory; }; errdefer { var alsa_capture_mut = alsa_capture; @@ -616,9 +616,9 @@ pub const Session = struct { // Initialize Vosk audio buffer (larger buffer for processing) 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); - return SttError.OutOfMemory; + return Error.OutOfMemory; }; errdefer { 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 session.initVosk() catch |err| { const error_info = switch (err) { - SttError.ModelLoadError => SttErrorInfo.initWithContext(err, "Failed to load Vosk speech recognition model", options.model_path), - SttError.OutOfMemory => SttErrorInfo.init(err, "Out of memory while loading Vosk model"), - else => SttErrorInfo.initWithContext(SttError.InitializationFailed, "Unexpected error during Vosk initialization", options.model_path), + Error.ModelLoadError => ErrorInfo.initWithContext(err, "Failed to load Vosk speech recognition model", options.model_path), + Error.OutOfMemory => ErrorInfo.init(err, "Out of memory while loading Vosk model"), + else => ErrorInfo.initWithContext(Error.InitializationFailed, "Unexpected error during Vosk initialization", options.model_path), }; options.event_handler.onDetailedError(error_info); session.deinitPartial(); @@ -648,7 +648,7 @@ pub const Session = struct { session.initialized = true; // 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); return session; @@ -668,7 +668,7 @@ pub const Session = struct { // Load Vosk model self.vosk_model = c.vosk_model_new(model_path_cstr.ptr); if (self.vosk_model == null) { - return SttError.ModelLoadError; + return Error.ModelLoadError; } // Create Vosk recognizer @@ -678,7 +678,7 @@ pub const Session = struct { c.vosk_model_free(model); self.vosk_model = null; } - return SttError.ModelLoadError; + return Error.ModelLoadError; } } @@ -723,12 +723,12 @@ pub const Session = struct { // Create detailed error information const error_info = switch (err) { - SttError.AudioDeviceNotFound => SttErrorInfo.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"), - SttError.PermissionDenied => SttErrorInfo.initWithContext(err, "Permission denied accessing audio device", self.options.audio_device), - SttError.OutOfMemory => SttErrorInfo.init(err, "Out of memory while opening audio device"), - SttError.SystemResourcesExhausted => SttErrorInfo.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), + Error.AudioDeviceNotFound => ErrorInfo.initWithContext(err, "Audio device not found", self.options.audio_device), + Error.AudioDeviceBusy => ErrorInfo.initRecoverable(err, "Audio device is busy", "Close other applications using the audio device"), + Error.PermissionDenied => ErrorInfo.initWithContext(err, "Permission denied accessing audio device", self.options.audio_device), + Error.OutOfMemory => ErrorInfo.init(err, "Out of memory while opening audio device"), + Error.SystemResourcesExhausted => ErrorInfo.initRecoverable(err, "System resources exhausted", "Close other applications to free system resources"), + else => ErrorInfo.initWithContext(err, "Failed to open audio device", self.options.audio_device), }; if (retry_count >= max_retries) { @@ -765,26 +765,26 @@ pub const Session = struct { // Create detailed error information based on error type const error_info = switch (err) { - SttError.AudioDeviceError => blk: { + Error.AudioDeviceError => blk: { // Try to determine if device was disconnected 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 { - 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"), - else => SttErrorInfo.initWithContext(err, "Unexpected audio capture error", self.options.audio_device), + Error.OutOfMemory => ErrorInfo.init(err, "Out of memory during audio processing"), + else => ErrorInfo.initWithContext(err, "Unexpected audio capture error", self.options.audio_device), }; // Report error with context self.options.event_handler.onDetailedError(error_info); // Handle different error types appropriately - if (err == SttError.AudioDeviceError) { + if (err == Error.AudioDeviceError) { retry_count += 1; 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); break; } @@ -793,15 +793,15 @@ pub const Session = struct { self.recoverAudioDevice() catch |recovery_err| { // Recovery failed, log the error and continue with retry logic const recovery_error_info = switch (recovery_err) { - SttError.AudioDeviceError => SttErrorInfo.init(SttError.AudioDeviceError, "Audio device recovery failed"), - else => SttErrorInfo.init(SttError.AudioDeviceError, "Audio device recovery failed with unknown error"), + Error.AudioDeviceError => ErrorInfo.init(Error.AudioDeviceError, "Audio device recovery failed"), + else => ErrorInfo.init(Error.AudioDeviceError, "Audio device recovery failed with unknown error"), }; self.options.event_handler.onDetailedError(recovery_error_info); }; std.Thread.sleep(retry_delay_ms * std.time.ns_per_ms * retry_count); continue; - } else if (err == SttError.OutOfMemory) { + } else if (err == Error.OutOfMemory) { // Memory error is usually fatal break; } else { @@ -824,7 +824,7 @@ pub const Session = struct { const written = self.vosk_audio_buffer.write(self.processing_buffer[0..samples_read]); if (written < samples_read) { // 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.vosk_audio_buffer.clear(); @@ -849,7 +849,7 @@ pub const Session = struct { const min_chunk_size = 1024; // Minimum chunk size for processing 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); return; }; @@ -879,28 +879,28 @@ pub const Session = struct { // Create detailed error information const error_info = switch (err) { - SttError.InvalidState => SttErrorInfo.initRecoverable(err, "Vosk recognizer is in invalid state", "Recognizer will be reinitialized"), - SttError.OutOfMemory => SttErrorInfo.init(err, "Out of memory during speech processing"), - SttError.CallbackError => SttErrorInfo.initWithContext(err, "Error in speech detection callback", "Check callback implementation"), - else => SttErrorInfo.init(err, "Unexpected error during speech processing"), + Error.InvalidState => ErrorInfo.initRecoverable(err, "Vosk recognizer is in invalid state", "Recognizer will be reinitialized"), + Error.OutOfMemory => ErrorInfo.init(err, "Out of memory during speech processing"), + Error.CallbackError => ErrorInfo.initWithContext(err, "Error in speech detection callback", "Check callback implementation"), + else => ErrorInfo.init(err, "Unexpected error during speech processing"), }; self.options.event_handler.onDetailedError(error_info); // Handle different error scenarios 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); break; } if (consecutive_failures >= max_consecutive_failures) { // 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.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); break; }; @@ -946,7 +946,7 @@ pub const Session = struct { if (samples_read > 0) { self.processVoskAudio(vosk_buffer[0..samples_read]) catch { // 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); }; } @@ -956,7 +956,7 @@ pub const Session = struct { /// Process audio chunk with Vosk and handle results fn processVoskAudio(self: *Session, audio_data: []const i16) !void { if (self.vosk_recognizer == null) { - return SttError.InvalidState; + return Error.InvalidState; } // Convert i16 samples to bytes for Vosk @@ -989,8 +989,8 @@ pub const Session = struct { self.parseVoskPartialResult(partial_str) catch |parse_err| { // Log partial result parsing errors but continue processing const parse_error_info = switch (parse_err) { - SttError.CallbackError => SttErrorInfo.init(SttError.CallbackError, "Failed to parse partial speech result"), - else => SttErrorInfo.init(SttError.CallbackError, "Unexpected error parsing partial speech result"), + Error.CallbackError => ErrorInfo.init(Error.CallbackError, "Failed to parse partial speech result"), + else => ErrorInfo.init(Error.CallbackError, "Unexpected error parsing partial speech result"), }; 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 - fn recoverAudioDevice(self: *Session) SttError!void { + fn recoverAudioDevice(self: *Session) Error!void { if (self.alsa_capture) |*capture| { // Close the current device handle capture.close(); @@ -1060,10 +1060,10 @@ pub const Session = struct { // Try to reopen the device with detailed error handling capture.open() catch |err| { const recovery_error = switch (err) { - SttError.AudioDeviceNotFound => SttErrorInfo.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"), - SttError.PermissionDenied => SttErrorInfo.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), + Error.AudioDeviceNotFound => ErrorInfo.initWithContext(err, "Audio device not found during recovery", self.options.audio_device), + Error.AudioDeviceBusy => ErrorInfo.initRecoverable(err, "Audio device busy during recovery", "Wait for other applications to release the device"), + Error.PermissionDenied => ErrorInfo.initWithContext(err, "Permission denied during audio device recovery", self.options.audio_device), + else => ErrorInfo.initWithContext(err, "Failed to recover audio device", self.options.audio_device), }; self.options.event_handler.onDetailedError(recovery_error); @@ -1073,13 +1073,13 @@ pub const Session = struct { // Clear audio buffers after successful recovery 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); } } /// Reinitialize Vosk recognizer for error recovery - fn reinitializeVosk(self: *Session) SttError!void { + fn reinitializeVosk(self: *Session) Error!void { // Clean up existing Vosk resources if (self.vosk_recognizer) |recognizer| { c.vosk_recognizer_free(recognizer); @@ -1090,18 +1090,18 @@ pub const Session = struct { if (self.vosk_model) |model| { self.vosk_recognizer = c.vosk_recognizer_new(model, @floatFromInt(self.options.sample_rate)); 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); - return SttError.ModelLoadError; + return Error.ModelLoadError; } // Clear processing buffer 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); } else { - return SttError.InvalidState; + return Error.InvalidState; } } @@ -1121,26 +1121,26 @@ pub const Session = struct { } /// Validate session options before initialization - fn validateOptions(options: Options) SttError!void { + fn validateOptions(options: Options) Error!void { if (options.model_path.len == 0) { - return SttError.InvalidParameter; + return Error.InvalidParameter; } if (options.audio_device.len == 0) { - return SttError.InvalidParameter; + return Error.InvalidParameter; } if (options.sample_rate == 0 or options.sample_rate > 48000) { - return SttError.InvalidParameter; + return Error.InvalidParameter; } if (options.channels == 0 or options.channels > 8) { - return SttError.InvalidParameter; + return Error.InvalidParameter; } if (options.buffer_size == 0 or options.buffer_size > 8192) { - return SttError.InvalidParameter; + return Error.InvalidParameter; } } /// 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) { self.stop_listening(); } @@ -1175,13 +1175,13 @@ pub const Session = struct { /// /// Returns: /// - void on success - /// - SttError on failure - pub fn start_listening(self: *Session) SttError!void { + /// - Error on failure + pub fn start_listening(self: *Session) Error!void { if (!self.initialized) { - return SttError.InvalidState; + return Error.InvalidState; } if (self.listening) { - return SttError.InvalidState; + return Error.InvalidState; } // Clear any existing audio buffers @@ -1197,8 +1197,8 @@ pub const Session = struct { self.audio_thread = std.Thread.spawn(.{}, audioThreadFn, .{self}) catch |err| { self.should_stop.store(true, .release); return switch (err) { - error.SystemResources, error.ThreadQuotaExceeded => SttError.ThreadingError, - else => SttError.ThreadingError, + error.SystemResources, error.ThreadQuotaExceeded => Error.ThreadingError, + else => Error.ThreadingError, }; }; @@ -1211,8 +1211,8 @@ pub const Session = struct { self.audio_thread = null; } return switch (err) { - error.SystemResources, error.ThreadQuotaExceeded => SttError.ThreadingError, - else => SttError.ThreadingError, + error.SystemResources, error.ThreadQuotaExceeded => Error.ThreadingError, + else => Error.ThreadingError, }; }; @@ -1328,8 +1328,8 @@ pub const Session = struct { /// /// Returns: /// - Session instance on success -/// - SttError on failure -pub fn init(allocator: std.mem.Allocator, options: Options) SttError!Session { +/// - Error on failure +pub fn init(allocator: std.mem.Allocator, options: Options) Error!Session { return Session.init(allocator, options); } @@ -1390,15 +1390,15 @@ pub export fn stt_deinit(handle: *SttHandle) void { } // Tests -test "SttError enum" { +test "Error enum" { const testing = std.testing; // Test that error types can be created and compared - const err1 = SttError.InitializationFailed; - const err2 = SttError.AudioDeviceError; + const err1 = Error.InitializationFailed; + const err2 = Error.AudioDeviceError; try testing.expect(err1 != err2); - try testing.expect(err1 == SttError.InitializationFailed); + try testing.expect(err1 == Error.InitializationFailed); } test "Options validation" { @@ -1410,7 +1410,7 @@ test "Options validation" { _ = ctx; _ = text; } - fn onError(ctx: *anyopaque, error_code: SttError, message: []const u8) void { + fn onError(ctx: *anyopaque, error_code: Error, message: []const u8) void { _ = ctx; _ = message; // Can't discard error types with _, so we just don't use it @@ -1447,7 +1447,7 @@ test "Session state management" { _ = ctx; _ = text; } - fn onError(ctx: *anyopaque, error_code: SttError, message: []const u8) void { + fn onError(ctx: *anyopaque, error_code: Error, message: []const u8) void { _ = ctx; _ = message; // Can't discard error types with _, so we just don't use it @@ -1483,7 +1483,7 @@ test "SpeechEventHandler interface" { speech_called: bool = false, error_called: bool = false, last_text: []const u8 = "", - last_error: SttError = SttError.InitializationFailed, + last_error: Error = Error.InitializationFailed, fn onSpeech(ctx: *anyopaque, text: []const u8) void { const self: *@This() = @ptrCast(@alignCast(ctx)); @@ -1491,7 +1491,7 @@ test "SpeechEventHandler interface" { 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)); self.error_called = true; self.last_error = error_code; @@ -1512,9 +1512,9 @@ test "SpeechEventHandler interface" { try testing.expectEqualStrings("hello world", handler.last_text); // 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.last_error == SttError.AudioDeviceError); + try testing.expect(handler.last_error == Error.AudioDeviceError); } test "Vosk integration with valid model" { @@ -1676,7 +1676,7 @@ test "Session session management API" { _ = 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)); self.error_count += 1; switch (error_code) { @@ -1712,7 +1712,7 @@ test "Session session management API" { }; 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" { diff --git a/src/test.zig b/src/test.zig index 8bdaa6f..ae537a3 100644 --- a/src/test.zig +++ b/src/test.zig @@ -17,11 +17,11 @@ var test_allocator = std.testing.allocator; const TestEventHandler = struct { speech_events: std.ArrayList([]const u8), error_events: std.ArrayList(TestError), - detailed_error_events: std.ArrayList(stt.SttErrorInfo), + detailed_error_events: std.ArrayList(stt.ErrorInfo), allocator: std.mem.Allocator, const TestError = struct { - error_code: stt.SttError, + error_code: stt.Error, message: []const u8, }; @@ -29,7 +29,7 @@ const TestEventHandler = struct { return TestEventHandler{ .speech_events = std.ArrayList([]const u8){}, .error_events = std.ArrayList(TestError){}, - .detailed_error_events = std.ArrayList(stt.SttErrorInfo){}, + .detailed_error_events = std.ArrayList(stt.ErrorInfo){}, .allocator = allocator, }; } @@ -63,7 +63,7 @@ const TestEventHandler = struct { 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 owned_message = self.allocator.dupe(u8, message) catch return; const error_event = TestError{ @@ -73,7 +73,7 @@ const TestEventHandler = struct { 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)); // 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 - const basic_error = stt.SttErrorInfo.init(stt.SttError.AudioDeviceError, "Test error message"); - try testing.expect(basic_error.error_code == stt.SttError.AudioDeviceError); + const basic_error = stt.ErrorInfo.init(stt.Error.AudioDeviceError, "Test error message"); + try testing.expect(basic_error.error_code == stt.Error.AudioDeviceError); try testing.expectEqualStrings("Test error message", basic_error.message); try testing.expect(basic_error.system_error == null); try testing.expect(basic_error.context == null); try testing.expect(basic_error.recoverable == false); // 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); // 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.?); // 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.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]); // 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[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); // 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); 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("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 const errors_to_test = [_]struct { - error_code: stt.SttError, + error_code: stt.Error, message: []const u8, should_be_recoverable: bool, }{ - .{ .error_code = stt.SttError.AudioDeviceBusy, .message = "Device busy", .should_be_recoverable = true }, - .{ .error_code = stt.SttError.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.SttError.AudioDeviceNotFound, .message = "Device not found", .should_be_recoverable = true }, + .{ .error_code = stt.Error.AudioDeviceBusy, .message = "Device busy", .should_be_recoverable = true }, + .{ .error_code = stt.Error.OutOfMemory, .message = "Out of memory", .should_be_recoverable = false }, + .{ .error_code = stt.Error.ModelLoadError, .message = "Model load failed", .should_be_recoverable = false }, + .{ .error_code = stt.Error.AudioDeviceNotFound, .message = "Device not found", .should_be_recoverable = true }, }; for (errors_to_test) |error_test| { @@ -355,9 +355,9 @@ test "Error recovery and handling" { // Create error info based on error type 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 - 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); @@ -386,9 +386,9 @@ test "Callback error handling robustness" { // Test mixed callback types 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); 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(); 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); } } @@ -473,7 +473,7 @@ test "Complete workflow simulation" { // Simulate a complete speech recognition workflow // 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); // 2. Audio processing phase @@ -493,14 +493,14 @@ test "Complete workflow simulation" { speech_handler.onSpeech("This is a test"); // 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); // 5. Recovery phase speech_handler.onSpeech("Speech recognition resumed"); // 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); // Verify the complete workflow