Current section
Files
Jump to
Current section
Files
lib/lotus/export/value_postgres.ex
# Postgres-specific type normalizers
# Only compiled when Postgrex types are available
if Code.ensure_loaded?(Postgrex.Range) do
defimpl Lotus.Export.Normalizer, for: Postgrex.Range do
def normalize(%Postgrex.Range{} = range) do
lower_bound = if range.lower_inclusive, do: "[", else: "("
upper_bound = if range.upper_inclusive, do: "]", else: ")"
lower = format_range_value(range.lower)
upper = format_range_value(range.upper)
"#{lower_bound}#{lower},#{upper}#{upper_bound}"
end
defp format_range_value(nil), do: ""
defp format_range_value(:unbound), do: ""
defp format_range_value(value) do
Lotus.Export.Normalizer.normalize(value)
end
end
end
if Code.ensure_loaded?(Postgrex.INET) do
defimpl Lotus.Export.Normalizer, for: Postgrex.INET do
def normalize(%Postgrex.INET{address: address, netmask: netmask}) do
addr_str = format_address(address)
case netmask do
nil -> addr_str
# IPv4 full mask
32 -> addr_str
# IPv6 full mask
128 -> addr_str
mask -> "#{addr_str}/#{mask}"
end
end
defp format_address(address) when is_tuple(address) do
case tuple_size(address) do
# IPv4
4 ->
address |> Tuple.to_list() |> Enum.join(".")
# IPv6
8 ->
address
|> Tuple.to_list()
|> Enum.map(&Integer.to_string(&1, 16))
|> Enum.join(":")
end
end
end
end
if Code.ensure_loaded?(Postgrex.Interval) do
defimpl Lotus.Export.Normalizer, for: Postgrex.Interval do
# Postgrex.Interval has: months, days, secs, microsecs
def normalize(%Postgrex.Interval{} = interval) do
parts = build_date_parts(interval)
time_parts = build_time_parts(interval)
format_duration(parts, time_parts)
end
defp build_date_parts(interval) do
[]
|> add_years_months(interval.months || 0)
|> add_days(interval.days)
end
defp add_years_months(parts, 0), do: parts
defp add_years_months(parts, total_months) do
years = div(total_months, 12)
months = rem(total_months, 12)
parts
|> add_if_nonzero(years, "Y")
|> add_if_nonzero(months, "M")
end
defp add_days(parts, nil), do: parts
defp add_days(parts, 0), do: parts
defp add_days(parts, days), do: parts ++ ["#{days}D"]
defp add_if_nonzero(parts, 0, _suffix), do: parts
defp add_if_nonzero(parts, value, suffix), do: parts ++ ["#{value}#{suffix}"]
defp build_time_parts(interval) do
total_microsecs = (interval.secs || 0) * 1_000_000 + (interval.microsecs || 0)
format_time_parts(total_microsecs)
end
defp format_duration([], []), do: "P0D"
defp format_duration(parts, []), do: "P" <> Enum.join(parts)
defp format_duration(parts, time_parts) do
"P" <> Enum.join(parts ++ ["T"] ++ time_parts)
end
defp format_time_parts(0), do: []
defp format_time_parts(microsecs) do
total_secs = div(microsecs, 1_000_000)
remaining_microsecs = rem(microsecs, 1_000_000)
hours = div(total_secs, 3600)
minutes = div(rem(total_secs, 3600), 60)
seconds = rem(total_secs, 60)
parts = []
parts =
if seconds != 0 or remaining_microsecs != 0 do
if remaining_microsecs != 0 do
# Format with fractional seconds
frac_seconds = seconds + remaining_microsecs / 1_000_000
parts ++ ["#{Float.to_string(frac_seconds)}S"]
else
parts ++ ["#{seconds}S"]
end
else
parts
end
parts = if minutes != 0, do: ["#{minutes}M"] ++ parts, else: parts
parts = if hours != 0, do: ["#{hours}H"] ++ parts, else: parts
parts
end
end
end
if Code.ensure_loaded?(Postgrex.Point) do
defimpl Lotus.Export.Normalizer, for: Postgrex.Point do
def normalize(%Postgrex.Point{x: x, y: y}) do
"(#{x},#{y})"
end
end
end
if Code.ensure_loaded?(Postgrex.LineSegment) do
defimpl Lotus.Export.Normalizer, for: Postgrex.LineSegment do
def normalize(%Postgrex.LineSegment{point1: p1, point2: p2}) do
"[#{Lotus.Export.Normalizer.normalize(p1)},#{Lotus.Export.Normalizer.normalize(p2)}]"
end
end
end
if Code.ensure_loaded?(Postgrex.Box) do
defimpl Lotus.Export.Normalizer, for: Postgrex.Box do
def normalize(%Postgrex.Box{upper_right: ur, bottom_left: bl}) do
"(#{Lotus.Export.Normalizer.normalize(ur)},#{Lotus.Export.Normalizer.normalize(bl)})"
end
end
end
if Code.ensure_loaded?(Postgrex.Path) do
defimpl Lotus.Export.Normalizer, for: Postgrex.Path do
def normalize(%Postgrex.Path{points: points, open: open}) do
points_str =
points
|> Enum.map(&Lotus.Export.Normalizer.normalize/1)
|> Enum.join(",")
if open do
"[#{points_str}]"
else
"(#{points_str})"
end
end
end
end
if Code.ensure_loaded?(Postgrex.Polygon) do
defimpl Lotus.Export.Normalizer, for: Postgrex.Polygon do
def normalize(%Postgrex.Polygon{vertices: vertices}) do
points_str =
vertices
|> Enum.map(&Lotus.Export.Normalizer.normalize/1)
|> Enum.join(",")
"(#{points_str})"
end
end
end
if Code.ensure_loaded?(Postgrex.Circle) do
defimpl Lotus.Export.Normalizer, for: Postgrex.Circle do
def normalize(%Postgrex.Circle{center: center, radius: radius}) do
"<#{Lotus.Export.Normalizer.normalize(center)},#{radius}>"
end
end
end