defmodule Plug.CacheControl.Helpers do @moduledoc """ Contains helper functions for working with cache-control header on Plug connections. """ alias Plug.CacheControl.Header alias Plug.Conn @typep unit :: :second | :seconds | :minute | :minutes | :hour | :hours | :day | :days | :week | :weeks | :year | :years @typep delta(t) :: {t, integer | {integer(), unit()}} @typep flag(t) :: t | {t, boolean()} @typep flag_directive :: :must_revalidate | :no_cache | :no_store | :no_transform | :proxy_revalidate | :private | :public @typep delta_directive :: :max_age | :s_maxage | :stale_while_revalidate | :stale_if_error @type directive_opt :: flag(flag_directive) | delta(delta_directive) | {:no_cache, String.t()} @doc """ Serializes the cache control directives and sets them on the connection. """ @spec put_cache_control(Conn.t(), [directive_opt()]) :: Conn.t() def put_cache_control(conn, directives) do value = directives |> directives_to_keyword_list() |> Header.new() |> Header.to_string() Conn.put_resp_header(conn, "cache-control", value) end @doc """ Merges directives into the current value of the `cache-control` header. """ @spec merge_cache_control(Conn.t(), [directive_opt()]) :: Conn.t() def merge_cache_control(conn, directives) do current = conn |> Conn.get_resp_header("cache-control") |> List.first("") |> Header.from_string() updated = directives |> directives_to_keyword_list() |> Header.new() new = current |> Header.merge(updated) |> Header.to_string() Conn.put_resp_header(conn, "cache-control", new) end @spec directives_to_keyword_list([directive_opt()]) :: list() defp directives_to_keyword_list(directives) do mapper = fn {key, _} = tuple when is_atom(key) -> tuple key when is_atom(key) -> {key, true} other -> raise ArgumentError, "Options' names must be atoms but got #{inspect(other)}" end :lists.map(mapper, directives) end end