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

Behaviour for defining custom manifest types with extensible logic.

This module enables applications to create specialized manifest types by extending
`DCATR.Manifest` with custom properties, loading logic, and resource extraction.
The behaviour provides a hierarchical callback system with low-level building blocks
and high-level orchestration points.

## Callback Hierarchy

**Low-level callbacks** (per-file/resource operations):
- `init_dataset/1` - Initialize empty dataset with optional seed data
- `graph_name_for_file/2` - Classify files into named graphs during loading
- `load_file/2` - Load and preprocess individual RDF files

**High-level callbacks** (pipeline orchestration):
- `load_dataset/1` - Complete dataset loading pipeline (calls low-level callbacks)
- `load_manifest/2` - Extract service and additional resources from loaded dataset

## Usage

Custom manifest types should `use DCATR.Manifest.Type` and define a Grax schema
extending `DCATR.Manifest`:

    defmodule MyApp.Manifest do
      use DCATR.Manifest.Type
      use Grax.Schema

      schema MyApp.NS.ManifestType < DCATR.Manifest do
        property custom_config: MyApp.NS.customConfig(), type: :string
        link agent: MyApp.NS.agent(), type: MyApp.Agent
      end

      @impl true
      def load_manifest(manifest, opts) do
        with {:ok, manifest} <- super(manifest, opts),
             {:ok, agent} <- load_custom_agent(manifest) do
          Grax.put(manifest, :agent, agent)
        end
      end
    end

The module automatically provides convenience functions for accessing the manifest
hierarchy: `manifest/1`, `service/1`, `repository/1`, `dataset/1` (with `!` variants).

# `schema`

```elixir
@type schema() :: Grax.Schema.t()
```

# `t`

```elixir
@type t() :: module()
```

# `generate`

```elixir
@callback generate(project_dir :: Path.t(), opts :: keyword()) :: :ok | {:error, any()}
```

Generates manifest files from templates for a project.

See `DCATR.Manifest.Generator` for details on the generation process which the
default implementation uses.

# `generator_template`

```elixir
@callback generator_template() :: String.t()
```

Returns the path to the template directory used for manifest generation.

The default implementation points to `DCATR.Manifest.Generator.default_template_dir/0`.
Override this to provide custom manifest templates for your manifest type.

# `graph_name_for_file`

```elixir
@callback graph_name_for_file(file :: String.t(), opts :: keyword()) ::
  RDF.Statement.graph_name() | nil
```

Low-level callback for determining the graph name for a file during loading.

Called by `c:load_file/2` to classify loaded graphs. Override this to add custom
file classification patterns (e.g., based on filename patterns or content inspection).

The default implementation calls `DCATR.Manifest.Loader.graph_name_for_file/2`.

## Example Override

    @impl true
    def graph_name_for_file(file, opts) do
      super(file, opts) ||
        cond do
          file =~ ~r/store\.(test|dev|prod)\.(ttl|nt)$/ -> DCATR.Manifest.Loader.service_manifest_graph_name()
          file =~ ~r/history\./ -> RDF.bnode("history")
          true -> nil
        end
    end

# `init_dataset`

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

Low-level callback for initializing the manifest dataset before loading files.

Called by `c:load_dataset/1` at the start of the loading process, before any files
are read. Override this to inject seed data or perform custom initialization.

The default implementation `DCATR.Manifest.Loader.init_dataset/1` returns an empty dataset.

## Example Override

    @impl true
    def init_dataset(opts) do
      # Add seed triples before file loading
      {:ok, RDF.Dataset.new(...)}
    end

# `load_dataset`

```elixir
@callback load_dataset(opts :: keyword()) :: {:ok, RDF.Dataset.t()} | {:error, any()}
```

High-level callback for orchestrating the complete dataset loading process.

Called by `DCATR.Manifest.Loader.load/2` to load all manifest files and merge them
into a dataset. Override this to customize the loading pipeline or add dataset-level
preprocessing after all files are loaded.

The default implementation calls `DCATR.Manifest.Loader.load_dataset/1`.

## Example Override

    @impl true
    def load_dataset(opts) do
      with {:ok, dataset} <- super(opts) do
        preprocess(dataset)
      end
    end

# `load_file`

```elixir
@callback load_file(file :: String.t(), opts :: keyword()) ::
  {:ok, RDF.Dataset.t() | RDF.Graph.t() | nil} | {:error, any()}
```

Low-level callback for loading and preprocessing a single RDF file.

Called by `c:load_dataset/1` for each file during the loading process. Override this
to add custom preprocessing (e.g., template expansion, validation) before the file
data is merged into the dataset.

Return `{:ok, nil}` to skip a file (e.g., based on custom filtering logic).

The default implementation calls `DCATR.Manifest.Loader.load_file/2`.

## Example Override

    @impl true
    def load_file(file, opts) do
      cond do
        file =~ ~r/\.ignore.nt\./ -> {:ok, nil}  # Skip files matching pattern
        true -> super(file, opts)
      end
    end

# `load_manifest`

```elixir
@callback load_manifest(schema(), opts :: keyword()) :: {:ok, schema()} | {:error, any()}
```

High-level callback for extracting and loading resources from the dataset.

Called by `DCATR.Manifest.Loader.load/2` after the dataset is loaded. Override this
to load additional resources (e.g., agents, policies) alongside the service.

The default implementation calls `DCATR.Manifest.Loader.load_manifest/2`.

## Options

- `:service_id` - Explicit service ID (skips auto-detection)

## Example Override

    @impl true
    def load_manifest(manifest, opts) do
      with {:ok, manifest} <- super(manifest, opts),
           {:ok, agent} <- Agent.load(manifest.dataset, agent_id(), ...) do
        Grax.put(manifest, :agent, agent)
      end
    end

# `service_type`

```elixir
@spec service_type(module()) :: module()
```

Returns the service type module for a given manifest type.

Extracts the `DCATR.Service.Type` from the manifest's `:service` property schema definition.

## Examples

    iex> DCATR.Manifest.Type.service_type(DCATR.Manifest)
    DCATR.Service

---

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