# `RDF.Data.Source`
[🔗](https://github.com/rdf-elixir/rdf-ex/blob/v3.0.1/lib/rdf/data/source.ex#L1)

Protocol for accessing and traversing RDF data structures.

This protocol is the RDF equivalent of Elixir's Enumerable protocol,
providing a minimal set of functions that enable a rich API in the
`RDF.Data` module.

## Structure Types

Implementations declare one of three structure types:

- `:description` - statements about a single subject
- `:graph` - statements in a single named graph
- `:dataset` - statements across multiple graphs

## Fallback Pattern

Many callbacks can return `{:error, __MODULE__}` to signal that the default
algorithm in `RDF.Data` should be used. This allows implementations to
provide optimized versions when possible while falling back to generic
traversal-based implementations.

# `acc`

```elixir
@type acc() :: {:cont, term()} | {:halt, term()} | {:suspend, term()}
```

# `continuation`

```elixir
@type continuation() :: (acc() -&gt; result())
```

# `derive_error`

```elixir
@type derive_error() :: :no_subject
```

# `reducer`

```elixir
@type reducer() :: (RDF.Statement.t(), term() -&gt; acc())
```

# `result`

```elixir
@type result() ::
  {:done, term()} | {:halted, term()} | {:suspended, term(), continuation()}
```

# `structure_type`

```elixir
@type structure_type() :: :description | :graph | :dataset
```

# `t`

```elixir
@type t() :: RDF.Description.t() | RDF.Graph.t() | RDF.Dataset.t() | term()
```

# `add`

```elixir
@spec add(t(), RDF.Statement.t() | [RDF.Statement.t()]) ::
  {:ok, t()} | {:error, module()}
```

Adds statements to the data structure.

Enables efficient statement-based addition without double traversal,
important for operations like `merge/2`.

Returns `{:ok, updated_data}` on success.
Returns `{:error, __MODULE__}` if not supported (falls back to generic implementation).

Cross-type adaptation:
- Quads → Graph: Graph component is removed (becomes triple)
- Triples → Dataset: Go into default graph (nil)

Statements are already coerced when this callback is invoked.

# `delete`

```elixir
@spec delete(t(), RDF.Statement.t() | [RDF.Statement.t()]) ::
  {:ok, t()} | {:error, module()}
```

Deletes statements from the data structure.

Enables efficient statement-based deletion without double traversal.

Returns `{:ok, updated_data}` on success.
Returns `{:error, __MODULE__}` if not supported (falls back to generic implementation).

Cross-type adaptation:
- Quads → Graph: Graph component is removed (becomes triple)
- Triples → Dataset: Go into default graph (nil)

Statements are already coerced when this callback is invoked.

# `derive`

```elixir
@spec derive(t(), structure_type(), keyword()) ::
  {:ok, t()} | {:error, derive_error()}
```

Derives an empty structure of the desired type from a template.

This callback serves two purposes:

1. **System preservation**: Custom implementations can return their own
   corresponding structures, keeping operations within the same system of
   structures.

2. **Metadata inheritance**: When `preserve_metadata: true` (default),
   relevant metadata from the template is copied to the new structure.

## When is this used?

`RDF.Data` functions that need to create new structures call this callback to
determine the appropriate target type. Examples include:

- `RDF.Data.merge/2` - merging data of different structure types
- `RDF.Data.map/2` - transforming statements while preserving structure type

## Options

- `:subject` - Subject for description. When target is `:description`:
  - From Description template: uses template subject, option overrides if provided
  - From Graph/Dataset template: required (no template subject available)
- `:preserve_metadata` - Whether to preserve metadata from template (default: true)
  - Graph → Graph: name, prefixes, base_iri
  - Dataset → Dataset: name

## Error

Return `{:error, :no_subject}` when target is `:description` but no `:subject`
option was provided and the template has no subject to inherit from (i.e.,
template is a graph or dataset).

# `description`

```elixir
@spec description(t(), RDF.Resource.coercible()) :: {:ok, t()} | :error
```

Gets the description of a specific subject.

Returns `{:ok, description}` if the subject exists, `:error` otherwise.

Behavior by structure type:

- `:description`: Returns the Description when its subject matches
- `:graph`: Returns the Description for the given subject
- `:dataset`: Aggregates descriptions for the subject across all graphs

# `description_count`

```elixir
@spec description_count(t()) :: {:ok, non_neg_integer()} | {:error, module()}
```

Counts unique subjects (descriptions).

It should return `{:ok, count}` if you can count the number of subjects
in `data` in a faster way than fully traversing it.

Otherwise, it should return `{:error, __MODULE__}` and a default algorithm
built on top of `reduce/3` that runs in linear time will be used.

# `graph`

```elixir
@spec graph(t(), RDF.IRI.coercible() | nil) :: {:ok, t()} | :error
```

Gets a specific graph from the data structure.

Returns `{:ok, graph}` if the graph exists, `:error` otherwise.

Behavior by structure type:

- `:description`: Returns the description as unnamed graph when `graph_name` is `nil`
- `:graph`: Returns the graph when its name matches
- `:dataset`: Returns the graph for the given name

# `graph_count`

```elixir
@spec graph_count(t()) :: {:ok, non_neg_integer()} | {:error, module()}
```

Counts graphs.

It should return `{:ok, count}` if you can count the number of graphs
in `data` in a faster way than fully traversing it.

Otherwise, it should return `{:error, __MODULE__}` and a default algorithm
built on top of `reduce/3` that runs in linear time will be used.

# `graph_name`

```elixir
@spec graph_name(t()) :: RDF.Resource.t() | nil
```

Returns the graph name when the data represents a single-graph structure.

Behavior by structure type:

- `:description`: Returns `nil` (no graph context)
- `:graph`: Returns the name of the graph (may be `nil` for unnamed graphs)
- `:dataset`: Returns `nil` (multiple graphs)

# `graph_names`

```elixir
@spec graph_names(t()) :: {:ok, [RDF.IRI.t() | nil]} | {:error, module()}
```

Returns all graph names in the data structure.

It should return `{:ok, graph_names}` if you can collect all graph names
in `data` in a faster way than fully traversing it.

Otherwise, it should return `{:error, __MODULE__}` and a default algorithm
built on top of `reduce/3` that runs in linear time will be used.

# `reduce`

```elixir
@spec reduce(t(), acc(), reducer()) :: result()
```

Reduces the RDF data structure into an element.

This is the fundamental operation for traversing RDF data.

# `statement_count`

```elixir
@spec statement_count(t()) :: {:ok, non_neg_integer()} | {:error, module()}
```

Counts statements (triples/quads).

It should return `{:ok, count}` if you can count the number of statements
in `data` in a faster way than fully traversing it.

Otherwise, it should return `{:error, __MODULE__}` and a default algorithm
built on top of `reduce/3` that runs in linear time will be used.

# `structure_type`

```elixir
@spec structure_type(t()) :: structure_type()
```

Returns the structure type of this implementation.

Must return one of:

- `:description` - Single subject structure
- `:graph` - Multiple subjects, single graph
- `:dataset` - Multiple graphs

# `subject`

```elixir
@spec subject(t()) :: RDF.Resource.t() | nil
```

Returns the subject when the data represents a single-subject structure.

Behavior by structure type:

- `:description`: Returns the subject of the Description
- `:graph`: Returns `nil` (multiple subjects)
- `:dataset`: Returns `nil` (multiple subjects)

# `subjects`

```elixir
@spec subjects(t()) :: {:ok, [RDF.Resource.t()]} | {:error, module()}
```

Returns all unique subjects in the data structure.

It should return `{:ok, subjects}` if you can collect all subjects
in `data` in a faster way than fully traversing it.

Otherwise, it should return `{:error, __MODULE__}` and a default algorithm
built on top of `reduce/3` that runs in linear time will be used.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
