Packages

An Elixir library for translating Cron expressions into human-readable text in multiple languages.

Current section

Files

Jump to
cronstrue_ex lib cronstrue_ex.ex
Raw

lib/cronstrue_ex.ex

defmodule CronstrueEx do
@moduledoc """
Converts standard cron expressions into human-readable descriptions.
CronstrueEx currently supports five-field cron expressions in English and
Spanish. It describes a schedule but does not calculate its next execution
time.
"""
alias CronstrueEx.{Descriptor, ParseError, Parser}
alias CronstrueEx.Locales.{En, Es}
@default_options [
locale: "en",
verbose: false,
use_24_hour_time_format: nil,
trim_hours_leading_zero: false,
throw_exception_on_parse_error: true
]
@doc """
Converts a five-field cron expression to a human-readable description.
The supported options are:
* `:locale` - accepts English (`"en"`/`:en`) or Spanish (`"es"`/`:es`)
* `:verbose` - includes wording normally omitted for brevity
* `:use_24_hour_time_format` - overrides the locale's default clock format
* `:trim_hours_leading_zero` - removes the leading zero from hours
* `:throw_exception_on_parse_error` - returns a localized generic error
description instead of raising when set to `false`
## Examples
iex> CronstrueEx.to_string("* * * * *")
"Every minute"
iex> CronstrueEx.to_string("*/5 * * * *")
"Every 5 minutes"
iex> CronstrueEx.to_string("30 11 * * 1-5")
"At 11:30 AM, Monday through Friday"
iex> CronstrueEx.to_string("30 22 * * *", use_24_hour_time_format: true)
"At 22:30"
iex> CronstrueEx.to_string("*/5 * * * *", locale: "es")
"Cada 5 minutos"
"""
@spec to_string(String.t(), keyword()) :: String.t()
def to_string(expression, options \\ []) do
options = normalize_options!(options)
case Parser.parse(expression) do
{:ok, parsed_expression} ->
Descriptor.describe(parsed_expression, options)
{:error, message} ->
handle_parse_error(message, options)
end
end
defp handle_parse_error(message, options) do
if options[:throw_exception_on_parse_error] do
raise ParseError, message: message
else
options[:locale_module].phrase(:parse_error, [])
end
end
defp normalize_options!(options) do
unless Keyword.keyword?(options) do
raise ArgumentError, "options must be a keyword list"
end
allowed_options = Keyword.keys(@default_options)
unknown_options = Keyword.keys(options) -- allowed_options
if unknown_options != [] do
raise ArgumentError, "unknown options: #{inspect(unknown_options)}"
end
normalized = Keyword.merge(@default_options, options)
locale_module = locale_module!(normalized[:locale])
for option <- allowed_options -- [:locale, :use_24_hour_time_format] do
unless is_boolean(normalized[option]) do
raise ArgumentError, "#{inspect(option)} must be a boolean"
end
end
unless is_nil(normalized[:use_24_hour_time_format]) or
is_boolean(normalized[:use_24_hour_time_format]) do
raise ArgumentError, ":use_24_hour_time_format must be a boolean"
end
use_24_hour_time_format =
if is_nil(normalized[:use_24_hour_time_format]) do
locale_module.use_24_hour_time_format_by_default()
else
normalized[:use_24_hour_time_format]
end
normalized
|> Keyword.put(:locale, locale_name(locale_module))
|> Keyword.put(:locale_module, locale_module)
|> Keyword.put(:use_24_hour_time_format, use_24_hour_time_format)
end
defp locale_module!(locale) when locale in ["en", :en], do: En
defp locale_module!(locale) when locale in ["es", :es], do: Es
defp locale_module!(locale) do
raise ArgumentError,
"unsupported locale: #{inspect(locale)}; available locales are English (en) and Spanish (es)"
end
defp locale_name(En), do: "en"
defp locale_name(Es), do: "es"
end