Migrate three pure-function modules from Elixir to Gleam: - polling_offset: deterministic hash-based polling offset (pure Gleam, no FFI) - changelog_parser: text parsing logic in Gleam, thin Elixir wrapper for File I/O - user_agent_parser: regex-based UA parsing in Gleam with Erlang FFI for atom-keyed map conversion Add gleam_regexp dependency for regex support in Gleam modules. Update all call sites and tests to use Gleam module atoms. Reviewed-on: graham/towerops-web#103
165 lines
4.4 KiB
Elixir
165 lines
4.4 KiB
Elixir
defmodule SnmpKit.SnmpLib.Error do
|
|
@moduledoc """
|
|
Standard SNMP error handling and error code utilities.
|
|
|
|
Core logic is implemented in Gleam (`:snmpkit@snmp_lib@error`).
|
|
This wrapper provides the idiomatic Elixir API that accepts atoms,
|
|
integers, and strings as error status identifiers.
|
|
"""
|
|
|
|
@gleam :snmpkit@snmp_lib@error
|
|
@ffi :snmpkit_error_ffi
|
|
|
|
@type error_status ::
|
|
:no_error
|
|
| :too_big
|
|
| :no_such_name
|
|
| :bad_value
|
|
| :read_only
|
|
| :gen_err
|
|
| non_neg_integer()
|
|
|
|
@type error_index :: non_neg_integer()
|
|
@type varbind :: {list(), any()}
|
|
@type varbinds :: [varbind()]
|
|
|
|
## Standard Error Code Constants
|
|
|
|
@spec no_error() :: 0
|
|
def no_error, do: 0
|
|
|
|
@spec too_big() :: 1
|
|
def too_big, do: 1
|
|
|
|
@spec no_such_name() :: 2
|
|
def no_such_name, do: 2
|
|
|
|
@spec bad_value() :: 3
|
|
def bad_value, do: 3
|
|
|
|
@spec read_only() :: 4
|
|
def read_only, do: 4
|
|
|
|
@spec gen_err() :: 5
|
|
def gen_err, do: 5
|
|
|
|
## Error Utilities
|
|
|
|
@spec error_name(error_status()) :: String.t()
|
|
def error_name(code) when is_integer(code) do
|
|
code |> @gleam.from_code() |> @gleam.error_name()
|
|
end
|
|
|
|
def error_name(atom) when is_atom(atom) do
|
|
atom |> @ffi.atom_to_error_status() |> @gleam.error_name()
|
|
end
|
|
|
|
@spec error_atom(error_status()) :: atom()
|
|
def error_atom(code) when is_integer(code) do
|
|
code |> @gleam.from_code() |> @ffi.error_status_to_atom()
|
|
end
|
|
|
|
def error_atom(atom) when is_atom(atom), do: atom
|
|
|
|
@spec error_code(atom() | String.t()) :: non_neg_integer()
|
|
def error_code(atom) when is_atom(atom) do
|
|
status = @ffi.atom_to_error_status(atom)
|
|
code = @gleam.error_code(status)
|
|
# UnknownError returns -1 from Gleam; map to gen_err (5) for compatibility
|
|
if code == -1, do: 5, else: code
|
|
end
|
|
|
|
def error_code(name) when is_binary(name) do
|
|
status = @gleam.from_name(name)
|
|
code = @gleam.error_code(status)
|
|
if code == -1, do: 5, else: code
|
|
end
|
|
|
|
def error_code(_), do: 5
|
|
|
|
@spec format_error(error_status(), error_index(), varbinds()) :: String.t()
|
|
def format_error(error_status, error_index, varbinds \\ []) do
|
|
status = to_gleam_status(error_status)
|
|
@gleam.format_error(status, error_index, varbinds)
|
|
end
|
|
|
|
@spec retriable_error?(error_status()) :: boolean()
|
|
def retriable_error?(error_status) do
|
|
error_status |> to_gleam_status() |> @gleam.is_retriable()
|
|
end
|
|
|
|
@spec create_error_response(map(), error_status(), error_index()) ::
|
|
{:ok, map()} | {:error, atom()}
|
|
def create_error_response(request_pdu, error_status, error_index) do
|
|
case validate_request_pdu(request_pdu) do
|
|
:ok ->
|
|
error_code_num =
|
|
if is_integer(error_status), do: error_status, else: error_code(error_status)
|
|
|
|
error_response = %{
|
|
type: :get_response,
|
|
request_id: Map.get(request_pdu, :request_id, 0),
|
|
error_status: error_code_num,
|
|
error_index: error_index,
|
|
varbinds: Map.get(request_pdu, :varbinds, [])
|
|
}
|
|
|
|
{:ok, error_response}
|
|
|
|
{:error, reason} ->
|
|
{:error, reason}
|
|
end
|
|
end
|
|
|
|
@spec valid_error_status?(any()) :: boolean()
|
|
def valid_error_status?(status) when is_integer(status) do
|
|
@gleam.is_valid_code(status)
|
|
end
|
|
|
|
def valid_error_status?(status) when is_atom(status) do
|
|
error_name(status) != "unknown_error"
|
|
end
|
|
|
|
def valid_error_status?(_), do: false
|
|
|
|
@spec all_error_codes() :: [non_neg_integer()]
|
|
def all_error_codes, do: @gleam.all_codes()
|
|
|
|
@spec all_error_atoms() :: [atom()]
|
|
def all_error_atoms do
|
|
[
|
|
:no_error,
|
|
:too_big,
|
|
:no_such_name,
|
|
:bad_value,
|
|
:read_only,
|
|
:gen_err,
|
|
:no_access,
|
|
:wrong_type,
|
|
:wrong_length,
|
|
:wrong_encoding,
|
|
:wrong_value,
|
|
:no_creation,
|
|
:inconsistent_value,
|
|
:resource_unavailable,
|
|
:commit_failed,
|
|
:undo_failed,
|
|
:authorization_error,
|
|
:not_writable,
|
|
:inconsistent_name
|
|
]
|
|
end
|
|
|
|
@spec error_severity(error_status()) :: :info | :warning | :error
|
|
def error_severity(error_status) do
|
|
error_status |> to_gleam_status() |> @gleam.severity() |> String.to_atom()
|
|
end
|
|
|
|
## Private Helpers
|
|
|
|
defp to_gleam_status(code) when is_integer(code), do: @gleam.from_code(code)
|
|
defp to_gleam_status(atom) when is_atom(atom), do: @ffi.atom_to_error_status(atom)
|
|
|
|
defp validate_request_pdu(request_pdu) when is_map(request_pdu), do: :ok
|
|
defp validate_request_pdu(_), do: {:error, :invalid_request_pdu}
|
|
end
|