Refactor conditionals to use multi-clause pattern matching

Replace case/cond/if blocks with multi-clause function dispatch
and pattern matching in function heads across 9 modules:
- WeatherUnits: case to multi-clause unit_system/1, unit_labels via do_unit_labels/1
- PacketUtils: if to pattern match on %{"id" => id}, multi-clause threshold/callsign
- SetLocale: case to multi-clause extract_locale/1
- IPGeolocation: if/cond to pattern match on conn, binary prefix matching for private_ip?
- DeviceIdentification: cond to extracted pattern_matches?/2
- MobileChannel: cond to multi-clause do_callsign_match/2
- PacketProcessor: cond to dispatch_visibility/6 on {in_bounds, has_marker} tuple
- PacketManager: destructure packet_state in heads, multi-clause maybe_cleanup/3
- AprsSymbol: case/if to multi-clause get_table_id/1, normalize_symbol_code/1
This commit is contained in:
Graham McIntire 2026-02-10 17:15:49 -06:00
parent 6b624e1365
commit 4366fa36da
No known key found for this signature in database
9 changed files with 171 additions and 256 deletions

View file

@ -221,28 +221,23 @@ defmodule Aprsme.DeviceIdentification do
end end
Enum.find(devices, fn device -> Enum.find(devices, fn device ->
pattern = device.identifier pattern_matches?(device.identifier, identifier)
cond do
String.contains?(pattern, "?") ->
try do
regex = wildcard_pattern_to_regex(pattern)
Regex.match?(regex, identifier)
rescue
_e in Regex.CompileError ->
false
end
String.contains?(pattern, "*") ->
# Compare literally if pattern contains * but not ?
pattern == identifier
true ->
pattern == identifier
end
end) end)
end end
defp pattern_matches?(pattern, identifier) do
if String.contains?(pattern, "?") do
try do
regex = wildcard_pattern_to_regex(pattern)
Regex.match?(regex, identifier)
rescue
_e in Regex.CompileError -> false
end
else
pattern == identifier
end
end
# Converts a pattern with ? wildcards to a regex # Converts a pattern with ? wildcards to a regex
defp wildcard_pattern_to_regex(pattern) when is_binary(pattern) do defp wildcard_pattern_to_regex(pattern) when is_binary(pattern) do
# Replace ? with a placeholder, escape all regex metacharacters except the placeholder, # Replace ? with a placeholder, escape all regex metacharacters except the placeholder,

View file

