From a361a018a068a11994cb6233724581337153b685 Mon Sep 17 00:00:00 2001 From: Marcel Otto Date: Wed, 4 Nov 2020 21:09:29 +0100 Subject: [PATCH] Add RDF.NQuads.Encoder.stream/2 --- CHANGELOG.md | 3 +- lib/rdf/serializations/nquads_encoder.ex | 49 ++++++++++++++++++++---- test/unit/nquads_encoder_test.exs | 31 +++++++++++++++ 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 657fb80..35bedf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ This project adheres to [Semantic Versioning](http://semver.org/) and ### Added -- serialization functions for reading from and writing to streams +- general serialization functions for reading from and writing to streams + and implementations for N-Triples and N-Quads (Turtle still to come) - `RDF.Dataset.prefixes/1` for getting an aggregated `RDF.PrefixMap` over all graphs - `RDF.PrefixMap.put/3` for adding a prefix mapping and overwrite an existing one - `RDF.BlankNode.value/1` for getting the internal string representation of a blank node diff --git a/lib/rdf/serializations/nquads_encoder.ex b/lib/rdf/serializations/nquads_encoder.ex index 25acc1d..09dbf7c 100644 --- a/lib/rdf/serializations/nquads_encoder.ex +++ b/lib/rdf/serializations/nquads_encoder.ex @@ -8,13 +8,20 @@ defmodule RDF.NQuads.Encoder do @impl RDF.Serialization.Encoder @callback encode(RDF.Data.t(), keyword) :: {:ok, String.t()} | {:error, any} def encode(data, _opts \\ []) do - result = - data - |> Enum.reduce([], &[statement(&1) | &2]) - |> Enum.reverse() - |> Enum.join("\n") + {:ok, + data + |> Enum.reduce([], &[statement(&1) | &2]) + |> Enum.reverse() + |> Enum.join()} + end - {:ok, if(result == "", do: result, else: "#{result}\n")} + @impl RDF.Serialization.Encoder + @spec stream(RDF.Data.t(), keyword) :: Enumerable.t() + def stream(data, opts \\ []) do + case Keyword.get(opts, :mode, :string) do + :string -> Stream.map(data, &statement(&1)) + :iodata -> Stream.map(data, &iolist_statement(&1)) + end end @spec statement(Statement.t()) :: String.t() @@ -25,12 +32,38 @@ defmodule RDF.NQuads.Encoder do end def statement({subject, predicate, object, graph}) do - "#{term(subject)} #{term(predicate)} #{term(object)} #{term(graph)} ." + "#{term(subject)} #{term(predicate)} #{term(object)} #{term(graph)} .\n" end def statement({subject, predicate, object}) do - "#{term(subject)} #{term(predicate)} #{term(object)} ." + "#{term(subject)} #{term(predicate)} #{term(object)} .\n" end defdelegate term(value), to: RDF.NTriples.Encoder + + @spec iolist_statement(Statement.t()) :: iolist + def iolist_statement(statement) + + def iolist_statement({subject, predicate, object, nil}) do + iolist_statement({subject, predicate, object}) + end + + def iolist_statement({subject, predicate, object, graph}) do + [ + iolist_term(subject), + " ", + iolist_term(predicate), + " ", + iolist_term(object), + " ", + iolist_term(graph), + " .\n" + ] + end + + def iolist_statement({subject, predicate, object}) do + [iolist_term(subject), " ", iolist_term(predicate), " ", iolist_term(object), " .\n"] + end + + defdelegate iolist_term(value), to: RDF.NTriples.Encoder end diff --git a/test/unit/nquads_encoder_test.exs b/test/unit/nquads_encoder_test.exs index 901ba23..6d1e5e5 100644 --- a/test/unit/nquads_encoder_test.exs +++ b/test/unit/nquads_encoder_test.exs @@ -14,6 +14,10 @@ defmodule RDF.NQuads.EncoderTest do defvocab EX, base_iri: "http://example.org/#", terms: [], strict: false + test "stream_support?/0" do + assert NQuads.Encoder.stream_support?() + end + describe "serializing a graph" do test "an empty graph is serialized to an empty string" do assert NQuads.Encoder.encode!(Graph.new()) == "" @@ -123,4 +127,31 @@ defmodule RDF.NQuads.EncoderTest do """ end end + + describe "stream/2" do + dataset = + Dataset.new([ + {EX.S1, EX.p1(), EX.O1}, + {EX.S2, EX.p2(), RDF.bnode("foo"), EX.G}, + {EX.S3, EX.p3(), ~L"foo"}, + {EX.S3, EX.p3(), ~L"foo"en, EX.G} + ]) + + expected_result = """ + . + "foo" . + _:foo . + "foo"@en . + """ + + assert NQuads.Encoder.stream(dataset, mode: :string) + |> Enum.to_list() + |> IO.iodata_to_binary() == + expected_result + + assert NQuads.Encoder.stream(dataset, mode: :iodata) + |> Enum.to_list() + |> IO.iodata_to_binary() == + expected_result + end end