From ff40022f6d097dda15ea0701ea65a569eaf06a0b Mon Sep 17 00:00:00 2001 From: Marcel Otto Date: Wed, 4 Nov 2020 10:26:30 +0100 Subject: [PATCH] Extend serialization API with functions for writing to streams --- lib/rdf/serialization/encoder.ex | 21 +++++++++++++++++++++ lib/rdf/serialization/format.ex | 10 ++++++++++ lib/rdf/serialization/serialization.ex | 18 ++++++++++++++++++ lib/rdf/serialization/writer.ex | 9 +++++++++ 4 files changed, 58 insertions(+) diff --git a/lib/rdf/serialization/encoder.ex b/lib/rdf/serialization/encoder.ex index 99503ba..985f180 100644 --- a/lib/rdf/serialization/encoder.ex +++ b/lib/rdf/serialization/encoder.ex @@ -21,6 +21,17 @@ defmodule RDF.Serialization.Encoder do """ @callback encode!(RDF.Data.t(), keyword) :: String.t() + @doc """ + Serializes a RDF data structure into a stream. + + It should return a stream emitting either strings or iodata of the + serialized RDF data structure. If both forms are supported the form + should be configurable via the `:mode` option. + """ + @callback stream(RDF.Data.t(), keyword) :: Enumerable.t() + + @optional_callbacks stream: 2 + defmacro __using__(_) do quote bind_quoted: [], unquote: true do @behaviour unquote(__MODULE__) @@ -36,6 +47,16 @@ defmodule RDF.Serialization.Encoder do end defoverridable unquote(__MODULE__) + + @before_compile unquote(__MODULE__) + end + end + + defmacro __before_compile__(_env) do + quote do + @stream_support __MODULE__ |> Module.definitions_in() |> Keyword.has_key?(:stream) + @doc false + def stream_support?, do: @stream_support end end end diff --git a/lib/rdf/serialization/format.ex b/lib/rdf/serialization/format.ex index f62ec22..cf2296f 100644 --- a/lib/rdf/serialization/format.ex +++ b/lib/rdf/serialization/format.ex @@ -151,6 +151,16 @@ defmodule RDF.Serialization.Format do @spec write_string!(RDF.Data.t(), keyword) :: String.t() def write_string!(data, opts \\ []), do: Writer.write_string!(encoder(), data, opts) + if @encoder.stream_support?() do + @doc """ + Serializes a RDF data structure to a stream. + + #{@encoder_doc_ref} + """ + @spec write_stream(RDF.Data.t(), keyword) :: Enumerable.t() + def write_stream(data, opts \\ []), do: Writer.write_stream(encoder(), data, opts) + end + @doc """ Serializes a RDF data structure to a file. diff --git a/lib/rdf/serialization/serialization.ex b/lib/rdf/serialization/serialization.ex index 61415b6..3ed7315 100644 --- a/lib/rdf/serialization/serialization.ex +++ b/lib/rdf/serialization/serialization.ex @@ -243,6 +243,24 @@ defmodule RDF.Serialization do end end + @doc """ + Serializes a RDF data structure to a stream. + + The format must be specified with the `format` option and a format name or the + `media_type` option and the media type of the format. + + Please refer to the documentation of the encoder of a RDF serialization format + for format-specific options and what the stream emits. + """ + @spec write_stream(RDF.Data.t(), keyword) :: Enumerable.t() + def write_stream(data, opts) do + with {:ok, format} <- string_format(opts) do + format.write_stream(data, opts) + else + {:error, error} -> raise error + end + end + @doc """ Serializes a RDF data structure to a file. diff --git a/lib/rdf/serialization/writer.ex b/lib/rdf/serialization/writer.ex index 91098ba..b0164ca 100644 --- a/lib/rdf/serialization/writer.ex +++ b/lib/rdf/serialization/writer.ex @@ -19,6 +19,15 @@ defmodule RDF.Serialization.Writer do encoder.encode!(data, opts) end + @spec write_stream(module, RDF.Data.t(), keyword) :: Enumerable.t() + def write_stream(encoder, data, opts \\ []) do + if encoder.stream_support?() do + encoder.stream(data, opts) + else + raise "#{inspect(encoder)} does not support streaming" + end + end + @spec write_file(module, RDF.Data.t(), Path.t(), keyword) :: :ok | {:error, any} def write_file(encoder, data, path, opts \\ []) do with {:ok, encoded_string} <- write_string(encoder, data, opts) do