@ -31,38 +31,37 @@ defmodule AprsmeWeb.AprsSymbol do
background_size: String.t() background_size: String.t()
} }
def get_sprite_info(symbol_table, symbol_code) do def get_sprite_info(symbol_table, symbol_code) do
# For overlay symbols (A-Z, 0-9), display the base symbol with overlay compute_sprite_info(overlay_symbol?(symbol_table), symbol_table, symbol_code)
if symbol_table && String.match?(symbol_table, ~r/^[A-Z0-9]$/) do end
# Use the base symbol from the overlay table, not the overlay character itself
get_overlay_base_symbol_info(symbol_code)
else
# Normal symbol table processing
symbol_table = normalize_symbol_table(symbol_table)
symbol_code = normalize_symbol_code(symbol_code)
# Map symbol table to sprite file ID defp overlay_symbol?(table) when is_binary(table), do: String.match?(table, ~r/^[A-Z0-9]$/)
table_id = get_table_id(symbol_table) defp overlay_symbol?(_), do: false
sprite_file = "/aprs-symbols/aprs-symbols-128-#{table_id}@2x.png" defp compute_sprite_info(true, _symbol_table, symbol_code) do
get_overlay_base_symbol_info(symbol_code)
end
# Get symbol position using ASCII-based calculation defp compute_sprite_info(false, symbol_table, symbol_code) do
symbol_code_ord = get_symbol_code_ord(symbol_code) symbol_table = normalize_symbol_table(symbol_table)
symbol_code = normalize_symbol_code(symbol_code)
index = symbol_code_ord - 33 table_id = get_table_id(symbol_table)
safe_index = max(0, min(index, 93)) sprite_file = "/aprs-symbols/aprs-symbols-128-#{table_id}@2x.png"
# Calculate positioning for 16-column grid symbol_code_ord = get_symbol_code_ord(symbol_code)
column = rem(safe_index, 16) index = symbol_code_ord - 33
row = div(safe_index, 16) safe_index = max(0, min(index, 93))
x = -column * 128
y = -row * 128
%{ column = rem(safe_index, 16)
sprite_file: sprite_file, row = div(safe_index, 16)
background_position: "#{x / 4}px #{y / 4}px", x = -column * 128
background_size: "512px 192px" y = -row * 128
}
end %{
sprite_file: sprite_file,
background_position: "#{x / 4}px #{y / 4}px",
background_size: "512px 192px"
}
end end
@doc """ @doc """
@ -188,9 +187,9 @@ defmodule AprsmeWeb.AprsSymbol do
">" ">"
""" """
@spec normalize_symbol_code(String.t() | nil) :: String.t() @spec normalize_symbol_code(String.t() | nil) :: String.t()
def normalize_symbol_code(symbol_code) do def normalize_symbol_code(nil), do: ">"
if symbol_code && symbol_code != "", do: symbol_code, else: ">" def normalize_symbol_code(""), do: ">"
end def normalize_symbol_code(symbol_code), do: symbol_code
@doc """ @doc """
Maps a symbol table to its sprite file ID. Maps a symbol table to its sprite file ID.
@ -207,18 +206,10 @@ defmodule AprsmeWeb.AprsSymbol do
"2" "2"
""" """
@spec get_table_id(String.t()) :: String.t() @spec get_table_id(String.t()) :: String.t()
def get_table_id(symbol_table) do def get_table_id("/"), do: "0"
case symbol_table do def get_table_id("\\"), do: "1"
# Primary table def get_table_id("]"), do: "2"
"/" -> "0" def get_table_id(_), do: "0"
# Alternate table
"\\" -> "1"
# Overlay table (A-Z, 0-9)
"]" -> "2"
# Default to primary table
_ -> "0"
end
end
@doc """ @doc """
Renders an APRS symbol as HTML for use in Leaflet markers. Renders an APRS symbol as HTML for use in Leaflet markers.

View file

@ -478,27 +478,19 @@ defmodule AprsmeWeb.MobileChannel do
end end
defp callsign_matches?(packet_callsign, tracked_callsign) do defp callsign_matches?(packet_callsign, tracked_callsign) do
# Normalize both to uppercase normalized_packet = String.upcase(packet_callsign)
packet_callsign = String.upcase(packet_callsign) normalized_tracked = String.upcase(tracked_callsign)
tracked_callsign = String.upcase(tracked_callsign) do_callsign_match(normalized_packet, normalized_tracked)
end
cond do defp do_callsign_match(callsign, callsign), do: true
# Exact match
packet_callsign == tracked_callsign ->
true
# Wildcard match (e.g., "W5ISP*" matches "W5ISP-9") defp do_callsign_match(packet_callsign, tracked_callsign) do
String.contains?(tracked_callsign, "*") -> if String.contains?(tracked_callsign, "*") do
pattern = String.replace(tracked_callsign, "*", "") pattern = String.replace(tracked_callsign, "*", "")
String.starts_with?(packet_callsign, pattern) String.starts_with?(packet_callsign, pattern)
else
# Base callsign match (e.g., "W5ISP" matches "W5ISP-9") String.starts_with?(packet_callsign, tracked_callsign <> "-")
String.starts_with?(packet_callsign, tracked_callsign <> "-") ->
true
# No match
true ->
false
end end
end end

View file

