defmodule Drone.Error do
@moduledoc """
Error types and helpers for ex_drone.
All errors in ex_drone follow explicit tuple conventions:
- `{:error, reason}` for simple errors
- `{:error, :safety, reason}` for safety rejections
- `{:error, :invalid_command, details}` for command validation errors
"""
@type safety_reason ::
:command_not_allowed
| :not_in_sdk_mode
| :not_flying
| :already_flying
| :emergency_active
| :max_altitude
| :max_distance
| :low_battery
| :geofence_violation
| :dangerous_without_prop_guards
| :invalid_distance
| :invalid_degrees
| :invalid_speed
| :invalid_seconds
@type adapter_reason ::
:timeout
| :connection_error
| :command_error
| :not_in_sdk_mode
| :not_flying
| :already_flying
| :emergency_active
| :simulated_failure
@type command_reason ::
:invalid_direction
| :invalid_distance
| :invalid_rotation
| :invalid_degrees
| :invalid_speed
| :invalid_flip_direction
| :invalid_query_type
@type reason :: safety_reason() | adapter_reason() | command_reason() | term()
@doc """
Creates a safety error tuple.
"""
@spec safety(safety_reason()) :: {:error, :safety, safety_reason()}
def safety(reason), do: {:error, :safety, reason}
@doc """
Creates an adapter error tuple.
"""
@spec adapter(adapter_reason()) :: {:error, adapter_reason()}
def adapter(reason), do: {:error, reason}
@doc """
Creates an invalid command error tuple.
"""
@spec invalid_command(command_reason()) :: {:error, :invalid_command, command_reason()}
def invalid_command(reason), do: {:error, :invalid_command, reason}
@doc """
Checks if an error is a safety error.
"""
@spec safety_error?(term()) :: boolean()
def safety_error?({:error, :safety, _}), do: true
def safety_error?(_), do: false
@doc """
Checks if an error is an adapter error.
"""
@spec adapter_error?(term()) :: boolean()
def adapter_error?({:error, reason}) when is_atom(reason), do: true
def adapter_error?(_), do: false
@doc """
Checks if an error is an invalid command error.
"""
@spec invalid_command_error?(term()) :: boolean()
def invalid_command_error?({:error, :invalid_command, _}), do: true
def invalid_command_error?(_), do: false
@doc """
Extracts the reason from any error tuple.
"""
@spec reason({:error, atom()} | {:error, :safety, atom()} | {:error, :invalid_command, atom()}) ::
atom()
def reason({:error, :safety, r}), do: r
def reason({:error, :invalid_command, r}), do: r
def reason({:error, r}), do: r
end