---
title: "From R to CFF"
subtitle: "Crosswalk"
description: >
  A comprehensive description of the internal mappings performed by **cffr**.
author: Diego Hernangómez
bibliography: REFERENCES.bib
link-citations: true
toc: true
vignette: >
  %\VignetteIndexEntry{From R to CFF}
  %\VignetteEngine{quarto::html}
  %\VignetteEncoding{UTF-8}
tbl-cap-location: bottom
knitr:
  opts_chunk:
    collapse: true
    comment: "#>"
    code-fold: true
    code-summary: "**Example**"
---

```{r}
#| include: false
library(cffr)
```

This vignette provides an explicit map between the metadata sources used by
**cffr** and each valid key in the [Citation File Format schema version
1.2.0](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#valid-keys).

## Summary {#summary}

This section summarizes the keys that **cffr** can populate and the metadata
source for each one. Details for each key are presented in the next section. This
mapping is based on the [Guide to Citation File Format schema version
1.2.0](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#valid-keys)
[@druskat_citation_2021].

```{r}
#| label: tbl-summary
#| echo: false
#| tbl-cap: "cffr and R packages: Conversion table"
keys <- cff_schema_keys(sorted = TRUE)
origin <- vector(length = length(keys))
origin[keys == "cff-version"] <- "function argument"
origin[keys == "type"] <- "Fixed value: 'software'"
origin[keys == "identifiers"] <- "DESCRIPTION, inst/CITATION and CRAN"
origin[keys == "references"] <- "DESCRIPTION and inst/CITATION"

origin[
  keys %in%
    c(
      "message",
      "title",
      "version",
      "authors",
      "abstract",
      "repository",
      "repository-code",
      "url",
      "date-released",
      "contact",
      "keywords",
      "license",
      "commit"
    )
] <- "DESCRIPTION file"

origin[keys == "doi"] <- "inst/CITATION or CRAN"
origin[keys == "preferred-citation"] <- "inst/CITATION"

origin[origin == "FALSE"] <- "Ignored by cffr"

df <- data.frame(
  key = paste0("<a href='#", keys, "'>", keys, "</a>"),
  source = origin
)

knitr::kable(df, escape = FALSE)
```

## Details

### abstract

This key is extracted from the `"Description"` field of the `DESCRIPTION` file.

```{r}
#| label: abstract
library(cffr)

# Create a `cff` object for YAML.

cff_obj <- cff_create("rmarkdown")

# Get the DESCRIPTION of **rmarkdown** to check.

pkg <- desc::desc(file.path(find.package("rmarkdown"), "DESCRIPTION"))

cat(cff_obj$abstract)

cat(pkg$get("Description"))
```

Back to @tbl-summary.

### authors

This key is coerced from the `"Authors"` or `"Authors@R"` field of the
`DESCRIPTION` file. By default, persons with the role `"aut"` or `"cre"` are
considered, but this can be modified with the `authors_roles` argument.

```{r}
#| label: authors
# An example DESCRIPTION.
path <- system.file("examples/DESCRIPTION_many_persons", package = "cffr")
pkg <- desc::desc(path)

# See the listed persons.
pkg$get_authors()

# Default behavior: use authors and creators (maintainers).
cff_obj <- cff_create(path)
cff_obj$authors

# Use copyright holders and maintainers.
cff_obj_alt <- cff_create(path, authors_roles = c("cre", "cph"))
cff_obj_alt$authors
```

Back to @tbl-summary.

### cff-version

Set this key with the `cff_version` argument of `cff_create()` or `cff_write()`:

```{r}
#| label: cffversion
cff_objv110 <- cff_create("jsonlite", cff_version = "v1.1.0")

cat(cff_objv110$`cff-version`)
```

Back to @tbl-summary.

### commit

This key is extracted from the `"RemoteSha"` field of the `DESCRIPTION` file.
This field is present in packages installed from
[**r-universe**](https://r-universe.dev/search) or with package managers such as
**remotes** and **pak**.

```{r}
#| label: commit
# An example DESCRIPTION.
path <- system.file("examples/DESCRIPTION_r_universe", package = "cffr")
pkg <- desc::desc(path)

# See `RemoteSha`.
pkg$get("RemoteSha")

cff_read(path)
```

Back to @tbl-summary.

### contact

This key is coerced from the `"Authors"` or `"Authors@R"` field of the
`DESCRIPTION` file. Only persons with the role `"cre"` (that is, maintainers)
are considered.

```{r}
#| label: contact
cff_obj <- cff_create("rmarkdown")
pkg <- desc::desc(file.path(find.package("rmarkdown"), "DESCRIPTION"))

cff_obj$contact

pkg$get_author()
```

Back to @tbl-summary.

### date-released

This key is extracted from the `DESCRIPTION` file using this logic:

- `"Date"`, when present.
- Otherwise, `"Date/Publication"`. This field is present in packages built on
  **CRAN** and **Bioconductor**.
- Otherwise, `"Packaged"`, which is present in packages built by
  [**r-universe**](https://r-universe.dev/search).

```{r}
#| label: date-released
# Use an installed package.

cff_obj <- cff_create("rmarkdown")
pkg <- desc::desc(file.path(find.package("rmarkdown"), "DESCRIPTION"))

cat(pkg$get("Date/Publication"))

cat(cff_obj$`date-released`)

# A DESCRIPTION file without a `Date`.
nodate <- system.file("examples/DESCRIPTION_basic", package = "cffr")
tmp <- tempfile("DESCRIPTION")

# Create a temporary file.
file.copy(nodate, tmp)

pkgnodate <- desc::desc(tmp)
cffnodate <- cff_create(tmp)

# No value is returned.
cat(cffnodate$`date-released`)

pkgnodate

# Add a `Date`.

desc::desc_set("Date", "1999-01-01", file = tmp)

cat(cff_create(tmp)$`date-released`)
```

Back to @tbl-summary.

### doi {#doi}

This key is coerced from the `"doi"` field of the
[preferred-citation](#preferred-citation) object. If it is not present, **cffr**
adds the canonical **CRAN** DOI only when the package metadata identifies
**CRAN** as its repository or a repository lookup resolves to the canonical
**CRAN** package URL. Finding the package in another configured repository does
not produce a **CRAN** DOI. For example, the canonical DOI for **cffr** is
<https://doi.org/10.32614/CRAN.package.cffr>.

```{r}
#| label: doi
cff_doi <- cff_create("cffr")

cat(cff_doi$doi)

cat(cff_doi$`preferred-citation`$doi)
```

Back to @tbl-summary.

### identifiers

This key contains the available package identifiers:

- From `DESCRIPTION` metadata, it includes all the URLs not included in
  [url](#url) or [repository-code](#repository-code).
- From the `CITATION` file, it includes all the DOIs not included in [doi](#doi)
  and the identifiers (if any) not included in the `"identifiers"` key of
  [preferred-citation](#preferred-citation).
- If the package is on **CRAN** and it has a `CITATION` file providing a DOI,
  the DOI provided by **CRAN** is added as well.

**cffr** removes identical entries after merging identifiers from these sources.

```{r}
#| label: identifiers
file <- system.file("examples/DESCRIPTION_many_urls", package = "cffr")

pkg <- desc::desc(file)

cat(pkg$get_urls())

cat(cff_create(file)$url)

cat(cff_create(file)$`repository-code`)

cff_create(file)$identifiers
```

Back to @tbl-summary.

### keywords

This key is extracted from the `DESCRIPTION` file. The keywords should appear in
the `DESCRIPTION` as:

```         
...
X-schema.org-keywords: keyword1, keyword2, keyword3
```

The comma-separated values are trimmed and empty or duplicate values are
removed. A single keyword is preserved as a one-item CFF `keywords` array.

```{r}
#| label: keyword
# A DESCRIPTION file without keywords.
nokeywords <- system.file("examples/DESCRIPTION_basic", package = "cffr")
tmp2 <- tempfile("DESCRIPTION")

# Create a temporary file.
file.copy(nokeywords, tmp2)

pkgnokeywords <- desc::desc(tmp2)
cffnokeywords <- cff_create(tmp2)

# No value is returned.
cat(cffnokeywords$keywords)

pkgnokeywords

# Add keywords.

desc::desc_set(
  "X-schema.org-keywords",
  "keyword1, keyword2, keyword3",
  file = tmp2
)

cat(cff_create(tmp2)$keywords)
```

If the package source code is hosted on **GitHub**, **cffr** can also retrieve
repository topics with the [**GitHub** API](https://docs.github.com/en/rest) and
include those topics as keywords. This option is controlled with the
`gh_keywords` argument:

```{r}
#| label: ghkeyword
# Get a `cff` object from **jsonvalidate**.

jsonval <- cff_create("jsonvalidate")

# Keywords are retrieved from the **GitHub** repository.

jsonval

# Check keywords.
jsonval$keywords

# The repository.
jsonval$`repository-code`
```

Back to @tbl-summary.

### license

This key is extracted from the `"License"` field of the `DESCRIPTION` file. All
alternatives recognized by the `cran_to_spdx` mapping are preserved.

```{r}
#| label: license
cff_obj <- cff_create("yaml")

cat(cff_obj$license)

pkg <- desc::desc(file.path(find.package("yaml"), "DESCRIPTION"))

cat(pkg$get("License"))
```

Back to @tbl-summary.

### license-url

This key is not extracted from package metadata. See its description in the
[Guide to CFF schema
v1.2.0](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#license-url).

> - **description**: The URL of the license text under which the software or
>   dataset is licensed (only for non-standard licenses not included in the
>   [SPDX License
>   List](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionslicense-enum)).
>
> - **usage**:
>
>   ``` yaml
>   license-url: "https://obscure-licenses.com?id=1234"
>   ```

Back to @tbl-summary.

### message

This key is generated from the package metadata with this template:

```{r}
#| eval: false
#| code-fold: false
msg <- paste0(
  'To cite package "',
  "NAME_OF_THE_PACKAGE",
  '" in publications use:'
)
```

```{r}
#| label: message
cat(cff_create("jsonlite")$message)
```

Back to @tbl-summary.

### preferred-citation {#preferred-citation}

This key is extracted from `inst/CITATION`. When `cff_create()` reads this file,
all fields from `DESCRIPTION`, including custom fields, are available through
the `meta` object used by the `CITATION` file. If multiple references are
provided, **cffr** selects the first citation as `"preferred-citation"` and uses
the remaining citations as [references](#references). If the file cannot be read
with the package metadata, **cffr** does not retry with metadata from another
package.

```{r}
#| label: preferred-citation
cffobj <- cff_create("rmarkdown")

cffobj$`preferred-citation`

citation("rmarkdown")[1]
```

Back to @tbl-summary.

### references {#references}

This key is extracted from `inst/CITATION` if multiple references are provided.
The first citation becomes the [preferred citation](#preferred-citation) and the
remaining citations become `"references"`.

When `dependencies = TRUE`, **cffr** also adds installed package dependencies by
calling `citation(auto = TRUE)` for each one. It preserves the citation
metadata, including the title, abstract, authors and year, while adapting the
reference to the CFF software type. It adds the dependency scope and version
constraint from the source `DESCRIPTION`, URLs and repository information from
the installed dependency's `DESCRIPTION` and the canonical **CRAN** DOI only
when the dependency is available from **CRAN**. A release date, when available,
determines the year, with the year returned by `citation()` retained otherwise.
The year is omitted only when neither source provides one.

```{r}
#| label: references
cffobj <- cff_create("rmarkdown")

cffobj$references

citation("rmarkdown")[-1]
```

Back to @tbl-summary.

### repository

This key is extracted from the `"Repository"` field of the `DESCRIPTION` file.
This field is usually populated when a package is hosted in a repository such as
**CRAN** or [**r-universe**](https://r-universe.dev/search). For packages
without this field in the `DESCRIPTION` file (typically packages under
development), **cffr** tries to find the package in any of the repositories
specified in `options("repos")`.

[**Bioconductor**](https://bioconductor.org/) packages are identified when the
[`"biocViews"`](https://contributions.bioconductor.org/description.html#biocviews)
field is present in the `DESCRIPTION` file.

If **cffr** detects that the package is available on **CRAN**, it returns the
canonical URL form of the package (that is,
<https://CRAN.R-project.org/package=jsonlite>).

```{r}
#| label: repository
# Use an installed package.

inst <- cff_create("jsonlite")

cat(inst$repository)

# Example file downloaded from r-universe.

runiv <- system.file("examples/DESCRIPTION_r_universe", package = "cffr")
runiv_cff <- cff_create(runiv)

cat(runiv_cff$repository)

desc::desc(runiv)$get("Repository")

# For an in-development package.

norepo <- system.file("examples/DESCRIPTION_basic", package = "cffr")

# No repository is available.
norepo_cff <- cff_create(norepo)

cat(norepo_cff[["repository"]])

# Change the name to a known package on CRAN: ggplot2.

tmp <- tempfile("DESCRIPTION")
file.copy(norepo, tmp)

# Change the package name.
desc::desc_set("Package", "ggplot2", file = tmp)

cat(cff_create(tmp)[["repository"]])
```

Back to @tbl-summary.

### repository-artifact

This key is not extracted from package metadata. See its description in the
[Guide to CFF schema
v1.2.0](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#repository-artifact).

> - **description**: The URL of the work in a build artifact/binary repository
>   (when the work is software).
>
> - **usage**:
>
>   ``` yaml
>   repository-artifact: "https://search.maven.org/artifact/org.corpus-tools/cff-maven-plugin/0.4.0/maven-plugin"
>   ```

Back to @tbl-summary.

### repository-code {#repository-code}

This key is extracted from the `"BugReports"` or `"URL"` fields in the
`DESCRIPTION` file. **cffr** tries to identify the URL of the source on the
following repositories:

- [**GitHub**](https://github.com/).
- [**GitLab**](https://about.gitlab.com/).
- [**R-Forge**](https://r-forge.r-project.org/).
- [**Bitbucket**](https://bitbucket.org/).
- [**Codeberg**](https://codeberg.org/).

```{r}
#| label: repository-code
# Installed package on GitHub.

cat(cff_create("jsonlite")$`repository-code`)

# GitLab.

gitlab <- system.file("examples/DESCRIPTION_gitlab", package = "cffr")

cat(cff_create(gitlab)$`repository-code`)

# Codeberg.
codeberg <- system.file("examples/DESCRIPTION_codeberg", package = "cffr")

cat(cff_create(codeberg)$`repository-code`)
```

Back to @tbl-summary.

### title

This key combines the `"Package"` and `"Title"` fields of the `DESCRIPTION`
file.

```{r}
#| eval: false
#| code-fold: false
title <- paste0("NAME_OF_THE_PACKAGE", ": ", "TITLE_OF_THE_PACKAGE")
```

```{r}
#| label: title
# Use an installed package.

cat(cff_create("testthat")$title)
```

Back to @tbl-summary.

### type

This key has the fixed value `"software"`. The other valid value is `"dataset"`.
See the [Guide to CFF schema
v1.2.0](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#type).

Back to @tbl-summary.

### url {#url}

This key is extracted from the `"BugReports"` or `"URL"` fields in the
`DESCRIPTION` file. It corresponds to the first URL that is different from
[repository-code](#repository-code).

```{r}
#| label: url
# Many URLs.
manyurls <- system.file("examples/DESCRIPTION_many_urls", package = "cffr")

cat(cff_create(manyurls)$url)

# Check the source metadata.

desc::desc(manyurls)
```

Back to @tbl-summary.

### version

This key is extracted from the `"Version"` field in the `DESCRIPTION` file.

```{r}
#| label: version
#| code-fold: false
# Should be (>= 3.0.0).
cat(cff_create("testthat")$version)
```

Back to @tbl-summary.

## References
