Current section

Files

Jump to
ex_aws lib ex_aws utils.ex
Raw

lib/ex_aws/utils.ex

defmodule ExAws.Utils do
@moduledoc false
def identity(x, _), do: x
# This isn't tail recursive. However, given that the structures
# being worked upon are relatively shallow, this is ok.
def camelize_keys(opts, kwargs \\ []) do
deep = kwargs[:deep] || false
spec = kwargs[:spec] || %{}
do_cmlz_keys(opts, deep: deep, spec: spec)
end
defp do_cmlz_keys([%{} | _] = opts, deep: deep, spec: spec) do
Enum.map(opts, &do_cmlz_keys(&1, deep: deep, spec: spec))
end
defp do_cmlz_keys(opts = %{}, deep: deep, spec: spec) do
opts
|> Enum.reduce(%{}, fn {k, v}, map ->
if deep do
Map.put(map, camelize_key(k, spec: spec), do_cmlz_keys(v, deep: true, spec: spec))
else
Map.put(map, camelize_key(k, spec: spec), v)
end
end)
end
defp do_cmlz_keys(opts, kwargs) do
try do
opts |> Map.new() |> do_cmlz_keys(kwargs)
rescue
[Protocol.UndefinedError, ArgumentError, FunctionClauseError] -> opts
end
end
def camelize_key(key, [spec: spec] \\ [spec: %{}]) do
spec[key] || key |> maybe_stringify |> camelize
end
def camelize(string) do
string
|> to_charlist
|> Enum.reduce({true, ~c""}, fn
?_, {_, acc} -> {true, acc}
?/, {_, acc} -> {false, [?. | acc]}
char, {false, acc} -> {false, [char | acc]}
char, {true, acc} -> {false, [upcase(char) | acc]}
end)
# charlist
|> elem(1)
# unreverses charlist after reducing
|> Enum.reverse()
|> to_string
end
def upcase(value) when is_atom(value), do: value |> Atom.to_string() |> String.upcase()
def upcase(value) when is_binary(value), do: String.upcase(value)
def upcase(char), do: :string.to_upper(char)
def uuid, do: DateTime.utc_now() |> :erlang.phash2()
def rename_keys(params, mapping) do
mapping = Map.new(mapping)
Enum.map(params, fn {k, v} ->
{Map.get(mapping, k, k), v}
end)
end
def format(params, kwargs \\ []) do
prefix = kwargs[:prefix] || ""
spec = kwargs[:spec] || %{}
case kwargs[:type] || :xml do
:xml ->
xml_format(params, prefix: prefix, spec: spec)
# :json -> json_format(params, prefix: prefix, spec: spec)
end
end
# NOTE: xml_format is not tail call optimized
# but it is unlikely that any AWS params will ever
# be nested enough for this to cause a stack overflow
# Indexed formats
defp xml_format([nested | _] = params, kwargs) when is_map(nested) do
params |> Enum.map(&Map.to_list/1) |> xml_format(kwargs)
end
defp xml_format([nested | _] = params, prefix: pre, spec: spec) when is_list(nested) do
params
|> Stream.with_index(1)
|> Stream.map(fn {params, i} -> {params, Integer.to_string(i)} end)
|> Stream.flat_map(fn {params, i} ->
xml_format(params, prefix: pre <> dot?(pre) <> i, spec: spec)
end)
|> Enum.to_list()
end
defp xml_format([param | _] = params, prefix: pre, spec: spec) when is_tuple(param) do
params
|> Stream.map(fn {key, values} -> {maybe_camelize(key, spec: spec), values} end)
|> Stream.flat_map(fn {key, values} ->
xml_format(values, prefix: pre <> dot?(pre) <> key, spec: spec)
end)
|> Enum.to_list()
end
# Non-indexed formats
defp xml_format(params, kwargs) when is_list(params) do
pre = kwargs[:prefix]
params
|> Stream.with_index(1)
|> Stream.map(fn {value, i} -> {value, Integer.to_string(i)} end)
|> Stream.map(fn {value, i} -> {pre <> dot?(pre) <> i, value} end)
|> Enum.to_list()
end
defp xml_format(params, kwargs) when is_map(params) do
xml_format(Map.to_list(params), kwargs)
end
defp xml_format(value, kwargs), do: [{kwargs[:prefix], value}]
defp dot?(""), do: ""
defp dot?(_), do: "."
def filter_nil_params(opts) do
opts
|> Enum.reject(fn {_key, value} -> value == nil end)
|> Enum.into(%{})
end
def maybe_camelize(elem, kwargs \\ [spec: %{}])
def maybe_camelize(elem, kwargs) when is_atom(elem), do: camelize_key(elem, kwargs)
def maybe_camelize(elem, _) when is_bitstring(elem), do: elem
def maybe_stringify(elem) when is_atom(elem), do: Atom.to_string(elem)
def maybe_stringify(elem) when is_bitstring(elem), do: elem
defmacro __using__(kwargs) do
camelize_inject =
quote do
[spec: unquote(kwargs[:non_standard_keys] || %{})] ++ kwargs
end
format_inject =
quote do
[type: unquote(kwargs[:format_type] || :xml)] ++ unquote(camelize_inject)
end
quote do
import ExAws.Utils,
except: [
format: 2,
format: 1,
camelize_keys: 2,
camelize_keys: 1,
camelize_key: 2,
camelize_key: 1,
maybe_camelize: 2,
maybe_camelize: 1
]
defp format(params, kwargs \\ []), do: ExAws.Utils.format(params, unquote(format_inject))
defp camelize_keys(opts, kwargs \\ []),
do: ExAws.Utils.camelize_keys(opts, unquote(camelize_inject))
defp camelize_key(opts, kwargs \\ []),
do: ExAws.Utils.camelize_key(opts, unquote(camelize_inject))
defp maybe_camelize(opts, kwargs \\ []),
do: ExAws.Utils.maybe_camelize(opts, unquote(camelize_inject))
end
end
end