@ -35,55 +35,43 @@ defmodule AprsmeWeb.MapLive.PacketManager do
Add packets to visible storage with memory management. Add packets to visible storage with memory management.
Returns updated state and packet IDs for broadcasting. Returns updated state and packet IDs for broadcasting.
""" """
def add_visible_packets(packet_state, packets) when is_list(packets) do def add_visible_packets(%{visible_packet_ids: visible_ids, packet_count: packet_count} = packet_state, packets)
# Store packets and get IDs when is_list(packets) do
packet_ids = PacketStore.store_packets(packets) packet_ids = PacketStore.store_packets(packets)
new_visible_ids = packet_ids ++ visible_ids
# Update visible packet tracking
new_visible_ids = packet_ids ++ packet_state.visible_packet_ids
# Apply memory management
{managed_ids, cleanup_needed} = apply_memory_limits(new_visible_ids, @max_visible_packets) {managed_ids, cleanup_needed} = apply_memory_limits(new_visible_ids, @max_visible_packets)
updated_state = %{ updated_state = %{
packet_state packet_state
| visible_packet_ids: managed_ids, | visible_packet_ids: managed_ids,
packet_count: Map.put(packet_state.packet_count, :visible, length(managed_ids)) packet_count: Map.put(packet_count, :visible, length(managed_ids))
} }
# Clean up if needed final_state = maybe_cleanup(updated_state, cleanup_needed, :visible)
final_state =
if cleanup_needed do
perform_cleanup(updated_state, :visible)
else
updated_state
end
{final_state, packet_ids} {final_state, packet_ids}
end end
@doc """ @doc """
Add packets to historical storage with sliding window management. Add packets to historical storage with sliding window management.
""" """
def add_historical_packets(packet_state, packets) when is_list(packets) do def add_historical_packets(
%{historical_packet_ids: historical_ids, packet_count: packet_count} = packet_state,
packets
)
when is_list(packets) do
packet_ids = PacketStore.store_packets(packets) packet_ids = PacketStore.store_packets(packets)
new_historical_ids = packet_ids ++ historical_ids
new_historical_ids = packet_ids ++ packet_state.historical_packet_ids
{managed_ids, cleanup_needed} = apply_memory_limits(new_historical_ids, @max_historical_packets) {managed_ids, cleanup_needed} = apply_memory_limits(new_historical_ids, @max_historical_packets)
updated_state = %{ updated_state = %{
packet_state packet_state
| historical_packet_ids: managed_ids, | historical_packet_ids: managed_ids,
packet_count: Map.put(packet_state.packet_count, :historical, length(managed_ids)) packet_count: Map.put(packet_count, :historical, length(managed_ids))
} }
final_state = final_state = maybe_cleanup(updated_state, cleanup_needed, :historical)
if cleanup_needed do
perform_cleanup(updated_state, :historical)
else
updated_state
end
{final_state, packet_ids} {final_state, packet_ids}
end end
@ -165,15 +153,15 @@ defmodule AprsmeWeb.MapLive.PacketManager do
end end
end end
defp perform_cleanup(packet_state, _type) do defp maybe_cleanup(state, false, _type), do: state
defp maybe_cleanup(%{last_cleanup: last_cleanup} = state, true, _type) do
current_time = System.monotonic_time(:millisecond) current_time = System.monotonic_time(:millisecond)
# Only cleanup if enough time has passed to avoid excessive cleanup if current_time - last_cleanup > 30_000 do
# 30 seconds %{state | last_cleanup: current_time}
if current_time - packet_state.last_cleanup > 30_000 do
%{packet_state | last_cleanup: current_time}
else else
packet_state state
end end
end end

View file

