aprs.me/lib/aprsme/callsign.ex
Graham McIntire b0832e5a9c
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 2s
refactor: harden packet delivery and operations
2026-07-26 13:11:26 -05:00

45 lines
1.5 KiB
Elixir

defmodule Aprsme.Callsign do
@moduledoc """
Validation and normalization for APRS source identifiers.
RF callsigns follow AX.25's shorter shape, while APRS-IS also carries
application and gateway identifiers. Validation therefore enforces a
conservative transport-safe character set and length without rejecting
established APRS-IS identifiers.
"""
@safe_identifier_regex ~r/^[A-Z0-9]+(?:-[A-Z0-9]+)*$/
@spec valid?(term()) :: boolean()
def valid?(callsign) when is_binary(callsign) do
normalized = normalize(callsign)
byte_size(normalized) in 1..20 and Regex.match?(@safe_identifier_regex, normalized)
end
def valid?(_callsign), do: false
@spec normalize(term()) :: String.t()
def normalize(callsign) when is_binary(callsign), do: callsign |> String.trim() |> String.upcase()
def normalize(_callsign), do: ""
@spec matches?(term(), term()) :: boolean()
def matches?(left, right), do: normalize(left) == normalize(right)
@spec extract_base(term()) :: String.t()
def extract_base(callsign), do: callsign |> extract_parts() |> elem(0)
@spec extract_ssid(term()) :: String.t()
def extract_ssid(callsign), do: callsign |> extract_parts() |> elem(1)
@spec extract_parts(term()) :: {String.t(), String.t()}
def extract_parts(callsign) when is_binary(callsign) do
normalized = normalize(callsign)
case Regex.run(~r/^(.*)-([0-9]+)$/, normalized) do
[_, base, ssid] -> {base, ssid}
_ -> {normalized, "0"}
end
end
def extract_parts(_callsign), do: {"", "0"}
end