defmodule Systemd.Calendar do
@moduledoc "Helpers for building systemd calendar expressions."
@weekdays %{
monday: "Mon",
mon: "Mon",
tuesday: "Tue",
tue: "Tue",
wednesday: "Wed",
wed: "Wed",
thursday: "Thu",
thu: "Thu",
friday: "Fri",
fri: "Fri",
saturday: "Sat",
sat: "Sat",
sunday: "Sun",
sun: "Sun"
}
@doc "Builds a daily calendar expression at a time of day."
@spec daily_at(String.t() | Time.t()) :: String.t()
def daily_at(time), do: "*-*-* #{time!(time)}"
@doc "Builds a weekly calendar expression for a weekday and time of day."
@spec weekly_at(atom() | String.t(), String.t() | Time.t()) :: String.t()
def weekly_at(day, time), do: "#{weekday!(day)} *-*-* #{time!(time)}"
@doc "Builds a monthly calendar expression for a day of month and time of day."
@spec monthly_at(pos_integer(), String.t() | Time.t()) :: String.t()
def monthly_at(day, time) when is_integer(day) and day in 1..31 do
"*-*-#{String.pad_leading(to_string(day), 2, "0")} #{time!(time)}"
end
def monthly_at(day, _time) do
raise ArgumentError, "monthly timer day must be an integer from 1 to 31, got: #{inspect(day)}"
end
defp time!(%Time{} = time) do
time
|> Time.truncate(:second)
|> Time.to_iso8601()
end
defp time!(value) when is_binary(value) do
value
|> normalize_time_string()
|> Time.from_iso8601()
|> case do
{:ok, time} ->
time!(time)
{:error, _reason} ->
raise ArgumentError,
"expected timer time as HH:MM or HH:MM:SS, got: #{inspect(value)}"
end
end
defp time!(value) do
raise ArgumentError, "expected timer time as HH:MM, HH:MM:SS, or Time, got: #{inspect(value)}"
end
defp normalize_time_string(<<_h1, _h2, ?:, _m1, _m2>> = time), do: time <> ":00"
defp normalize_time_string(time), do: time
defp weekday!(day) when is_atom(day) do
case Map.fetch(@weekdays, day) do
{:ok, weekday} -> weekday
:error -> raise ArgumentError, "unknown weekday #{inspect(day)}"
end
end
defp weekday!(day) when is_binary(day) do
weekday = String.downcase(day)
@weekdays
|> Enum.find_value(fn {key, value} -> if Atom.to_string(key) == weekday, do: value end)
|> case do
nil -> raise ArgumentError, "unknown weekday #{inspect(day)}"
value -> value
end
end
end