@ -51,35 +51,40 @@ defmodule AprsmeWeb.MapLive.PacketProcessor do
""" """
@spec handle_packet_visibility(map(), number() | nil, number() | nil, binary(), Socket.t()) :: Socket.t() @spec handle_packet_visibility(map(), number() | nil, number() | nil, binary(), Socket.t()) :: Socket.t()
def handle_packet_visibility(packet, lat, lon, callsign_key, socket) do def handle_packet_visibility(packet, lat, lon, callsign_key, socket) do
cond do visibility_action(packet, lat, lon, callsign_key, socket)
should_remove_marker?(lat, lon, callsign_key, socket) -> end
remove_marker_from_map(callsign_key, socket)
should_add_marker?(lat, lon, callsign_key, socket) -> defp visibility_action(packet, lat, lon, callsign_key, socket) when not is_nil(lat) and not is_nil(lon) do
handle_valid_postgres_packet(packet, lat, lon, socket) in_bounds = within_bounds?(%{lat: lat, lon: lon}, socket.assigns.map_bounds)
has_marker = Map.has_key?(socket.assigns.visible_packets, callsign_key)
dispatch_visibility({in_bounds, has_marker}, packet, lat, lon, callsign_key, socket)
end
should_update_marker?(lat, lon, callsign_key, socket) -> defp visibility_action(_packet, _lat, _lon, _callsign_key, socket), do: socket
# Marker exists and is within bounds - check if there's significant movement
existing_packet = socket.assigns.visible_packets[callsign_key]
{existing_lat, existing_lon, _} = CoordinateUtils.get_coordinates(existing_packet)
# Check if we have valid existing coordinates defp dispatch_visibility({false, true}, _packet, _lat, _lon, callsign_key, socket) do
if is_number(existing_lat) and is_number(existing_lon) and remove_marker_from_map(callsign_key, socket)
GeoUtils.significant_movement?(existing_lat, existing_lon, lat, lon, 50) do end
# Significant movement detected (more than 50 meters), update the marker
handle_valid_postgres_packet(packet, lat, lon, socket)
else
# Just GPS drift (less than 50 meters) or invalid coordinates,
# update the packet data but don't send visual update
new_visible_packets = Map.put(socket.assigns.visible_packets, callsign_key, packet)
assign(socket, :visible_packets, new_visible_packets)
end
true -> defp dispatch_visibility({true, false}, packet, lat, lon, _callsign_key, socket) do
socket handle_valid_postgres_packet(packet, lat, lon, socket)
end
defp dispatch_visibility({true, true}, packet, lat, lon, callsign_key, socket) do
existing_packet = socket.assigns.visible_packets[callsign_key]
{existing_lat, existing_lon, _} = CoordinateUtils.get_coordinates(existing_packet)
if is_number(existing_lat) and is_number(existing_lon) and
GeoUtils.significant_movement?(existing_lat, existing_lon, lat, lon, 50) do
handle_valid_postgres_packet(packet, lat, lon, socket)
else
new_visible_packets = Map.put(socket.assigns.visible_packets, callsign_key, packet)
assign(socket, :visible_packets, new_visible_packets)
end end
end end
defp dispatch_visibility(_, _packet, _lat, _lon, _callsign_key, socket), do: socket
@doc """ @doc """
Handle valid postgres packet by adding it to visible packets and displaying it. Handle valid postgres packet by adding it to visible packets and displaying it.
""" """
@ -128,24 +133,6 @@ defmodule AprsmeWeb.MapLive.PacketProcessor do
# Private helper functions # Private helper functions
defp should_remove_marker?(lat, lon, callsign_key, socket) do
!is_nil(lat) and !is_nil(lon) and
Map.has_key?(socket.assigns.visible_packets, callsign_key) and
not within_bounds?(%{lat: lat, lon: lon}, socket.assigns.map_bounds)
end
defp should_add_marker?(lat, lon, callsign_key, socket) do
!is_nil(lat) and !is_nil(lon) and
not Map.has_key?(socket.assigns.visible_packets, callsign_key) and
within_bounds?(%{lat: lat, lon: lon}, socket.assigns.map_bounds)
end
defp should_update_marker?(lat, lon, callsign_key, socket) do
!is_nil(lat) and !is_nil(lon) and
Map.has_key?(socket.assigns.visible_packets, callsign_key) and
within_bounds?(%{lat: lat, lon: lon}, socket.assigns.map_bounds)
end
# Use shared bounds utility # Use shared bounds utility
defp within_bounds?(coords, bounds) do defp within_bounds?(coords, bounds) do
BoundsUtils.within_bounds?(coords, bounds) BoundsUtils.within_bounds?(coords, bounds)

