Files
bDS2/lib/bds/scripting/capabilities/util.ex
2026-05-01 10:47:25 +02:00

302 lines
8.7 KiB
Elixir

defmodule BDS.Scripting.Capabilities.Util do
@moduledoc false
alias BDS.Projects
def project_path(project_id) do
project_id
|> Projects.get_project()
|> Projects.project_data_dir()
end
def sanitize(%DateTime{} = value), do: DateTime.to_iso8601(value)
def sanitize(%_struct{} = struct) do
struct
|> Map.from_struct()
|> Map.drop([:__meta__, :post, :project, :media, :translations])
|> sanitize()
end
def sanitize(map) when is_map(map) do
Map.new(map, fn {key, value} -> {to_string(key), sanitize(value)} end)
end
def sanitize(list) when is_list(list), do: Enum.map(list, &sanitize/1)
def sanitize(value) when is_boolean(value), do: value
def sanitize(value) when is_atom(value), do: Atom.to_string(value)
def sanitize(value), do: value
def sanitize_nilable(nil), do: nil
def sanitize_nilable(value), do: sanitize(value)
def normalize_input(%_struct{} = struct), do: struct |> Map.from_struct() |> normalize_input()
def normalize_input(map) when is_map(map) do
normalized =
Map.new(map, fn {key, value} -> {normalize_input_key(key), normalize_input(value)} end)
if numeric_sequence_map?(normalized) do
normalized
|> Enum.sort_by(fn {key, _value} -> key end)
|> Enum.map(fn {_key, value} -> value end)
else
normalized
end
end
def normalize_input(list) when is_list(list) do
if Enum.all?(list, &match?({key, _value} when is_integer(key) or is_float(key) or is_binary(key) or is_atom(key), &1)) do
normalized =
Map.new(list, fn {key, value} -> {normalize_input_key(key), normalize_input(value)} end)
if numeric_sequence_map?(normalized) do
normalized
|> Enum.sort_by(fn {key, _value} -> key end)
|> Enum.map(fn {_key, value} -> value end)
else
normalized
end
else
Enum.map(list, &normalize_input/1)
end
end
def normalize_input(value) when is_atom(value), do: Atom.to_string(value)
def normalize_input(value), do: value
def normalize_input_key(key) when is_integer(key), do: key
def normalize_input_key(key) when is_float(key) and trunc(key) == key, do: trunc(key)
def normalize_input_key(key) when is_binary(key) do
case Integer.parse(key) do
{integer, ""} -> integer
_other -> key
end
end
def normalize_input_key(key) when is_atom(key), do: Atom.to_string(key)
def normalize_input_key(key), do: key
def numeric_sequence_map?(map) when map == %{}, do: false
def numeric_sequence_map?(map) do
keys = Map.keys(map)
Enum.all?(keys, &is_integer/1) and Enum.sort(keys) == Enum.to_list(1..length(keys))
end
def normalize_map(value) when is_map(value) do
case normalize_input(value) do
normalized when is_map(normalized) -> normalized
_other -> %{}
end
end
def normalize_map(value) when is_list(value) do
if Enum.all?(value, &match?({key, _value} when is_binary(key) or is_atom(key), &1)) do
Map.new(value, fn {key, entry_value} -> {to_string(key), normalize_input(entry_value)} end)
else
%{}
end
end
def normalize_map(_value), do: %{}
def normalize_string_list(value) when is_list(value), do: Enum.map(value, &to_string/1)
def normalize_string_list(value) when is_map(value) do
value
|> normalize_input()
|> case do
normalized when is_list(normalized) -> Enum.map(normalized, &to_string/1)
_other -> []
end
end
def normalize_string_list(_value), do: []
def normalize_search_filters(filters) do
filters
|> normalize_map()
|> Enum.into(%{}, fn {key, value} ->
normalized_key =
case key do
"start_date" -> "from"
"end_date" -> "to"
other -> other
end
{normalized_key, value}
end)
end
def integer_or_default(value, _default) when is_integer(value), do: value
def integer_or_default(value, _default) when is_float(value), do: trunc(value)
def integer_or_default(_value, default), do: default
def string_or_nil(value) when is_binary(value), do: value
def string_or_nil(value) when is_atom(value), do: Atom.to_string(value)
def string_or_nil(value) when is_number(value), do: to_string(value)
def string_or_nil(_value), do: nil
def truthy?(value), do: value in [true, "true", 1, 1.0, "1"]
def pad2(value), do: value |> Integer.to_string() |> String.pad_leading(2, "0")
def blank_to_nil(nil), do: nil
def blank_to_nil(value) when is_binary(value) do
if String.trim(value) == "", do: nil, else: String.trim(value)
end
def blank_to_nil(value), do: value
def maybe_put_query(query, _key, false), do: query
def maybe_put_query(query, _key, nil), do: query
def maybe_put_query(query, key, value), do: Map.put(query, key, value)
def maybe_put_opt(opts, _key, nil), do: opts
def maybe_put_opt(opts, key, value), do: Keyword.put(opts, key, value)
def maybe_put_normalized_list(attrs, key) do
case Map.fetch(attrs, key) do
{:ok, value} -> Map.put(attrs, key, normalize_string_list(value))
:error -> attrs
end
end
def compare_optional(nil, _fun), do: true
def compare_optional(value, fun) when is_function(fun, 1), do: fun.(value)
def parse_datetime(nil), do: nil
def parse_datetime(value) when is_integer(value), do: DateTime.from_unix!(value, :millisecond)
def parse_datetime(value) when is_binary(value) do
case DateTime.from_iso8601(value) do
{:ok, datetime, _offset} -> datetime
_other -> nil
end
end
def parse_datetime(_value), do: nil
def unwrap_result(result, transform \\ &sanitize/1)
def unwrap_result({:ok, value}, transform), do: transform.(value)
def unwrap_result({:error, _reason}, _transform), do: nil
def boolean_result({:ok, _value}), do: true
def boolean_result({:error, _reason}), do: false
def atom_result({:ok, value}, expected_value), do: value == expected_value
def atom_result(_result, _expected_value), do: false
def thumbnail_size(size) do
case blank_to_nil(size) do
"medium" -> :medium
"large" -> :large
"ai" -> :ai
_other -> :small
end
end
def thumbnail_mime(path) do
case Path.extname(path) do
".jpg" -> "image/jpeg"
".jpeg" -> "image/jpeg"
_other -> "image/webp"
end
end
def shell_open_system_path(path) do
{command, args} =
case :os.type() do
{:unix, :darwin} -> {"open", [path]}
{:unix, _other} -> {"xdg-open", [path]}
{:win32, _other} -> {"cmd", ["/c", "start", "", path]}
end
case System.cmd(command, args, stderr_to_stdout: true) do
{_output, 0} -> :ok
{output, status} -> {:error, {status, String.trim(output)}}
end
rescue
error -> {:error, error}
end
def shell_reveal_system_path(path) do
{command, args} =
case :os.type() do
{:unix, :darwin} -> {"open", ["-R", path]}
{:unix, _other} -> {"xdg-open", [Path.dirname(path)]}
{:win32, _other} -> {"explorer", ["/select,", path]}
end
case System.cmd(command, args, stderr_to_stdout: true) do
{_output, 0} -> :ok
{output, status} -> {:error, {status, String.trim(output)}}
end
rescue
error -> {:error, error}
end
def zero_or_one_arg(callback) when is_function(callback, 1) do
fn args, state ->
decoded_args = :luerl.decode_list(args, state)
value = callback.(normalize_input(decoded_args))
:luerl.encode_list([sanitize(value)], state)
end
end
def one_arg(callback) when is_function(callback, 1) do
fn args, state ->
decoded_args = :luerl.decode_list(args, state)
value =
case decoded_args do
[first | _rest] -> callback.(normalize_input(first))
[] -> callback.(nil)
end
:luerl.encode_list([sanitize(value)], state)
end
end
def two_arg(callback) when is_function(callback, 2) do
fn args, state ->
decoded_args = :luerl.decode_list(args, state)
value =
case decoded_args do
[first, second | _rest] -> callback.(normalize_input(first), normalize_input(second))
[first] -> callback.(normalize_input(first), nil)
[] -> callback.(nil, nil)
end
:luerl.encode_list([sanitize(value)], state)
end
end
def three_arg(callback) when is_function(callback, 3) do
fn args, state ->
decoded_args = :luerl.decode_list(args, state)
value =
case decoded_args do
[first, second, third | _rest] ->
callback.(normalize_input(first), normalize_input(second), normalize_input(third))
[first, second] ->
callback.(normalize_input(first), normalize_input(second), nil)
[first] ->
callback.(normalize_input(first), nil, nil)
[] ->
callback.(nil, nil, nil)
end
:luerl.encode_list([sanitize(value)], state)
end
end
end