Current section
Files
Jump to
Current section
Files
lib/exceptions.ex
defmodule RustyJson.EncodeError do
@moduledoc """
Exception raised when JSON encoding fails.
This exception is raised by `RustyJson.encode!/2` when the input cannot be
encoded to valid JSON.
## Fields
* `:message` - Human-readable error description
## Common Causes
| Error | Cause |
|-------|-------|
| `"Failed to decode binary"` | Binary is not valid UTF-8 |
| `"Non-finite float"` | Float is NaN or Infinity |
| `"Nesting depth exceeds maximum"` | More than 128 levels of nesting |
| `"Unsupported term type"` | Term type cannot be encoded (e.g., PID, Reference) |
## Examples
iex> RustyJson.encode!(<<0xFF>>)
** (RustyJson.EncodeError) Failed to decode binary
iex> RustyJson.encode!(:math.log(-1))
** (RustyJson.EncodeError) Non-finite float
## Handling Errors
Use `RustyJson.encode/2` to get `{:error, reason}` instead of raising:
case RustyJson.encode(data) do
{:ok, json} -> send_response(json)
{:error, reason} -> Logger.error("Encoding failed: \#{reason}")
end
"""
@typedoc """
Encode error exception struct.
"""
@type t :: %__MODULE__{
message: String.t(),
__exception__: true
}
defexception [:message]
@doc """
Creates an `EncodeError` from a tagged error reason.
Matches Jason's `EncodeError.new/1` API for compatibility.
## Examples
iex> RustyJson.EncodeError.new({:duplicate_key, "name"})
%RustyJson.EncodeError{message: "duplicate key: name"}
iex> RustyJson.EncodeError.new({:invalid_byte, ?\\n, "hello\\nworld"})
%RustyJson.EncodeError{message: "invalid byte 0x0A in string: \\"hello\\\\nworld\\""}
"""
@spec new({:duplicate_key, term()} | {:invalid_byte, byte(), String.t()}) :: t()
def new({:duplicate_key, key}) do
%__MODULE__{message: "duplicate key: #{key}"}
end
def new({:invalid_byte, byte, original}) do
%__MODULE__{
message:
"invalid byte 0x#{Integer.to_string(byte, 16) |> String.pad_leading(2, "0")} in string: #{inspect(original)}"
}
end
@impl true
@spec exception(String.t() | keyword()) :: t()
def exception(message) when is_binary(message) do
%__MODULE__{message: message}
end
def exception(opts) when is_list(opts) do
message = Keyword.get(opts, :message, "JSON encode error")
%__MODULE__{message: message}
end
end
defmodule RustyJson.DecodeError do
@moduledoc """
Exception raised when JSON decoding fails.
This exception is raised by `RustyJson.decode!/2` when the input is not valid JSON.
## Fields
* `:message` - Human-readable error description
* `:data` - The original input data that failed to decode
* `:position` - The byte position in the input where the error occurred
* `:token` - A short snippet of input around the error position
## Common Causes
| Error | Cause |
|-------|-------|
| `"Unexpected character at position N"` | Unexpected character where a JSON value was expected |
| `"Expected string key at position N"` | Object key is not a quoted string |
| `"Unexpected end of input"` | JSON is truncated |
| `"Unexpected trailing characters"` | Extra content after valid JSON |
| `"Nesting depth exceeds maximum"` | More than 128 levels of nesting |
## Examples
iex> try do
...> RustyJson.decode!("invalid")
...> rescue
...> e in RustyJson.DecodeError ->
...> {e.message, e.position, e.data}
...> end
{"Unexpected character at position 0", 0, "invalid"}
## Handling Errors
Use `RustyJson.decode/2` to get `{:error, reason}` instead of raising:
case RustyJson.decode(user_input) do
{:ok, data} -> process(data)
{:error, reason} -> Logger.warning("Invalid JSON: \#{reason}")
end
"""
@typedoc """
Decode error exception struct.
"""
@type t :: %__MODULE__{
message: String.t(),
data: String.t() | nil,
position: non_neg_integer() | nil,
token: String.t() | nil,
__exception__: true
}
defexception [:message, :data, :position, :token]
@impl true
@spec exception(String.t() | keyword() | map()) :: t()
def exception(message) when is_binary(message) do
%__MODULE__{message: message, data: nil, position: nil, token: nil}
end
def exception(%{} = attrs) do
struct!(__MODULE__, attrs)
end
def exception(opts) when is_list(opts) do
message = Keyword.get(opts, :message, "JSON decode error")
%__MODULE__{
message: message,
data: Keyword.get(opts, :data),
position: Keyword.get(opts, :position),
token: Keyword.get(opts, :token)
}
end
end