View file

@ -10,11 +10,8 @@ defmodule AprsmeWeb.Live.Shared.PacketUtils do
Get unique callsign key from packet. Get unique callsign key from packet.
""" """
@spec get_callsign_key(map()) :: binary() @spec get_callsign_key(map()) :: binary()
def get_callsign_key(packet) do def get_callsign_key(%{"id" => id}), do: to_string(id)
if Map.has_key?(packet, "id"), def get_callsign_key(_packet), do: [:positive] |> System.unique_integer() |> to_string()
do: to_string(packet["id"]),
else: [:positive] |> System.unique_integer() |> to_string()
end
@doc """ @doc """
Prune oldest packets from a map to enforce memory limits. Prune oldest packets from a map to enforce memory limits.
@ -155,18 +152,13 @@ defmodule AprsmeWeb.Live.Shared.PacketUtils do
Check if a packet is within the time threshold (not too old). Check if a packet is within the time threshold (not too old).
""" """
@spec packet_within_time_threshold?(struct(), any()) :: boolean() @spec packet_within_time_threshold?(struct(), any()) :: boolean()
def packet_within_time_threshold?(packet, threshold) do def packet_within_time_threshold?(%{received_at: received_at}, threshold) when not is_nil(received_at) do
case packet do threshold_dt = convert_threshold_to_datetime(threshold)
%{received_at: received_at} when not is_nil(received_at) -> DateTime.compare(received_at, threshold_dt) in [:gt, :eq]
threshold_dt = convert_threshold_to_datetime(threshold)
DateTime.compare(received_at, threshold_dt) in [:gt, :eq]
_ ->
# If no timestamp, treat as current
true
end
end end
def packet_within_time_threshold?(_packet, _threshold), do: true
defp convert_threshold_to_datetime(threshold) when is_integer(threshold) do defp convert_threshold_to_datetime(threshold) when is_integer(threshold) do
# Assume seconds since epoch # Assume seconds since epoch
DateTime.from_unix!(threshold) DateTime.from_unix!(threshold)
@ -228,11 +220,10 @@ defmodule AprsmeWeb.Live.Shared.PacketUtils do
def generate_callsign(packet) do def generate_callsign(packet) do
base_callsign = get_packet_field(packet, :base_callsign, "") base_callsign = get_packet_field(packet, :base_callsign, "")
ssid = get_packet_field(packet, :ssid, nil) ssid = get_packet_field(packet, :ssid, nil)
format_callsign_with_ssid(base_callsign, ssid)
if ssid != nil and ssid != "" do
"#{base_callsign}-#{ssid}"
else
base_callsign
end
end end
defp format_callsign_with_ssid(base_callsign, nil), do: base_callsign
defp format_callsign_with_ssid(base_callsign, ""), do: base_callsign
defp format_callsign_with_ssid(base_callsign, ssid), do: "#{base_callsign}-#{ssid}"
end end

View file

