aprs.me/lib/aprsme/callsign.ex

137 lines
3.2 KiB
Elixir

defmodule Aprsme.Callsign do
@moduledoc """
Utilities for callsign normalization, validation, and manipulation.
"""
@doc """
Normalizes a callsign by trimming whitespace and converting to uppercase.
"""
@spec normalize(String.t() | nil) :: String.t()
def normalize(nil), do: ""
def normalize(callsign) when is_binary(callsign) do
callsign
|> String.trim()
|> String.upcase()
end
def normalize(_), do: ""
@doc """
Validates if a callsign format is reasonable for amateur radio use.
## Examples
iex> Aprsme.Callsign.valid?("W5ABC")
true
iex> Aprsme.Callsign.valid?("W5ABC-15")
true
iex> Aprsme.Callsign.valid?("")
false
iex> Aprsme.Callsign.valid?("A")
false
"""
@spec valid?(String.t() | nil) :: boolean()
def valid?(nil), do: false
def valid?(callsign) when is_binary(callsign) do
trimmed = String.trim(callsign)
# Accept any non-empty callsign
trimmed != ""
end
def valid?(_), do: false
@doc """
Checks if a packet's sender matches the target callsign.
Both callsigns are normalized before comparison.
"""
@spec matches?(String.t() | nil, String.t() | nil) :: boolean()
def matches?(packet_callsign, target_callsign) do
normalize(packet_callsign) == normalize(target_callsign)
end
@doc """
Extracts the base callsign from a full callsign (removes SSID if present).
## Examples
iex> Aprsme.Callsign.extract_base("W5ABC-15")
"W5ABC"
iex> Aprsme.Callsign.extract_base("W5ABC")
"W5ABC"
"""
@spec extract_base(String.t() | nil) :: String.t()
def extract_base(nil), do: ""
def extract_base(callsign) when is_binary(callsign) do
# Split on last hyphen to get base callsign
case String.split(callsign, "-") do
parts when length(parts) > 1 ->
parts |> Enum.drop(-1) |> Enum.join("-")
_ ->
callsign
end
end
def extract_base(_), do: ""
@doc """
Extracts the SSID from a callsign, returning "0" if no SSID is present.
## Examples
iex> Aprsme.Callsign.extract_ssid("W5ABC-15")
"15"
iex> Aprsme.Callsign.extract_ssid("W5ABC")
"0"
"""
@spec extract_ssid(String.t() | nil) :: String.t()
def extract_ssid(nil), do: "0"
def extract_ssid(callsign) when is_binary(callsign) do
# Extract whatever comes after the last hyphen as SSID
case String.split(callsign, "-") do
parts when length(parts) > 1 -> List.last(parts)
_ -> "0"
end
end
def extract_ssid(_), do: "0"
@doc """
Extracts both base callsign and SSID as a tuple.
## Examples
iex> Aprsme.Callsign.extract_parts("W5ABC-15")
{"W5ABC", "15"}
iex> Aprsme.Callsign.extract_parts("W5ABC")
{"W5ABC", "0"}
"""
@spec extract_parts(String.t() | nil) :: {String.t(), String.t()}
def extract_parts(nil), do: {"", "0"}
def extract_parts(callsign) when is_binary(callsign) do
# Split on last hyphen to separate base and SSID
case String.split(callsign, "-") do
parts when length(parts) > 1 ->
ssid = List.last(parts)
base = parts |> Enum.drop(-1) |> Enum.join("-")
{base, ssid}
_ ->
{callsign, "0"}
end
end
def extract_parts(_), do: {"", "0"}
end