lib/gluttony.ex

defmodule Gluttony do
  @moduledoc """
  Parses RSS 2.0 and Atom 1.0 feeds.

  # References
  - RSS 2.0 Specs: https://www.rssboard.org/rss-specification
  - Atom 1.0 Specs: https://xml2rfc.tools.ietf.org/public/rfc/html/rfc4287.html

  ## Extra information

  - W3C Feed Docs: https://validator.w3.org/feed/docs/
  - RSS 2.0 and Atom 1.0 compared: http://www.intertwingly.net/wiki/pie/Rss20AndAtom10Compared
  """

  @doc """
  Parses the given string and returns the values.

  # Examples

    Parsing a xml string from a RSS feed:

      {:ok, %{feed: feed, entries: entries}} = Gluttony.parse_string(xml)

    When a error happens, the reason is returned:

      {:error, reason} = Gluttony.parse_string(xml)

    You can retrieve a common result interface by specifing the `raw` option as `false`.
    This might be usefull to retrieve only relevant and common information, since RSS 2.0 and Atom 1.0 are very different specs:

      {:ok, %Gluttony.Feed{}} = Gluttony.parse_string(xml, raw: false)
  """
  def parse_string(xml, opts \\ []), do: parse(xml, opts)

  @doc """
  Fetches the given url and parses the response.
  See `parse_string/2` for more information on the result values.
  """
  def fetch_feed(url, opts \\ []) do
    with {:ok, response} <- HTTPoison.get(url),
         {:ok, result} <- parse(response.body, opts) do
      {:ok, result}
    end
  end

  @doc """
  Parses a stream of input and returns the values.

  # Examples

    Parsing an XML stream from an RSS feed:

      stream = File.stream!("rss_feed.xml", :line, [encoding: :utf8])
      {:ok, %{feed: feed, entries: entries}} = Gluttony.parse_stream(stream)
  """
  def parse_stream(stream, opts \\ []) do
    case Saxy.parse_stream(stream, Gluttony.Parser, opts) do
      {:ok, result} -> {:ok, result}
      {:halt, reason, _buffer} -> {:error, reason}
      {:error, reason} -> {:error, reason}
    end
  end

  defp parse(xml, opts) do
    case Saxy.parse_string(xml, Gluttony.Parser, opts) do
      {:ok, result} -> {:ok, result}
      {:halt, reason, _buffer} -> {:error, reason}
      {:error, reason} -> {:error, reason}
    end
  end
end