@ -13,23 +13,17 @@ defmodule AprsmeWeb.Plugs.IPGeolocation do
def init(opts), do: opts def init(opts), do: opts
@impl true @impl true
def call(conn, _opts) do def call(%{request_path: "/", method: "GET"} = conn, _opts) do
# Only run for the main map page conn
if conn.request_path == "/" && conn.method == "GET" do |> get_session(:ip_geolocation)
case get_session(conn, :ip_geolocation) do |> handle_geolocation(conn)
nil ->
# No cached geolocation, fetch it
perform_geolocation(conn)
_cached ->
# Already have geolocation in session
conn
end
else
conn
end
end end
def call(conn, _opts), do: conn
defp handle_geolocation(nil, conn), do: perform_geolocation(conn)
defp handle_geolocation(_cached, conn), do: conn
defp perform_geolocation(conn) do defp perform_geolocation(conn) do
ip = get_client_ip(conn) ip = get_client_ip(conn)
Logger.info("IP geolocation: Starting lookup for IP #{ip}") Logger.info("IP geolocation: Starting lookup for IP #{ip}")
@ -133,16 +127,12 @@ defmodule AprsmeWeb.Plugs.IPGeolocation do
defp valid_ip_for_geolocation?(_), do: false defp valid_ip_for_geolocation?(_), do: false
defp private_ip?(ip) do defp private_ip?("127." <> _), do: true
cond do defp private_ip?("::1" <> _), do: true
String.starts_with?(ip, "127.") -> true defp private_ip?("10." <> _), do: true
String.starts_with?(ip, "::1") -> true defp private_ip?("192.168." <> _), do: true
String.starts_with?(ip, "10.") -> true defp private_ip?("172." <> _ = ip), do: in_172_private_range?(ip)
String.starts_with?(ip, "192.168.") -> true defp private_ip?(_), do: false
String.starts_with?(ip, "172.") -> in_172_private_range?(ip)
true -> false
end
end
# Check if IP is in 172.16.0.0/12 range (172.16.0.0 - 172.31.255.255) # Check if IP is in 172.16.0.0/12 range (172.16.0.0 - 172.31.255.255)
defp in_172_private_range?(ip) do defp in_172_private_range?(ip) do

View file

@ -17,15 +17,14 @@ defmodule AprsmeWeb.Plugs.SetLocale do
end end
defp get_locale_from_header(conn) do defp get_locale_from_header(conn) do
case get_req_header(conn, "accept-language") do conn
[accept_language | _] -> |> get_req_header("accept-language")
parse_accept_language(accept_language) |> extract_locale()
_ ->
nil
end
end end
defp extract_locale([accept_language | _]), do: parse_accept_language(accept_language)
defp extract_locale(_), do: nil
defp parse_accept_language(accept_language) do defp parse_accept_language(accept_language) do
accept_language accept_language
|> String.split(",") |> String.split(",")

View file

