Skip to main content

lib/systemd/unit_file/value_parser.ex

defmodule Systemd.UnitFile.ValueParser do
  @moduledoc false

  import NimbleParsec

  digits = ascii_string([?0..?9], min: 1)
  decimal = digits |> optional(ignore(string(".")) |> concat(digits))
  spacing = repeat(choice([string(" "), string("\t")]))

  duration_unit =
    choice([
      string("week"),
      string("day"),
      string("min"),
      string("sec"),
      string("hr"),
      string("us"),
      string("µs"),
      string("ms"),
      string("s"),
      string("m"),
      string("h"),
      string("d"),
      string("w")
    ])

  duration =
    choice([
      string("infinity"),
      decimal |> ignore(spacing) |> optional(duration_unit)
    ])
    |> eos()

  resource_unit = choice(Enum.map(~w(K M G T P E), &string/1))
  resource_quantity = digits |> optional(resource_unit) |> eos()

  positive_integer =
    ascii_string([?1..?9], min: 1) |> concat(repeat(ascii_string([?0..?9], 1))) |> eos()

  percentage = decimal |> ignore(string("%")) |> eos()
  octal_mode = ascii_string([?0..?7], min: 3, max: 4) |> eos()

  defparsecp(:parse_duration_value, duration)
  defparsecp(:parse_resource_quantity_value, resource_quantity)
  defparsecp(:parse_positive_integer_value, positive_integer)
  defparsecp(:parse_percentage_value, percentage)
  defparsecp(:parse_octal_mode_value, octal_mode)

  @doc false
  @spec duration?(String.t()) :: boolean()
  def duration?(value) when is_binary(value) do
    parse_duration_value(value) |> parse_ok?()
  end

  @doc false
  @spec resource_quantity?(String.t()) :: boolean()
  def resource_quantity?(value) when is_binary(value) do
    parse_resource_quantity_value(value) |> parse_ok?()
  end

  @doc false
  @spec positive_integer?(String.t()) :: boolean()
  def positive_integer?(value) when is_binary(value) do
    parse_positive_integer_value(value) |> parse_ok?()
  end

  @doc false
  @spec percentage?(String.t()) :: boolean()
  def percentage?(value) when is_binary(value) do
    parse_percentage_value(value) |> parse_ok?()
  end

  @doc false
  @spec octal_mode?(String.t()) :: boolean()
  def octal_mode?(value) when is_binary(value) do
    parse_octal_mode_value(value) |> parse_ok?()
  end

  defp parse_ok?({:ok, _tokens, "", _context, _line, _offset}), do: true
  defp parse_ok?(_other), do: false
end