---
title: "Extending krt with plugins"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Extending krt with plugins}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```

```{r setup}
library(krt)
```

`krt` is built on five registries, and each is a public extension point. An
institution can add profiles, validators, resolvers, LLM providers, and
autocomplete sources without forking the package.

```{r}
krt_plugin_api()
```

Check an object against its contract before registering it:

```{r}
validate_plugin_contract("validator", function(x, ctx) list())
```

## A custom validator

A validator is `function(x, ctx)` returning a list of issues. Here is one that
flags datasets lacking a license.

```{r}
license_rule <- function(x, ctx) {
  out <- list()
  for (r in x$resources) {
    if (identical(r$resource_type, "Dataset") && is.null(r$license)) {
      out <- c(out, list(list(message = "Dataset has no license.",
                              resource_id = r$resource_id, field = "license")))
    }
  }
  out
}
register_validator("inst-dataset-license", license_rule,
                   layer = "semantic", severity = "warning")

k <- add_resource(new_krt("Demo"), "Dataset", "D",
                  doi = "10.5281/zenodo.1", new_or_reuse = "new")
"inst-dataset-license" %in% as.data.frame(validate_krt(k))$rule_id
```

## A custom institutional profile

A profile is a directory with `schema.yml` and `mappings.yml`. Register it by
path:

```{r, eval = FALSE}
register_profile(name = "my-institute", path = "path/to/profile/dir")
export_krt(k, file.path(tempdir(), "out.csv"), profile = "my-institute")
```

## A custom resolver or suggest source

```{r, eval = FALSE}
register_resolver("myscheme", function(id, resolve = TRUE, ...) {
  list(input = id, normalized = id, resolved = FALSE, source = "mine")
})

register_suggest_source("mysource", function(query, n) {
  data.frame(label = query, id = "X:1", authority = "mine",
             score = 1, uri = NA_character_, stringsAsFactors = FALSE)
})
```

Registered plugins run inside the same error isolation as the built-ins: a rule
or resolver that throws is caught, so a faulty plugin cannot abort a validation
or resolution run. Plugins are still trusted code. A plugin registered with
`replace = TRUE` deliberately overrides a built-in, and a validator whose
predicate or body errors is downgraded to a warning rather than failing the
run, so a plugin can change validation and output behavior.