@ -11,21 +11,11 @@ defmodule AprsmeWeb.WeatherUnits do
Returns :imperial for US, :metric for most other countries. Returns :imperial for US, :metric for most other countries.
""" """
@spec unit_system(String.t() | nil | any()) :: :imperial | :metric @spec unit_system(String.t() | nil | any()) :: :imperial | :metric
def unit_system(locale) when is_binary(locale) do def unit_system("en"), do: :imperial
case locale do def unit_system("es"), do: :metric
# Default to imperial for English def unit_system("de"), do: :metric
"en" -> :imperial def unit_system("fr"), do: :metric
# Spanish-speaking countries mostly use metric def unit_system(locale) when is_binary(locale), do: :metric
"es" -> :metric
# Germany uses metric
"de" -> :metric
# France uses metric
"fr" -> :metric
# Default to metric for unknown locales
_ -> :metric
end
end
def unit_system(nil), do: :imperial def unit_system(nil), do: :imperial
def unit_system(_), do: :imperial def unit_system(_), do: :imperial
@ -34,12 +24,8 @@ defmodule AprsmeWeb.WeatherUnits do
Returns the temperature in the appropriate unit for the locale. Returns the temperature in the appropriate unit for the locale.
""" """
@spec format_temperature(number() | any(), String.t()) :: {number() | any(), String.t()} @spec format_temperature(number() | any(), String.t()) :: {number() | any(), String.t()}
def format_temperature(temp, locale) when is_number(temp) do def format_temperature(temp, locale) when is_number(temp),
case unit_system(locale) do do: format_by_unit(temp, locale, &Convert.f_to_c/1, "°F", "°C")
:imperial -> {temp, "°F"}
:metric -> {Convert.f_to_c(temp), "°C"}
end
end
def format_temperature(temp, _locale), do: {temp, "°F"} def format_temperature(temp, _locale), do: {temp, "°F"}
@ -48,12 +34,8 @@ defmodule AprsmeWeb.WeatherUnits do
Returns the wind speed in the appropriate unit for the locale. Returns the wind speed in the appropriate unit for the locale.
""" """
@spec format_wind_speed(number() | any(), String.t()) :: {number() | any(), String.t()} @spec format_wind_speed(number() | any(), String.t()) :: {number() | any(), String.t()}
def format_wind_speed(speed, locale) when is_number(speed) do def format_wind_speed(speed, locale) when is_number(speed),
case unit_system(locale) do do: format_by_unit(speed, locale, &Convert.mph_to_kph/1, "mph", "km/h")
:imperial -> {speed, "mph"}
:metric -> {Convert.mph_to_kph(speed), "km/h"}
end
end
def format_wind_speed(speed, _locale), do: {speed, "mph"} def format_wind_speed(speed, _locale), do: {speed, "mph"}
@ -62,12 +44,8 @@ defmodule AprsmeWeb.WeatherUnits do
Returns the rain amount in the appropriate unit for the locale. Returns the rain amount in the appropriate unit for the locale.
""" """
@spec format_rain(number() | any(), String.t()) :: {number() | any(), String.t()} @spec format_rain(number() | any(), String.t()) :: {number() | any(), String.t()}
def format_rain(rain, locale) when is_number(rain) do def format_rain(rain, locale) when is_number(rain),
case unit_system(locale) do do: format_by_unit(rain, locale, &Convert.inches_to_mm/1, "in", "mm")
:imperial -> {rain, "in"}
:metric -> {Convert.inches_to_mm(rain), "mm"}
end
end
def format_rain(rain, _locale), do: {rain, "in"} def format_rain(rain, _locale), do: {rain, "in"}
@ -75,10 +53,7 @@ defmodule AprsmeWeb.WeatherUnits do
Formats pressure (keeps hPa for all locales as it's standard). Formats pressure (keeps hPa for all locales as it's standard).
""" """
@spec format_pressure(number() | any(), any()) :: {number() | any(), String.t()} @spec format_pressure(number() | any(), any()) :: {number() | any(), String.t()}
def format_pressure(pressure, _locale) when is_number(pressure) do def format_pressure(pressure, _locale) when is_number(pressure), do: {pressure, "hPa"}
{pressure, "hPa"}
end
def format_pressure(pressure, _locale), do: {pressure, "hPa"} def format_pressure(pressure, _locale), do: {pressure, "hPa"}
@doc """ @doc """
@ -90,23 +65,30 @@ defmodule AprsmeWeb.WeatherUnits do
rain: String.t(), rain: String.t(),
pressure: String.t() pressure: String.t()
} }
def unit_labels(locale) do def unit_labels(locale), do: do_unit_labels(unit_system(locale))
case unit_system(locale) do
:imperial ->
%{
temperature: "°F",
wind_speed: "mph",
rain: "in",
pressure: "hPa"
}
:metric -> defp do_unit_labels(:imperial) do
%{ %{
temperature: "°C", temperature: "°F",
wind_speed: "km/h", wind_speed: "mph",
rain: "mm", rain: "in",
pressure: "hPa" pressure: "hPa"
} }
end
defp do_unit_labels(:metric) do
%{
temperature: "°C",
wind_speed: "km/h",
rain: "mm",
pressure: "hPa"
}
end
defp format_by_unit(value, locale, converter, imperial_unit, metric_unit) do
case unit_system(locale) do
:imperial -> {value, imperial_unit}
:metric -> {converter.(value), metric_unit}
end end
end end
end end