aboutsummaryrefslogtreecommitdiffstats
path: root/serd
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2018-06-16 10:26:47 -0400
committerDavid Robillard <d@drobilla.net>2018-11-25 22:12:48 +0100
commita70ee297d71d03c004e78aa1061877f231cfd8d2 (patch)
tree7f0095fcda00b69901d1f133e7c8b4ffc358884b /serd
parent535bc536f0541792b0bab84041e7d82eaa457368 (diff)
downloadserd-a70ee297d71d03c004e78aa1061877f231cfd8d2.tar.gz
serd-a70ee297d71d03c004e78aa1061877f231cfd8d2.tar.bz2
serd-a70ee297d71d03c004e78aa1061877f231cfd8d2.zip
WIP: Add C++ bindings
Diffstat (limited to 'serd')
-rw-r--r--serd/serd.hpp1029
1 files changed, 1029 insertions, 0 deletions
diff --git a/serd/serd.hpp b/serd/serd.hpp
new file mode 100644
index 00000000..bb2d48a1
--- /dev/null
+++ b/serd/serd.hpp
@@ -0,0 +1,1029 @@
+/*
+ Copyright 2018 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file serd.hpp C++ API for Serd, a lightweight RDF syntax library.
+*/
+
+#ifndef SERD_SERD_HPP
+#define SERD_SERD_HPP
+
+#include <serd/serd.h>
+
+#include <cassert>
+#include <memory>
+#include <ostream>
+#include <type_traits>
+#include <utility>
+
+/**
+ @defgroup serdxx Serdxx
+ C++ bindings for Serd, a lightweight RDF syntax library.
+ @{
+*/
+
+namespace serd {
+
+class Node;
+
+/**
+ Return status code.
+*/
+enum class Status {
+ Success = SERD_SUCCESS, /**< No error */
+ Failure = SERD_FAILURE, /**< Non-fatal failure */
+ ErrUnknown = SERD_ERR_UNKNOWN, /**< Unknown error */
+ ErrBadSyntax = SERD_ERR_BAD_SYNTAX, /**< Invalid syntax */
+ ErrBadArg = SERD_ERR_BAD_ARG, /**< Invalid argument */
+ ErrNotFound = SERD_ERR_NOT_FOUND, /**< Not found */
+ ErrIdClash = SERD_ERR_ID_CLASH, /**< Clashing blank node IDs */
+ ErrBadCurie = SERD_ERR_BAD_CURIE, /**< Invalid CURIE */
+ ErrInternal = SERD_ERR_INTERNAL /**< Unexpected internal error */
+};
+
+namespace detail {
+
+template <typename T>
+using FreeFunc = void (*)(T*);
+
+template <typename T>
+using CopyFunc = T* (*)(const T*);
+
+template <typename T>
+void
+no_free(T*)
+{
+}
+
+template <typename T>
+struct Deleter
+{
+ Deleter(FreeFunc<T> free_func) : _free_func(free_func) {}
+
+ void operator()(T* ptr)
+ {
+ if (_free_func) {
+ _free_func(ptr);
+ }
+ }
+
+ FreeFunc<T> _free_func;
+};
+
+/** C++ wrapper for a C object. */
+template <typename T, void FreeFunc(T*)>
+class Wrapper
+{
+public:
+ using CType = T;
+
+ Wrapper(T* ptr) : _ptr(ptr, Deleter<T>(FreeFunc)) {}
+
+ Wrapper(const T* ptr) : _ptr(const_cast<T*>(ptr), nullptr) {}
+
+ Wrapper(Wrapper&& wrapper) = default;
+ Wrapper& operator=(Wrapper&& wrapper) = default;
+
+ Wrapper(const Wrapper&) = delete;
+ Wrapper& operator=(const Wrapper&) = delete;
+
+ bool operator==(const Wrapper&) = delete;
+ bool operator!=(const Wrapper&) = delete;
+
+ operator const T*() const { return _ptr.get(); }
+
+ inline T* c_obj() { return _ptr.get(); }
+ inline const T* c_obj() const { return _ptr.get(); }
+
+protected:
+ Wrapper(std::nullptr_t) : _ptr(nullptr, nullptr) {}
+
+ std::unique_ptr<T, Deleter<T>> _ptr;
+};
+
+template <typename T, T* CopyFunc(const T*), void FreeFunc(T*)>
+class Copyable : public Wrapper<T, FreeFunc>
+{
+public:
+ Copyable(T* ptr) : Wrapper<T, FreeFunc>(ptr) {}
+
+ Copyable(const T* ptr) : Wrapper<T, FreeFunc>(ptr) {}
+
+ Copyable(const Copyable& wrapper)
+ : Wrapper<T, FreeFunc>(CopyFunc(wrapper.c_obj()))
+ {
+ }
+
+ Copyable(Copyable&&) = default;
+ Copyable& operator=(Copyable&&) = default;
+
+ Copyable& operator=(const Copyable& wrapper)
+ {
+ this->_ptr = std::unique_ptr<T, Deleter<T>>(CopyFunc(wrapper.c_obj()),
+ FreeFunc);
+ return *this;
+ }
+
+protected:
+ Copyable(std::nullptr_t) : Wrapper<T, FreeFunc>(nullptr) {}
+};
+
+template <typename Traits>
+size_t
+stream_sink(const void* buf, size_t size, size_t nmemb, void* stream)
+{
+ using Stream = std::basic_ostream<char, Traits>;
+
+ try {
+ Stream* const s = static_cast<Stream*>(stream);
+ s->write(static_cast<const char*>(buf), std::streamsize(size * nmemb));
+ if (s->good()) {
+ return nmemb;
+ }
+ } catch (...) {
+ }
+ return 0;
+}
+
+} // namespace detail
+
+/**
+ A simple optional wrapper around a wrapped type with a pointer-like API.
+
+ This works like a typical optional type, but only works with Wrapper types.
+ The fact that these are interally just pointers is used to avoid adding
+ space overhead for an "is_set" flag, like a generic optional class would.
+*/
+template <typename T>
+class Optional
+{
+public:
+ using CType = typename T::CType;
+
+ Optional() : _value() {}
+ Optional(std::nullptr_t) : _value() {}
+ Optional(const T& value) : _value(value) {}
+ Optional(T&& value) : _value(std::move(value)) {}
+
+ Optional(const Optional&) = default;
+ Optional(Optional&&) = default;
+ Optional& operator=(const Optional&) = default;
+ Optional& operator=(Optional&&) = default;
+
+ void reset() { *this = nullptr; }
+
+ const T& operator*() const
+ {
+ assert(_value.c_obj());
+ return _value;
+ }
+
+ T& operator*()
+ {
+ assert(_value.c_obj());
+ return _value;
+ }
+
+ const T* operator->() const
+ {
+ assert(_value.c_obj());
+ return &_value;
+ }
+
+ T* operator->()
+ {
+ assert(_value.c_obj());
+ return &_value;
+ }
+
+ bool operator==(const Optional& optional)
+ {
+ return (!*this && !optional) ||
+ (*this && optional && _value == optional._value);
+ }
+
+ bool operator!=(const Optional& optional) { return !operator==(optional); }
+
+ explicit operator bool() const { return _value.c_obj(); }
+
+ bool operator!() const { return !_value.c_obj(); }
+
+ inline CType* ptr() { return _value.c_obj(); }
+ inline const CType* ptr() const { return _value.c_obj(); }
+
+private:
+ T _value;
+};
+
+/**
+ A temporary reference to a string.
+
+ This is used as a function parameter type to allow passing either
+ `std::string` or `const char*` without requiring multiple wrapper
+ definitions or the copying overhead of `std::string`.
+*/
+struct StringRef
+{
+ StringRef(const char* str) : _ptr(str) {}
+ StringRef(const std::string& str) : _ptr(str.c_str()) {}
+
+ StringRef(const StringRef&) = delete;
+ StringRef& operator=(const StringRef&) = delete;
+
+ operator const char*() const { return _ptr; }
+
+private:
+ const char* const _ptr;
+};
+
+using Type = SerdType;
+using NodeFlags = SerdNodeFlags;
+using StyleFlags = SerdStyleFlags;
+using IndexOptions = SerdIndexOptions;
+using Field = SerdField;
+using StatementFlags = SerdStatementFlags;
+
+class Statement;
+
+/**
+ RDF syntax.
+*/
+enum class Syntax {
+ /**
+ Turtle - Terse RDF Triple Language (UTF-8).
+ @see <a href="http://www.w3.org/TeamSubmission/turtle/">Turtle</a>
+ */
+ Turtle = SERD_TURTLE,
+
+ /**
+ NTriples - Line-based RDF triples (ASCII).
+ @see <a href="http://www.w3.org/TR/rdf-testcases#ntriples">NTriples</a>
+ */
+ NTriples = SERD_NTRIPLES,
+
+ /**
+ NQuads - Line-based RDF quads (UTF-8).
+ @see <a href="https://www.w3.org/TR/n-quads/">NQuads</a>
+ */
+ NQuads = SERD_NQUADS,
+
+ /**
+ TriG - Terse RDF quads (UTF-8).
+ @see <a href="https://www.w3.org/TR/trig/">Trig</a>
+ */
+ TriG = SERD_TRIG
+};
+
+/**
+ Syntax style options.
+
+ The style of the writer output can be controlled by ORing together
+ values from this enumeration. Note that some options are only supported
+ for some syntaxes (e.g. NTriples does not support abbreviation and is
+ always ASCII).
+*/
+enum class Style {
+ ascii = SERD_STYLE_ASCII, /**< Escape all non-ASCII characters. */
+};
+
+inline StyleFlags
+operator|(const Style a, const Style b)
+{
+ return (StyleFlags)(int(a) | int(b));
+}
+
+inline StyleFlags
+operator|(const StyleFlags a, const Style b)
+{
+ return (StyleFlags)(int(a) | int(b));
+}
+
+/**
+ Indexing option.
+*/
+enum class IndexOption {
+ SPO = SERD_SPO, /**< Subject, Predicate, Object */
+ SOP = SERD_SOP, /**< Subject, Object, Predicate */
+ OPS = SERD_OPS, /**< Object, Predicate, Subject */
+ OSP = SERD_OSP, /**< Object, Subject, Predicate */
+ PSO = SERD_PSO, /**< Predicate, Subject, Object */
+ POS = SERD_POS /**< Predicate, Object, Subject */
+};
+
+inline IndexOptions
+operator|(const IndexOption a, const IndexOption b)
+{
+ return (IndexOptions)(int(a) | int(b));
+}
+
+inline IndexOptions
+operator|(const IndexOptions a, const IndexOption b)
+{
+ return (IndexOptions)(int(a) | int(b));
+}
+
+/**
+ @name String Utilities
+ @{
+*/
+
+inline const char*
+strerror(const Status status)
+{
+ return serd_strerror((SerdStatus)status);
+}
+
+/**
+ @}
+ @name URI
+ @{
+*/
+
+inline std::string
+file_uri_parse(const StringRef& uri, std::string* hostname = nullptr)
+{
+ char* c_hostname = nullptr;
+ char* c_path = serd_file_uri_parse(uri, &c_hostname);
+ if (hostname && c_hostname) {
+ *hostname = c_hostname;
+ }
+ const std::string path(c_path);
+ free(c_hostname);
+ free(c_path);
+ return path;
+}
+
+/**
+ @}
+ @name Node
+ @{
+*/
+
+class Node : public detail::Copyable<SerdNode, serd_node_copy, serd_node_free>
+{
+public:
+ explicit Node(SerdNode* ptr) : Copyable(ptr) {}
+ explicit Node(const SerdNode* ptr) : Copyable(ptr) {}
+
+ Type type() const { return serd_node_get_type(c_obj()); }
+ const char* c_str() const { return serd_node_get_string(c_obj()); }
+ std::string str() const { return c_str(); }
+ size_t size() const { return serd_node_get_length(c_obj()); }
+ size_t length() const { return serd_node_get_length(c_obj()); }
+ NodeFlags flags() const { return serd_node_get_flags(c_obj()); }
+
+ Optional<Node> datatype() const
+ {
+ return Node(serd_node_get_datatype(c_obj()));
+ }
+
+ Optional<Node> language() const
+ {
+ return Node(serd_node_get_language(c_obj()));
+ }
+
+ bool operator==(const Node& node) const
+ {
+ return serd_node_equals(c_obj(), node.c_obj());
+ }
+
+ bool operator!=(const Node& node) const
+ {
+ return !serd_node_equals(c_obj(), node.c_obj());
+ }
+
+ bool operator<(const Node& node) const
+ {
+ return serd_node_compare(c_obj(), node.c_obj()) < 0;
+ }
+
+ operator std::string() const
+ {
+ return std::string(serd_node_get_string(c_obj()),
+ serd_node_get_length(c_obj()));
+ }
+
+private:
+ friend class Optional<Node>;
+ Node() : Copyable(nullptr) {}
+};
+
+inline std::ostream&
+operator<<(std::ostream& os, const Node& node)
+{
+ return os << node.c_str();
+}
+
+inline Node
+make_string(const StringRef& str)
+{
+ return Node(serd_new_string(str));
+}
+
+inline Node
+make_plain_literal(const StringRef& str, const StringRef& lang)
+{
+ return Node(serd_new_plain_literal(str, lang));
+}
+
+inline Node
+make_typed_literal(const StringRef& str, const Node& datatype)
+{
+ return Node(serd_new_typed_literal(str, datatype.c_obj()));
+}
+
+inline Node
+make_blank(const StringRef& str)
+{
+ return Node(serd_new_blank(str));
+}
+
+inline Node
+make_curie(const StringRef& str)
+{
+ return Node(serd_new_curie(str));
+}
+
+inline Node
+make_uri(const StringRef& str)
+{
+ return Node(serd_new_uri(str));
+}
+
+inline Node
+make_resolved_uri(const StringRef& str, const Node& base)
+{
+ return Node(serd_new_resolved_uri(str, base.c_obj()));
+}
+
+// TODO: serd_node_resolve?
+
+inline Node
+make_file_uri(const StringRef& path)
+{
+ return Node(serd_new_file_uri(path, nullptr));
+}
+
+inline Node
+make_file_uri(const StringRef& path, const StringRef& hostname)
+{
+ return Node(serd_new_file_uri(path, hostname));
+}
+
+inline Node
+make_relative_uri(const StringRef& str,
+ const Node& base,
+ const Optional<Node>& root = {})
+{
+ return Node(serd_new_relative_uri(str, base.c_obj(), root.ptr()));
+}
+
+inline Node
+make_decimal(double d,
+ unsigned frac_digits,
+ const Optional<Node>& datatype = {})
+{
+ return Node(serd_new_decimal(d, frac_digits, datatype.ptr()));
+}
+
+inline Node
+make_integer(int64_t i, const Optional<Node>& datatype = {})
+{
+ return Node(serd_new_integer(i, datatype.ptr()));
+}
+
+inline Node
+make_blob(const void* buf,
+ size_t size,
+ bool wrap_lines,
+ const Optional<Node>& datatype = {})
+{
+ return Node(serd_new_blob(buf, size, wrap_lines, datatype.ptr()));
+}
+
+/**
+ @}
+ @name World
+ @{
+*/
+
+class World : public detail::Wrapper<SerdWorld, serd_world_free>
+{
+public:
+ World() : Wrapper(serd_world_new()) {}
+
+ explicit World(SerdWorld* ptr) : Wrapper(ptr) {}
+
+ Node get_blank() { return Node(serd_world_get_blank(c_obj())); }
+
+ void set_error_sink(SerdErrorSink error_sink, void* handle)
+ {
+ serd_world_set_error_sink(c_obj(), error_sink, handle);
+ }
+
+ // operator SerdWorld*() { return c_obj(); }
+};
+
+/**
+ @}
+ @name Statement
+ @{
+*/
+
+class Statement : public detail::Wrapper<SerdStatement, detail::no_free>
+{
+public:
+ Statement(const SerdStatement* statement) : Wrapper(statement) {}
+
+ Node node(Field field)
+ {
+ return Node(serd_statement_get_node(c_obj(), field));
+ }
+
+ Node subject() const { return Node(serd_statement_get_subject(c_obj())); }
+
+ Node predicate() const
+ {
+ return Node(serd_statement_get_predicate(c_obj()));
+ }
+
+ Node object() const { return Node(serd_statement_get_object(c_obj())); }
+
+ Node graph() const { return Node(serd_statement_get_graph(c_obj())); }
+};
+
+/**
+ @}
+ @name Sink
+ @{
+*/
+
+class Sink
+{
+public:
+ Sink()
+ : _sink(serd_sink_new(this), detail::Deleter<SerdSink>(serd_sink_free))
+ , _is_user(true)
+ {
+ serd_sink_set_base_func(_sink.get(), s_base);
+ serd_sink_set_prefix_func(_sink.get(), s_prefix);
+ serd_sink_set_statement_func(_sink.get(), s_statement);
+ serd_sink_set_end_func(_sink.get(), s_end);
+ }
+
+ explicit Sink(const SerdSink* ptr)
+ : _sink(const_cast<SerdSink*>(ptr), detail::Deleter<SerdSink>(nullptr))
+ , _is_user(false)
+ {
+ }
+
+ virtual ~Sink() = default;
+
+ virtual Status base(const Node& uri)
+ {
+ return _is_user ? Status::Success
+ : Status(serd_sink_set_base(_sink.get(), uri.c_obj()));
+ }
+
+ virtual Status prefix(const Node& name, const Node& uri)
+ {
+ return _is_user ? Status::Success
+ : Status(serd_sink_set_prefix(
+ _sink.get(), name.c_obj(), uri.c_obj()));
+ }
+
+ virtual Status statement(StatementFlags flags, const Statement& statement)
+ {
+ return _is_user ? Status::Success
+ : Status(serd_sink_write_statement(
+ _sink.get(), flags, statement.c_obj()));
+ }
+
+ virtual Status nodes(StatementFlags flags,
+ const Node& subject,
+ const Node& predicate,
+ const Node& object,
+ const Optional<Node>& graph = {})
+ {
+ return _is_user ? Status::Success
+ : Status(serd_sink_write_nodes(_sink.get(),
+ flags,
+ subject.c_obj(),
+ predicate.c_obj(),
+ object.c_obj(),
+ graph.ptr()));
+ }
+
+ virtual Status end(const Node& node)
+ {
+ return _is_user ? Status::Success
+ : Status(serd_sink_end(_sink.get(), node.c_obj()));
+ }
+
+ // operator const SerdSink*() const { return _sink.get(); }
+ // operator SerdSink*() { return _sink.get(); }
+
+ const SerdSink* c_sink() const { return _sink.get(); }
+ SerdSink* c_sink() { return _sink.get(); }
+
+private:
+ static SerdStatus s_base(void* handle, const SerdNode* uri)
+ {
+ return (SerdStatus)((Sink*)handle)->base(Node(uri));
+ }
+
+ static SerdStatus
+ s_prefix(void* handle, const SerdNode* name, const SerdNode* uri)
+ {
+ return (SerdStatus)((Sink*)handle)->prefix(Node(name), Node(uri));
+ }
+
+ static SerdStatus s_statement(void* handle,
+ SerdStatementFlags flags,
+ const SerdStatement* statement)
+ {
+ return (SerdStatus)((Sink*)handle)->statement(flags, statement);
+ }
+
+ static SerdStatus s_end(void* handle, const SerdNode* node)
+ {
+ return (SerdStatus)((Sink*)handle)->end(Node(node));
+ }
+
+ std::unique_ptr<SerdSink, detail::Deleter<SerdSink>> _sink;
+ bool _is_user;
+};
+
+/**
+ @}
+ @name Environment
+ @{
+*/
+
+class Env : public detail::Copyable<SerdEnv, serd_env_copy, serd_env_free>
+{
+public:
+ explicit Env(const Optional<Node>& base = {})
+ : Copyable(serd_env_new(base.ptr()))
+ {
+ }
+
+ /// Return the base URI
+ Node base_uri() const { return Node(serd_env_get_base_uri(c_obj())); }
+
+ /// Set the base URI
+ Status set_base_uri(const Node& uri)
+ {
+ return Status(serd_env_set_base_uri(c_obj(), uri.c_obj()));
+ }
+
+ /// Set a namespace prefix
+ Status set_prefix(const Node& name, const Node& uri)
+ {
+ return Status(serd_env_set_prefix(c_obj(), name.c_obj(), uri.c_obj()));
+ }
+
+ /// Set a namespace prefix
+ Status set_prefix(const StringRef& name, const StringRef& uri)
+ {
+ return Status(serd_env_set_prefix_from_strings(c_obj(), name, uri));
+ }
+
+ /// Qualify `uri` into a CURIE if possible
+ Optional<Node> qualify(const Node& uri) const
+ {
+ return Node(serd_env_qualify(c_obj(), uri.c_obj()));
+ }
+
+ /// Expand `node` into an absolute URI if possible
+ Optional<Node> expand(const Node& node) const
+ {
+ return Node(serd_env_expand(c_obj(), node.c_obj()));
+ }
+
+ /// Send all prefixes to `sink`
+ void send_prefixes(Sink& sink)
+ {
+ serd_env_send_prefixes(c_obj(), sink.c_sink());
+ }
+
+ bool operator==(const Env& env)
+ {
+ return serd_env_equals(c_obj(), env.c_obj());
+ }
+
+ bool operator!=(const Env& env) { return !operator==(env); }
+
+ // operator SerdEnv*() { return c_obj(); }
+};
+
+/**
+ @}
+ @name Reader
+ @{
+*/
+
+class Reader : public detail::Wrapper<SerdReader, serd_reader_free>, public Sink
+{
+public:
+ Reader(World& world, Syntax syntax, const Sink& sink, size_t stack_size)
+ : Wrapper(serd_reader_new(world.c_obj(),
+ SerdSyntax(syntax),
+ sink.c_sink(),
+ stack_size))
+ {
+ }
+
+ void set_strict(bool strict) { serd_reader_set_strict(c_obj(), strict); }
+
+ void add_blank_prefix(const StringRef& prefix)
+ {
+ serd_reader_add_blank_prefix(c_obj(), prefix);
+ }
+
+ SerdStatus start_file(const StringRef& uri, bool bulk = true)
+ {
+ return serd_reader_start_file(c_obj(), uri, bulk);
+ }
+
+ SerdStatus start_stream(SerdReadFunc read_func,
+ SerdStreamErrorFunc error_func,
+ void* stream,
+ const Node& name,
+ size_t page_size)
+ {
+ return serd_reader_start_stream(c_obj(),
+ read_func,
+ error_func,
+ stream,
+ name.c_obj(),
+ page_size);
+ }
+
+ SerdStatus
+ start_string(const StringRef& utf8, const Optional<Node>& name = {})
+ {
+ return serd_reader_start_string(c_obj(), utf8, name.ptr());
+ }
+
+ SerdStatus read_chunk() { return serd_reader_read_chunk(c_obj()); }
+
+ SerdStatus read_document() { return serd_reader_read_document(c_obj()); }
+
+ SerdStatus finish() { return serd_reader_finish(c_obj()); }
+};
+
+/**
+ @}
+ @name Writer
+ @{
+*/
+
+class ByteSink : public detail::Wrapper<SerdByteSink, serd_byte_sink_free>
+{
+public:
+ ByteSink(SerdWriteFunc sink, void* stream, size_t block_size)
+ : Wrapper(serd_byte_sink_new(sink, stream, block_size))
+ {
+ }
+
+ template <typename Traits>
+ ByteSink(std::basic_ostream<char, Traits>& stream)
+ : Wrapper(serd_byte_sink_new(detail::stream_sink<Traits>, &stream, 1))
+ {
+ }
+
+ // operator SerdByteSink*() { return c_obj(); }
+};
+
+class Writer : public detail::Wrapper<SerdWriter, serd_writer_free>, public Sink
+{
+public:
+ Writer(World& world,
+ const Syntax syntax,
+ const StyleFlags style,
+ Env& env,
+ ByteSink& sink)
+ : Wrapper(serd_writer_new(world.c_obj(),
+ SerdSyntax(syntax),
+ style,
+ env.c_obj(),
+ (SerdWriteFunc)serd_byte_sink_write,
+ sink.c_obj()))
+ , Sink(serd_writer_get_sink(c_obj()))
+ {
+ }
+
+ Writer(World& world,
+ const Syntax syntax,
+ const StyleFlags style,
+ Env& env,
+ SerdWriteFunc write_func,
+ void* stream)
+ : Wrapper(serd_writer_new(world.c_obj(),
+ SerdSyntax(syntax),
+ style,
+ env.c_obj(),
+ write_func,
+ stream))
+ , Sink(serd_writer_get_sink(c_obj()))
+ {
+ }
+
+ // FIXME: test
+ template <typename Traits>
+ Writer(World& world,
+ const Syntax syntax,
+ const StyleFlags style,
+ Env& env,
+ std::basic_ostream<char, Traits>& stream)
+ : Wrapper(serd_writer_new(world.c_obj(),
+ SerdSyntax(syntax),
+ style,
+ env.c_obj(),
+ detail::stream_sink<Traits>,
+ &stream))
+ , Sink(serd_writer_get_sink(c_obj()))
+ {
+ }
+
+ Status set_root_uri(const Node& uri)
+ {
+ return Status(serd_writer_set_root_uri(c_obj(), uri.c_obj()));
+ }
+
+ SerdStatus finish() { return serd_writer_finish(c_obj()); }
+};
+
+/**
+ @}
+*/
+
+class Iter : public detail::Copyable<SerdIter, serd_iter_copy, serd_iter_free>
+{
+public:
+ Iter(SerdIter* i) : Copyable(i) {}
+ Iter(const SerdIter* i) : Copyable(i) {}
+
+ Iter& operator++()
+ {
+ serd_iter_next(c_obj());
+ return *this;
+ }
+
+ Statement operator*() const { return Statement(serd_iter_get(c_obj())); }
+
+ bool operator==(const Iter& i)
+ {
+ return serd_iter_equals(c_obj(), i.c_obj());
+ }
+ bool operator!=(const Iter& i)
+ {
+ return !serd_iter_equals(c_obj(), i.c_obj());
+ }
+};
+
+class Range
+ : public detail::Copyable<SerdRange, serd_range_copy, serd_range_free>
+{
+public:
+ Range(SerdRange* r) : Copyable(r) {}
+ Range(const SerdRange* r) : Copyable(r) {}
+
+ Iter begin() const { return Iter(serd_range_begin(c_obj())); }
+ Iter end() const { return Iter(serd_range_end(c_obj())); }
+
+ bool operator==(const Range& i)
+ {
+ return serd_range_equals(c_obj(), i.c_obj());
+ }
+ bool operator!=(const Range& i)
+ {
+ return !serd_range_equals(c_obj(), i.c_obj());
+ }
+};
+
+class Model
+ : public detail::Copyable<SerdModel, serd_model_copy, serd_model_free>
+{
+public:
+ using value_type = Statement;
+ using iterator = Iter;
+ using const_iterator = Iter;
+
+ Model(World& world, SerdIndexOptions indices, SerdModelFlags flags)
+ : Copyable(serd_model_new(world.c_obj(), indices, flags))
+ {
+ }
+
+ Model(World& world, IndexOption indices, SerdModelFlags flags)
+ : Copyable(serd_model_new(world.c_obj(),
+ (SerdIndexOptions)indices,
+ flags))
+ {
+ }
+
+ size_t size() const { return serd_model_size(c_obj()); }
+
+ bool empty() const { return serd_model_empty(c_obj()); }
+
+ void insert(const Statement& s)
+ {
+ serd_model_insert(c_obj(), s.c_obj());
+ }
+
+ void insert(const Node& s,
+ const Node& p,
+ const Node& o,
+ const Optional<Node>& g = {})
+ {
+ serd_model_add(c_obj(), s.c_obj(), p.c_obj(), o.c_obj(), g.ptr());
+ }
+
+ Iter find(const Optional<Node>& s,
+ const Optional<Node>& p,
+ const Optional<Node>& o,
+ const Optional<Node>& g = {})
+ {
+ return Iter(
+ serd_model_find(c_obj(), s.ptr(), p.ptr(), o.ptr(), g.ptr()));
+ }
+
+ Range range(const Optional<Node>& s,
+ const Optional<Node>& p,
+ const Optional<Node>& o,
+ const Optional<Node>& g = {})
+ {
+ return Range(
+ serd_model_range(c_obj(), s.ptr(), p.ptr(), o.ptr(), g.ptr()));
+ }
+
+ Optional<Node> get(const Optional<Node>& s,
+ const Optional<Node>& p,
+ const Optional<Node>& o,
+ const Optional<Node>& g = {})
+ {
+ return Node(
+ serd_model_get(c_obj(), s.ptr(), p.ptr(), o.ptr(), g.ptr()));
+ }
+
+ bool ask(const Optional<Node>& s,
+ const Optional<Node>& p,
+ const Optional<Node>& o,
+ const Optional<Node>& g = {}) const
+ {
+ return serd_model_ask(c_obj(), s.ptr(), p.ptr(), o.ptr(), g.ptr());
+ }
+
+ bool operator==(const Model& model) const
+ {
+ return serd_model_equals(c_obj(), model.c_obj());
+ }
+
+ bool operator!=(const Model& model) const
+ {
+ return !serd_model_equals(c_obj(), model.c_obj());
+ }
+
+ Range all() const { return Range(serd_model_all(c_obj())); }
+
+ iterator begin() const { return iterator(serd_model_begin(c_obj())); }
+
+ iterator end() const { return iterator(serd_model_end(c_obj())); }
+
+ // operator SerdModel*() { return c_obj(); }
+};
+
+class Inserter : public detail::Wrapper<SerdInserter, serd_inserter_free>,
+ public Sink
+{
+public:
+ Inserter(Model& model, Env& env, const Optional<Node>& default_graph = {})
+ : Wrapper(serd_inserter_new(model.c_obj(),
+ env.c_obj(),
+ default_graph.ptr()))
+ , Sink(serd_inserter_get_sink(c_obj()))
+ {
+ }
+};
+
+} // namespace serd
+
+/**
+ @}
+*/
+
+#endif /* SERD_SERD_HPP */