# `DCATR.Manifest.Loader`
[🔗](https://github.com/dcat-r/dcatr-ex/blob/v0.1.0/lib/dcatr/manifest/loader.ex#L1)

Loads the manifest from the configured load path.

This module provides the default implementations of the `DCATR.Manifest.Type` callbacks.
Custom manifest types can override any of these by implementing the corresponding callback.

## Loading Pipeline

Manifest loading proceeds in two phases:

1. **Dataset loading** (`load_dataset/1` → `load_file/2` per file → `graph_name_for_file/2`):
   Resolves files from the load path, reads each RDF file, classifies it into the appropriate
   manifest graph, and merges everything into a single `RDF.Dataset`.

2. **Manifest building** (`load_manifest/2`):
   Finds the service resource in the dataset, auto-injects missing `dcatr:ServiceData` and
   `dcatr:ServiceManifestGraph` structures with default IDs when not explicitly declared,
   then loads the complete service hierarchy (Service → Repository → Dataset).

## Well-Known Blank Node Graph Names

When manifest configuration is stored in separate Turtle files (rather than a single TriG file),
each file is classified and placed into a named graph using well-known blank node labels:

- `_:service-manifest` — for the service manifest graph
- `_:repository-manifest` — for the repository manifest graph

These conventions follow the DCAT-R specification and enable implementations to locate
manifest graphs by convention without prior configuration.

# `base`

```elixir
@spec base(keyword()) :: String.t() | nil
```

Returns the configured base IRI for the manifest graph.

Can be configured with the `:manifest_base` option of the `:dcatr` application:
  
    config :dcatr, manifest_base: "https://example.com/base/"

## Options

- `:base` - Explicitly specify the base IRI (overrides application config)

# `default_service_data_id`

```elixir
@spec default_service_data_id() :: RDF.Statement.graph_name()
```

Returns the configured default ID for the `DCATR.ServiceData` catalog.

This is the auto-injected ID when a `DCATR.ServiceData` resource is not found in the manifest.

By default, this is `_:service-data`, but can be configured with
the `:default_service_data_id` option of the `:dcatr` application and should be a
string with an IRI or a blank node ID starting with `_:`.

    config :dcatr, default_service_data_id: "_:service-data"

# `default_service_manifest_graph_id`

```elixir
@spec default_service_manifest_graph_id() :: RDF.Statement.graph_name()
```

Returns the configured default ID for the `DCATR.ServiceManifestGraph`.

By default, this is `_:service-manifest`, but can be configured with
the `:default_service_manifest_graph_id` option of the `:dcatr` application and should be a
string with an IRI or a blank node ID starting with `_:`.

    config :dcatr, default_service_manifest_graph_id: "_:service-manifest"

# `graph_name_for_file`

```elixir
@spec graph_name_for_file(
  Path.t(),
  keyword()
) :: RDF.Statement.graph_name() | nil
```

Classifies a file and returns its target graph name.

Uses `DCATR.Manifest.LoadPath.classify_file/1` to identify files as
`:service_manifest`, `:repository_manifest`, or unclassified. Returns the
corresponding well-known blank node for manifests, or `nil` for unclassified files.

Default implementation of `c:DCATR.Manifest.Type.graph_name_for_file/2`.

## Example

    iex> DCATR.Manifest.Loader.graph_name_for_file("service.ttl")
    RDF.bnode("service-manifest")

    iex> DCATR.Manifest.Loader.graph_name_for_file("repository.ttl")
    RDF.bnode("repository-manifest")

    iex> DCATR.Manifest.Loader.graph_name_for_file("dataset.ttl")
    RDF.bnode("repository-manifest")

    iex> DCATR.Manifest.Loader.graph_name_for_file("other.ttl")
    nil

# `init_dataset`

```elixir
@spec init_dataset(keyword()) :: {:ok, RDF.Dataset.t()} | {:error, any()}
```

Initializes an empty RDF dataset for manifest loading.

Default implementation of `c:DCATR.Manifest.Type.init_dataset/1`.

# `load`

```elixir
@spec load(
  module(),
  keyword()
) :: {:ok, struct()} | {:error, any()}
```

Loads the manifest from the configured load path.

Orchestrates the complete manifest loading pipeline:

1. Resolves the load path
2. Loads and merges RDF files into a dataset
3. Builds the manifest structure
4. Loads and validates the service

## Options

- `:load_path` - Explicit load path (skips path resolution)
- `:manifest_id` - ID for the manifest struct (defaults to Application config `:dcatr, :manifest_id` or a generated blank node)
- `:service_id` - Explicit service ID (skips auto-detection)
- `:env` - Environment for environment-specific configs
- `:base` - Base IRI for relative URI resolution

# `load_dataset`

```elixir
@spec load_dataset(opts :: keyword()) ::
  {:ok, RDF.Dataset.t()} | {:error, DCATR.Manifest.LoadingError.t()}
```

Resolves files, loads them, and merges them into a dataset.

Performs the following steps:

1. Resolves files from load path (unless `opts[:files]` provided)
2. Calls `init_dataset/1` to create the initial dataset
3. Calls `load_file/2` for each file
4. Merges results via `RDF.Dataset.put_properties/2`

## Options

- `:manifest_type` - The manifest type module (required, set by `Type.__using__/1`)
- `:files` - Explicit list of files to load (skips load path resolution)
- `:load_path` - Load path for file resolution

Default implementation of `c:DCATR.Manifest.Type.load_dataset/1`.

# `load_file`

```elixir
@spec load_file(
  Path.t(),
  keyword()
) :: {:ok, RDF.Dataset.t() | RDF.Graph.t()} | {:error, any()}
```

Reads an RDF file and classifies it into a named graph.

Applies the base IRI from `base/1` if configured, then calls `graph_name_for_file/2`
to determine the target graph name. Files classified as `:service_manifest` or
`:repository_manifest` are placed into their well-known blank node graphs, others
remain in the default graph.

Default implementation of `c:DCATR.Manifest.Type.load_file/2`.

# `load_manifest`

```elixir
@spec load_manifest(
  DCATR.Manifest.Type.schema(),
  keyword()
) :: {:ok, DCATR.Manifest.Type.schema()} | {:error, DCATR.ManifestError.t()}
```

Extracts and loads the service from the dataset, populating the manifest struct.

Performs the following steps:

1. Finds the service resource ID (via `:service_id` option or RDF.type() query)
2. Auto-injects missing `dcatr:ServiceData` and `dcatr:ServiceManifestGraph` structures with default IDs
3. Loads the complete service hierarchy via `load_service/3`

Default implementation of `c:DCATR.Manifest.Type.load_manifest/2`.

# `repository_manifest_graph_name`

```elixir
@spec repository_manifest_graph_name() :: RDF.BlankNode.t()
```

Returns the well-known blank node name for the repository manifest graph.

This is the canonical name used during manifest loading to identify the
repository manifest graph in a dataset: `_:repository-manifest`.

# `service_manifest_graph_name`

```elixir
@spec service_manifest_graph_name() :: RDF.BlankNode.t()
```

Returns the well-known blank node name for the service manifest graph.

This is the canonical name used during manifest loading to identify the
service manifest graph in a dataset: `_:service-manifest`.

---

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