defmodule OEmbedProviders.Provider do @moduledoc """ Represents an OEmbed provider. """ use OEmbedProviders.Parser alias OEmbedProviders.Provider alias OEmbedProviders.Provider.Endpoint @type t :: %__MODULE__{ provider_name: String.t() | nil, provider_url: String.t() | nil, endpoints: [Endpoint.t()] } defstruct provider_name: nil, provider_url: nil, endpoints: [] @doc """ Convert a map into a `OEmbedProviders.Provider` struct. """ @spec from_map(data :: map()) :: Provider.t() def from_map(data) when is_map(data) do data = atomize_keys(data) Provider |> struct(data) |> parse_endpoints() end defp parse_endpoints(%Provider{endpoints: endpoints} = provider) when is_list(endpoints) do parsed = Enum.map(endpoints, &Endpoint.from_map/1) Map.put(provider, :endpoints, parsed) end defp parse_endpoints(provider), do: provider @doc """ Check if the given URL matches this `Provider`. """ @spec matches?(provider :: Provider.t(), url :: String.t()) :: boolean() def matches?(%Provider{endpoints: endpoints}, url) when is_list(endpoints) do Enum.any?(endpoints, &Endpoint.matches?(&1, url)) end def matches?(_provider, _url), do: false @doc """ Get the `OEmbedProviders.Provider.Endpoint` for a `Provider`. """ @spec find_endpoint(provider :: Provider.t(), url :: String.t()) :: Endpoint.t() | nil def find_endpoint(%Provider{endpoints: endpoints}, url) when is_list(endpoints) do Enum.find(endpoints, &Endpoint.matches?(&1, url)) end def find_endpoint(_provider, _url), do: false @doc """ Construct the OEmbed endpoint URL for the given `Provider` and URL. """ @spec oembed_url(provider :: Provider.t(), url :: String.t()) :: {:ok, String.t()} | {:error, any()} def oembed_url(%Provider{} = provider, url) do format = "json" params = %{"url" => url, "format" => format} q = URI.encode_query(params) with %Endpoint{url: endpoint_url} <- find_endpoint(provider, url) do result = endpoint_url |> String.replace("*", URI.encode_www_form(url)) |> String.replace("{format}", URI.encode_www_form(format)) |> URI.merge("?#{q}") |> URI.to_string() {:ok, result} else nil -> {:error, :no_provider} error -> {:error, error} end end end